mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2024-11-27 02:24:04 -05:00
Merge branch 'master' into gltf-2.0
This commit is contained in:
commit
dc4ba7fc6a
@ -5,6 +5,7 @@
|
||||
/specs
|
||||
/test
|
||||
/output
|
||||
.editorconfig
|
||||
.jshintrc
|
||||
.npmignore
|
||||
.travis.yml
|
||||
|
10
CHANGES.md
10
CHANGES.md
@ -1,6 +1,16 @@
|
||||
Change Log
|
||||
==========
|
||||
|
||||
### 1.1.1 2017-04-25
|
||||
|
||||
* Fixed `CHANGES.md` formatting.
|
||||
|
||||
### 1.1.0 2017-04-25
|
||||
|
||||
* Added ability to convert the up-axis of the obj model. [#68](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/68)
|
||||
* Fixed issues with an extra .bin file being saved when using `--separate`. [#62](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/62)
|
||||
* Fixed issue where an ambient color of `[1, 1, 1]` overly brightens the converted model. [#70](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/70)
|
||||
|
||||
### 1.0.0 2017-04-13
|
||||
|
||||
* Breaking changes
|
||||
|
@ -45,6 +45,8 @@ Using obj2gltf as a command-line tool:
|
||||
|`--bypassPipeline`|Bypass the gltf-pipeline for debugging purposes. This option overrides many of the options above.|No, default `false`|
|
||||
|`--checkTransparency`|Do a more exhaustive check for texture transparency by looking at the alpha channel of each pixel. By default textures are considered to be opaque.|No, default `false`|
|
||||
|`--secure`|Prevent the converter from reading image or mtl files outside of the input obj directory.|No, default `false`|
|
||||
|`--inputUpAxis`|Up axis of the obj. Choices are 'X', 'Y', and 'Z'.|No, default `Y`|
|
||||
|`--outputUpAxis`|Up axis of the converted glTF. Choices are 'X', 'Y', and 'Z'.|No, default `Y`|
|
||||
|`--packOcclusion`|Pack the occlusion texture in the red channel of metallic-roughness texture.|No, default `false`|
|
||||
|`--inputMetallicRoughness`|The values in the mtl file are already metallic-roughness PBR values and no conversion step should be applied. Metallic is stored in the Ks and map_Ks slots and roughness is stored in the Ns and map_Ns slots.|No, default `false`|
|
||||
|`--inputSpecularGlossiness`|The values in the mtl file are already specular-glossiness PBR values and no conversion step should be applied. Specular is stored in the Ks and map_Ks slots and glossiness is stored in the Ns and map_Ns slots. The glTF will be saved with the `KHR_materials_pbrSpecularGlossiness` extension.|No, default `false`|
|
||||
|
@ -91,6 +91,18 @@ var argv = yargs
|
||||
type: 'boolean',
|
||||
default: defaults.secure
|
||||
},
|
||||
inputUpAxis : {
|
||||
describe: 'Up axis of the obj.',
|
||||
choices: ['X', 'Y', 'Z'],
|
||||
type: 'string',
|
||||
default: 'Y'
|
||||
},
|
||||
outputUpAxis : {
|
||||
describe: 'Up axis of the converted glTF.',
|
||||
choices: ['X', 'Y', 'Z'],
|
||||
type: 'string',
|
||||
default: 'Y'
|
||||
},
|
||||
packOcclusion : {
|
||||
describe: 'Pack the occlusion texture in the red channel of metallic-roughness texture.',
|
||||
type: 'boolean',
|
||||
@ -129,7 +141,11 @@ var options = {
|
||||
bypassPipeline : argv.bypassPipeline,
|
||||
checkTransparency : argv.checkTransparency,
|
||||
secure : argv.secure,
|
||||
packOcclusion : argv.packOcclusion
|
||||
inputUpAxis : argv.inputUpAxis,
|
||||
outputUpAxis : argv.outputUpAxis,
|
||||
packOcclusion : argv.packOcclusion,
|
||||
inputMetallicRoughness : argv.inputMetallicRoughness,
|
||||
inputSpecularGlossiness : argv.inputSpecularGlossiness
|
||||
};
|
||||
|
||||
console.time('Total');
|
||||
|
@ -8,9 +8,12 @@ var loadImage = require('./loadImage');
|
||||
var loadMtl = require('./loadMtl');
|
||||
var readLines = require('./readLines');
|
||||
|
||||
var Axis = Cesium.Axis;
|
||||
var Cartesian3 = Cesium.Cartesian3;
|
||||
var ComponentDatatype = Cesium.ComponentDatatype;
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
var defined = Cesium.defined;
|
||||
var Matrix4 = Cesium.Matrix4;
|
||||
var RuntimeError = Cesium.RuntimeError;
|
||||
|
||||
module.exports = loadObj;
|
||||
@ -46,6 +49,8 @@ var facePattern2 = /f( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(
|
||||
var facePattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
|
||||
var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/; // f vertex//normal vertex//normal vertex//normal ...
|
||||
|
||||
var scratchCartesian = new Cartesian3();
|
||||
|
||||
/**
|
||||
* Parse an obj file.
|
||||
*
|
||||
@ -53,6 +58,8 @@ var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(
|
||||
* @param {Object} options An object with the following properties:
|
||||
* @param {Boolean} options.checkTransparency Do a more exhaustive check for texture transparency by looking at the alpha channel of each pixel.
|
||||
* @param {Boolean} options.secure Prevent the converter from reading image or mtl files outside of the input obj directory.
|
||||
* @param {String} options.inputUpAxis Up axis of the obj.
|
||||
* @param {String} options.outputUpAxis Up axis of the converted glTF.
|
||||
* @param {Boolean} options.logger A callback function for handling logged messages. Defaults to console.log.
|
||||
* @returns {Promise} A promise resolving to the obj data.
|
||||
* @exception {RuntimeError} The file does not have any geometry information in it.
|
||||
@ -60,6 +67,8 @@ var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(
|
||||
* @private
|
||||
*/
|
||||
function loadObj(objPath, options) {
|
||||
var axisTransform = getAxisTransform(options.inputUpAxis, options.outputUpAxis);
|
||||
|
||||
// Global store of vertex attributes listed in the obj file
|
||||
var positions = new ArrayStorage(ComponentDatatype.FLOAT);
|
||||
var normals = new ArrayStorage(ComponentDatatype.FLOAT);
|
||||
@ -223,13 +232,27 @@ function loadObj(objPath, options) {
|
||||
var paths = line.substring(7).trim().split(' ');
|
||||
mtlPaths = mtlPaths.concat(paths);
|
||||
} else if ((result = vertexPattern.exec(line)) !== null) {
|
||||
positions.push(parseFloat(result[1]));
|
||||
positions.push(parseFloat(result[2]));
|
||||
positions.push(parseFloat(result[3]));
|
||||
var position = scratchCartesian;
|
||||
position.x = parseFloat(result[1]);
|
||||
position.y = parseFloat(result[2]);
|
||||
position.z = parseFloat(result[3]);
|
||||
if (defined(axisTransform)) {
|
||||
Matrix4.multiplyByPoint(axisTransform, position, position);
|
||||
}
|
||||
positions.push(position.x);
|
||||
positions.push(position.y);
|
||||
positions.push(position.z);
|
||||
} else if ((result = normalPattern.exec(line) ) !== null) {
|
||||
normals.push(parseFloat(result[1]));
|
||||
normals.push(parseFloat(result[2]));
|
||||
normals.push(parseFloat(result[3]));
|
||||
var normal = scratchCartesian;
|
||||
normal.x = parseFloat(result[1]);
|
||||
normal.y = parseFloat(result[2]);
|
||||
normal.z = parseFloat(result[3]);
|
||||
if (defined(axisTransform)) {
|
||||
Matrix4.multiplyByPointAsVector(axisTransform, normal, normal);
|
||||
}
|
||||
normals.push(normal.x);
|
||||
normals.push(normal.y);
|
||||
normals.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
|
||||
@ -471,3 +494,19 @@ function cleanNodes(nodes) {
|
||||
setDefaults(nodes);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
function getAxisTransform(inputUpAxis, outputUpAxis) {
|
||||
if (inputUpAxis === 'X' && outputUpAxis === 'Y') {
|
||||
return Axis.X_UP_TO_Y_UP;
|
||||
} else if (inputUpAxis === 'X' && outputUpAxis === 'Z') {
|
||||
return Axis.X_UP_TO_Z_UP;
|
||||
} else if (inputUpAxis === 'Y' && outputUpAxis === 'X') {
|
||||
return Axis.Y_UP_TO_X_UP;
|
||||
} else if (inputUpAxis === 'Y' && outputUpAxis === 'Z') {
|
||||
return Axis.Y_UP_TO_Z_UP;
|
||||
} else if (inputUpAxis === 'Z' && outputUpAxis === 'X') {
|
||||
return Axis.Z_UP_TO_X_UP;
|
||||
} else if (inputUpAxis === 'Z' && outputUpAxis === 'Y') {
|
||||
return Axis.Z_UP_TO_Y_UP;
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,20 @@
|
||||
var Cesium = require('cesium');
|
||||
var fsExtra = require('fs-extra');
|
||||
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
||||
var os = require('os');
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
var uuid = require('uuid');
|
||||
var createGltf = require('./createGltf');
|
||||
var loadObj = require('./loadObj');
|
||||
var writeUris = require('./writeUris');
|
||||
|
||||
var fsExtraOutputJson = Promise.promisify(fsExtra.outputJson);
|
||||
var fsExtraRemove = Promise.promisify(fsExtra.remove);
|
||||
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
var defined = Cesium.defined;
|
||||
var DeveloperError = Cesium.DeveloperError;
|
||||
var RuntimeError = Cesium.RuntimeError;
|
||||
|
||||
module.exports = obj2gltf;
|
||||
|
||||
@ -35,6 +37,8 @@ module.exports = obj2gltf;
|
||||
* @param {Boolean} [options.bypassPipeline=false] Bypass the gltf-pipeline for debugging purposes. This option overrides many of the options above.
|
||||
* @param {Boolean} [options.checkTransparency=false] Do a more exhaustive check for texture transparency by looking at the alpha channel of each pixel.
|
||||
* @param {Boolean} [options.secure=false] Prevent the converter from reading image or mtl files outside of the input obj directory.
|
||||
* @param {String} [options.inputUpAxis='Y'] Up axis of the obj. Choices are 'X', 'Y', and 'Z'.
|
||||
* @param {String} [options.outputUpAxis='Y'] Up axis of the converted glTF. Choices are 'X', 'Y', and 'Z'.
|
||||
* @param {Boolean} [options.packOcclusion=false] Pack the occlusion texture in the red channel of metallic-roughness texture.
|
||||
* @param {Boolean} [options.inputMetallicRoughness=false] The values in the mtl file are already metallic-roughness PBR values and no conversion step should be applied. Metallic is stored in the Ks and map_Ks slots and roughness is stored in the Ns and map_Ns slots.
|
||||
* @param {Boolean} [options.inputSpecularGlossiness=false] The values in the mtl file are already specular-glossiness PBR values and no conversion step should be applied. Specular is stored in the Ks and map_Ks slots and glossiness is stored in the Ns and map_Ns slots. The glTF will be saved with the KHR_materials_pbrSpecularGlossiness extension.
|
||||
@ -56,6 +60,8 @@ function obj2gltf(objPath, gltfPath, options) {
|
||||
var bypassPipeline = defaultValue(options.bypassPipeline, defaults.bypassPipeline);
|
||||
var checkTransparency = defaultValue(options.checkTransparency, defaults.checkTransparency);
|
||||
var secure = defaultValue(options.secure, defaults.secure);
|
||||
var inputUpAxis = defaultValue(options.inputUpAxis, defaults.inputUpAxis);
|
||||
var outputUpAxis = defaultValue(options.outputUpAxis, defaults.outputUpAxis);
|
||||
var packOcclusion = defaultValue(options.packOcclusion, defaults.packOcclusion);
|
||||
var inputMetallicRoughness = defaultValue(options.inputMetallicRoughness, defaults.inputMetallicRoughness);
|
||||
var inputSpecularGlossiness = defaultValue(options.inputSpecularGlossiness, defaults.inputSpecularGlossiness);
|
||||
@ -65,6 +71,8 @@ function obj2gltf(objPath, gltfPath, options) {
|
||||
options.separateTextures = separateTextures;
|
||||
options.checkTransparency = checkTransparency;
|
||||
options.secure = secure;
|
||||
options.inputUpAxis = inputUpAxis;
|
||||
options.outputUpAxis = outputUpAxis;
|
||||
options.packOcclusion = packOcclusion;
|
||||
options.inputMetallicRoughness = inputMetallicRoughness;
|
||||
options.inputSpecularGlossiness = inputSpecularGlossiness;
|
||||
@ -79,14 +87,13 @@ function obj2gltf(objPath, gltfPath, options) {
|
||||
}
|
||||
|
||||
var extension = path.extname(gltfPath).toLowerCase();
|
||||
var basePath = path.dirname(gltfPath);
|
||||
var modelName = path.basename(gltfPath, path.extname(gltfPath));
|
||||
if (extension === '.glb') {
|
||||
binary = true;
|
||||
}
|
||||
|
||||
if (binary && bypassPipeline) {
|
||||
throw new DeveloperError('--bypassPipeline does not convert to binary glTF');
|
||||
return Promise.reject(new RuntimeError('--bypassPipeline does not convert to binary glTF'));
|
||||
}
|
||||
|
||||
if (inputMetallicRoughness && inputSpecularGlossiness) {
|
||||
@ -94,12 +101,13 @@ function obj2gltf(objPath, gltfPath, options) {
|
||||
}
|
||||
|
||||
gltfPath = path.join(path.dirname(gltfPath), modelName + extension);
|
||||
var resourcesDirectory = options.bypassPipeline ? path.dirname(gltfPath) : obj2gltf._getTempDirectory();
|
||||
|
||||
var aoOptions = ao ? {} : undefined;
|
||||
|
||||
var pipelineOptions = {
|
||||
createDirectory : false,
|
||||
basePath : basePath,
|
||||
basePath : resourcesDirectory,
|
||||
binary : binary,
|
||||
embed : !separate,
|
||||
embedImage : !separateTextures,
|
||||
@ -118,7 +126,7 @@ function obj2gltf(objPath, gltfPath, options) {
|
||||
return createGltf(objData, options);
|
||||
})
|
||||
.then(function(gltf) {
|
||||
return writeUris(gltf, gltfPath, options);
|
||||
return writeUris(gltf, gltfPath, resourcesDirectory, options);
|
||||
})
|
||||
.then(function(gltf) {
|
||||
if (bypassPipeline) {
|
||||
@ -127,18 +135,17 @@ function obj2gltf(objPath, gltfPath, options) {
|
||||
return GltfPipeline.processJSONToDisk(gltf, gltfPath, pipelineOptions);
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
return cleanup(gltfPath, options);
|
||||
.finally(function() {
|
||||
return cleanup(resourcesDirectory, options);
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup(gltfPath, options) {
|
||||
// gltf-pipeline also saves out a buffer so remove the one generated by obj2gltf
|
||||
function cleanup(resourcesDirectory, options) {
|
||||
if (!options.bypassPipeline && options.separate) {
|
||||
var bufferName = path.basename(gltfPath, path.extname(gltfPath));
|
||||
var bufferUri = bufferName + '.bin';
|
||||
var bufferPath = path.join(path.dirname(gltfPath), bufferUri);
|
||||
return fsExtraRemove(bufferPath);
|
||||
fsExtra.remove(resourcesDirectory, function () {
|
||||
// Don't fail simply because we couldn't
|
||||
// clean up the temporary files.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,6 +220,18 @@ obj2gltf.defaults = {
|
||||
* @default false
|
||||
*/
|
||||
secure: false,
|
||||
/**
|
||||
* Gets or sets the up axis of the obj.
|
||||
* @type String
|
||||
* @default 'Y'
|
||||
*/
|
||||
inputUpAxis: 'Y',
|
||||
/**
|
||||
* Gets or sets the up axis of the converted glTF.
|
||||
* @type String
|
||||
* @default 'Y'
|
||||
*/
|
||||
outputUpAxis: 'Y',
|
||||
/**
|
||||
* Gets or sets whether to pack the occlusion texture in the red channel of the metallic-roughness texture.
|
||||
* @type Boolean
|
||||
@ -247,6 +266,15 @@ obj2gltf.defaults = {
|
||||
*/
|
||||
obj2gltf._outputJson = fsExtraOutputJson;
|
||||
|
||||
/**
|
||||
* Exposed for testing
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
obj2gltf._getTempDirectory = function () {
|
||||
return path.join(os.tmpdir(), uuid());
|
||||
};
|
||||
|
||||
/**
|
||||
* A callback function that logs messages.
|
||||
* @callback Logger
|
||||
|
@ -16,6 +16,7 @@ module.exports = writeUris;
|
||||
*
|
||||
* @param {Object} gltf The glTF asset.
|
||||
* @param {String} gltfPath Path where the glTF will be saved.
|
||||
* @param {String} resourcesDirectory Path where separate resources will be saved.
|
||||
* @param {Object} options An object with the following properties:
|
||||
* @param {Boolean} options.separate Writes out separate buffers.
|
||||
* @param {Boolean} options.separateTextures Write out separate textures only.
|
||||
@ -23,7 +24,7 @@ module.exports = writeUris;
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function writeUris(gltf, gltfPath, options) {
|
||||
function writeUris(gltf, gltfPath, resourcesDirectory, options) {
|
||||
var separate = options.separate;
|
||||
var separateTextures = options.separateTextures;
|
||||
|
||||
@ -43,17 +44,19 @@ function writeUris(gltf, gltfPath, options) {
|
||||
var exceedsMaximum = (texturesByteLength + bufferByteLength > 201326580);
|
||||
|
||||
if (exceedsMaximum && !separate) {
|
||||
return Promise.reject(new RuntimeError('Buffers and textures are too large to encode in the glTF, saving as separate resources.'));
|
||||
return Promise.reject(new RuntimeError('Buffers and textures are too large to encode in the glTF. Use the --separate flag instead.'));
|
||||
}
|
||||
|
||||
var name = path.basename(gltfPath, path.extname(gltfPath));
|
||||
|
||||
if (separate) {
|
||||
promises.push(writeSeparateBuffer(gltf, gltfPath));
|
||||
promises.push(writeSeparateBuffer(gltf, resourcesDirectory, name));
|
||||
} else {
|
||||
writeEmbeddedBuffer(gltf);
|
||||
}
|
||||
|
||||
if (separateTextures) {
|
||||
promises.push(writeSeparateTextures(gltf, gltfPath));
|
||||
promises.push(writeSeparateTextures(gltf, resourcesDirectory));
|
||||
} else {
|
||||
writeEmbeddedTextures(gltf);
|
||||
}
|
||||
@ -95,23 +98,22 @@ function cleanup(gltf) {
|
||||
}
|
||||
}
|
||||
|
||||
function writeSeparateBuffer(gltf, gltfPath) {
|
||||
function writeSeparateBuffer(gltf, resourcesDirectory, name) {
|
||||
var buffer = gltf.buffers[0];
|
||||
var source = buffer.extras._obj2gltf.source;
|
||||
var bufferName = path.basename(gltfPath, path.extname(gltfPath));
|
||||
var bufferUri = bufferName + '.bin';
|
||||
var bufferUri = name + '.bin';
|
||||
buffer.uri = bufferUri;
|
||||
var bufferPath = path.join(path.dirname(gltfPath), bufferUri);
|
||||
var bufferPath = path.join(resourcesDirectory, bufferUri);
|
||||
return writeUris._outputFile(bufferPath, source);
|
||||
}
|
||||
|
||||
function writeSeparateTextures(gltf, gltfPath) {
|
||||
function writeSeparateTextures(gltf, resourcesDirectory) {
|
||||
var images = gltf.images;
|
||||
return Promise.map(images, function(image) {
|
||||
var extras = image.extras._obj2gltf;
|
||||
var imageUri = image.name + extras.extension;
|
||||
image.uri = imageUri;
|
||||
var imagePath = path.join(path.dirname(gltfPath), imageUri);
|
||||
var imagePath = path.join(resourcesDirectory, imageUri);
|
||||
return writeUris._outputFile(imagePath, extras.source);
|
||||
}, {concurrency : 10});
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obj2gltf",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.1",
|
||||
"description": "Convert OBJ model format to glTF",
|
||||
"license": "Apache-2.0",
|
||||
"contributors": [
|
||||
@ -34,6 +34,7 @@
|
||||
"jpeg-js": "^0.2.0",
|
||||
"mime": "^1.3.4",
|
||||
"pngjs": "^3.2.0",
|
||||
"uuid": "^3.0.1",
|
||||
"yargs": "^7.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
10
specs/data/box-rotated/box-rotated.mtl
Normal file
10
specs/data/box-rotated/box-rotated.mtl
Normal file
@ -0,0 +1,10 @@
|
||||
# Blender MTL File: 'axis.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl None
|
||||
Ns 0
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.8 0.8 0.8
|
||||
Ks 0.8 0.8 0.8
|
||||
d 1
|
||||
illum 2
|
112
specs/data/box-rotated/box-rotated.obj
Normal file
112
specs/data/box-rotated/box-rotated.obj
Normal file
@ -0,0 +1,112 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'axis.blend'
|
||||
# www.blender.org
|
||||
mtllib box-rotated.mtl
|
||||
o Cube.002_Cube
|
||||
v -1.707107 -0.292893 0.000000
|
||||
v -0.707107 0.707107 1.414214
|
||||
v -0.707107 0.707107 -1.414214
|
||||
v 0.292893 1.707107 0.000000
|
||||
v -0.292893 -1.707107 0.000000
|
||||
v 0.707107 -0.707107 1.414214
|
||||
v 0.707107 -0.707107 -1.414214
|
||||
v 1.707107 0.292893 0.000000
|
||||
vn -0.7071 0.7071 0.0000
|
||||
vn 0.5000 0.5000 -0.7071
|
||||
vn 0.7071 -0.7071 -0.0000
|
||||
vn -0.5000 -0.5000 0.7071
|
||||
vn -0.5000 -0.5000 -0.7071
|
||||
vn 0.5000 0.5000 0.7071
|
||||
usemtl None
|
||||
s off
|
||||
f 1//1 2//1 4//1 3//1
|
||||
f 3//2 4//2 8//2 7//2
|
||||
f 7//3 8//3 6//3 5//3
|
||||
f 5//4 6//4 2//4 1//4
|
||||
f 3//5 7//5 5//5 1//5
|
||||
f 8//6 4//6 2//6 6//6
|
||||
o Cube.001_Cube.002
|
||||
v -3.997752 -1.000000 3.997752
|
||||
v -3.997752 1.000000 3.997752
|
||||
v -3.997752 -1.000000 -3.997752
|
||||
v -3.997752 1.000000 -3.997752
|
||||
v 3.997752 -1.000000 3.997752
|
||||
v 3.997752 1.000000 3.997752
|
||||
v 3.997752 -1.000000 -3.997752
|
||||
v 3.997752 1.000000 -3.997752
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
usemtl None
|
||||
s off
|
||||
f 9//7 10//7 12//7 11//7
|
||||
f 11//8 12//8 16//8 15//8
|
||||
f 15//9 16//9 14//9 13//9
|
||||
f 13//10 14//10 10//10 9//10
|
||||
f 11//11 15//11 13//11 9//11
|
||||
f 16//12 12//12 10//12 14//12
|
||||
o Cube_Cube.001
|
||||
v -1.000000 4.235625 1.000000
|
||||
v -1.000000 6.235625 1.000000
|
||||
v -1.000000 4.235625 -1.000000
|
||||
v -1.000000 6.235625 -1.000000
|
||||
v 1.000000 4.235625 1.000000
|
||||
v 1.000000 6.235625 1.000000
|
||||
v 1.000000 4.235625 -1.000000
|
||||
v 1.000000 6.235625 -1.000000
|
||||
v 0.000000 9.029284 0.000000
|
||||
v 1.000000 4.663946 0.571679
|
||||
v 1.000000 5.807305 0.571679
|
||||
v 1.000000 4.663946 -0.571679
|
||||
v 1.000000 5.807305 -0.571679
|
||||
v 2.375958 4.663946 0.571679
|
||||
v 2.375958 5.807305 0.571679
|
||||
v 2.375958 4.663946 -0.571679
|
||||
v 2.375958 5.807305 -0.571679
|
||||
v -0.310735 4.924891 -1.000000
|
||||
v -0.310735 5.546360 -1.000000
|
||||
v 0.310735 4.924891 -1.000000
|
||||
v 0.310735 5.546360 -1.000000
|
||||
v 0.000000 5.235625 -1.538583
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.0000 0.3370 -0.9415
|
||||
vn -0.9415 0.3370 0.0000
|
||||
vn 0.0000 0.3370 0.9415
|
||||
vn 0.9415 0.3370 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn 0.8662 0.0000 -0.4997
|
||||
vn 0.0000 -0.8662 -0.4997
|
||||
vn 0.0000 0.8662 -0.4997
|
||||
vn -0.8662 0.0000 -0.4997
|
||||
usemtl None
|
||||
s off
|
||||
f 17//13 18//13 20//13 19//13
|
||||
f 23//14 19//14 34//14 36//14
|
||||
f 22//15 21//15 26//15 27//15
|
||||
f 21//16 22//16 18//16 17//16
|
||||
f 19//17 23//17 21//17 17//17
|
||||
f 24//18 20//18 25//18
|
||||
f 20//19 18//19 25//19
|
||||
f 18//20 22//20 25//20
|
||||
f 22//21 24//21 25//21
|
||||
f 29//22 27//22 31//22 33//22
|
||||
f 21//15 23//15 28//15 26//15
|
||||
f 24//15 22//15 27//15 29//15
|
||||
f 23//15 24//15 29//15 28//15
|
||||
f 32//15 33//15 31//15 30//15
|
||||
f 28//14 29//14 33//14 32//14
|
||||
f 27//16 26//16 30//16 31//16
|
||||
f 26//17 28//17 32//17 30//17
|
||||
f 37//23 36//23 38//23
|
||||
f 20//14 24//14 37//14 35//14
|
||||
f 19//14 20//14 35//14 34//14
|
||||
f 24//14 23//14 36//14 37//14
|
||||
f 36//24 34//24 38//24
|
||||
f 35//25 37//25 38//25
|
||||
f 34//26 35//26 38//26
|
@ -70,7 +70,7 @@ describe('createGltf', function() {
|
||||
|
||||
it('simple gltf', function(done) {
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
expect(writeUris(gltf, boxGltfUrl, defaultOptions)
|
||||
expect(writeUris(gltf, boxGltfUrl, path.dirname(boxGltfUrl), defaultOptions)
|
||||
.then(function() {
|
||||
expect(gltf).toEqual(boxGltf);
|
||||
}), done).toResolve();
|
||||
@ -79,7 +79,7 @@ describe('createGltf', function() {
|
||||
it('multiple nodes, meshes, and primitives', function(done) {
|
||||
var gltf = createGltf(groupObjData, defaultOptions);
|
||||
|
||||
expect(writeUris(gltf, groupGltfUrl, defaultOptions)
|
||||
expect(writeUris(gltf, groupGltfUrl, path.dirname(groupGltfUrl), defaultOptions)
|
||||
.then(function() {
|
||||
expect(gltf).toEqual(groupGltf);
|
||||
expect(Object.keys(gltf.materials).length).toBe(3);
|
||||
@ -359,4 +359,13 @@ describe('createGltf', function() {
|
||||
var positionAccessor = gltf.accessors[primitive.attributes.POSITION];
|
||||
expect(positionAccessor.count).toBe(vertexCount);
|
||||
});
|
||||
|
||||
it('ambient of [1, 1, 1] is treated as [0, 0, 0]', function() {
|
||||
boxObjData.materials.Material.ambientColor = [1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var ambient = gltf.materials.Material.extensions.KHR_materials_common.values.ambient;
|
||||
|
||||
expect(ambient).toEqual([0.0, 0.0, 0.0, 1.0]);
|
||||
});
|
||||
});
|
||||
|
@ -5,10 +5,12 @@ var Promise = require('bluebird');
|
||||
var loadObj = require('../../lib/loadObj');
|
||||
var obj2gltf = require('../../lib/obj2gltf');
|
||||
|
||||
var Cartesian3 = Cesium.Cartesian3;
|
||||
var clone = Cesium.clone;
|
||||
var RuntimeError = Cesium.RuntimeError;
|
||||
|
||||
var objUrl = 'specs/data/box/box.obj';
|
||||
var objRotatedUrl = 'specs/data/box-rotated/box-rotated.obj';
|
||||
var objNormalsUrl = 'specs/data/box-normals/box-normals.obj';
|
||||
var objUvsUrl = 'specs/data/box-uvs/box-uvs.obj';
|
||||
var objPositionsOnlyUrl = 'specs/data/box-positions-only/box-positions-only.obj';
|
||||
@ -62,6 +64,10 @@ function getImagePath(objPath, relativePath) {
|
||||
var defaultOptions = obj2gltf.defaults;
|
||||
|
||||
describe('loadObj', function() {
|
||||
beforeEach(function() {
|
||||
spyOn(console, 'log');
|
||||
});
|
||||
|
||||
it('loads obj with positions, normals, and uvs', function(done) {
|
||||
expect(loadObj(objUrl, defaultOptions)
|
||||
.then(function(data) {
|
||||
@ -270,7 +276,6 @@ describe('loadObj', function() {
|
||||
});
|
||||
|
||||
it('loads obj with missing mtllib', function(done) {
|
||||
spyOn(console, 'log');
|
||||
expect(loadObj(objMissingMtllibUrl, defaultOptions)
|
||||
.then(function(data) {
|
||||
expect(data.materials).toEqual({});
|
||||
@ -288,8 +293,6 @@ describe('loadObj', function() {
|
||||
});
|
||||
|
||||
it('does not load resources outside of the obj directory when secure is true', function(done) {
|
||||
spyOn(console, 'log');
|
||||
|
||||
var options = clone(defaultOptions);
|
||||
options.secure = true;
|
||||
|
||||
@ -314,7 +317,6 @@ describe('loadObj', function() {
|
||||
});
|
||||
|
||||
it('loads obj with missing texture', function(done) {
|
||||
spyOn(console, 'log');
|
||||
expect(loadObj(objMissingTextureUrl, defaultOptions)
|
||||
.then(function(data) {
|
||||
var imagePath = getImagePath(objMissingTextureUrl, 'cesium.png');
|
||||
@ -341,7 +343,54 @@ describe('loadObj', function() {
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('does not process file with invalid contents', function(done) {
|
||||
function getFirstPosition(data) {
|
||||
var positions = data.nodes[0].meshes[0].positions;
|
||||
return new Cartesian3(positions.get(0), positions.get(1), positions.get(2));
|
||||
}
|
||||
|
||||
function getFirstNormal(data) {
|
||||
var normals = data.nodes[0].meshes[0].normals;
|
||||
return new Cartesian3(normals.get(0), normals.get(1), normals.get(2));
|
||||
}
|
||||
|
||||
function checkAxisConversion(inputUpAxis, outputUpAxis, position, normal) {
|
||||
var sameAxis = (inputUpAxis === outputUpAxis);
|
||||
var options = clone(defaultOptions);
|
||||
options.inputUpAxis = inputUpAxis;
|
||||
options.outputUpAxis = outputUpAxis;
|
||||
return loadObj(objRotatedUrl, options)
|
||||
.then(function(data) {
|
||||
var rotatedPosition = getFirstPosition(data);
|
||||
var rotatedNormal = getFirstNormal(data);
|
||||
if (sameAxis) {
|
||||
expect(rotatedPosition).toEqual(position);
|
||||
expect(rotatedNormal).toEqual(normal);
|
||||
} else {
|
||||
expect(rotatedPosition).not.toEqual(position);
|
||||
expect(rotatedNormal).not.toEqual(normal);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it('performs up axis conversion', function(done) {
|
||||
expect(loadObj(objRotatedUrl, defaultOptions)
|
||||
.then(function(data) {
|
||||
var position = getFirstPosition(data);
|
||||
var normal = getFirstNormal(data);
|
||||
|
||||
var axes = ['X', 'Y', 'Z'];
|
||||
var axesLength = axes.length;
|
||||
var promises = [];
|
||||
for (var i = 0; i < axesLength; ++i) {
|
||||
for (var j = 0; j < axesLength; ++j) {
|
||||
promises.push(checkAxisConversion(axes[i], axes[j], position, normal));
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('throws when file has invalid contents', function(done) {
|
||||
expect(loadObj(objInvalidContentsUrl, defaultOptions), done).toRejectWith(RuntimeError);
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
'use strict';
|
||||
var fsExtra = require('fs-extra');
|
||||
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
||||
var os = require('os');
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
var obj2gltf = require('../../lib/obj2gltf');
|
||||
var writeUris = require('../../lib/writeUris');
|
||||
|
||||
@ -10,28 +13,34 @@ var glbPath = 'specs/data/box-textured/box-textured.glb';
|
||||
var objPathNonExistent = 'specs/data/non-existent.obj';
|
||||
|
||||
describe('obj2gltf', function() {
|
||||
var tempDirectory;
|
||||
|
||||
beforeAll(function() {
|
||||
expect(obj2gltf._getTempDirectory()).toContain(os.tmpdir());
|
||||
tempDirectory = path.join(os.tmpdir(), 'testPath');
|
||||
spyOn(obj2gltf, '_getTempDirectory').and.returnValue(tempDirectory);
|
||||
spyOn(obj2gltf, '_outputJson');
|
||||
spyOn(writeUris, '_outputFile');
|
||||
spyOn(fsExtra, 'remove');
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(GltfPipeline, 'processJSONToDisk').and.returnValue(Promise.resolve());
|
||||
});
|
||||
|
||||
it('converts an obj to gltf', function(done) {
|
||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
expect(obj2gltf(objPath, gltfPath)
|
||||
.then(function() {
|
||||
var args = spy.calls.first().args;
|
||||
var args = GltfPipeline.processJSONToDisk.calls.first().args;
|
||||
var gltf = args[0];
|
||||
var outputPath = args[1];
|
||||
var options = args[2];
|
||||
expect(path.normalize(outputPath)).toEqual(path.normalize(gltfPath));
|
||||
expect(gltf).toBeDefined();
|
||||
expect(gltf.images.cesium).toBeDefined();
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('uses default gltf-pipeline options', function(done) {
|
||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
expect(obj2gltf(objPath, gltfPath)
|
||||
.then(function() {
|
||||
var args = spy.calls.first().args;
|
||||
var options = args[2];
|
||||
expect(options).toEqual({
|
||||
basePath : tempDirectory,
|
||||
createDirectory : false,
|
||||
basePath : path.dirname(objPath),
|
||||
binary : false,
|
||||
embed : true,
|
||||
embedImage : true,
|
||||
@ -48,8 +57,6 @@ describe('obj2gltf', function() {
|
||||
});
|
||||
|
||||
it('sets options', function(done) {
|
||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
spyOn(writeUris, '_outputFile');
|
||||
var textureCompressionOptions = {
|
||||
format : 'dxt1',
|
||||
quality : 10
|
||||
@ -66,16 +73,18 @@ describe('obj2gltf', function() {
|
||||
textureCompressionOptions : textureCompressionOptions,
|
||||
checkTransparency : true,
|
||||
secure : true,
|
||||
inputUpAxis : 'Z',
|
||||
outputUpAxis : 'X',
|
||||
logger : obj2gltf.defaults.logger
|
||||
};
|
||||
|
||||
expect(obj2gltf(objPath, gltfPath, options)
|
||||
.then(function() {
|
||||
var args = spy.calls.first().args;
|
||||
var args = GltfPipeline.processJSONToDisk.calls.first().args;
|
||||
var options = args[2];
|
||||
expect(options).toEqual({
|
||||
basePath : tempDirectory,
|
||||
createDirectory : false,
|
||||
basePath : path.dirname(objPath),
|
||||
binary : true,
|
||||
embed : false,
|
||||
embedImage : false,
|
||||
@ -93,18 +102,15 @@ describe('obj2gltf', function() {
|
||||
});
|
||||
|
||||
it('saves as binary if gltfPath has a .glb extension', function(done) {
|
||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
expect(obj2gltf(objPath, glbPath)
|
||||
.then(function() {
|
||||
var args = spy.calls.first().args;
|
||||
var args = GltfPipeline.processJSONToDisk.calls.first().args;
|
||||
var options = args[2];
|
||||
expect(options.binary).toBe(true);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('bypassPipeline flag bypasses gltf-pipeline', function(done) {
|
||||
spyOn(obj2gltf, '_outputJson');
|
||||
spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
var options = {
|
||||
bypassPipeline : true
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user