From 0fe6d6c02a08725da9d53c7d66eb38bdfd369be2 Mon Sep 17 00:00:00 2001 From: diyaayay Date: Thu, 15 Aug 2024 15:51:33 +0530 Subject: [PATCH] Textures support to mtl files --- src/webgl/loading.js | 66 +++++++++++++++++++++++------ src/webgl/p5.Geometry.js | 25 +++++++++-- src/webgl/p5.RendererGL.Retained.js | 45 +++++++++++--------- 3 files changed, 99 insertions(+), 37 deletions(-) diff --git a/src/webgl/loading.js b/src/webgl/loading.js index d201a50af1..2323e66e97 100755 --- a/src/webgl/loading.js +++ b/src/webgl/loading.js @@ -411,15 +411,6 @@ p5.prototype.loadModel = function(path,options) { } } - - async function fileExists(url) { - try { - const response = await fetch(url, { method: 'HEAD' }); - return response.ok; - } catch (error) { - return false; - } - } if (fileType.match(/\.stl$/i)) { this.httpDo( path, @@ -496,6 +487,15 @@ p5.prototype.loadModel = function(path,options) { return model; }; +async function fileExists(url) { + try { + const response = await fetch(url, { method: 'HEAD' }); + return response.ok; + } catch (error) { + return false; + } +} + function parseMtl(p5,mtlPath){ return new Promise((resolve, reject)=>{ let currentMaterial = null; @@ -533,7 +533,7 @@ function parseMtl(p5,mtlPath){ }else if (tokens[0] === 'map_Kd') { //Texture path - materials[currentMaterial].texturePath = tokens[1]; + materials[currentMaterial].diffuseTexturePath = tokens[1]; } } resolve(materials); @@ -584,8 +584,28 @@ function parseObj(model, lines, materials= {}) { if (tokens.length > 0) { if (tokens[0] === 'usemtl') { - // Switch to a new material currentMaterial = tokens[1]; + if (materials[currentMaterial] && + materials[currentMaterial].diffuseTexturePath) { + if (!model.textures) { + model.textures = {}; + } + if (!model.textures[currentMaterial]) { + model.textures[currentMaterial] = {}; + } + model.hasTextures = true; + model.textures[currentMaterial].diffuseTexture = + model.textures[currentMaterial].diffuseTexture = loadImage( + materials[currentMaterial].diffuseTexturePath, + img => { + //skip + }, + err => { + console.warn(`Error loading textures file: ${tokens[1]}, proceeding without textures:`); + } + ); + model.textures[currentMaterial].faces = []; + } }else if (tokens[0] === 'v' || tokens[0] === 'vn') { // Check if this line describes a vertex or vertex normal. // It will have three numeric parameters. @@ -652,7 +672,15 @@ function parseObj(model, lines, materials= {}) { face[0] !== face[2] && face[1] !== face[2] ) { - model.faces.push(face); + // model.faces.push(face); + if (currentMaterial + && model.textures[currentMaterial] + && model.textures[currentMaterial].diffuseTexture + ) { + model.textures[currentMaterial].faces.push(face); + } else { + model.faces.push(face); + } //same material for all vertices in a particular face if (currentMaterial && materials[currentMaterial] @@ -1125,8 +1153,18 @@ p5.prototype.model = function(model) { model._edgesToVertices(); this._renderer.createBuffers(model.gid, model); } - - this._renderer.drawBuffers(model.gid); + if(model.hasTextures) { + this._renderer.updateIndexBuffer(model.gid, model.faces); + this._renderer.drawBuffers(model.gid); + for (let material of Object.keys(model.textures)) { + const texture = model.textures[material]; + this._renderer.updateIndexBuffer(model.gid, texture.faces); + this._renderer._tex = texture.diffuseTexture; + this._renderer.drawBuffers(model.gid); + } + } else{ + this._renderer.drawBuffers(model.gid); + } } }; diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 15dbcad5ca..8d5b5a43ad 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -664,6 +664,10 @@ p5.Geometry = class Geometry { // One color per vertex representing the stroke color at that vertex this.vertexStrokeColors = []; + // Map storing the textures and faces for each texture + this.textures = {}; + this.hasTextures = false; + // One color per line vertex, generated automatically based on // vertexStrokeColors in _edgesToVertices() this.lineVertexColors = new p5.DataArray(); @@ -925,6 +929,11 @@ p5.Geometry = class Geometry { return this; } + clearTextures() { + this.textures = {}; + return this; + } + /** * The `saveObj()` function exports `p5.Geometry` objects as * 3D models in the Wavefront .obj file format. @@ -1955,12 +1964,21 @@ p5.Geometry = class Geometry { _makeTriangleEdges() { this.edges.length = 0; + const _addEdge = face => { + this.edges.push([face[0], face[1]]); + this.edges.push([face[1], face[2]]); + this.edges.push([face[2], face[0]]); + }; + for (let j = 0; j < this.faces.length; j++) { - this.edges.push([this.faces[j][0], this.faces[j][1]]); - this.edges.push([this.faces[j][1], this.faces[j][2]]); - this.edges.push([this.faces[j][2], this.faces[j][0]]); + _addEdge(this.faces[j]); + } + + for (let material of Object.keys(this.textures)) { + this.textures[material].faces.map(face => _addEdge(face)); } + return this; } @@ -2299,6 +2317,7 @@ p5.Geometry = class Geometry { } return this; } + }; export default p5.Geometry; diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 49f2dd772b..46c70a7aee 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -64,6 +64,30 @@ p5.RendererGL.prototype._freeBuffers = function(gId) { freeBuffers(this.retainedMode.buffers.fill); }; +p5.RendererGL.prototype.updateIndexBuffer = function(gId, faces) { + const gl = this.GL; + const buffers = this.retainedMode.geometry[gId]; + let indexBuffer = buffers.indexBuffer; + + if (!indexBuffer) indexBuffer = buffers.indexBuffer = gl.createBuffer(); + const vals = p5.RendererGL.prototype._flatten(faces); + + // If any face references a vertex with an index greater than the maximum + // un-singed 16 bit integer, then we need to use a Uint32Array instead of a + // Uint16Array + + const hasVertexIndicesOverMaxUInt16 = vals.some(v => v > 65535); + let type = hasVertexIndicesOverMaxUInt16 ? Uint32Array : Uint16Array; + this._bindBuffer(indexBuffer, gl.ELEMENT_ARRAY_BUFFER, vals, type); + buffers.indexBufferType = hasVertexIndicesOverMaxUInt16 + ? gl.UNSIGNED_INT + : gl.UNSIGNED_SHORT; + + // the vertex count is based on the number of faces + buffers.vertexCount = faces.length * 3; +}; + + /** * creates a buffers object that holds the WebGL render buffers * for a geometry. @@ -80,26 +104,7 @@ p5.RendererGL.prototype.createBuffers = function(gId, model) { let indexBuffer = buffers.indexBuffer; if (model.faces.length) { - // allocate space for faces - if (!indexBuffer) indexBuffer = buffers.indexBuffer = gl.createBuffer(); - const vals = p5.RendererGL.prototype._flatten(model.faces); - - // If any face references a vertex with an index greater than the maximum - // un-singed 16 bit integer, then we need to use a Uint32Array instead of a - // Uint16Array - const hasVertexIndicesOverMaxUInt16 = vals.some(v => v > 65535); - let type = hasVertexIndicesOverMaxUInt16 ? Uint32Array : Uint16Array; - this._bindBuffer(indexBuffer, gl.ELEMENT_ARRAY_BUFFER, vals, type); - - // If we're using a Uint32Array for our indexBuffer we will need to pass a - // different enum value to WebGL draw triangles. This happens in - // the _drawElements function. - buffers.indexBufferType = hasVertexIndicesOverMaxUInt16 - ? gl.UNSIGNED_INT - : gl.UNSIGNED_SHORT; - - // the vertex count is based on the number of faces - buffers.vertexCount = model.faces.length * 3; + this.updateIndexBuffer(gId, model.faces); } else { // the index buffer is unused, remove it if (indexBuffer) {