From 8f69da949766f081bb73d087ea0069a559bdaf66 Mon Sep 17 00:00:00 2001 From: nityam Date: Sun, 9 Aug 2026 22:01:27 +0530 Subject: [PATCH 1/4] test no-split, per-part normals and localised uvs on mtl loading --- test/unit/io/loadModel.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/unit/io/loadModel.js b/test/unit/io/loadModel.js index 0c2efa9cf7..f95200c424 100644 --- a/test/unit/io/loadModel.js +++ b/test/unit/io/loadModel.js @@ -131,6 +131,42 @@ suite('loadModel', function () { } }); + test('a single-material OBJ stays one part', async function () { + // eg1.obj has one real material, so it is not split + const model = await mockP5Prototype.loadModel(inconsistentColorObjFile); + assert.equal(model.parts.length, 1); + assert.equal(model.parts[0], model, 'the geometry is its own single part'); + }); + + test('parts get computed normals when the OBJ has none', async function () { + // textured.obj has no vn lines, so normals are computed before the split + const model = await mockP5Prototype.loadModel( + '/test/unit/assets/textured.obj' + ); + assert.equal(model.parts.length, 2); + for (const part of model.parts) { + assert.equal( + part.vertexNormals.length, + part.vertices.length, + 'each part has one computed normal per vertex' + ); + } + }); + + test('each part carries its own localised uvs', async function () { + const model = await mockP5Prototype.loadModel( + '/test/unit/assets/textured.obj' + ); + assert.equal(model.parts.length, 2); + for (const part of model.parts) { + assert.equal( + part.uvs.length, + part.vertices.length, + 'each part has one uv per localised vertex' + ); + } + }); + test('mixed material coloring loads model with sentinel colors for uncolored vertices', async function () { const model = await mockP5Prototype.loadModel(inconsistentColorObjFile); assert.instanceOf(model, Geometry); From b7e33627be6f57da591e14f1da50694073b8b874 Mon Sep 17 00:00:00 2001 From: nityam Date: Sun, 9 Aug 2026 22:33:09 +0530 Subject: [PATCH 2/4] add visual test for multi-material obj loading --- test/unit/visual/cases/webgl.js | 17 +++++++++++++++++ .../000.png | Bin 0 -> 3841 bytes .../metadata.json | 3 +++ 3 files changed, 20 insertions(+) create mode 100644 test/unit/visual/screenshots/WebGL/3DModel/multi-material OBJ renders each material part/000.png create mode 100644 test/unit/visual/screenshots/WebGL/3DModel/multi-material OBJ renders each material part/metadata.json diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index ed2af5fdc6..e0bf2af2be 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -411,6 +411,23 @@ visualSuite('WebGL', function () { screenshot(); } ); + visualTest( + 'multi-material OBJ renders each material part', + async function (p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + // textured.obj has two materials: a cat texture and a plain colour + const model = await new Promise(resolve => + p5.loadModel('test/unit/assets/textured.obj', resolve) + ); + model.normalize(); + p5.background(255); + p5.rotateX(0.4); + p5.rotateY(0.4); + p5.noStroke(); + p5.model(model); + screenshot(); + } + ); }); visualSuite('vertexProperty', function () { diff --git a/test/unit/visual/screenshots/WebGL/3DModel/multi-material OBJ renders each material part/000.png b/test/unit/visual/screenshots/WebGL/3DModel/multi-material OBJ renders each material part/000.png new file mode 100644 index 0000000000000000000000000000000000000000..f6bbe85a95bbddf8d39c688f4b63c1467b626d88 GIT binary patch literal 3841 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 DF?d~^ literal 0 HcmV?d00001 diff --git a/test/unit/visual/screenshots/WebGL/3DModel/multi-material OBJ renders each material part/metadata.json b/test/unit/visual/screenshots/WebGL/3DModel/multi-material OBJ renders each material part/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/3DModel/multi-material OBJ renders each material part/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file From e9b5b7842aa491bdd6d11814b35ec514b1e7f094 Mon Sep 17 00:00:00 2001 From: nityam Date: Sun, 9 Aug 2026 22:55:17 +0530 Subject: [PATCH 3/4] add error-mode tests: texture warn+fallback, malformed and kd-only mtl --- test/unit/assets/missing_texture.mtl | 6 +++++ test/unit/assets/missing_texture.obj | 13 +++++++++++ test/unit/io/loadModel.js | 22 ++++++++++++++++++ test/unit/io/parseMtl.js | 15 ++++++++++++ test/unit/visual/cases/webgl.js | 17 ++++++++++++++ .../000.png | Bin 0 -> 470 bytes .../metadata.json | 3 +++ 7 files changed, 76 insertions(+) create mode 100644 test/unit/assets/missing_texture.mtl create mode 100644 test/unit/assets/missing_texture.obj create mode 100644 test/unit/visual/screenshots/WebGL/3DModel/a slice with a missing texture falls back to its colour/000.png create mode 100644 test/unit/visual/screenshots/WebGL/3DModel/a slice with a missing texture falls back to its colour/metadata.json diff --git a/test/unit/assets/missing_texture.mtl b/test/unit/assets/missing_texture.mtl new file mode 100644 index 0000000000..d0a94874de --- /dev/null +++ b/test/unit/assets/missing_texture.mtl @@ -0,0 +1,6 @@ +newmtl m0 +Kd 1 0 0 +map_Kd does_not_exist.png + +newmtl m1 +Kd 0 0 1 diff --git a/test/unit/assets/missing_texture.obj b/test/unit/assets/missing_texture.obj new file mode 100644 index 0000000000..6e70707c38 --- /dev/null +++ b/test/unit/assets/missing_texture.obj @@ -0,0 +1,13 @@ +mtllib missing_texture.mtl +v 0 0 0 +v 1 0 0 +v 0 1 0 +v 1 1 0 +vt 0 0 +vt 1 0 +vt 0 1 +vt 1 1 +usemtl m0 +f 1/1 2/2 3/3 +usemtl m1 +f 2/2 4/4 3/3 diff --git a/test/unit/io/loadModel.js b/test/unit/io/loadModel.js index f95200c424..3a8c47eb7c 100644 --- a/test/unit/io/loadModel.js +++ b/test/unit/io/loadModel.js @@ -1,6 +1,7 @@ import { mockP5, mockP5Prototype, httpMock } from '../../js/mocks'; import loading from '../../../src/webgl/loading'; import { Geometry } from '../../../src/webgl/p5.Geometry'; +import { vi } from 'vitest'; suite('loadModel', function () { const invalidFile = '404file'; @@ -167,6 +168,27 @@ suite('loadModel', function () { } }); + test('a failed texture load warns instead of failing silently', async function () { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + mockP5Prototype.loadImage = async () => { + throw new Error('Not Found'); + }; + try { + const model = await mockP5Prototype.loadModel( + '/test/unit/assets/textured.obj' + ); + // the model still loads with both material parts + assert.equal(model.parts.length, 2); + // the failed texture is skipped, so no part carries it + assert.ok(model.parts.every(p => p.partState.texture == null)); + // and the failure is surfaced, not silent + assert.ok(warnSpy.mock.calls.length > 0, 'a warning is emitted'); + } finally { + delete mockP5Prototype.loadImage; + warnSpy.mockRestore(); + } + }); + test('mixed material coloring loads model with sentinel colors for uncolored vertices', async function () { const model = await mockP5Prototype.loadModel(inconsistentColorObjFile); assert.instanceOf(model, Geometry); diff --git a/test/unit/io/parseMtl.js b/test/unit/io/parseMtl.js index c01ef7947a..7e094e764b 100644 --- a/test/unit/io/parseMtl.js +++ b/test/unit/io/parseMtl.js @@ -48,6 +48,21 @@ suite('parseMtlData', function () { expect(materials.a.diffuseColor).toEqual([0, 0, 0]); expect(Object.keys(materials)).toEqual(['a']); }); + + test('a Kd-only material parses without error', function () { + // the common case: a material that only sets a diffuse colour + const materials = parseMtlData('newmtl plain\nKd 0.2 0.4 0.6'); + expect(materials.plain.diffuseColor).toEqual([0.2, 0.4, 0.6]); + expect(materials.plain.texturePath).toBeUndefined(); + }); + + test('a malformed mtl does not throw', function () { + // an unknown token, a token missing its value, and a blank line + const mtl = 'newmtl weird\nKd 1 0 0\nfoo bar baz\nNs\n\nsomething_else'; + expect(() => parseMtlData(mtl)).not.toThrow(); + // the valid tokens around the junk are still read + expect(parseMtlData(mtl).weird.diffuseColor).toEqual([1, 0, 0]); + }); }); suite('mtlToPartState', function () { diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index e0bf2af2be..62d068a748 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -428,6 +428,23 @@ visualSuite('WebGL', function () { screenshot(); } ); + visualTest( + 'a slice with a missing texture falls back to its colour', + async function (p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + // missing_texture.obj: m0 has a 404 map_Kd (falls back to red), m1 blue + const model = await new Promise(resolve => + p5.loadModel('test/unit/assets/missing_texture.obj', resolve) + ); + model.normalize(); + p5.background(255); + p5.rotateX(0.4); + p5.rotateY(0.4); + p5.noStroke(); + p5.model(model); + screenshot(); + } + ); }); visualSuite('vertexProperty', function () { diff --git a/test/unit/visual/screenshots/WebGL/3DModel/a slice with a missing texture falls back to its colour/000.png b/test/unit/visual/screenshots/WebGL/3DModel/a slice with a missing texture falls back to its colour/000.png new file mode 100644 index 0000000000000000000000000000000000000000..211724fc49da40da3b8ebe575ca5d5167f2e1379 GIT binary patch literal 470 zcmV;{0V)28P)Xqr8O6?Kk*oa6pG?9k&&wTzRw9Y#`RBzI1O)RipFa8G2J#7k#|4B| zdY1$U$~1&wUJf;f&XrjRtwa*JL+5@o2?4RzN=uA6NC^UzDG05!H3<-u2?#?Cf|Wy+ zlsbe~+KT|L3IS2tN*q3|nvnFltKVxP*h~dd8_^xYFf$Le+6h3cZi$cr2rzN;nrjyb zh!!cNFQPf52Le!2NH0V)2y?(Zv~DK=v8pFR3LwD5O?s?VAs~9DkiMX55KRQ2DiCeN zZU}S0JZQBOfLO(eL Date: Sun, 9 Aug 2026 23:04:59 +0530 Subject: [PATCH 4/4] add 12-material and single-material fixtures with tests --- test/unit/assets/multi_material_12.mtl | 36 +++++++++++ test/unit/assets/multi_material_12.obj | 61 ++++++++++++++++++ test/unit/assets/single_material.mtl | 3 + test/unit/assets/single_material.obj | 22 +++++++ test/unit/io/loadModel.js | 10 +++ test/unit/visual/cases/webgl.js | 30 +++++++++ .../000.png | Bin 0 -> 364 bytes .../metadata.json | 3 + .../000.png | Bin 0 -> 239 bytes .../metadata.json | 3 + 10 files changed, 168 insertions(+) create mode 100644 test/unit/assets/multi_material_12.mtl create mode 100644 test/unit/assets/multi_material_12.obj create mode 100644 test/unit/assets/single_material.mtl create mode 100644 test/unit/assets/single_material.obj create mode 100644 test/unit/visual/screenshots/WebGL/3DModel/a 12-material OBJ renders every material/000.png create mode 100644 test/unit/visual/screenshots/WebGL/3DModel/a 12-material OBJ renders every material/metadata.json create mode 100644 test/unit/visual/screenshots/WebGL/3DModel/a single-material OBJ renders through the part path/000.png create mode 100644 test/unit/visual/screenshots/WebGL/3DModel/a single-material OBJ renders through the part path/metadata.json diff --git a/test/unit/assets/multi_material_12.mtl b/test/unit/assets/multi_material_12.mtl new file mode 100644 index 0000000000..eb05a5f7ab --- /dev/null +++ b/test/unit/assets/multi_material_12.mtl @@ -0,0 +1,36 @@ +newmtl m00 +Kd 0.000 0.876 0.844 + +newmtl m01 +Kd 0.500 0.518 0.999 + +newmtl m02 +Kd 0.866 0.021 0.886 + +newmtl m03 +Kd 1.000 0.482 0.536 + +newmtl m04 +Kd 0.866 0.855 0.042 + +newmtl m05 +Kd 0.500 1.000 0.463 + +newmtl m06 +Kd 0.000 0.876 0.844 + +newmtl m07 +Kd 0.500 0.518 0.999 + +newmtl m08 +Kd 0.866 0.021 0.886 + +newmtl m09 +Kd 1.000 0.482 0.536 + +newmtl m10 +Kd 0.866 0.855 0.042 + +newmtl m11 +Kd 0.500 1.000 0.463 + diff --git a/test/unit/assets/multi_material_12.obj b/test/unit/assets/multi_material_12.obj new file mode 100644 index 0000000000..3fa749b5ab --- /dev/null +++ b/test/unit/assets/multi_material_12.obj @@ -0,0 +1,61 @@ +mtllib multi_material_12.mtl +v -1.5 -1 0 +v -0.7 -1 0 +v -1.5 -0.19999999999999996 0 +v -0.5 -1 0 +v 0.30000000000000004 -1 0 +v -0.5 -0.19999999999999996 0 +v 0.5 -1 0 +v 1.3 -1 0 +v 0.5 -0.19999999999999996 0 +v 1.5 -1 0 +v 2.3 -1 0 +v 1.5 -0.19999999999999996 0 +v -1.5 0 0 +v -0.7 0 0 +v -1.5 0.8 0 +v -0.5 0 0 +v 0.30000000000000004 0 0 +v -0.5 0.8 0 +v 0.5 0 0 +v 1.3 0 0 +v 0.5 0.8 0 +v 1.5 0 0 +v 2.3 0 0 +v 1.5 0.8 0 +v -1.5 1 0 +v -0.7 1 0 +v -1.5 1.8 0 +v -0.5 1 0 +v 0.30000000000000004 1 0 +v -0.5 1.8 0 +v 0.5 1 0 +v 1.3 1 0 +v 0.5 1.8 0 +v 1.5 1 0 +v 2.3 1 0 +v 1.5 1.8 0 +usemtl m00 +f 1 2 3 +usemtl m01 +f 4 5 6 +usemtl m02 +f 7 8 9 +usemtl m03 +f 10 11 12 +usemtl m04 +f 13 14 15 +usemtl m05 +f 16 17 18 +usemtl m06 +f 19 20 21 +usemtl m07 +f 22 23 24 +usemtl m08 +f 25 26 27 +usemtl m09 +f 28 29 30 +usemtl m10 +f 31 32 33 +usemtl m11 +f 34 35 36 diff --git a/test/unit/assets/single_material.mtl b/test/unit/assets/single_material.mtl new file mode 100644 index 0000000000..74b68eb8b4 --- /dev/null +++ b/test/unit/assets/single_material.mtl @@ -0,0 +1,3 @@ +newmtl only +Kd 0.2 0.7 0.9 +Ns 40 diff --git a/test/unit/assets/single_material.obj b/test/unit/assets/single_material.obj new file mode 100644 index 0000000000..f6397bf84e --- /dev/null +++ b/test/unit/assets/single_material.obj @@ -0,0 +1,22 @@ +mtllib single_material.mtl +v -0.5 -0.5 -0.5 +v 0.5 -0.5 -0.5 +v 0.5 0.5 -0.5 +v -0.5 0.5 -0.5 +v -0.5 -0.5 0.5 +v 0.5 -0.5 0.5 +v 0.5 0.5 0.5 +v -0.5 0.5 0.5 +usemtl only +f 1 2 3 +f 1 3 4 +f 5 6 7 +f 5 7 8 +f 1 5 8 +f 1 8 4 +f 2 6 7 +f 2 7 3 +f 4 8 7 +f 4 7 3 +f 1 5 6 +f 1 6 2 diff --git a/test/unit/io/loadModel.js b/test/unit/io/loadModel.js index 3a8c47eb7c..87b40d9aab 100644 --- a/test/unit/io/loadModel.js +++ b/test/unit/io/loadModel.js @@ -139,6 +139,16 @@ suite('loadModel', function () { assert.equal(model.parts[0], model, 'the geometry is its own single part'); }); + test('a 12-material OBJ splits into 12 parts', async function () { + const model = await mockP5Prototype.loadModel( + '/test/unit/assets/multi_material_12.obj' + ); + assert.equal(model.parts.length, 12); + // every face still lands in exactly one part + const totalFaces = model.parts.reduce((s, p) => s + p.faces.length, 0); + assert.equal(totalFaces, model.faces.length); + }); + test('parts get computed normals when the OBJ has none', async function () { // textured.obj has no vn lines, so normals are computed before the split const model = await mockP5Prototype.loadModel( diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index 62d068a748..798efa6e52 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -445,6 +445,36 @@ visualSuite('WebGL', function () { screenshot(); } ); + visualTest( + 'a 12-material OBJ renders every material', + async function (p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + const model = await new Promise(resolve => + p5.loadModel('test/unit/assets/multi_material_12.obj', resolve) + ); + model.normalize(); + p5.background(255); + p5.noStroke(); + p5.model(model); + screenshot(); + } + ); + visualTest( + 'a single-material OBJ renders through the part path', + async function (p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + const model = await new Promise(resolve => + p5.loadModel('test/unit/assets/single_material.obj', resolve) + ); + model.normalize(); + p5.background(255); + p5.rotateX(0.4); + p5.rotateY(0.4); + p5.noStroke(); + p5.model(model); + screenshot(); + } + ); }); visualSuite('vertexProperty', function () { diff --git a/test/unit/visual/screenshots/WebGL/3DModel/a 12-material OBJ renders every material/000.png b/test/unit/visual/screenshots/WebGL/3DModel/a 12-material OBJ renders every material/000.png new file mode 100644 index 0000000000000000000000000000000000000000..bb5fc67e44101b727568db48726641271559407c GIT binary patch literal 364 zcmV-y0h9iTP)IU@4x=Ygma`c?)F%%A)&|YzF4Nuz}ak=9|nyQoRaU zl@zi@2--nbXE};h?sALv?Vb;r+^=Ra8?C(@WGx%ISS5M$4+0XPqJ-+v!Z`A&R&xSKN>fLPwhRMCu4y$S;Bt(uAOcv>~V< zT?iUTAA$lBgMf#`BA_8D2v|re0unL=0SBoebZ<$4xohPhR15*hML<9-5bh8wge$}n z;R-p;7y5m(K>X|$nh|2rMavp*00030|3M65p8x;=21!IgR09Cm3@fblh!NHR0000< KMNUMnLSTXq3Wx{* literal 0 HcmV?d00001 diff --git a/test/unit/visual/screenshots/WebGL/3DModel/a 12-material OBJ renders every material/metadata.json b/test/unit/visual/screenshots/WebGL/3DModel/a 12-material OBJ renders every material/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/3DModel/a 12-material OBJ renders every material/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/3DModel/a single-material OBJ renders through the part path/000.png b/test/unit/visual/screenshots/WebGL/3DModel/a single-material OBJ renders through the part path/000.png new file mode 100644 index 0000000000000000000000000000000000000000..49140f8bf954c7fb6d18b58b0a3593817da548e9 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETC7v#hAr*{oCLQE#a1daA8nvmo zdJ|u+!hGJg6NXLm52#r@wp#!D=EK9X}xv`GY|@@xbF>xjlP;&iwyBe)6?QAcGC$ aUS