mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2024-11-23 08:34:14 -05:00
Auto-generate normals if they are missing
This commit is contained in:
parent
3708651148
commit
b37a582723
@ -13,7 +13,6 @@ var defaultValue = util.defaultValue;
|
||||
// TODO : add command line flag for y-up to z-up
|
||||
// TODO : support zlib
|
||||
// TODO : support binary export
|
||||
// TODO : generate normals if they don't exist
|
||||
if (process.argv.length < 3 || defined(argv.h) || defined(argv.help)) {
|
||||
console.log('Usage: ./bin/obj2gltf.js [INPUT] [OPTIONS]\n');
|
||||
console.log(' -i, --input Path to obj file');
|
||||
@ -32,7 +31,7 @@ var combine = defaultValue(defaultValue(argv.c, argv.combine), false);
|
||||
var technique = defaultValue(argv.t, argv.technique);
|
||||
|
||||
if (!defined(objFile)) {
|
||||
console.error('-i or --input argument is required. See --help for details.');
|
||||
console.error('-i or --input argument is required. See --help for details.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
152
lib/obj.js
152
lib/obj.js
@ -4,11 +4,12 @@ var path = require('path');
|
||||
var Material = require('./mtl');
|
||||
var util = require('./util');
|
||||
var defined = util.defined;
|
||||
var defaultValue = util.defaultValue;
|
||||
var normalize = util.normalize;
|
||||
var faceNormal = util.faceNormal;
|
||||
|
||||
module.exports = parseObj;
|
||||
|
||||
// Obj regex patterns are from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js)
|
||||
// OBJ regex patterns are from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js)
|
||||
|
||||
function getMaterials(contents, inputPath, done) {
|
||||
var hasMaterialGroups = /^usemtl/gm.test(contents);
|
||||
@ -36,7 +37,9 @@ function parseObj(objFile, inputPath, done) {
|
||||
}
|
||||
|
||||
getMaterials(contents, inputPath, function (materials) {
|
||||
// A vertex in a face is specified by indexes into each of the attribute arrays,
|
||||
var i, length;
|
||||
|
||||
// A vertex is specified by indexes into each of the attribute arrays,
|
||||
// but these indexes may be different. This maps the separate indexes to a single index.
|
||||
var vertexCache = {};
|
||||
var vertexCount = 0;
|
||||
@ -55,7 +58,22 @@ function parseObj(objFile, inputPath, done) {
|
||||
var hasUVs = /^vt/gm.test(contents);
|
||||
|
||||
if (!hasPositions) {
|
||||
console.log('Could not process obj file, no positions.');
|
||||
console.log('Error: could not process OBJ file, no positions.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Auto-generate normals if they are missing from the obj file
|
||||
var generateNormals = !hasNormals;
|
||||
var vertexLocations = [];
|
||||
var vertexNormals;
|
||||
if (generateNormals) {
|
||||
var locations = contents.match(/^v\s/gm).length;
|
||||
vertexNormals = new Array(locations*3);
|
||||
for (i = 0; i < locations; ++i) {
|
||||
vertexNormals[i * 3 + 0] = 0;
|
||||
vertexNormals[i * 3 + 1] = 0;
|
||||
vertexNormals[i * 3 + 2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Map material to index array
|
||||
@ -88,9 +106,6 @@ function parseObj(objFile, inputPath, done) {
|
||||
useDefaultMaterial();
|
||||
}
|
||||
|
||||
// Sometimes the obj will have objects with different vertex formats
|
||||
// e.g. one object has uvs, the other does not.
|
||||
// In this case, use default values.
|
||||
function createVertex(p, u, n) {
|
||||
// Positions
|
||||
var pi = (parseInt(p) - 1) * 3;
|
||||
@ -106,18 +121,28 @@ function parseObj(objFile, inputPath, done) {
|
||||
vertexArray.push(px, py, pz);
|
||||
|
||||
// Normals
|
||||
var ni = (parseInt(n) - 1) * 3;
|
||||
var nx = defaultValue(normals[ni + 0], 0.0);
|
||||
var ny = defaultValue(normals[ni + 1], 0.0);
|
||||
var nz = defaultValue(normals[ni + 2], 0.0);
|
||||
vertexArray.push(nx, ny, nz);
|
||||
if (hasNormals) {
|
||||
var ni = (parseInt(n) - 1) * 3;
|
||||
var nx = normals[ni + 0];
|
||||
var ny = normals[ni + 1];
|
||||
var nz = normals[ni + 2];
|
||||
vertexArray.push(nx, ny, nz);
|
||||
} else {
|
||||
// Normals will be auto-generated later
|
||||
vertexArray.push(0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
// UVs
|
||||
if (hasUVs) {
|
||||
var ui = (parseInt(u) - 1) * 2;
|
||||
var ux = defaultValue(uvs[ui + 0], 0.0);
|
||||
var uy = defaultValue(uvs[ui + 1], 0.0);
|
||||
vertexArray.push(ux, uy);
|
||||
if (defined(u)) {
|
||||
var ui = (parseInt(u) - 1) * 2;
|
||||
var ux = uvs[ui + 0];
|
||||
var uy = uvs[ui + 1];
|
||||
vertexArray.push(ux, uy);
|
||||
} else {
|
||||
// Some objects in the model may not have uvs, fill with 0's for consistency
|
||||
vertexArray.push(0.0, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +152,13 @@ function parseObj(objFile, inputPath, done) {
|
||||
index = vertexCount++;
|
||||
vertexCache[v] = index;
|
||||
createVertex(p, u, n);
|
||||
|
||||
if (generateNormals) {
|
||||
var pi = (parseInt(p) - 1);
|
||||
vertexLocations.push(pi);
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
@ -147,6 +178,36 @@ function parseObj(objFile, inputPath, done) {
|
||||
matIndexArray.push(index3);
|
||||
matIndexArray.push(index4);
|
||||
}
|
||||
|
||||
if (generateNormals) {
|
||||
// Get face normal
|
||||
var i1 = (parseInt(p1) - 1) * 3;
|
||||
var i2 = (parseInt(p2) - 1) * 3;
|
||||
var i3 = (parseInt(p3) - 1) * 3;
|
||||
var normal = faceNormal(
|
||||
positions[i1], positions[i1 + 1], positions[i1 + 2],
|
||||
positions[i2], positions[i2 + 1], positions[i2 + 2],
|
||||
positions[i3], positions[i3 + 1], positions[i3 + 2]
|
||||
);
|
||||
|
||||
// Add face normal to each vertex normal
|
||||
vertexNormals[i1 + 0] += normal[0];
|
||||
vertexNormals[i1 + 1] += normal[1];
|
||||
vertexNormals[i1 + 2] += normal[2];
|
||||
vertexNormals[i2 + 0] += normal[0];
|
||||
vertexNormals[i2 + 1] += normal[1];
|
||||
vertexNormals[i2 + 2] += normal[2];
|
||||
vertexNormals[i3 + 0] += normal[0];
|
||||
vertexNormals[i3 + 1] += normal[1];
|
||||
vertexNormals[i3 + 2] += normal[2];
|
||||
|
||||
if (defined(v4)) {
|
||||
var i4 = (parseInt(p4) - 1) * 3;
|
||||
vertexNormals[i4 + 0] += normal[0];
|
||||
vertexNormals[i4 + 1] += normal[1];
|
||||
vertexNormals[i4 + 2] += normal[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// v float float float
|
||||
@ -171,8 +232,8 @@ function parseObj(objFile, inputPath, done) {
|
||||
var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/;
|
||||
|
||||
var lines = contents.split('\n');
|
||||
var length = lines.length;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
length = lines.length;
|
||||
for (i = 0; i < length; ++i) {
|
||||
var line = lines[i].trim();
|
||||
var result;
|
||||
if ((line.length === 0) || (line.charAt(0) === '#')) {
|
||||
@ -184,18 +245,14 @@ function parseObj(objFile, inputPath, done) {
|
||||
parseFloat(result[3])
|
||||
);
|
||||
} else if ((result = normalPattern.exec(line) ) !== null) {
|
||||
// Normalize
|
||||
var nx = parseFloat(result[1]);
|
||||
var ny = parseFloat(result[2]);
|
||||
var nz = parseFloat(result[3]);
|
||||
var magnitude = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
||||
nx /= magnitude;
|
||||
ny /= magnitude;
|
||||
nz /= magnitude;
|
||||
var normal = normalize(nx, ny, nz);
|
||||
normals.push(
|
||||
nx,
|
||||
ny,
|
||||
nz
|
||||
normal[0],
|
||||
normal[1],
|
||||
normal[2]
|
||||
);
|
||||
} else if ((result = uvPattern.exec(line)) !== null) {
|
||||
uvs.push(
|
||||
@ -205,17 +262,17 @@ function parseObj(objFile, inputPath, done) {
|
||||
|
||||
} else if ((result = facePattern1.exec(line)) !== null) {
|
||||
addFace(
|
||||
result[1], result[1], 0, 0,
|
||||
result[2], result[2], 0, 0,
|
||||
result[3], result[3], 0, 0,
|
||||
result[4], result[4], 0, 0
|
||||
result[1], result[1], undefined, undefined,
|
||||
result[2], result[2], undefined, undefined,
|
||||
result[3], result[3], undefined, undefined,
|
||||
result[4], result[4], undefined, undefined
|
||||
);
|
||||
} else if ((result = facePattern2.exec(line)) !== null) {
|
||||
addFace(
|
||||
result[1], result[2], result[3], 0,
|
||||
result[4], result[5], result[6], 0,
|
||||
result[7], result[8], result[9], 0,
|
||||
result[10], result[11], result[12], 0
|
||||
result[1], result[2], result[3], undefined,
|
||||
result[4], result[5], result[6], undefined,
|
||||
result[7], result[8], result[9], undefined,
|
||||
result[10], result[11], result[12], undefined
|
||||
);
|
||||
} else if ((result = facePattern3.exec(line)) !== null) {
|
||||
addFace(
|
||||
@ -226,10 +283,10 @@ function parseObj(objFile, inputPath, done) {
|
||||
);
|
||||
} else if ((result = facePattern4.exec(line)) !== null) {
|
||||
addFace(
|
||||
result[1], result[2], 0, result[3],
|
||||
result[4], result[5], 0, result[6],
|
||||
result[7], result[8], 0, result[9],
|
||||
result[10], result[11], 0, result[12]
|
||||
result[1], result[2], undefined, result[3],
|
||||
result[4], result[5], undefined, result[6],
|
||||
result[7], result[8], undefined, result[9],
|
||||
result[10], result[11], undefined, result[12]
|
||||
);
|
||||
} else if (/^usemtl /.test(line)) {
|
||||
var materialName = line.substring(7).trim();
|
||||
@ -237,6 +294,25 @@ function parseObj(objFile, inputPath, done) {
|
||||
}
|
||||
}
|
||||
|
||||
if (generateNormals) {
|
||||
length = vertexLocations.length;
|
||||
for (i = 0; i < length; ++i) {
|
||||
// Normalize normal
|
||||
var index = vertexLocations[i] * 3;
|
||||
var normal = normalize(
|
||||
vertexNormals[index + 0],
|
||||
vertexNormals[index + 1],
|
||||
vertexNormals[index + 2]
|
||||
);
|
||||
|
||||
// Set new normal in vertex array
|
||||
var offset = i * (hasUVs ? 8 : 6) + 3;
|
||||
vertexArray[offset + 0] = normal[0];
|
||||
vertexArray[offset + 1] = normal[1];
|
||||
vertexArray[offset + 2] = normal[2];
|
||||
}
|
||||
}
|
||||
|
||||
done({
|
||||
vertexCount: vertexCount,
|
||||
vertexArray: vertexArray,
|
||||
|
27
lib/util.js
27
lib/util.js
@ -1,7 +1,9 @@
|
||||
"use strict";
|
||||
module.exports = {
|
||||
defined : defined,
|
||||
defaultValue : defaultValue
|
||||
defaultValue : defaultValue,
|
||||
normalize : normalize,
|
||||
faceNormal : faceNormal
|
||||
};
|
||||
|
||||
function defined(value) {
|
||||
@ -14,3 +16,26 @@ function defaultValue(a, b) {
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
var result = [0, 0, 0];
|
||||
|
||||
function normalize(x, y, z) {
|
||||
var magnitude = Math.sqrt(x * x + y * y + z * z);
|
||||
result[0] = x / magnitude;
|
||||
result[1] = y / magnitude;
|
||||
result[2] = z / magnitude;
|
||||
return result;
|
||||
}
|
||||
|
||||
function faceNormal(x1, y1, z1, x2, y2, z2, x3, y3, z3) {
|
||||
var e1x = x2 - x1;
|
||||
var e1y = y2 - y1;
|
||||
var e1z = z2 - z1;
|
||||
var e2x = x3 - x1;
|
||||
var e2y = y3 - y1;
|
||||
var e2z = z3 - z1;
|
||||
result[0] = (e1y * e2z) - (e1z * e2y);
|
||||
result[1] = (e1z * e2x) - (e1x * e2z);
|
||||
result[2] = (e1x * e2y) - (e1y * e2x);
|
||||
return result;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user