Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions src/webgl/loading.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,12 +593,10 @@ function loading(p5, fn){
const vertString = tokens[vertexTokens[tokenInd]];
let vertParts = vertString.split('/');

// TODO: Faces can technically use negative numbers to refer to the
// previous nth vertex. I haven't seen this used in practice, but
// it might be good to implement this in the future.

for (let i = 0; i < vertParts.length; i++) {
vertParts[i] = parseInt(vertParts[i]) - 1;
let index = parseInt(vertParts[i]);
if (index > 0) index -= 1; // OBJ uses 1-based indexing
vertParts[i] = index;
}

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

if (usedVerts[vertString][currentMaterial] === undefined) {
const vertIndex = model.vertices.length;
model.vertices.push(loadedVerts.v[vertParts[0]].copy());
model.uvs.push(loadedVerts.vt[vertParts[1]] ?
loadedVerts.vt[vertParts[1]].slice() : [0, 0]);
model.vertexNormals.push(loadedVerts.vn[vertParts[2]] ?
loadedVerts.vn[vertParts[2]].copy() : new Vector());
model.vertices.push(loadedVerts.v.at(vertParts[0]).copy());
model.uvs.push(loadedVerts.vt.at(vertParts[1]) ?
loadedVerts.vt.at(vertParts[1]).slice() : [0, 0]);
model.vertexNormals.push(loadedVerts.vn.at(vertParts[2]) ?
loadedVerts.vn.at(vertParts[2]).copy() : new Vector());

usedVerts[vertString][currentMaterial] = vertIndex;
face.push(vertIndex);
Expand Down
20 changes: 20 additions & 0 deletions test/unit/assets/cube-negative-indices.obj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Cube using negative vertex indices
# Negative indices count backwards: -1 = last vertex, -2 = second-to-last, etc.

# Vertices
v 0.0 0.0 0.0
v 1.0 0.0 0.0
v 1.0 1.0 0.0
v 0.0 1.0 0.0
v 0.0 0.0 1.0
v 1.0 0.0 1.0
v 1.0 1.0 1.0
v 0.0 1.0 1.0

# Faces using negative indices
f -8 -7 -6 -5
f -4 -3 -2 -1
f -8 -4 -1 -5
f -7 -3 -2 -6
f -5 -6 -2 -1
f -8 -7 -3 -4
18 changes: 18 additions & 0 deletions test/unit/io/loadModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ suite('loadModel', function() {
const inconsistentColorObjFile = '/test/unit/assets/eg1.obj';
const objMtlMissing = '/test/unit/assets/objMtlMissing.obj';
const validSTLfileWithoutExtension = '/test/unit/assets/ascii';
const validCubeFile = '/test/unit/assets/cube.obj';
const negativeIndexCubeFile = '/test/unit/assets/cube-negative-indices.obj';

beforeAll(async () => {
loading(mockP5, mockP5Prototype);
Expand Down Expand Up @@ -115,4 +117,20 @@ suite('loadModel', function() {
const model = await mockP5Prototype.loadModel(validSTLfileWithoutExtension, '.STL');
assert.instanceOf(model, Geometry);
});

test('OBJ with negative vertex indices loads correctly', async function() {
const model = await mockP5Prototype.loadModel(negativeIndexCubeFile);
assert.instanceOf(model, Geometry);
assert.isAbove(model.vertices.length, 0, 'Model should have vertices');
assert.isAbove(model.faces.length, 0, 'Model should have faces');
});

test('OBJ negative indices produce same geometry as positive', async function() {
const positiveModel = await mockP5Prototype.loadModel(validCubeFile);
const negativeModel = await mockP5Prototype.loadModel(negativeIndexCubeFile);
assert.equal(positiveModel.vertices.length, negativeModel.vertices.length,
'Vertex count should match');
assert.equal(positiveModel.faces.length, negativeModel.faces.length,
'Face count should match');
});
});
Loading