mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2024-11-23 08:34:14 -05:00
Merge pull request #8 from AnalyticalGraphicsInc/stream
Read obj with streams and handle large buffers
This commit is contained in:
commit
c3e8337ed9
@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="file://$PROJECT_DIR$" libraries="{OBJ2GLTF node_modules}" />
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
@ -8,7 +8,6 @@
|
||||
"immed": true,
|
||||
"latedef": "nofunc",
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"nonbsp": true,
|
||||
"nonew": true,
|
||||
"plusplus": false,
|
||||
|
@ -34,11 +34,12 @@ function convert(objFile, outputPath, options, done) {
|
||||
var gltfFile = path.join(outputPath, modelName + extension);
|
||||
|
||||
parseObj(objFile, inputPath, function(data) {
|
||||
createGltf(data, modelName, function(gltf) {
|
||||
createGltf(data, inputPath, modelName, function(gltf) {
|
||||
var options = {
|
||||
binary : binary,
|
||||
embed : embed,
|
||||
createDirectory : false
|
||||
createDirectory : false,
|
||||
basePath : inputPath
|
||||
};
|
||||
gltfPipeline.processJSONToDisk(gltf, gltfFile, options, function(error) {
|
||||
if (error) {
|
||||
|
26
lib/gltf.js
26
lib/gltf.js
@ -1,4 +1,5 @@
|
||||
"use strict";
|
||||
var fs = require('fs-extra');
|
||||
var path = require('path');
|
||||
var Cesium = require('cesium');
|
||||
var defined = Cesium.defined;
|
||||
@ -7,7 +8,7 @@ var WebGLConstants = Cesium.WebGLConstants;
|
||||
|
||||
module.exports = createGltf;
|
||||
|
||||
function createGltf(data, modelName, done) {
|
||||
function createGltf(data, inputPath, modelName, done) {
|
||||
var vertexCount = data.vertexCount;
|
||||
var vertexArray = data.vertexArray;
|
||||
var positionMin = data.positionMin;
|
||||
@ -171,7 +172,16 @@ function createGltf(data, modelName, done) {
|
||||
|
||||
gltf.samplers[samplerId] = {}; // Use default values
|
||||
|
||||
var bufferUri = 'data:application/octet-stream;base64,' + buffer.toString('base64');
|
||||
var bufferSeparate = false;
|
||||
var bufferUri;
|
||||
if (buffer.length > 201326580) {
|
||||
// toString fails for buffers larger than ~192MB. Instead save the buffer to a .bin file.
|
||||
// Source: https://github.com/nodejs/node/issues/4266
|
||||
bufferSeparate = true;
|
||||
bufferUri = modelName + '.bin';
|
||||
} else {
|
||||
bufferUri = 'data:application/octet-stream;base64,' + buffer.toString('base64');
|
||||
}
|
||||
|
||||
gltf.buffers[bufferId] = {
|
||||
byteLength : bufferByteLength,
|
||||
@ -316,5 +326,15 @@ function createGltf(data, modelName, done) {
|
||||
}
|
||||
}
|
||||
|
||||
done(gltf);
|
||||
if (bufferSeparate) {
|
||||
var bufferPath = path.join(inputPath, modelName + '.bin');
|
||||
fs.writeFile(bufferPath, buffer, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done(gltf);
|
||||
});
|
||||
} else {
|
||||
done(gltf);
|
||||
}
|
||||
}
|
||||
|
12
lib/mtl.js
12
lib/mtl.js
@ -1,5 +1,6 @@
|
||||
"use strict";
|
||||
var fs = require('fs-extra');
|
||||
var defined = require('cesium').defined;
|
||||
|
||||
module.exports = {
|
||||
getDefault : getDefault,
|
||||
@ -13,7 +14,7 @@ function createMaterial() {
|
||||
diffuseColor : undefined, // Kd
|
||||
specularColor : undefined, // Ks
|
||||
specularShininess : undefined, // Ns
|
||||
alpha : undefined, // d
|
||||
alpha : undefined, // d / Tr
|
||||
ambientColorMap : undefined, // map_Ka
|
||||
emissionColorMap : undefined, // map_Ke
|
||||
diffuseColorMap : undefined, // map_Kd
|
||||
@ -31,7 +32,7 @@ function getDefault() {
|
||||
}
|
||||
|
||||
function parse(mtlPath, done) {
|
||||
fs.readFile(mtlPath, 'utf-8', function (error, contents) {
|
||||
fs.readFile(mtlPath, 'utf8', function (error, contents) {
|
||||
if (error) {
|
||||
console.log('Could not read material file at ' + mtlPath + '. Using default material instead.');
|
||||
done({});
|
||||
@ -89,6 +90,9 @@ function parse(mtlPath, done) {
|
||||
} else if (/^d /i.test(line)) {
|
||||
value = line.substring(2).trim();
|
||||
material.alpha = parseFloat(value);
|
||||
} else if (/^Tr /i.test(line)) {
|
||||
value = line.substring(3).trim();
|
||||
material.alpha = parseFloat(value);
|
||||
} else if (/^map_Ka /i.test(line)) {
|
||||
material.ambientColorMap = line.substring(7).trim();
|
||||
} else if (/^map_Ke /i.test(line)) {
|
||||
@ -105,6 +109,10 @@ function parse(mtlPath, done) {
|
||||
material.alphaMap = line.substring(6).trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (defined(material.alpha)) {
|
||||
material.diffuseColor[3] = material.alpha;
|
||||
}
|
||||
|
||||
done(materials);
|
||||
});
|
||||
|
153
lib/obj.js
153
lib/obj.js
@ -2,6 +2,7 @@
|
||||
var async = require('async');
|
||||
var fs = require('fs-extra');
|
||||
var path = require('path');
|
||||
var readline = require('readline');
|
||||
var loadImage = require('./image');
|
||||
var Material = require('./mtl');
|
||||
var Cesium = require('cesium');
|
||||
@ -13,22 +14,12 @@ module.exports = parseObj;
|
||||
// OBJ regex patterns are from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js)
|
||||
|
||||
function parseObj(objFile, inputPath, done) {
|
||||
fs.readFile(objFile, 'utf-8', function (error, contents) {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
getMaterials(contents, inputPath, function (materials) {
|
||||
getImages(inputPath, materials, function (images) {
|
||||
processObj(contents, materials, images, done);
|
||||
});
|
||||
});
|
||||
getObjInfo(objFile, inputPath, function(info, materials, images) {
|
||||
processObj(objFile, info, materials, images, done);
|
||||
});
|
||||
}
|
||||
|
||||
function processObj(contents, materials, images, done) {
|
||||
var i, length;
|
||||
|
||||
function processObj(objFile, info, materials, images, done) {
|
||||
// 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 = {};
|
||||
@ -41,15 +32,10 @@ function processObj(contents, materials, images, done) {
|
||||
var uvs = [];
|
||||
|
||||
var positionMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE];
|
||||
var positionMax = [Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE];
|
||||
var positionMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE];
|
||||
|
||||
var hasPositions = /^v\s/gm.test(contents);
|
||||
var hasNormals = /^vn/gm.test(contents);
|
||||
var hasUVs = /^vt/gm.test(contents);
|
||||
|
||||
if (!hasPositions) {
|
||||
throw new Error('Could not process OBJ file, no positions.');
|
||||
}
|
||||
var hasNormals = info.hasNormals;
|
||||
var hasUVs = info.hasUVs;
|
||||
|
||||
var materialGroups = {}; // Map material to index array
|
||||
var currentIndexArray;
|
||||
@ -80,12 +66,22 @@ function processObj(contents, materials, images, done) {
|
||||
useDefaultMaterial();
|
||||
}
|
||||
|
||||
function getOffset(a, data, components) {
|
||||
var i = parseInt(a);
|
||||
if (i < 0) {
|
||||
// Negative vertex indexes reference the vertices immediately above it
|
||||
return (data.length / components + i) * components;
|
||||
}
|
||||
return (i - 1) * components;
|
||||
}
|
||||
|
||||
function createVertex(p, u, n) {
|
||||
// Positions
|
||||
var pi = (parseInt(p) - 1) * 3;
|
||||
var pi = getOffset(p, positions, 3);
|
||||
var px = positions[pi + 0];
|
||||
var py = positions[pi + 1];
|
||||
var pz = positions[pi + 2];
|
||||
|
||||
positionMin[0] = Math.min(px, positionMin[0]);
|
||||
positionMin[1] = Math.min(py, positionMin[1]);
|
||||
positionMin[2] = Math.min(pz, positionMin[2]);
|
||||
@ -96,7 +92,7 @@ function processObj(contents, materials, images, done) {
|
||||
|
||||
// Normals
|
||||
if (hasNormals) {
|
||||
var ni = (parseInt(n) - 1) * 3;
|
||||
var ni = getOffset(n, normals, 3);
|
||||
var nx = normals[ni + 0];
|
||||
var ny = normals[ni + 1];
|
||||
var nz = normals[ni + 2];
|
||||
@ -106,7 +102,7 @@ function processObj(contents, materials, images, done) {
|
||||
// UVs
|
||||
if (hasUVs) {
|
||||
if (defined(u)) {
|
||||
var ui = (parseInt(u) - 1) * 2;
|
||||
var ui = getOffset(u, uvs, 2);
|
||||
var ux = uvs[ui + 0];
|
||||
var uy = uvs[ui + 1];
|
||||
vertexArray.push(ux, uy);
|
||||
@ -167,13 +163,16 @@ function processObj(contents, materials, images, done) {
|
||||
// f vertex//normal vertex//normal vertex//normal ...
|
||||
var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/;
|
||||
|
||||
var lines = contents.split('\n');
|
||||
length = lines.length;
|
||||
for (i = 0; i < length; ++i) {
|
||||
var line = lines[i].trim();
|
||||
var stream = fs.createReadStream(objFile, 'utf8');
|
||||
var reader = readline.createInterface({
|
||||
input : stream
|
||||
});
|
||||
|
||||
reader.on('line', function(line) {
|
||||
line = line.trim();
|
||||
var result;
|
||||
if ((line.length === 0) || (line.charAt(0) === '#')) {
|
||||
continue;
|
||||
// Don't process empty lines or comments
|
||||
} else if ((result = vertexPattern.exec(line)) !== null) {
|
||||
positions.push(
|
||||
parseFloat(result[1]),
|
||||
@ -191,7 +190,6 @@ function processObj(contents, materials, images, done) {
|
||||
parseFloat(result[1]),
|
||||
parseFloat(result[2])
|
||||
);
|
||||
|
||||
} else if ((result = facePattern1.exec(line)) !== null) {
|
||||
addFace(
|
||||
result[1], result[1], undefined, undefined,
|
||||
@ -224,18 +222,20 @@ function processObj(contents, materials, images, done) {
|
||||
var materialName = line.substring(7).trim();
|
||||
useMaterial(materialName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
done({
|
||||
vertexCount : vertexCount,
|
||||
vertexArray : vertexArray,
|
||||
positionMin : positionMin,
|
||||
positionMax : positionMax,
|
||||
hasUVs : hasUVs,
|
||||
hasNormals : hasNormals,
|
||||
materialGroups : materialGroups,
|
||||
materials : materials,
|
||||
images : images
|
||||
reader.on('close', function() {
|
||||
done({
|
||||
vertexCount : vertexCount,
|
||||
vertexArray : vertexArray,
|
||||
positionMin : positionMin,
|
||||
positionMax : positionMax,
|
||||
hasUVs : hasUVs,
|
||||
hasNormals : hasNormals,
|
||||
materialGroups : materialGroups,
|
||||
materials : materials,
|
||||
images : images
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -284,25 +284,70 @@ function getImages(inputPath, materials, done) {
|
||||
});
|
||||
}
|
||||
|
||||
function getMaterials(contents, inputPath, done) {
|
||||
var hasMaterialGroups = /^usemtl/gm.test(contents);
|
||||
function getMaterials(mtlPath, hasMaterialGroups, done) {
|
||||
if (!hasMaterialGroups) {
|
||||
done({});
|
||||
return;
|
||||
}
|
||||
|
||||
var mtllibMatches = contents.match(/^mtllib.*/gm);
|
||||
if (mtllibMatches === null) {
|
||||
done({});
|
||||
} else {
|
||||
var mtlFile = mtllibMatches[0].substring(7).trim();
|
||||
var mtlPath = mtlFile;
|
||||
if (!path.isAbsolute(mtlPath)) {
|
||||
mtlPath = path.join(inputPath, mtlFile);
|
||||
}
|
||||
|
||||
Material.parse(mtlPath, function (materials) {
|
||||
if (defined(mtlPath)) {
|
||||
Material.parse(mtlPath, function(materials) {
|
||||
done(materials);
|
||||
});
|
||||
} else {
|
||||
done({});
|
||||
}
|
||||
}
|
||||
|
||||
function getObjInfo(objFile, inputPath, done) {
|
||||
var mtlPath;
|
||||
var hasMaterialGroups = false;
|
||||
var hasPositions = false;
|
||||
var hasNormals = false;
|
||||
var hasUVs = false;
|
||||
|
||||
var stream = fs.createReadStream(objFile, 'utf8');
|
||||
var reader = readline.createInterface({
|
||||
input : stream
|
||||
});
|
||||
|
||||
reader.on('line', function(line) {
|
||||
if (!defined(mtlPath)) {
|
||||
var mtllibMatches = line.match(/^mtllib.*/gm);
|
||||
if (mtllibMatches !== null) {
|
||||
var mtlFile = mtllibMatches[0].substring(7).trim();
|
||||
mtlPath = mtlFile;
|
||||
if (!path.isAbsolute(mtlPath)) {
|
||||
mtlPath = path.join(inputPath, mtlFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasMaterialGroups) {
|
||||
hasMaterialGroups = /^usemtl/gm.test(line);
|
||||
}
|
||||
if (!hasPositions) {
|
||||
hasPositions = /^v\s/gm.test(line);
|
||||
}
|
||||
if (!hasNormals) {
|
||||
hasNormals = /^vn/gm.test(line);
|
||||
}
|
||||
if (!hasUVs) {
|
||||
hasUVs = /^vt/gm.test(line);
|
||||
}
|
||||
});
|
||||
|
||||
reader.on('close', function() {
|
||||
if (!hasPositions) {
|
||||
throw new Error('Could not process OBJ file, no positions.');
|
||||
}
|
||||
var info = {
|
||||
hasNormals : hasNormals,
|
||||
hasUVs : hasUVs
|
||||
};
|
||||
getMaterials(mtlPath, hasMaterialGroups, function(materials) {
|
||||
getImages(inputPath, materials, function(images) {
|
||||
done(info, materials, images);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user