Skip to content

Commit 3d1f4d6

Browse files
committed
Add support for negative vertex indices in OBJ loader for 2.x
1 parent ad26404 commit 3d1f4d6

3 files changed

Lines changed: 46 additions & 10 deletions

File tree

src/webgl/loading.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -593,12 +593,10 @@ function loading(p5, fn){
593593
const vertString = tokens[vertexTokens[tokenInd]];
594594
let vertParts = vertString.split('/');
595595

596-
// TODO: Faces can technically use negative numbers to refer to the
597-
// previous nth vertex. I haven't seen this used in practice, but
598-
// it might be good to implement this in the future.
599-
600596
for (let i = 0; i < vertParts.length; i++) {
601-
vertParts[i] = parseInt(vertParts[i]) - 1;
597+
let index = parseInt(vertParts[i]);
598+
if (index > 0) index -= 1; // OBJ uses 1-based indexing
599+
vertParts[i] = index;
602600
}
603601

604602
if (!usedVerts[vertString]) {
@@ -607,11 +605,11 @@ function loading(p5, fn){
607605

608606
if (usedVerts[vertString][currentMaterial] === undefined) {
609607
const vertIndex = model.vertices.length;
610-
model.vertices.push(loadedVerts.v[vertParts[0]].copy());
611-
model.uvs.push(loadedVerts.vt[vertParts[1]] ?
612-
loadedVerts.vt[vertParts[1]].slice() : [0, 0]);
613-
model.vertexNormals.push(loadedVerts.vn[vertParts[2]] ?
614-
loadedVerts.vn[vertParts[2]].copy() : new Vector());
608+
model.vertices.push(loadedVerts.v.at(vertParts[0]).copy());
609+
model.uvs.push(loadedVerts.vt.at(vertParts[1]) ?
610+
loadedVerts.vt.at(vertParts[1]).slice() : [0, 0]);
611+
model.vertexNormals.push(loadedVerts.vn.at(vertParts[2]) ?
612+
loadedVerts.vn.at(vertParts[2]).copy() : new Vector());
615613

616614
usedVerts[vertString][currentMaterial] = vertIndex;
617615
face.push(vertIndex);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Cube using negative vertex indices
2+
# Negative indices count backwards: -1 = last vertex, -2 = second-to-last, etc.
3+
4+
# Vertices
5+
v 0.0 0.0 0.0
6+
v 1.0 0.0 0.0
7+
v 1.0 1.0 0.0
8+
v 0.0 1.0 0.0
9+
v 0.0 0.0 1.0
10+
v 1.0 0.0 1.0
11+
v 1.0 1.0 1.0
12+
v 0.0 1.0 1.0
13+
14+
# Faces using negative indices
15+
f -8 -7 -6 -5
16+
f -4 -3 -2 -1
17+
f -8 -4 -1 -5
18+
f -7 -3 -2 -6
19+
f -5 -6 -2 -1
20+
f -8 -7 -3 -4

test/unit/io/loadModel.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ suite('loadModel', function() {
1010
const inconsistentColorObjFile = '/test/unit/assets/eg1.obj';
1111
const objMtlMissing = '/test/unit/assets/objMtlMissing.obj';
1212
const validSTLfileWithoutExtension = '/test/unit/assets/ascii';
13+
const validCubeFile = '/test/unit/assets/cube.obj';
14+
const negativeIndexCubeFile = '/test/unit/assets/cube-negative-indices.obj';
1315

1416
beforeAll(async () => {
1517
loading(mockP5, mockP5Prototype);
@@ -115,4 +117,20 @@ suite('loadModel', function() {
115117
const model = await mockP5Prototype.loadModel(validSTLfileWithoutExtension, '.STL');
116118
assert.instanceOf(model, Geometry);
117119
});
120+
121+
test('OBJ with negative vertex indices loads correctly', async function() {
122+
const model = await mockP5Prototype.loadModel(negativeIndexCubeFile);
123+
assert.instanceOf(model, Geometry);
124+
assert.isAbove(model.vertices.length, 0, 'Model should have vertices');
125+
assert.isAbove(model.faces.length, 0, 'Model should have faces');
126+
});
127+
128+
test('OBJ negative indices produce same geometry as positive', async function() {
129+
const positiveModel = await mockP5Prototype.loadModel(validCubeFile);
130+
const negativeModel = await mockP5Prototype.loadModel(negativeIndexCubeFile);
131+
assert.equal(positiveModel.vertices.length, negativeModel.vertices.length,
132+
'Vertex count should match');
133+
assert.equal(positiveModel.faces.length, negativeModel.faces.length,
134+
'Face count should match');
135+
});
118136
});

0 commit comments

Comments
 (0)