Fix negative indices

This commit is contained in:
Sean Lilley 2019-04-07 17:17:59 -04:00
parent e82f0453f0
commit 2fb661c8cf
4 changed files with 69 additions and 55 deletions

View File

@ -58,9 +58,9 @@ const facePattern = /(-?\d+)\/?(-?\d*)\/?(-?\d*)/g;
*/
function loadObj(objPath, options) {
// Global store of vertex attributes listed in the obj file
let positions = new ArrayStorage(ComponentDatatype.FLOAT);
let normals = new ArrayStorage(ComponentDatatype.FLOAT);
let uvs = new ArrayStorage(ComponentDatatype.FLOAT);
let globalPositions = new ArrayStorage(ComponentDatatype.FLOAT);
let globalNormals = new ArrayStorage(ComponentDatatype.FLOAT);
let globalUvs = new ArrayStorage(ComponentDatatype.FLOAT);
// The current node, mesh, and primitive
let node;
@ -144,8 +144,8 @@ function loadObj(objPath, options) {
}
function faceAndPrimitiveMatch(uvs, normals, primitive) {
const faceHasUvs = uvs[0].length > 0;
const faceHasNormals = normals[0].length > 0;
const faceHasUvs = defined(uvs[0]);
const faceHasNormals = defined(normals[0]);
const primitiveHasUvs = primitive.uvs.length > 0;
const primitiveHasNormals = primitive.normals.length > 0;
return primitiveHasUvs === faceHasUvs && primitiveHasNormals === faceHasNormals;
@ -160,43 +160,58 @@ function loadObj(objPath, options) {
}
}
function getOffset(a, attributeData, components) {
const i = parseInt(a);
function getIndexFromStart(index, attributeData, components) {
const i = parseInt(index);
if (i < 0) {
// Negative vertex indexes reference the vertices immediately above it
return (attributeData.length / components + i) * components;
return (attributeData.length / components + i);
}
return i - 1;
}
function correctAttributeIndices(attributeIndices, attributeData, components) {
const length = attributeIndices.length;
for (let i = 0; i < length; ++i) {
if (attributeIndices[i].length === 0) {
attributeIndices[i] = undefined;
} else {
attributeIndices[i] = getIndexFromStart(attributeIndices[i], attributeData, components);
}
}
}
function correctVertices(vertices, positions, uvs, normals) {
const length = vertices.length;
for (let i = 0; i < length; ++i) {
vertices[i] = defaultValue(positions[i], '') + '/' + defaultValue(uvs[i], '') + '/' + defaultValue(normals[i], '');
}
return (i - 1) * components;
}
function createVertex(p, u, n) {
// Positions
if (p.length > 0) {
const pi = getOffset(p, positions, 3);
const px = positions.get(pi + 0);
const py = positions.get(pi + 1);
const pz = positions.get(pi + 2);
if (defined(p)) {
const px = globalPositions.get(p * 3);
const py = globalPositions.get(p * 3 + 1);
const pz = globalPositions.get(p * 3 + 2);
primitive.positions.push(px);
primitive.positions.push(py);
primitive.positions.push(pz);
}
// Normals
if (n.length > 0) {
const ni = getOffset(n, normals, 3);
const nx = normals.get(ni + 0);
const ny = normals.get(ni + 1);
const nz = normals.get(ni + 2);
if (defined(n)) {
const nx = globalNormals.get(n * 3);
const ny = globalNormals.get(n * 3 + 1);
const nz = globalNormals.get(n * 3 + 2);
primitive.normals.push(nx);
primitive.normals.push(ny);
primitive.normals.push(nz);
}
// UVs
if (u.length > 0) {
const ui = getOffset(u, uvs, 2);
const ux = uvs.get(ui + 0);
const uy = uvs.get(ui + 1);
if (defined(u)) {
const ux = globalUvs.get(u * 2);
const uy = globalUvs.get(u * 2 + 1);
primitive.uvs.push(ux);
primitive.uvs.push(uy);
}
@ -220,18 +235,16 @@ function loadObj(objPath, options) {
}
function getPosition(index, result) {
const pi = getOffset(index, positions, 3);
const px = positions.get(pi + 0);
const py = positions.get(pi + 1);
const pz = positions.get(pi + 2);
const px = globalPositions.get(index * 3);
const py = globalPositions.get(index * 3 + 1);
const pz = globalPositions.get(index * 3 + 2);
return Cartesian3.fromElements(px, py, pz, result);
}
function getNormal(index, result) {
const ni = getOffset(index, normals, 3);
const nx = normals.get(ni + 0);
const ny = normals.get(ni + 1);
const nz = normals.get(ni + 2);
const nx = globalNormals.get(index * 3);
const ny = globalNormals.get(index * 3 + 1);
const nz = globalNormals.get(index * 3 + 2);
return Cartesian3.fromElements(nx, ny, nz, result);
}
@ -249,7 +262,7 @@ function loadObj(objPath, options) {
const scratchPoints = [];
function checkWindingCorrect(positionIndex1, positionIndex2, positionIndex3, normalIndex) {
if (normalIndex.length === 0) {
if (!defined(normalIndex)) {
// If no face normal, we have to assume the winding is correct.
return true;
}
@ -278,7 +291,12 @@ function loadObj(objPath, options) {
}
function addFace(vertices, positions, uvs, normals) {
checkPrimitive(uvs, normals);
correctAttributeIndices(positions, globalPositions, 3);
correctAttributeIndices(normals, globalNormals, 3);
correctAttributeIndices(uvs, globalUvs, 2);
correctVertices(vertices, positions, uvs, normals);
checkPrimitive(uvs, faceNormals);
if (vertices.length === 3) {
const isWindingCorrect = checkWindingCorrect(positions[0], positions[1], positions[2], normals[0]);
@ -336,9 +354,9 @@ function loadObj(objPath, options) {
const mtllibLine = line.substring(7).trim();
mtlPaths = mtlPaths.concat(getMtlPaths(mtllibLine));
} else if ((result = vertexPattern.exec(line)) !== null) {
positions.push(parseFloat(result[1]));
positions.push(parseFloat(result[2]));
positions.push(parseFloat(result[3]));
globalPositions.push(parseFloat(result[1]));
globalPositions.push(parseFloat(result[2]));
globalPositions.push(parseFloat(result[3]));
} else if ((result = normalPattern.exec(line) ) !== null) {
const normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal);
if (Cartesian3.equals(normal, Cartesian3.ZERO)) {
@ -346,12 +364,12 @@ function loadObj(objPath, options) {
} else {
Cartesian3.normalize(normal, normal);
}
normals.push(normal.x);
normals.push(normal.y);
normals.push(normal.z);
globalNormals.push(normal.x);
globalNormals.push(normal.y);
globalNormals.push(normal.z);
} else if ((result = uvPattern.exec(line)) !== null) {
uvs.push(parseFloat(result[1]));
uvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image
globalUvs.push(parseFloat(result[1]));
globalUvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image
} else { // face line or invalid line
// Because face lines can contain n vertices, we use a line buffer in case the face data spans multiple lines.
// If there's a line continuation don't create face yet
@ -386,13 +404,10 @@ function loadObj(objPath, options) {
// Parse the obj file
return readLines(objPath, parseLine)
.then(function() {
// Add hasNormals to options object for loadMtl
options.hasNormals = normals.length > 0;
// Unload resources
positions = undefined;
normals = undefined;
uvs = undefined;
globalPositions = undefined;
globalNormals = undefined;
globalUvs = undefined;
// Load materials and textures
return finishLoading(nodes, mtlPaths, objPath, defined(activeMaterial), options);

View File

@ -9,12 +9,12 @@ v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
usemtl Material
s off
f -8 -7 -5 -6
f -7 -6 -4 -5
f -3 -2 -6 -7
f -5 -1 -3 -7
v 1.000000 1.000000 -1.000000
f -1 -5 -7 -3
f -6 -5 -1 -2
f -2 -1 -3 -4
f -4 -3 -7 -8
f -6 -2 -4 -8
f -1 -5 -7 -3

View File

@ -13,8 +13,8 @@ v 1.000000 1.000000 -1.000000
usemtl Material
s off
f 1 2 4 3
f 3 4 8 7
f 7 8 6 5
f 5 6 2 1
f 3 7 5 1
f 8 4 2 6
f 3 4 8 7
f 7 8 6 5

View File

@ -60,7 +60,6 @@ describe('loadMtl', () => {
options = clone(obj2gltf.defaults);
options.overridingTextures = {};
options.logger = () => {};
options.hasNormals = true;
});
it('loads mtl', async () => {