From 82122e1cec0369aa4d5689ac613955fe47dcb6ad Mon Sep 17 00:00:00 2001 From: Nixxx19 Date: Sun, 22 Mar 2026 07:20:12 +0530 Subject: [PATCH] fix: gracefully handle mixed-material OBJ models instead of crashing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, `parseObj()` used `hasColoredVertices === hasColorlessVertices` to detect inconsistent vertex coloring, but this condition is true in two very different cases: 1. Both flags `false` → model has no faces at all (should never crash) 2. Both flags `true` → model has some faces with material colors and some without (the genuine "mixed" case) Real-world OBJ exports from Blender, Sketchfab, and other tools commonly produce files where only some mesh groups have an explicit MTL material assignment. The previous `throw` caused a hard crash for any such model, which is a significant barrier for beginners trying to use pre-made 3D assets in p5.js. This commit changes the check to `hasColoredVertices && hasColorlessVertices` (only the genuinely inconsistent case) and replaces the thrown error with a `console.warn` + `model.vertexColors = []` reset, so the model still loads and renders using the default fill color. The existing test that expected a throw is updated to assert that the model loads successfully with an empty vertexColors array. --- src/webgl/loading.js | 16 +++++++++++++--- test/unit/io/loadModel.js | 17 ++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/webgl/loading.js b/src/webgl/loading.js index 23fe61b123..cb0d04878f 100755 --- a/src/webgl/loading.js +++ b/src/webgl/loading.js @@ -652,9 +652,19 @@ function loading(p5, fn){ if (model.vertexNormals.length === 0) { model.computeNormals(); } - if (hasColoredVertices === hasColorlessVertices) { - // If both are true or both are false, throw an error because the model is inconsistent - throw new Error('Model coloring is inconsistent. Either all vertices should have colors or none should.'); + if (hasColoredVertices && hasColorlessVertices) { + // Mixed model: some faces have a material diffuse color assigned, others do not. + // This is common in real-world OBJ exports (e.g. Blender, Sketchfab) where only + // some mesh groups have an explicit MTL material. Rather than crashing the sketch, + // we degrade gracefully: strip the partial vertex colors so the model renders + // with the default fill color instead of corrupted per-vertex coloring. + console.warn( + 'p5.js: This OBJ model has mixed material coloring — some faces have a ' + + 'material diffuse color and some do not. Vertex colors will not be applied. ' + + 'Consider assigning a material to every face group in your 3D software, ' + + 'or use a model where all faces share the same material.' + ); + model.vertexColors = []; } return model; diff --git a/test/unit/io/loadModel.js b/test/unit/io/loadModel.js index f88a5807cc..e010390586 100644 --- a/test/unit/io/loadModel.js +++ b/test/unit/io/loadModel.js @@ -79,11 +79,18 @@ suite('loadModel', function() { assert.deepEqual(model.vertexColors, expectedColors); }); - test('inconsistent vertex coloring throws error', async function() { - // Attempt to load the model and catch the error - await expect(mockP5Prototype.loadModel(inconsistentColorObjFile)) - .rejects - .toThrow('Model coloring is inconsistent. Either all vertices should have colors or none should.'); + test('mixed material coloring loads model with empty vertexColors instead of crashing', async function() { + // eg1.obj has some faces without a material and some with one. + // Real-world exports from Blender/Sketchfab frequently produce this structure. + // The loader should degrade gracefully rather than throwing, so beginners' + // sketches don't crash when loading common 3D assets. + const model = await mockP5Prototype.loadModel(inconsistentColorObjFile); + assert.instanceOf(model, Geometry); + assert.equal( + model.vertexColors.length, + 0, + 'Mixed-material model should have no vertex colors (graceful degradation)' + ); }); test('missing MTL file shows OBJ model without vertexColors', async function() {