Merge pull request #191 from AnalyticalGraphicsInc/fix-negative-indices

Fix negative indices
This commit is contained in:
Tom Fili 2019-06-26 14:03:08 -04:00 committed by GitHub
commit a81d88bdcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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) { function loadObj(objPath, options) {
// Global store of vertex attributes listed in the obj file // Global store of vertex attributes listed in the obj file
let positions = new ArrayStorage(ComponentDatatype.FLOAT); let globalPositions = new ArrayStorage(ComponentDatatype.FLOAT);
let normals = new ArrayStorage(ComponentDatatype.FLOAT); let globalNormals = new ArrayStorage(ComponentDatatype.FLOAT);
let uvs = new ArrayStorage(ComponentDatatype.FLOAT); let globalUvs = new ArrayStorage(ComponentDatatype.FLOAT);
// The current node, mesh, and primitive // The current node, mesh, and primitive
let node; let node;
@ -144,8 +144,8 @@ function loadObj(objPath, options) {
} }
function faceAndPrimitiveMatch(uvs, normals, primitive) { function faceAndPrimitiveMatch(uvs, normals, primitive) {
const faceHasUvs = uvs[0].length > 0; const faceHasUvs = defined(uvs[0]);
const faceHasNormals = normals[0].length > 0; const faceHasNormals = defined(normals[0]);
const primitiveHasUvs = primitive.uvs.length > 0; const primitiveHasUvs = primitive.uvs.length > 0;
const primitiveHasNormals = primitive.normals.length > 0; const primitiveHasNormals = primitive.normals.length > 0;
return primitiveHasUvs === faceHasUvs && primitiveHasNormals === faceHasNormals; return primitiveHasUvs === faceHasUvs && primitiveHasNormals === faceHasNormals;
@ -160,43 +160,58 @@ function loadObj(objPath, options) {
} }
} }
function getOffset(a, attributeData, components) { function getIndexFromStart(index, attributeData, components) {
const i = parseInt(a); const i = parseInt(index);
if (i < 0) { if (i < 0) {
// Negative vertex indexes reference the vertices immediately above it // 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) { function createVertex(p, u, n) {
// Positions // Positions
if (p.length > 0) { if (defined(p)) {
const pi = getOffset(p, positions, 3); const px = globalPositions.get(p * 3);
const px = positions.get(pi + 0); const py = globalPositions.get(p * 3 + 1);
const py = positions.get(pi + 1); const pz = globalPositions.get(p * 3 + 2);
const pz = positions.get(pi + 2);
primitive.positions.push(px); primitive.positions.push(px);
primitive.positions.push(py); primitive.positions.push(py);
primitive.positions.push(pz); primitive.positions.push(pz);
} }
// Normals // Normals
if (n.length > 0) { if (defined(n)) {
const ni = getOffset(n, normals, 3); const nx = globalNormals.get(n * 3);
const nx = normals.get(ni + 0); const ny = globalNormals.get(n * 3 + 1);
const ny = normals.get(ni + 1); const nz = globalNormals.get(n * 3 + 2);
const nz = normals.get(ni + 2);
primitive.normals.push(nx); primitive.normals.push(nx);
primitive.normals.push(ny); primitive.normals.push(ny);
primitive.normals.push(nz); primitive.normals.push(nz);
} }
// UVs // UVs
if (u.length > 0) { if (defined(u)) {
const ui = getOffset(u, uvs, 2); const ux = globalUvs.get(u * 2);
const ux = uvs.get(ui + 0); const uy = globalUvs.get(u * 2 + 1);
const uy = uvs.get(ui + 1);
primitive.uvs.push(ux); primitive.uvs.push(ux);
primitive.uvs.push(uy); primitive.uvs.push(uy);
} }
@ -220,18 +235,16 @@ function loadObj(objPath, options) {
} }
function getPosition(index, result) { function getPosition(index, result) {
const pi = getOffset(index, positions, 3); const px = globalPositions.get(index * 3);
const px = positions.get(pi + 0); const py = globalPositions.get(index * 3 + 1);
const py = positions.get(pi + 1); const pz = globalPositions.get(index * 3 + 2);
const pz = positions.get(pi + 2);
return Cartesian3.fromElements(px, py, pz, result); return Cartesian3.fromElements(px, py, pz, result);
} }
function getNormal(index, result) { function getNormal(index, result) {
const ni = getOffset(index, normals, 3); const nx = globalNormals.get(index * 3);
const nx = normals.get(ni + 0); const ny = globalNormals.get(index * 3 + 1);
const ny = normals.get(ni + 1); const nz = globalNormals.get(index * 3 + 2);
const nz = normals.get(ni + 2);
return Cartesian3.fromElements(nx, ny, nz, result); return Cartesian3.fromElements(nx, ny, nz, result);
} }
@ -249,7 +262,7 @@ function loadObj(objPath, options) {
const scratchPoints = []; const scratchPoints = [];
function checkWindingCorrect(positionIndex1, positionIndex2, positionIndex3, normalIndex) { 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. // If no face normal, we have to assume the winding is correct.
return true; return true;
} }
@ -278,7 +291,12 @@ function loadObj(objPath, options) {
} }
function addFace(vertices, positions, uvs, normals) { 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) { if (vertices.length === 3) {
const isWindingCorrect = checkWindingCorrect(positions[0], positions[1], positions[2], normals[0]); 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(); const mtllibLine = line.substring(7).trim();
mtlPaths = mtlPaths.concat(getMtlPaths(mtllibLine)); mtlPaths = mtlPaths.concat(getMtlPaths(mtllibLine));
} else if ((result = vertexPattern.exec(line)) !== null) { } else if ((result = vertexPattern.exec(line)) !== null) {
positions.push(parseFloat(result[1])); globalPositions.push(parseFloat(result[1]));
positions.push(parseFloat(result[2])); globalPositions.push(parseFloat(result[2]));
positions.push(parseFloat(result[3])); globalPositions.push(parseFloat(result[3]));
} else if ((result = normalPattern.exec(line) ) !== null) { } else if ((result = normalPattern.exec(line) ) !== null) {
const normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal); const normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal);
if (Cartesian3.equals(normal, Cartesian3.ZERO)) { if (Cartesian3.equals(normal, Cartesian3.ZERO)) {
@ -346,12 +364,12 @@ function loadObj(objPath, options) {
} else { } else {
Cartesian3.normalize(normal, normal); Cartesian3.normalize(normal, normal);
} }
normals.push(normal.x); globalNormals.push(normal.x);
normals.push(normal.y); globalNormals.push(normal.y);
normals.push(normal.z); globalNormals.push(normal.z);
} else if ((result = uvPattern.exec(line)) !== null) { } else if ((result = uvPattern.exec(line)) !== null) {
uvs.push(parseFloat(result[1])); globalUvs.push(parseFloat(result[1]));
uvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image globalUvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image
} else { // face line or invalid line } 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. // 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 // If there's a line continuation don't create face yet
@ -386,13 +404,10 @@ function loadObj(objPath, options) {
// Parse the obj file // Parse the obj file
return readLines(objPath, parseLine) return readLines(objPath, parseLine)
.then(function() { .then(function() {
// Add hasNormals to options object for loadMtl
options.hasNormals = normals.length > 0;
// Unload resources // Unload resources
positions = undefined; globalPositions = undefined;
normals = undefined; globalNormals = undefined;
uvs = undefined; globalUvs = undefined;
// Load materials and textures // Load materials and textures
return finishLoading(nodes, mtlPaths, objPath, defined(activeMaterial), options); 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
v 1.000000 -1.000000 -1.000000 v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
usemtl Material usemtl Material
s off 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 -6 -5 -1 -2
f -2 -1 -3 -4 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 usemtl Material
s off s off
f 1 2 4 3 f 1 2 4 3
f 3 4 8 7
f 7 8 6 5
f 5 6 2 1 f 5 6 2 1
f 3 7 5 1 f 3 7 5 1
f 8 4 2 6 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 = clone(obj2gltf.defaults);
options.overridingTextures = {}; options.overridingTextures = {};
options.logger = () => {}; options.logger = () => {};
options.hasNormals = true;
}); });
it('loads mtl', async () => { it('loads mtl', async () => {