mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2024-11-23 16:44:05 -05:00
Merge pull request #191 from AnalyticalGraphicsInc/fix-negative-indices
Fix negative indices
This commit is contained in:
commit
a81d88bdcb
109
lib/loadObj.js
109
lib/loadObj.js
@ -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);
|
||||||
|
@ -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
|
|
||||||
|
@ -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
|
||||||
|
@ -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 () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user