Refactor and tests
10
.gitignore
vendored
@ -2,14 +2,16 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
# TypeScript definitions
|
||||
typings
|
||||
|
||||
# WebStorm user-specific
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
|
||||
# TypeScript definitions
|
||||
typings
|
||||
|
||||
# Generate data
|
||||
test
|
||||
coverage
|
||||
doc
|
||||
output
|
||||
test
|
||||
*.tgz
|
||||
|
@ -2,7 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/OBJ2GLTF.iml" filepath="$PROJECT_DIR$/.idea/OBJ2GLTF.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/obj2gltf.iml" filepath="$PROJECT_DIR$/.idea/obj2gltf.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -2,6 +2,7 @@
|
||||
/doc
|
||||
/specs
|
||||
/test
|
||||
/output
|
||||
/typings
|
||||
/coverage
|
||||
.jshintrc
|
||||
|
@ -4,3 +4,8 @@ node_js:
|
||||
script:
|
||||
- npm run jsHint -- --failTaskOnError
|
||||
- npm run test -- --failTaskOnError --suppressPassed
|
||||
|
||||
after_success:
|
||||
## We only need to run coveralls for one node version (doesn't matter which one).
|
||||
## We also ignore publishing failures, since restarting an existing travis build would otherwise break.
|
||||
- if [$(node --version) == v6*]; then npm run coverage && npm run coveralls; fi
|
||||
|
121
LICENSE.md
@ -11,53 +11,29 @@ Third-Party Code
|
||||
|
||||
obj2gltf includes the following third-party code.
|
||||
|
||||
### async
|
||||
### bluebird
|
||||
|
||||
https://www.npmjs.com/package/async
|
||||
|
||||
> Copyright (c) 2010-2016 Caolan McMahon
|
||||
> The MIT License (MIT)
|
||||
>
|
||||
> Copyright (c) 2013-2015 Petka Antonov
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
> all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
### byline
|
||||
|
||||
https://www.npmjs.com/package/byline
|
||||
|
||||
> node-byline (C) 2011-2015 John Hewson
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
> THE SOFTWARE.
|
||||
|
||||
### Cesium
|
||||
|
||||
@ -73,6 +49,35 @@ http://cesiumjs.org/
|
||||
|
||||
See https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md
|
||||
|
||||
### event-stream
|
||||
|
||||
https://www.npmjs.com/package/event-stream
|
||||
|
||||
> The MIT License (MIT)
|
||||
>
|
||||
> Copyright (c) 2011 Dominic Tarr
|
||||
>
|
||||
> Permission is hereby granted, free of charge,
|
||||
> to any person obtaining a copy of this software and
|
||||
> associated documentation files (the "Software"), to
|
||||
> deal in the Software without restriction, including
|
||||
> without limitation the rights to use, copy, modify,
|
||||
> merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom
|
||||
> the Software is furnished to do so,
|
||||
> subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice
|
||||
> shall be included in all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
> OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
> IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
> ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
> TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
> SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### fs-extra
|
||||
|
||||
https://www.npmjs.com/package/fs-extra
|
||||
@ -82,16 +87,16 @@ https://www.npmjs.com/package/fs-extra
|
||||
> Copyright (c) 2011-2016 JP Richardson
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
|
||||
(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
|
||||
> (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
|
||||
> merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
> WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
> OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### gltf-pipeline
|
||||
|
||||
@ -112,24 +117,24 @@ https://www.npmjs.com/package/yargs
|
||||
The command-line tool uses yargs.
|
||||
|
||||
> Copyright 2010 James Halliday (mail@substack.net)
|
||||
Modified work Copyright 2014 Contributors (ben@npmjs.com)
|
||||
> Modified work Copyright 2014 Contributors (ben@npmjs.com)
|
||||
>
|
||||
> This project is free software released under the MIT/X11 license:
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
> all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
> THE SOFTWARE.
|
||||
|
18
README.md
@ -13,7 +13,7 @@ Using obj2gltf as a library:
|
||||
var obj2gltf = require('obj2gltf');
|
||||
var convert = obj2gltf.convert;
|
||||
var options = {
|
||||
embedImage : false // Don't embed image in the converted glTF
|
||||
separateTextures : true // Don't embed textures in the converted glTF
|
||||
}
|
||||
convert('model.obj', 'model.gltf', options)
|
||||
.then(function() {
|
||||
@ -30,20 +30,24 @@ Using obj2gltf as a command-line tool:
|
||||
|
||||
`node bin/obj2gltf.js -i model.obj -o model.gltf -s`
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
###Command line flags:
|
||||
|
||||
|Flag|Description|Required|
|
||||
|----|-----------|--------|
|
||||
|`-i`|Path to the input OBJ file.| :white_check_mark: Yes|
|
||||
|`-o`|Directory or filename for the exported glTF file.|No|
|
||||
|`-b`|Output binary glTF.|No, default `false`|
|
||||
|`-h`|Display help.|No|
|
||||
|`-i`|Path to the obj file.| :white_check_mark: Yes|
|
||||
|`-o`|Path of the converted glTF file.|No|
|
||||
|`-b`|Save as binary glTF.|No, default `false`|
|
||||
|`-s`|Writes out separate geometry/animation data files, shader files, and textures instead of embedding them in the glTF file.|No, default `false`|
|
||||
|`-t`|Write out separate textures only.|No, default `false`|
|
||||
|`-c`|Quantize positions, compress texture coordinates, and oct-encode normals.|No, default `false`|
|
||||
|`-z`|Use the optimization stages in the glTF pipeline.|No, default `false`|
|
||||
|`-n`|Generate normals if they are missing.|No, default `false`|
|
||||
|`--cesium`|Optimize the glTF for Cesium by using the sun as a default light source.|No, default `false`|
|
||||
|`--ao`|Apply ambient occlusion to the converted model.|No, default `false`|
|
||||
|`-h`|Display help|No|
|
||||
|`--bypassPipeline`|Bypass the gltf-pipeline for debugging purposes. This option overrides many of the options above and will save the glTF with the KHR_materials_common extension.|No, default `false`|
|
||||
|
||||
## Contributions
|
||||
|
||||
@ -53,5 +57,5 @@ Pull requests are appreciated. Please use the same [Contributor License Agreeme
|
||||
|
||||
Developed by the Cesium team.
|
||||
<p align="center">
|
||||
<a href="http://cesiumjs.org/"><img src="doc/cesium.png" /></a>
|
||||
<a href="http://cesiumjs.org/"><img src="doc/cesium.png" onerror="this.src='cesium.png'"/></a>
|
||||
</p>
|
||||
|
143
bin/obj2gltf.js
@ -1,53 +1,116 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var argv = require('yargs').argv;
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var defined = Cesium.defined;
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
var path = require('path');
|
||||
var yargs = require('yargs');
|
||||
var convert = require('../lib/convert');
|
||||
|
||||
if (process.argv.length < 3 || defined(argv.h) || defined(argv.help)) {
|
||||
console.log('Usage: ./bin/obj2gltf.js [INPUT] [OPTIONS]');
|
||||
console.log(' -i, --input Path to obj file');
|
||||
console.log(' -o, --output Directory or filename for the exported glTF file');
|
||||
console.log(' -b, --binary Output binary glTF');
|
||||
console.log(' -s --separate Writes out separate geometry/animation data files, shader files, and textures instead of embedding them in the glTF file.');
|
||||
console.log(' -t --separateImage Write out separate textures only.');
|
||||
console.log(' -c --compress Quantize positions, compress texture coordinates, and oct-encode normals.');
|
||||
console.log(' -h, --help Display this help');
|
||||
console.log(' --ao Apply ambient occlusion to the converted model');
|
||||
console.log(' --cesium Optimize the glTF for Cesium by using the sun as a default light source.');
|
||||
process.exit(0);
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
var defined = Cesium.defined;
|
||||
|
||||
var args = process.argv;
|
||||
args = args.slice(2, args.length);
|
||||
|
||||
var argv = yargs
|
||||
.usage('Usage: node $0 -i inputPath -o outputPath')
|
||||
.example('node $0 -i ./specs/data/box/box.obj -o box.gltf')
|
||||
.help('h')
|
||||
.alias('h', 'help')
|
||||
.options({
|
||||
'input': {
|
||||
alias: 'i',
|
||||
describe: 'Path to the obj file.',
|
||||
type: 'string',
|
||||
normalize: true
|
||||
},
|
||||
'output': {
|
||||
alias: 'o',
|
||||
describe: 'Path of the converted glTF file.',
|
||||
type: 'string',
|
||||
normalize: true
|
||||
},
|
||||
'binary': {
|
||||
alias: 'b',
|
||||
describe: 'Save as binary glTF.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
'separate': {
|
||||
alias: 's',
|
||||
describe: 'Write separate geometry/animation data files, shader files, and textures instead of embedding them in the glTF.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
'separateTexture': {
|
||||
alias: 't',
|
||||
describe: 'Write out separate textures only.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
'compress': {
|
||||
alias: 'c',
|
||||
describe: 'Quantize positions, compress texture coordinates, and oct-encode normals.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
'optimize': {
|
||||
alias: 'z',
|
||||
describe: 'Use the optimization stages in the glTF pipeline.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
'cesium': {
|
||||
describe: 'Optimize the glTF for Cesium by using the sun as a default light source.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
'generateNormals': {
|
||||
alias: 'n',
|
||||
describe: 'Generate normals if they are missing.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
'ao': {
|
||||
describe: 'Apply ambient occlusion to the converted model.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
'bypassPipeline': {
|
||||
describe: 'Bypass the gltf-pipeline for debugging purposes. This option overrides many of the options above and will save the glTF with the KHR_materials_common extension.',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}
|
||||
}).parse(args);
|
||||
|
||||
var objPath = defaultValue(argv.i, argv._[0]);
|
||||
var gltfPath = defaultValue(argv.o, argv._[1]);
|
||||
|
||||
if (!defined(objPath)) {
|
||||
yargs.showHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
var objFile = defaultValue(argv._[0], defaultValue(argv.i, argv.input));
|
||||
var outputPath = defaultValue(argv._[1], defaultValue(argv.o, argv.output));
|
||||
var binary = defaultValue(defaultValue(argv.b, argv.binary), false);
|
||||
var separate = defaultValue(defaultValue(argv.s, argv.separate), false);
|
||||
var separateImage = defaultValue(defaultValue(argv.t, argv.separateImage), false);
|
||||
var compress = defaultValue(defaultValue(argv.c, argv.compress), false);
|
||||
var ao = defaultValue(argv.ao, false);
|
||||
var optimizeForCesium = defaultValue(argv.cesium, false);
|
||||
|
||||
if (!defined(objFile)) {
|
||||
throw new Error('-i or --input argument is required. See --help for details.');
|
||||
if (!defined(gltfPath)) {
|
||||
var extension = argv.b ? '.glb' : '.gltf';
|
||||
var modelName = path.basename(objPath, path.extname(objPath));
|
||||
gltfPath = path.join(path.dirname(objPath), modelName + extension);
|
||||
}
|
||||
|
||||
var options = {
|
||||
binary : argv.b,
|
||||
separate : argv.s,
|
||||
separateTextures : argv.t,
|
||||
compress : argv.c,
|
||||
optimize : argv.z,
|
||||
generateNormals : argv.n,
|
||||
ao : argv.ao,
|
||||
optimizeForCesium : argv.cesium,
|
||||
bypassPipeline : argv.bypassPipeline
|
||||
};
|
||||
|
||||
console.time('Total');
|
||||
|
||||
var options = {
|
||||
binary : binary,
|
||||
embed : !separate,
|
||||
embedImage : !separateImage,
|
||||
compress : compress,
|
||||
ao : ao,
|
||||
optimizeForCesium : optimizeForCesium
|
||||
};
|
||||
|
||||
convert(objFile, outputPath, options)
|
||||
convert(objPath, gltfPath, options)
|
||||
.then(function() {
|
||||
console.timeEnd('Total');
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
|
12
gulpfile.js
@ -6,7 +6,7 @@ var fsExtra = require('fs-extra');
|
||||
var gulp = require('gulp');
|
||||
var gulpJshint = require('gulp-jshint');
|
||||
var Jasmine = require('jasmine');
|
||||
var JasmineSpecReporter = require('jasmine-spec-reporter');
|
||||
var JasmineSpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
||||
var open = require('open');
|
||||
var path = require('path');
|
||||
var yargs = require('yargs');
|
||||
@ -20,8 +20,8 @@ var environmentSeparator = process.platform === 'win32' ? ';' : ':';
|
||||
var nodeBinaries = path.join(__dirname, 'node_modules', '.bin');
|
||||
process.env.PATH += environmentSeparator + nodeBinaries;
|
||||
|
||||
var jsHintFiles = ['**/*.js', '!node_modules/**', '!coverage/**'];
|
||||
var specFiles = ['**/*.js', '!node_modules/**', '!coverage/**'];
|
||||
var jsHintFiles = ['**/*.js', '!node_modules/**', '!coverage/**', '!doc/**'];
|
||||
var specFiles = ['**/*.js', '!node_modules/**', '!coverage/**', '!doc/**', '!bin/**'];
|
||||
|
||||
gulp.task('jsHint', function () {
|
||||
var stream = gulp.src(jsHintFiles)
|
||||
@ -53,8 +53,8 @@ gulp.task('test', function (done) {
|
||||
|
||||
gulp.task('test-watch', function () {
|
||||
gulp.watch(specFiles).on('change', function () {
|
||||
//We can't simply depend on the test task because Jasmine
|
||||
//does not like being run multiple times in the same process.
|
||||
// We can't simply depend on the test task because Jasmine
|
||||
// does not like being run multiple times in the same process.
|
||||
try {
|
||||
child_process.execSync('jasmine JASMINE_CONFIG_PATH=specs/jasmine.json', {
|
||||
stdio: [process.stdin, process.stdout, process.stderr]
|
||||
@ -71,7 +71,7 @@ gulp.task('coverage', function () {
|
||||
' cover' +
|
||||
' --include-all-sources' +
|
||||
' --dir coverage' +
|
||||
' -x "specs/** coverage/** index.js gulpfile.js"' +
|
||||
' -x "specs/**" -x "coverage/**" -x "doc/**" -x "bin/**" -x "index.js" -x "gulpfile.js"' +
|
||||
' node_modules/jasmine/bin/jasmine.js' +
|
||||
' JASMINE_CONFIG_PATH=specs/jasmine.json', {
|
||||
stdio: [process.stdin, process.stdout, process.stderr]
|
||||
|
107
lib/ArrayStorage.js
Normal file
@ -0,0 +1,107 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
|
||||
var ComponentDatatype = Cesium.ComponentDatatype;
|
||||
|
||||
module.exports = ArrayStorage;
|
||||
|
||||
var initialLength = 1024; // 2^10
|
||||
var doublingThreshold = 33554432; // 2^25 (~134 MB for a Float32Array)
|
||||
var fixedExpansionLength = 33554432; // 2^25 (~134 MB for a Float32Array)
|
||||
|
||||
/**
|
||||
* Provides expandable typed array storage for geometry data. This is preferable to JS arrays which are
|
||||
* stored with double precision. The resizing mechanism is similar to std::vector.
|
||||
*
|
||||
* @param {ComponentDatatype} componentDatatype The data type.
|
||||
* @constructor
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function ArrayStorage(componentDatatype) {
|
||||
this.componentDatatype = componentDatatype;
|
||||
this.typedArray = ComponentDatatype.createTypedArray(componentDatatype, 0);
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
function resize(storage, length) {
|
||||
var typedArray = ComponentDatatype.createTypedArray(storage.componentDatatype, length);
|
||||
typedArray.set(storage.typedArray);
|
||||
storage.typedArray = typedArray;
|
||||
}
|
||||
|
||||
ArrayStorage.prototype.push = function(value) {
|
||||
var length = this.length;
|
||||
var typedArrayLength = this.typedArray.length;
|
||||
|
||||
if (length === 0) {
|
||||
resize(this, initialLength);
|
||||
} else if (length === typedArrayLength) {
|
||||
if (length < doublingThreshold) {
|
||||
resize(this, typedArrayLength * 2);
|
||||
} else {
|
||||
resize(this, typedArrayLength + fixedExpansionLength);
|
||||
}
|
||||
}
|
||||
|
||||
this.typedArray[this.length++] = value;
|
||||
};
|
||||
|
||||
ArrayStorage.prototype.get = function(index) {
|
||||
return this.typedArray[index];
|
||||
};
|
||||
|
||||
var sizeOfUint16 = 2;
|
||||
var sizeOfUint32 = 4;
|
||||
var sizeOfFloat = 4;
|
||||
|
||||
ArrayStorage.prototype.toUint16Buffer = function() {
|
||||
var length = this.length;
|
||||
var typedArray = this.typedArray;
|
||||
var paddedLength = length + ((length % 2 === 0) ? 0 : 1); // Round to next multiple of 2
|
||||
var buffer = Buffer.alloc(paddedLength * sizeOfUint16);
|
||||
for (var i = 0; i < length; ++i) {
|
||||
buffer.writeUInt16LE(typedArray[i], i * sizeOfUint16);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
ArrayStorage.prototype.toUint32Buffer = function() {
|
||||
var length = this.length;
|
||||
var typedArray = this.typedArray;
|
||||
var buffer = Buffer.alloc(length * sizeOfUint32);
|
||||
for (var i = 0; i < length; ++i) {
|
||||
buffer.writeUInt32LE(typedArray[i], i * sizeOfUint32);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
ArrayStorage.prototype.toFloatBuffer = function() {
|
||||
var length = this.length;
|
||||
var typedArray = this.typedArray;
|
||||
var buffer = Buffer.alloc(length * sizeOfFloat);
|
||||
for (var i = 0; i < length; ++i) {
|
||||
buffer.writeFloatLE(typedArray[i], i * sizeOfFloat);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
ArrayStorage.prototype.getMinMax = function(components) {
|
||||
var length = this.length;
|
||||
var typedArray = this.typedArray;
|
||||
var count = length / components;
|
||||
var min = new Array(components).fill(Number.POSITIVE_INFINITY);
|
||||
var max = new Array(components).fill(Number.NEGATIVE_INFINITY);
|
||||
for (var i = 0; i < count; ++i) {
|
||||
for (var j = 0; j < components; ++j) {
|
||||
var index = i * components + j;
|
||||
var value = typedArray[index];
|
||||
min[j] = Math.min(min[j], value);
|
||||
max[j] = Math.max(max[j], value);
|
||||
}
|
||||
}
|
||||
return {
|
||||
min : min,
|
||||
max : max
|
||||
};
|
||||
};
|
54
lib/clone.js
Normal file
@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var ArrayStorage = require('./ArrayStorage');
|
||||
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
|
||||
module.exports = clone;
|
||||
|
||||
/**
|
||||
* Clones an object, returning a new object containing the same properties.
|
||||
* Modified from Cesium.clone to support typed arrays, buffers, and the ArrayStorage class.
|
||||
*
|
||||
* @param {Object} object The object to clone.
|
||||
* @param {Boolean} [deep=false] If true, all properties will be deep cloned recursively.
|
||||
* @returns {Object} The cloned object.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function clone(object, deep) {
|
||||
if (object === null || typeof object !== 'object') {
|
||||
return object;
|
||||
}
|
||||
|
||||
deep = defaultValue(deep, false);
|
||||
|
||||
var isBuffer = Buffer.isBuffer(object);
|
||||
var isTypedArray = Object.prototype.toString.call(object.buffer) === '[object ArrayBuffer]';
|
||||
var isArrayStorage = object instanceof ArrayStorage;
|
||||
|
||||
var result;
|
||||
if (isBuffer) {
|
||||
result = Buffer.from(object);
|
||||
return result;
|
||||
} else if (isTypedArray) {
|
||||
result = object.slice();
|
||||
return result;
|
||||
} else if (isArrayStorage) {
|
||||
result = new ArrayStorage(object.componentDatatype);
|
||||
} else {
|
||||
result = new object.constructor();
|
||||
}
|
||||
|
||||
for (var propertyName in object) {
|
||||
if (object.hasOwnProperty(propertyName)) {
|
||||
var value = object[propertyName];
|
||||
if (deep) {
|
||||
value = clone(value, deep);
|
||||
}
|
||||
result[propertyName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
141
lib/convert.js
@ -1,60 +1,121 @@
|
||||
"use strict";
|
||||
var path = require('path');
|
||||
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
||||
var parseObj = require('./obj');
|
||||
var createGltf = require('./gltf');
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var defined = Cesium.defined;
|
||||
var fsExtra = require('fs-extra');
|
||||
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
var createGltf = require('./gltf');
|
||||
var loadObj = require('./obj');
|
||||
|
||||
var fxExtraOutputFile = Promise.promisify(fsExtra.outputFile);
|
||||
var fsExtraOutputJson = Promise.promisify(fsExtra.outputJson);
|
||||
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
var defined = Cesium.defined;
|
||||
var DeveloperError = Cesium.DeveloperError;
|
||||
|
||||
module.exports = convert;
|
||||
|
||||
function convert(objFile, outputPath, options) {
|
||||
options = defaultValue(options, {});
|
||||
/**
|
||||
* Converts an obj file to a glTF file.
|
||||
*
|
||||
* @param {String} objPath Path to the obj file.
|
||||
* @param {String} gltfPath Path of the converted glTF file.
|
||||
* @param {Object} [options] An object with the following properties:
|
||||
* @param {Boolean} [options.binary=false] Save as binary glTF.
|
||||
* @param {Boolean} [options.separate=false] Writes out separate geometry/animation data files, shader files, and textures instead of embedding them in the glTF.
|
||||
* @param {Boolean} [options.separateTextures=false] Write out separate textures only.
|
||||
* @param {Boolean} [options.compress=false] Quantize positions, compress texture coordinates, and oct-encode normals.
|
||||
* @param {Boolean} [options.optimize=false] Use the optimization stages in the glTF pipeline.
|
||||
* @param {Boolean} [options.optimizeForCesium=false] Optimize the glTF for Cesium by using the sun as a default light source.
|
||||
* @param {Boolean} [options.generateNormals=false] Generate normals if they are missing.
|
||||
* @param {Boolean} [options.ao=false] Apply ambient occlusion to the converted model.
|
||||
* @param {Boolean} [options.textureCompressionOptions] Options sent to the compressTextures stage of gltf-pipeline.
|
||||
* @param {Boolean} [options.bypassPipeline=false] Bypass the gltf-pipeline for debugging purposes. This option overrides many of the options above and will save the glTF with the KHR_materials_common extension.
|
||||
*/
|
||||
|
||||
function convert(objPath, gltfPath, options) {
|
||||
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
|
||||
var binary = defaultValue(options.binary, false);
|
||||
var embed = defaultValue(options.embed, true);
|
||||
var embedImage = defaultValue(options.embedImage, true);
|
||||
var separate = defaultValue(options.separate, false);
|
||||
var separateTextures = defaultValue(options.separateTextures, false);
|
||||
var compress = defaultValue(options.compress, false);
|
||||
var ao = defaultValue(options.ao, false);
|
||||
var optimize = defaultValue(options.optimize, false);
|
||||
var optimizeForCesium = defaultValue(options.optimizeForCesium, false);
|
||||
var generateNormals = defaultValue(options.generateNormals, false);
|
||||
var ao = defaultValue(options.ao, false);
|
||||
var textureCompressionOptions = options.textureCompressionOptions;
|
||||
var bypassPipeline = defaultValue(options.bypassPipeline, false);
|
||||
|
||||
if (!defined(objFile)) {
|
||||
throw new Error('objFile is required');
|
||||
if (!defined(objPath)) {
|
||||
throw new DeveloperError('objPath is required');
|
||||
}
|
||||
|
||||
if (!defined(outputPath)) {
|
||||
outputPath = path.dirname(objFile);
|
||||
if (!defined(gltfPath)) {
|
||||
throw new DeveloperError('gltfPath is required');
|
||||
}
|
||||
|
||||
var inputPath = path.dirname(objFile);
|
||||
var modelName = path.basename(objFile, '.obj');
|
||||
|
||||
var extension = path.extname(outputPath);
|
||||
if (extension !== '') {
|
||||
modelName = path.basename(outputPath, extension);
|
||||
outputPath = path.dirname(outputPath);
|
||||
var basePath = path.dirname(objPath);
|
||||
var modelName = path.basename(objPath, path.extname(objPath));
|
||||
var extension = path.extname(gltfPath);
|
||||
if (extension === '.glb') {
|
||||
binary = true;
|
||||
}
|
||||
gltfPath = path.join(path.dirname(gltfPath), modelName + extension);
|
||||
|
||||
extension = binary ? '.glb' : '.gltf';
|
||||
var gltfFile = path.join(outputPath, modelName + extension);
|
||||
var aoOptions = ao ? {} : undefined;
|
||||
|
||||
return parseObj(objFile, inputPath)
|
||||
.then(function(data) {
|
||||
return createGltf(data, inputPath, modelName);
|
||||
var pipelineOptions = {
|
||||
createDirectory : false,
|
||||
basePath : basePath,
|
||||
binary : binary,
|
||||
embed : !separate,
|
||||
embedImage : !separate && !separateTextures,
|
||||
quantize : compress,
|
||||
compressTextureCoordinates : compress,
|
||||
encodeNormals : compress,
|
||||
preserve : !optimize,
|
||||
optimizeForCesium : optimizeForCesium,
|
||||
smoothNormals : generateNormals,
|
||||
aoOptions : aoOptions,
|
||||
textureCompressionOptions : textureCompressionOptions
|
||||
};
|
||||
|
||||
return loadObj(objPath)
|
||||
.then(function(objData) {
|
||||
return createGltf(objData);
|
||||
})
|
||||
.then(function(gltf) {
|
||||
var aoOptions = ao ? {} : undefined;
|
||||
var options = {
|
||||
binary: binary,
|
||||
embed: embed,
|
||||
embedImage: embedImage,
|
||||
encodeNormals: compress,
|
||||
quantize: compress,
|
||||
aoOptions: aoOptions,
|
||||
optimizeForCesium : optimizeForCesium,
|
||||
createDirectory: false,
|
||||
basePath: inputPath
|
||||
};
|
||||
return GltfPipeline.processJSONToDisk(gltf, gltfFile, options);
|
||||
return saveExternalBuffer(gltf, gltfPath);
|
||||
})
|
||||
.then(function(gltf) {
|
||||
if (bypassPipeline) {
|
||||
return convert._outputJson(gltfPath, gltf);
|
||||
} else {
|
||||
return GltfPipeline.processJSONToDisk(gltf, gltfPath, pipelineOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposed for testing
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
convert._outputJson = fsExtraOutputJson;
|
||||
|
||||
function saveExternalBuffer(gltf, gltfPath) {
|
||||
var buffer = gltf.buffers[Object.keys(gltf.buffers)[0]];
|
||||
if (defined(buffer.uri)) {
|
||||
return Promise.resolve(gltf);
|
||||
}
|
||||
|
||||
var bufferName = path.basename(gltfPath, path.extname(gltfPath));
|
||||
var bufferUri = bufferName + '.bin';
|
||||
buffer.uri = bufferUri;
|
||||
var bufferPath = path.join(path.dirname(gltfPath), bufferUri);
|
||||
return fxExtraOutputFile(bufferPath, buffer)
|
||||
.then(function() {
|
||||
return gltf;
|
||||
});
|
||||
}
|
||||
|
562
lib/gltf.js
@ -1,148 +1,37 @@
|
||||
"use strict";
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var Promise = require('bluebird');
|
||||
var fs = require('fs-extra');
|
||||
var path = require('path');
|
||||
|
||||
var defined = Cesium.defined;
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
var WebGLConstants = Cesium.WebGLConstants;
|
||||
|
||||
var fsWriteFile = Promise.promisify(fs.writeFile);
|
||||
|
||||
module.exports = createGltf;
|
||||
|
||||
function createGltf(data, inputPath, modelName) {
|
||||
var vertexCount = data.vertexCount;
|
||||
var vertexArray = data.vertexArray;
|
||||
var positionMin = data.positionMin;
|
||||
var positionMax = data.positionMax;
|
||||
var hasUVs = data.hasUVs;
|
||||
var hasNormals = data.hasNormals;
|
||||
var materialGroups = data.materialGroups;
|
||||
var materials = data.materials;
|
||||
var images = data.images;
|
||||
|
||||
var i, j, name;
|
||||
|
||||
var sizeOfFloat32 = 4;
|
||||
var sizeOfUint32 = 4;
|
||||
var sizeOfUint16 = 2;
|
||||
|
||||
var indexComponentType;
|
||||
var indexComponentSize;
|
||||
|
||||
// Reserve the 65535 index for primitive restart
|
||||
if (vertexCount < 65535) {
|
||||
indexComponentType = WebGLConstants.UNSIGNED_SHORT;
|
||||
indexComponentSize = sizeOfUint16;
|
||||
} else {
|
||||
indexComponentType = WebGLConstants.UNSIGNED_INT;
|
||||
indexComponentSize = sizeOfUint32;
|
||||
}
|
||||
|
||||
// Create primitives
|
||||
var primitives = [];
|
||||
var indexArrayLength = 0;
|
||||
var indexArray;
|
||||
var indexCount;
|
||||
for (name in materialGroups) {
|
||||
if (materialGroups.hasOwnProperty(name)) {
|
||||
indexArray = materialGroups[name];
|
||||
indexCount = indexArray.length;
|
||||
primitives.push({
|
||||
indexArray : indexArray,
|
||||
indexOffset : indexArrayLength,
|
||||
indexCount : indexCount,
|
||||
material : name
|
||||
});
|
||||
indexArrayLength += indexCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Create buffer to store vertex and index data
|
||||
var indexArrayByteLength = indexArrayLength * indexComponentSize;
|
||||
var vertexArrayLength = vertexArray.length; // In floats
|
||||
var vertexArrayByteLength = vertexArrayLength * sizeOfFloat32;
|
||||
var bufferByteLength = vertexArrayByteLength + indexArrayByteLength;
|
||||
var buffer = new Buffer(bufferByteLength);
|
||||
|
||||
// Write vertex data
|
||||
var byteOffset = 0;
|
||||
for (i = 0; i < vertexArrayLength; ++i) {
|
||||
buffer.writeFloatLE(vertexArray[i], byteOffset);
|
||||
byteOffset += sizeOfFloat32;
|
||||
}
|
||||
|
||||
// Write index data
|
||||
var primitivesLength = primitives.length;
|
||||
for (i = 0; i < primitivesLength; ++i) {
|
||||
indexArray = primitives[i].indexArray;
|
||||
indexCount = indexArray.length;
|
||||
for (j = 0; j < indexCount; ++j) {
|
||||
if (indexComponentSize === sizeOfUint16) {
|
||||
buffer.writeUInt16LE(indexArray[j], byteOffset);
|
||||
} else {
|
||||
buffer.writeUInt32LE(indexArray[j], byteOffset);
|
||||
}
|
||||
byteOffset += indexComponentSize;
|
||||
}
|
||||
}
|
||||
|
||||
var positionByteOffset = 0;
|
||||
var normalByteOffset = 0;
|
||||
var uvByteOffset = 0;
|
||||
var vertexByteStride = 0;
|
||||
|
||||
if (hasNormals && hasUVs) {
|
||||
normalByteOffset = sizeOfFloat32 * 3;
|
||||
uvByteOffset = sizeOfFloat32 * 6;
|
||||
vertexByteStride = sizeOfFloat32 * 8;
|
||||
} else if (hasNormals && !hasUVs) {
|
||||
normalByteOffset = sizeOfFloat32 * 3;
|
||||
vertexByteStride = sizeOfFloat32 * 6;
|
||||
} else if (!hasNormals && hasUVs) {
|
||||
uvByteOffset = sizeOfFloat32 * 3;
|
||||
vertexByteStride = sizeOfFloat32 * 5;
|
||||
} else if (!hasNormals && !hasUVs) {
|
||||
vertexByteStride = sizeOfFloat32 * 3;
|
||||
}
|
||||
|
||||
var bufferId = modelName + '_buffer';
|
||||
var bufferViewVertexId = 'bufferView_vertex';
|
||||
var bufferViewIndexId = 'bufferView_index';
|
||||
var accessorPositionId = 'accessor_position';
|
||||
var accessorUVId = 'accessor_uv';
|
||||
var accessorNormalId = 'accessor_normal';
|
||||
var meshId = 'mesh_' + modelName;
|
||||
var sceneId = 'scene_' + modelName;
|
||||
var nodeId = 'node_' + modelName;
|
||||
var samplerId = 'sampler_0';
|
||||
|
||||
function getAccessorIndexId(i) {
|
||||
return 'accessor_index_' + i;
|
||||
}
|
||||
|
||||
function getMaterialId(material) {
|
||||
return 'material_' + material;
|
||||
}
|
||||
|
||||
function getTextureId(image) {
|
||||
if (!defined(image)) {
|
||||
return undefined;
|
||||
}
|
||||
return 'texture_' + path.basename(image).substr(0, image.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
function getImageId(image) {
|
||||
return path.basename(image, path.extname(image));
|
||||
}
|
||||
/**
|
||||
* Create a glTF from obj data.
|
||||
*
|
||||
* @param {Object} objData Output of obj.js, containing an array of nodes containing geometry information, materials, and images.
|
||||
* @returns {Object} A glTF asset with the KHR_materials_common extension.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function createGltf(objData) {
|
||||
var nodes = objData.nodes;
|
||||
var materials = objData.materials;
|
||||
var images = objData.images;
|
||||
var sceneId = 'scene';
|
||||
var samplerId = 'sampler';
|
||||
var bufferId = 'buffer';
|
||||
var vertexBufferViewId = 'bufferView_vertex';
|
||||
var indexBufferViewId = 'bufferView_index';
|
||||
|
||||
var gltf = {
|
||||
accessors : {},
|
||||
asset : {},
|
||||
buffers : {},
|
||||
bufferViews : {},
|
||||
extensionsUsed : ['KHR_materials_common'],
|
||||
images : {},
|
||||
materials : {},
|
||||
meshes : {},
|
||||
@ -154,174 +43,97 @@ function createGltf(data, inputPath, modelName) {
|
||||
};
|
||||
|
||||
gltf.asset = {
|
||||
"generator": "OBJ2GLTF",
|
||||
"premultipliedAlpha": true,
|
||||
"profile": {
|
||||
"api": "WebGL",
|
||||
"version": "1.0"
|
||||
generator : 'obj2gltf',
|
||||
profile : {
|
||||
api : 'WebGL',
|
||||
version : '1.0'
|
||||
},
|
||||
"version": 1
|
||||
version: '1.0'
|
||||
};
|
||||
|
||||
gltf.scenes[sceneId] = {
|
||||
nodes : [nodeId]
|
||||
nodes : []
|
||||
};
|
||||
|
||||
gltf.nodes[nodeId] = {
|
||||
children : [],
|
||||
matrix : [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
|
||||
meshes : [meshId],
|
||||
name : modelName
|
||||
};
|
||||
|
||||
gltf.samplers[samplerId] = {}; // Use default values
|
||||
|
||||
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');
|
||||
function getImageId(imagePath) {
|
||||
return path.basename(imagePath, path.extname(imagePath));
|
||||
}
|
||||
|
||||
gltf.buffers[bufferId] = {
|
||||
byteLength : bufferByteLength,
|
||||
type : 'arraybuffer',
|
||||
uri : bufferUri
|
||||
};
|
||||
|
||||
gltf.bufferViews[bufferViewVertexId] = {
|
||||
buffer : bufferId,
|
||||
byteLength : vertexArrayByteLength,
|
||||
byteOffset : 0,
|
||||
target : WebGLConstants.ARRAY_BUFFER
|
||||
};
|
||||
gltf.bufferViews[bufferViewIndexId] = {
|
||||
buffer : bufferId,
|
||||
byteLength : indexArrayByteLength,
|
||||
byteOffset : vertexArrayByteLength,
|
||||
target : WebGLConstants.ELEMENT_ARRAY_BUFFER
|
||||
};
|
||||
|
||||
for (i = 0; i < primitivesLength; ++i) {
|
||||
var primitive = primitives[i];
|
||||
gltf.accessors[getAccessorIndexId(i)] = {
|
||||
bufferView : bufferViewIndexId,
|
||||
byteOffset : primitive.indexOffset * indexComponentSize,
|
||||
byteStride : 0,
|
||||
componentType : indexComponentType,
|
||||
count : primitive.indexCount,
|
||||
type : 'SCALAR'
|
||||
};
|
||||
}
|
||||
|
||||
gltf.accessors[accessorPositionId] = {
|
||||
bufferView : bufferViewVertexId,
|
||||
byteOffset : positionByteOffset,
|
||||
byteStride : vertexByteStride,
|
||||
componentType : WebGLConstants.FLOAT,
|
||||
count : vertexCount,
|
||||
min : positionMin,
|
||||
max : positionMax,
|
||||
type : 'VEC3'
|
||||
};
|
||||
|
||||
if (hasNormals) {
|
||||
gltf.accessors[accessorNormalId] = {
|
||||
bufferView : bufferViewVertexId,
|
||||
byteOffset : normalByteOffset,
|
||||
byteStride : vertexByteStride,
|
||||
componentType : WebGLConstants.FLOAT,
|
||||
count : vertexCount,
|
||||
type : 'VEC3'
|
||||
};
|
||||
}
|
||||
|
||||
if (hasUVs) {
|
||||
gltf.accessors[accessorUVId] = {
|
||||
bufferView : bufferViewVertexId,
|
||||
byteOffset : uvByteOffset,
|
||||
byteStride : vertexByteStride,
|
||||
componentType : WebGLConstants.FLOAT,
|
||||
count : vertexCount,
|
||||
type : 'VEC2'
|
||||
};
|
||||
}
|
||||
|
||||
var gltfPrimitives = [];
|
||||
gltf.meshes[meshId] = {
|
||||
name : modelName,
|
||||
primitives : gltfPrimitives
|
||||
};
|
||||
|
||||
var gltfAttributes = {};
|
||||
gltfAttributes.POSITION = accessorPositionId;
|
||||
if (hasNormals) {
|
||||
gltfAttributes.NORMAL = accessorNormalId;
|
||||
}
|
||||
if (hasUVs) {
|
||||
gltfAttributes.TEXCOORD_0 = accessorUVId;
|
||||
}
|
||||
|
||||
for (i = 0; i < primitivesLength; ++i) {
|
||||
gltfPrimitives.push({
|
||||
attributes : gltfAttributes,
|
||||
indices : getAccessorIndexId(i),
|
||||
material : getMaterialId(primitives[i].material),
|
||||
mode : WebGLConstants.TRIANGLES
|
||||
});
|
||||
}
|
||||
|
||||
for (name in materials) {
|
||||
if (materials.hasOwnProperty(name)) {
|
||||
var material = materials[name];
|
||||
var materialId = getMaterialId(name);
|
||||
var values = {
|
||||
ambient : defaultValue(defaultValue(getTextureId(material.ambientColorMap), material.ambientColor), [0, 0, 0, 1]),
|
||||
diffuse : defaultValue(defaultValue(getTextureId(material.diffuseColorMap), material.diffuseColor), [0, 0, 0, 1]),
|
||||
emission : defaultValue(defaultValue(getTextureId(material.emissionColorMap), material.emissionColor), [0, 0, 0, 1]),
|
||||
specular : defaultValue(defaultValue(getTextureId(material.specularColorMap), material.specularColor), [0, 0, 0, 1]),
|
||||
shininess : defaultValue(material.specularShininess, 0.0)
|
||||
};
|
||||
|
||||
gltf.materials[materialId] = {
|
||||
name: name,
|
||||
values: values
|
||||
};
|
||||
function getTextureId(imagePath) {
|
||||
if (!defined(imagePath) || !defined(images[imagePath])) {
|
||||
return undefined;
|
||||
}
|
||||
return 'texture_' + getImageId(imagePath);
|
||||
}
|
||||
|
||||
for (name in images) {
|
||||
if (images.hasOwnProperty(name)) {
|
||||
var image = images[name];
|
||||
var imageId = getImageId(name);
|
||||
var textureId = getTextureId(name);
|
||||
var format;
|
||||
var channels = image.channels;
|
||||
switch (channels) {
|
||||
case 1:
|
||||
format = WebGLConstants.ALPHA;
|
||||
break;
|
||||
case 2:
|
||||
format = WebGLConstants.LUMINANCE_ALPHA;
|
||||
break;
|
||||
case 3:
|
||||
format = WebGLConstants.RGB;
|
||||
break;
|
||||
case 4:
|
||||
format = WebGLConstants.RGBA;
|
||||
break;
|
||||
function createMaterial(material, hasNormals) {
|
||||
var ambient = defaultValue(defaultValue(getTextureId(material.ambientColorMap), material.ambientColor), [0, 0, 0, 1]);
|
||||
var diffuse = defaultValue(defaultValue(getTextureId(material.diffuseColorMap), material.diffuseColor), [0.5, 0.5, 0.5, 1]);
|
||||
var emission = defaultValue(defaultValue(getTextureId(material.emissionColorMap), material.emissionColor), [0, 0, 0, 1]);
|
||||
var specular = defaultValue(defaultValue(getTextureId(material.specularColorMap), material.specularColor), [0, 0, 0, 1]);
|
||||
var alpha = defaultValue(defaultValue(material.alpha), 1.0);
|
||||
var shininess = defaultValue(material.specularShininess, 0.0);
|
||||
var hasSpecular = (shininess > 0.0) && (specular[0] > 0.0 || specular[1] > 0.0 || specular[2] > 0.0);
|
||||
|
||||
var transparent;
|
||||
var transparency = 1.0;
|
||||
if (typeof diffuse === 'string') {
|
||||
transparency = alpha;
|
||||
transparent = images[material.diffuseColorMap].transparent || (transparency < 1.0);
|
||||
} else {
|
||||
diffuse[3] = alpha;
|
||||
transparent = diffuse[3] < 1.0;
|
||||
}
|
||||
|
||||
var doubleSided = transparent;
|
||||
|
||||
if (!hasNormals) {
|
||||
// Constant technique only factors in ambient and emission sources - set emission to diffuse
|
||||
emission = diffuse;
|
||||
}
|
||||
|
||||
var technique = hasNormals ? (hasSpecular ? 'PHONG' : 'LAMBERT') : 'CONSTANT';
|
||||
return {
|
||||
extensions : {
|
||||
KHR_materials_common : {
|
||||
technique : technique,
|
||||
values : {
|
||||
ambient : ambient,
|
||||
diffuse : diffuse,
|
||||
emission : emission,
|
||||
specular : specular,
|
||||
shininess : shininess,
|
||||
transparency : transparency,
|
||||
transparent : transparent,
|
||||
doubleSided : doubleSided
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (Object.keys(images).length > 0) {
|
||||
gltf.samplers[samplerId] = {
|
||||
magFilter : WebGLConstants.LINEAR,
|
||||
minFilter : WebGLConstants.LINEAR,
|
||||
wrapS : WebGLConstants.REPEAT,
|
||||
wrapT : WebGLConstants.REPEAT
|
||||
};
|
||||
}
|
||||
|
||||
for (var imagePath in images) {
|
||||
if (images.hasOwnProperty(imagePath)) {
|
||||
var image = images[imagePath];
|
||||
var imageId = getImageId(imagePath);
|
||||
var textureId = getTextureId(imagePath);
|
||||
|
||||
gltf.images[imageId] = {
|
||||
name : imageId,
|
||||
uri : image.uri
|
||||
};
|
||||
gltf.textures[textureId] = {
|
||||
format : format,
|
||||
internalFormat : format,
|
||||
format : image.format,
|
||||
internalFormat : image.format,
|
||||
sampler : samplerId,
|
||||
source : imageId,
|
||||
target : WebGLConstants.TEXTURE_2D,
|
||||
@ -330,9 +142,191 @@ function createGltf(data, inputPath, modelName) {
|
||||
}
|
||||
}
|
||||
|
||||
if (bufferSeparate) {
|
||||
var bufferPath = path.join(inputPath, modelName + '.bin');
|
||||
return fsWriteFile(bufferPath, buffer);
|
||||
var vertexBuffers = [];
|
||||
var vertexByteOffset = 0;
|
||||
var indexBuffers = [];
|
||||
var indexBuffersByteOffset = 0;
|
||||
var accessorCount = 0;
|
||||
|
||||
function addVertexAttribute(array, components) {
|
||||
var count = array.length / components;
|
||||
var buffer = array.toFloatBuffer();
|
||||
var minMax = array.getMinMax(components);
|
||||
|
||||
var type = (components === 3 ? 'VEC3' : 'VEC2');
|
||||
var accessor = {
|
||||
bufferView : vertexBufferViewId,
|
||||
byteOffset : vertexByteOffset,
|
||||
byteStride : 0,
|
||||
componentType : WebGLConstants.FLOAT,
|
||||
count : count,
|
||||
min : minMax.min,
|
||||
max : minMax.max,
|
||||
type : type
|
||||
};
|
||||
|
||||
vertexByteOffset += buffer.length;
|
||||
vertexBuffers.push(buffer);
|
||||
var accessorId = 'accessor_' + accessorCount++;
|
||||
gltf.accessors[accessorId] = accessor;
|
||||
return accessorId;
|
||||
}
|
||||
|
||||
function addIndexArray(array, uint32Indices) {
|
||||
var buffer = uint32Indices ? array.toUint32Buffer() : array.toUint16Buffer();
|
||||
var componentType = uint32Indices ? WebGLConstants.UNSIGNED_INT : WebGLConstants.UNSIGNED_SHORT;
|
||||
var length = array.length;
|
||||
var minMax = array.getMinMax(1);
|
||||
var accessor = {
|
||||
bufferView : indexBufferViewId,
|
||||
byteOffset : indexBuffersByteOffset,
|
||||
byteStride : 0,
|
||||
componentType : componentType,
|
||||
count : length,
|
||||
min : minMax.min,
|
||||
max : minMax.max,
|
||||
type : 'SCALAR'
|
||||
};
|
||||
|
||||
indexBuffersByteOffset += buffer.length;
|
||||
indexBuffers.push(buffer);
|
||||
|
||||
var accessorId = 'accessor_' + accessorCount++;
|
||||
gltf.accessors[accessorId] = accessor;
|
||||
return accessorId;
|
||||
}
|
||||
|
||||
function requiresUint32Indices(nodes) {
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
var meshes = nodes[i].meshes;
|
||||
var meshesLength = meshes.length;
|
||||
for (var j = 0; j < meshesLength; ++j) {
|
||||
// Reserve the 65535 index for primitive restart
|
||||
var vertexCount = meshes[j].positions.length / 3;
|
||||
if (vertexCount > 65534) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var uint32Indices = requiresUint32Indices(nodes);
|
||||
var gltfSceneNodes = gltf.scenes[sceneId].nodes;
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
// Add node
|
||||
var node = nodes[i];
|
||||
var nodeId = node.name;
|
||||
gltfSceneNodes.push(nodeId);
|
||||
var gltfNodeMeshes = [];
|
||||
gltf.nodes[nodeId] = {
|
||||
name : nodeId,
|
||||
meshes : gltfNodeMeshes
|
||||
};
|
||||
|
||||
// Add meshes to node
|
||||
var meshes = node.meshes;
|
||||
var meshesLength = meshes.length;
|
||||
for (var j = 0; j < meshesLength; ++j) {
|
||||
var mesh = meshes[j];
|
||||
var meshId = mesh.name;
|
||||
gltfNodeMeshes.push(meshId);
|
||||
|
||||
var hasPositions = mesh.positions.length > 0;
|
||||
var hasNormals = mesh.normals.length > 0;
|
||||
var hasUVs = mesh.uvs.length > 0;
|
||||
|
||||
var attributes = {};
|
||||
if (hasPositions) {
|
||||
attributes.POSITION = addVertexAttribute(mesh.positions, 3);
|
||||
}
|
||||
if (hasNormals) {
|
||||
attributes.NORMAL = addVertexAttribute(mesh.normals, 3);
|
||||
}
|
||||
if (hasUVs) {
|
||||
attributes.TEXCOORD_0 = addVertexAttribute(mesh.uvs, 2);
|
||||
}
|
||||
|
||||
// Unload resources
|
||||
mesh.positions = undefined;
|
||||
mesh.normals = undefined;
|
||||
mesh.uvs = undefined;
|
||||
|
||||
var gltfMeshPrimitives = [];
|
||||
gltf.meshes[meshId] = {
|
||||
name : meshId,
|
||||
primitives : gltfMeshPrimitives
|
||||
};
|
||||
|
||||
// Add primitives to mesh
|
||||
var primitives = mesh.primitives;
|
||||
var primitivesLength = primitives.length;
|
||||
for (var k = 0; k < primitivesLength; ++k) {
|
||||
var primitive = primitives[k];
|
||||
var indexAccessorId = addIndexArray(primitive.indices, uint32Indices);
|
||||
primitive.indices = undefined; // Unload resources
|
||||
var materialId = primitive.material;
|
||||
|
||||
if (!defined(materialId)) {
|
||||
// Create a default material if the primitive does not specify one
|
||||
materialId = 'default';
|
||||
}
|
||||
|
||||
var material = defaultValue(materials[materialId], {});
|
||||
var gltfMaterial = gltf.materials[materialId];
|
||||
if (defined(gltfMaterial)) {
|
||||
// Check if this material has already been added but with incompatible shading
|
||||
var normalShading = (gltfMaterial.extensions.KHR_materials_common.technique !== 'CONSTANT');
|
||||
if (hasNormals !== normalShading) {
|
||||
materialId += (hasNormals ? '_shaded' : '_constant');
|
||||
gltfMaterial = gltf.materials[materialId];
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined(gltfMaterial)) {
|
||||
gltf.materials[materialId] = createMaterial(material, hasNormals);
|
||||
}
|
||||
|
||||
gltfMeshPrimitives.push({
|
||||
attributes : attributes,
|
||||
indices : indexAccessorId,
|
||||
material : materialId,
|
||||
mode : WebGLConstants.TRIANGLES
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vertexBuffer = Buffer.concat(vertexBuffers);
|
||||
var indexBuffer = Buffer.concat(indexBuffers);
|
||||
var buffer = Buffer.concat([vertexBuffer, indexBuffer]);
|
||||
|
||||
// Buffers larger than ~192MB cannot be base64 encoded due to a NodeJS limitation. Instead the buffer will be saved to a .bin file. Source: https://github.com/nodejs/node/issues/4266
|
||||
var bufferUri;
|
||||
if (buffer.length <= 201326580) {
|
||||
bufferUri = 'data:application/octet-stream;base64,' + buffer.toString('base64');
|
||||
}
|
||||
|
||||
gltf.buffers[bufferId] = {
|
||||
byteLength : buffer.byteLength,
|
||||
uri : bufferUri
|
||||
};
|
||||
|
||||
gltf.bufferViews[vertexBufferViewId] = {
|
||||
buffer : bufferId,
|
||||
byteLength : vertexBuffer.length,
|
||||
byteOffset : 0,
|
||||
target : WebGLConstants.ARRAY_BUFFER
|
||||
};
|
||||
|
||||
gltf.bufferViews[indexBufferViewId] = {
|
||||
buffer : bufferId,
|
||||
byteLength : indexBuffer.length,
|
||||
byteOffset : vertexBuffer.length,
|
||||
target : WebGLConstants.ELEMENT_ARRAY_BUFFER
|
||||
};
|
||||
|
||||
return gltf;
|
||||
}
|
||||
|
92
lib/image.js
@ -1,12 +1,55 @@
|
||||
"use strict";
|
||||
var Promise = require('bluebird');
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var fs = require('fs-extra');
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
var fsReadFile = Promise.promisify(fs.readFile);
|
||||
|
||||
var WebGLConstants = Cesium.WebGLConstants;
|
||||
|
||||
module.exports = loadImage;
|
||||
|
||||
/**
|
||||
* Load an image file and get information about it.
|
||||
*
|
||||
* @param {String} imagePath Path to the image file.
|
||||
* @returns {Promise} A promise resolving to the image information, or undefined if the file doesn't exist.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function loadImage(imagePath) {
|
||||
return fsReadFile(imagePath)
|
||||
.then(function(data) {
|
||||
var extension = path.extname(imagePath);
|
||||
var uriType = getUriType(extension);
|
||||
var uri = uriType + ';base64,' + data.toString('base64');
|
||||
|
||||
var info = {
|
||||
transparent : false,
|
||||
channels : 3,
|
||||
data : data,
|
||||
uri : uri,
|
||||
format : getFormat(3)
|
||||
};
|
||||
|
||||
if (extension === '.png') {
|
||||
// Color type is encoded in the 25th bit of the png
|
||||
var colorType = data[25];
|
||||
var channels = getChannels(colorType);
|
||||
info.channels = channels;
|
||||
info.transparent = (channels === 4);
|
||||
info.format = getFormat(channels);
|
||||
}
|
||||
|
||||
return info;
|
||||
})
|
||||
.catch(function() {
|
||||
console.log('Could not read image file at ' + imagePath + '. Material will ignore this image.');
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function getChannels(colorType) {
|
||||
switch (colorType) {
|
||||
case 0: // greyscale
|
||||
@ -24,40 +67,27 @@ function getChannels(colorType) {
|
||||
|
||||
function getUriType(extension) {
|
||||
switch (extension) {
|
||||
case 'png':
|
||||
case '.png':
|
||||
return 'data:image/png';
|
||||
case 'jpg':
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
return 'data:image/jpeg';
|
||||
case 'jpeg':
|
||||
return 'data:image/jpeg';
|
||||
case 'gif':
|
||||
case '.gif':
|
||||
return 'data:image/gif';
|
||||
default:
|
||||
return 'data:image/' + extension;
|
||||
return 'data:image/' + extension.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
function loadImage(imagePath) {
|
||||
return fsReadFile(imagePath)
|
||||
.then(function(data) {
|
||||
var extension = path.extname(imagePath).slice(1);
|
||||
var uriType = getUriType(extension);
|
||||
var uri = uriType + ';base64,' + data.toString('base64');
|
||||
|
||||
var info = {
|
||||
transparent: false,
|
||||
channels: 3,
|
||||
data: data,
|
||||
uri: uri
|
||||
};
|
||||
|
||||
if (path.extname(imagePath) === 'png') {
|
||||
// Color type is encoded in the 25th bit of the png
|
||||
var colorType = data[25];
|
||||
var channels = getChannels(colorType);
|
||||
info.channels = channels;
|
||||
info.transparent = (channels === 4);
|
||||
}
|
||||
return info;
|
||||
});
|
||||
function getFormat(channels) {
|
||||
switch (channels) {
|
||||
case 1:
|
||||
return WebGLConstants.ALPHA;
|
||||
case 2:
|
||||
return WebGLConstants.LUMINANCE_ALPHA;
|
||||
case 3:
|
||||
return WebGLConstants.RGB;
|
||||
case 4:
|
||||
return WebGLConstants.RGBA;
|
||||
}
|
||||
}
|
||||
|
210
lib/mtl.js
@ -1,114 +1,105 @@
|
||||
"use strict";
|
||||
var Promise = require('bluebird');
|
||||
var fs = require('fs-extra');
|
||||
var defined = require('cesium').defined;
|
||||
'use strict';
|
||||
var path = require('path');
|
||||
var readLines = require('./readLines');
|
||||
|
||||
var fsReadFile = Promise.promisify(fs.readFile);
|
||||
module.exports = loadMtl;
|
||||
|
||||
module.exports = {
|
||||
getDefault : getDefault,
|
||||
parse : parse
|
||||
};
|
||||
|
||||
function createMaterial() {
|
||||
return {
|
||||
ambientColor : undefined, // Ka
|
||||
emissionColor : undefined, // Ke
|
||||
diffuseColor : undefined, // Kd
|
||||
specularColor : undefined, // Ks
|
||||
specularShininess : undefined, // Ns
|
||||
alpha : undefined, // d / Tr
|
||||
ambientColorMap : undefined, // map_Ka
|
||||
emissionColorMap : undefined, // map_Ke
|
||||
diffuseColorMap : undefined, // map_Kd
|
||||
specularColorMap : undefined, // map_Ks
|
||||
specularShininessMap : undefined, // map_Ns
|
||||
normalMap : undefined, // map_Bump
|
||||
alphaMap : undefined // map_d
|
||||
};
|
||||
function Material() {
|
||||
this.ambientColor = undefined; // Ka
|
||||
this.emissionColor = undefined; // Ke
|
||||
this.diffuseColor = undefined; // Kd
|
||||
this.specularColor = undefined; // Ks
|
||||
this.specularShininess = undefined; // Ns
|
||||
this.alpha = undefined; // d / Tr
|
||||
this.ambientColorMap = undefined; // map_Ka
|
||||
this.emissionColorMap = undefined; // map_Ke
|
||||
this.diffuseColorMap = undefined; // map_Kd
|
||||
this.specularColorMap = undefined; // map_Ks
|
||||
this.specularShininessMap = undefined; // map_Ns
|
||||
this.normalMap = undefined; // map_Bump
|
||||
this.alphaMap = undefined; // map_d
|
||||
}
|
||||
|
||||
function getDefault() {
|
||||
var material = createMaterial();
|
||||
material.diffuseColor = [0.5, 0.5, 0.5, 1.0];
|
||||
return material;
|
||||
}
|
||||
/**
|
||||
* Parse an mtl file.
|
||||
*
|
||||
* @param {String} mtlPath Path to the mtl file.
|
||||
* @returns {Promise} A promise resolving to the materials, or an empty object if the mtl file doesn't exist.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function loadMtl(mtlPath) {
|
||||
var material;
|
||||
var values;
|
||||
var value;
|
||||
var materials = {};
|
||||
|
||||
function parse(mtlPath) {
|
||||
return fsReadFile(mtlPath, 'utf8')
|
||||
.then(function (contents) {
|
||||
var materials = {};
|
||||
var material;
|
||||
var values;
|
||||
var value;
|
||||
var lines = contents.split('\n');
|
||||
var length = lines.length;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
var line = lines[i].trim();
|
||||
if (/^newmtl /i.test(line)) {
|
||||
var name = line.substring(7).trim();
|
||||
material = createMaterial();
|
||||
materials[name] = material;
|
||||
} else if (/^Ka /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.ambientColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
1.0
|
||||
];
|
||||
} else if (/^Ke /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.emissionColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
1.0
|
||||
];
|
||||
} else if (/^Kd /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.diffuseColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
1.0
|
||||
];
|
||||
} else if (/^Ks /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.specularColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
1.0
|
||||
];
|
||||
} else if (/^Ns /i.test(line)) {
|
||||
value = line.substring(3).trim();
|
||||
material.specularShininess = parseFloat(value);
|
||||
} 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)) {
|
||||
material.emissionColorMap = line.substring(7).trim();
|
||||
} else if (/^map_Kd /i.test(line)) {
|
||||
material.diffuseColorMap = line.substring(7).trim();
|
||||
} else if (/^map_Ks /i.test(line)) {
|
||||
material.specularColorMap = line.substring(7).trim();
|
||||
} else if (/^map_Ns /i.test(line)) {
|
||||
material.specularShininessMap = line.substring(7).trim();
|
||||
} else if (/^map_Bump /i.test(line)) {
|
||||
material.normalMap = line.substring(9).trim();
|
||||
} else if (/^map_d /i.test(line)) {
|
||||
material.alphaMap = line.substring(6).trim();
|
||||
}
|
||||
}
|
||||
if (defined(material.alpha)) {
|
||||
material.diffuseColor[3] = material.alpha;
|
||||
}
|
||||
function parseLine(line) {
|
||||
line = line.trim();
|
||||
if (/^newmtl /i.test(line)) {
|
||||
var name = line.substring(7).trim();
|
||||
material = new Material();
|
||||
materials[name] = material;
|
||||
} else if (/^Ka /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.ambientColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
1.0
|
||||
];
|
||||
} else if (/^Ke /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.emissionColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
1.0
|
||||
];
|
||||
} else if (/^Kd /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.diffuseColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
1.0
|
||||
];
|
||||
} else if (/^Ks /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.specularColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
1.0
|
||||
];
|
||||
} else if (/^Ns /i.test(line)) {
|
||||
value = line.substring(3).trim();
|
||||
material.specularShininess = parseFloat(value);
|
||||
} 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 = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||
} else if (/^map_Ke /i.test(line)) {
|
||||
material.emissionColorMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||
} else if (/^map_Kd /i.test(line)) {
|
||||
material.diffuseColorMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||
} else if (/^map_Ks /i.test(line)) {
|
||||
material.specularColorMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||
} else if (/^map_Ns /i.test(line)) {
|
||||
material.specularShininessMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||
} else if (/^map_Bump /i.test(line)) {
|
||||
material.normalMap = getAbsolutePath(line.substring(9).trim(), mtlPath);
|
||||
} else if (/^map_d /i.test(line)) {
|
||||
material.alphaMap = getAbsolutePath(line.substring(6).trim(), mtlPath);
|
||||
}
|
||||
}
|
||||
|
||||
return readLines(mtlPath, parseLine)
|
||||
.then(function() {
|
||||
return materials;
|
||||
})
|
||||
.catch(function() {
|
||||
@ -116,3 +107,10 @@ function parse(mtlPath) {
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
function getAbsolutePath(imagePath, mtlPath) {
|
||||
if (!path.isAbsolute(imagePath)) {
|
||||
imagePath = path.join(path.dirname(mtlPath), imagePath);
|
||||
}
|
||||
return imagePath;
|
||||
}
|
||||
|
740
lib/obj.js
@ -1,365 +1,447 @@
|
||||
"use strict";
|
||||
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var Promise = require('bluebird');
|
||||
var byline = require('byline');
|
||||
var fs = require('fs-extra');
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
var ArrayStorage = require('./ArrayStorage');
|
||||
var loadImage = require('./image');
|
||||
var Material = require('./mtl');
|
||||
var loadMtl = require('./mtl');
|
||||
var readLines = require('./readLines');
|
||||
|
||||
var Cartesian3 = Cesium.Cartesian3;
|
||||
var combine = Cesium.combine;
|
||||
var ComponentDatatype = Cesium.ComponentDatatype;
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
var defined = Cesium.defined;
|
||||
var RuntimeError = Cesium.RuntimeError;
|
||||
|
||||
module.exports = parseObj;
|
||||
module.exports = loadObj;
|
||||
|
||||
// OBJ regex patterns are from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js)
|
||||
// Object name (o) -> node
|
||||
// Group name (g) -> mesh
|
||||
// Material name (usemtl) -> primitive
|
||||
|
||||
function parseObj(objFile, inputPath) {
|
||||
return getObjInfo(objFile, inputPath)
|
||||
.then(function(result) {
|
||||
var info = result.info;
|
||||
var materials = result.materials;
|
||||
var images = result.images;
|
||||
return processObj(objFile, info, materials, images);
|
||||
function Node() {
|
||||
this.name = undefined;
|
||||
this.meshes = [];
|
||||
}
|
||||
|
||||
function Mesh() {
|
||||
this.name = undefined;
|
||||
this.primitives = [];
|
||||
this.positions = new ArrayStorage(ComponentDatatype.FLOAT);
|
||||
this.normals = new ArrayStorage(ComponentDatatype.FLOAT);
|
||||
this.uvs = new ArrayStorage(ComponentDatatype.FLOAT);
|
||||
}
|
||||
|
||||
function Primitive() {
|
||||
this.material = undefined;
|
||||
this.indices = new ArrayStorage(ComponentDatatype.UNSIGNED_INT);
|
||||
}
|
||||
|
||||
// OBJ regex patterns are modified from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js)
|
||||
var vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // v float float float
|
||||
var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vn float float float
|
||||
var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float
|
||||
var facePattern1 = /f( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)?\/?/; // f vertex vertex vertex ...
|
||||
var facePattern2 = /f( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)?/; // f vertex/uv vertex/uv vertex/uv ...
|
||||
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 ...
|
||||
|
||||
/**
|
||||
* Parse an obj file.
|
||||
*
|
||||
* @param {String} objPath Path to the obj file.
|
||||
* @returns {Promise} A promise resolving to the obj data.
|
||||
* @exception {RuntimeError} The file does not have any geometry information in it.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function loadObj(objPath) {
|
||||
// Global store of vertex attributes listed in the obj file
|
||||
var positions = new ArrayStorage(ComponentDatatype.FLOAT);
|
||||
var normals = new ArrayStorage(ComponentDatatype.FLOAT);
|
||||
var uvs = new ArrayStorage(ComponentDatatype.FLOAT);
|
||||
|
||||
// The current node, mesh, and primitive
|
||||
var node;
|
||||
var mesh;
|
||||
var primitive;
|
||||
|
||||
// All nodes seen in the obj
|
||||
var nodes = [];
|
||||
|
||||
// Used to build the indices. The vertex cache is unique to each mesh.
|
||||
var vertexCache = {};
|
||||
var vertexCacheLimit = 1000000;
|
||||
var vertexCacheCount = 0;
|
||||
var vertexCount = 0;
|
||||
|
||||
// All mtl paths seen in the obj
|
||||
var mtlPaths = [];
|
||||
|
||||
function getName(name) {
|
||||
return (name === '' ? undefined : name);
|
||||
}
|
||||
|
||||
function addNode(name) {
|
||||
node = new Node();
|
||||
node.name = getName(name);
|
||||
nodes.push(node);
|
||||
addMesh();
|
||||
}
|
||||
|
||||
function addMesh(name) {
|
||||
mesh = new Mesh();
|
||||
mesh.name = getName(name);
|
||||
node.meshes.push(mesh);
|
||||
addPrimitive();
|
||||
|
||||
// Clear the vertex cache for each new mesh
|
||||
vertexCache = {};
|
||||
vertexCacheCount = 0;
|
||||
vertexCount = 0;
|
||||
}
|
||||
|
||||
function addPrimitive() {
|
||||
primitive = new Primitive();
|
||||
mesh.primitives.push(primitive);
|
||||
}
|
||||
|
||||
function useMaterial(name) {
|
||||
// Look to see if this material has already been used by a primitive in the mesh
|
||||
var material = getName(name);
|
||||
var primitives = mesh.primitives;
|
||||
var primitivesLength = primitives.length;
|
||||
for (var i = 0; i < primitivesLength; ++i) {
|
||||
primitive = primitives[i]; // Sets the active primitive in case of returning early
|
||||
if (primitive.material === material) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Add a new primitive with this material
|
||||
addPrimitive();
|
||||
primitive.material = getName(name);
|
||||
}
|
||||
|
||||
function getOffset(a, attributeData, components) {
|
||||
var i = parseInt(a);
|
||||
if (i < 0) {
|
||||
// Negative vertex indexes reference the vertices immediately above it
|
||||
return (attributeData.length / components + i) * components;
|
||||
}
|
||||
return (i - 1) * components;
|
||||
}
|
||||
|
||||
function createVertex(p, u, n) {
|
||||
// Positions
|
||||
if (defined(p)) {
|
||||
var pi = getOffset(p, positions, 3);
|
||||
var px = positions.get(pi + 0);
|
||||
var py = positions.get(pi + 1);
|
||||
var pz = positions.get(pi + 2);
|
||||
mesh.positions.push(px);
|
||||
mesh.positions.push(py);
|
||||
mesh.positions.push(pz);
|
||||
}
|
||||
|
||||
// Normals
|
||||
if (defined(n)) {
|
||||
var ni = getOffset(n, normals, 3);
|
||||
var nx = normals.get(ni + 0);
|
||||
var ny = normals.get(ni + 1);
|
||||
var nz = normals.get(ni + 2);
|
||||
mesh.normals.push(nx);
|
||||
mesh.normals.push(ny);
|
||||
mesh.normals.push(nz);
|
||||
}
|
||||
|
||||
// UVs
|
||||
if (defined(u)) {
|
||||
var ui = getOffset(u, uvs, 2);
|
||||
var ux = uvs.get(ui + 0);
|
||||
var uy = uvs.get(ui + 1);
|
||||
mesh.uvs.push(ux);
|
||||
mesh.uvs.push(uy);
|
||||
}
|
||||
}
|
||||
|
||||
function addVertex(v, p, u, n) {
|
||||
var index = vertexCache[v];
|
||||
if (!defined(index)) {
|
||||
index = vertexCount++;
|
||||
vertexCache[v] = index;
|
||||
createVertex(p, u, n);
|
||||
|
||||
// Prevent the vertex cache from growing too large. As a result of clearing the cache there
|
||||
// may be some duplicate vertices.
|
||||
vertexCacheCount++;
|
||||
if (vertexCacheCount > vertexCacheLimit) {
|
||||
vertexCacheCount = 0;
|
||||
vertexCache = {};
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
function addFace(v1, p1, u1, n1, v2, p2, u2, n2, v3, p3, u3, n3, v4, p4, u4, n4) {
|
||||
var index1 = addVertex(v1, p1, u1, n1);
|
||||
var index2 = addVertex(v2, p2, u2, n2);
|
||||
var index3 = addVertex(v3, p3, u3, n3);
|
||||
|
||||
primitive.indices.push(index1);
|
||||
primitive.indices.push(index2);
|
||||
primitive.indices.push(index3);
|
||||
|
||||
// Triangulate if the face is a quad
|
||||
if (defined(v4)) {
|
||||
var index4 = addVertex(v4, p4, u4, n4);
|
||||
primitive.indices.push(index1);
|
||||
primitive.indices.push(index3);
|
||||
primitive.indices.push(index4);
|
||||
}
|
||||
}
|
||||
|
||||
function parseLine(line) {
|
||||
line = line.trim();
|
||||
var result;
|
||||
|
||||
if ((line.length === 0) || (line.charAt(0) === '#')) {
|
||||
// Don't process empty lines or comments
|
||||
} else if (/^o\s/i.test(line)) {
|
||||
var objectName = line.substring(2).trim();
|
||||
addNode(objectName);
|
||||
} else if (/^g\s/i.test(line)) {
|
||||
var groupName = line.substring(2).trim();
|
||||
addMesh(groupName);
|
||||
} else if (/^usemtl\s/i.test(line)) {
|
||||
var materialName = line.substring(7).trim();
|
||||
useMaterial(materialName);
|
||||
} else if (/^mtllib/i.test(line)) {
|
||||
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]));
|
||||
} else if ((result = normalPattern.exec(line) ) !== null) {
|
||||
normals.push(parseFloat(result[1]));
|
||||
normals.push(parseFloat(result[2]));
|
||||
normals.push(parseFloat(result[3]));
|
||||
} 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
|
||||
} else if ((result = facePattern1.exec(line)) !== null) {
|
||||
addFace(
|
||||
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], 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(
|
||||
result[1], result[2], result[3], result[4],
|
||||
result[5], result[6], result[7], result[8],
|
||||
result[9], result[10], result[11], result[12],
|
||||
result[13], result[14], result[15], result[16]
|
||||
);
|
||||
} else if ((result = facePattern4.exec(line)) !== null) {
|
||||
addFace(
|
||||
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]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a default node in case there are no o/g/usemtl lines in the obj
|
||||
addNode();
|
||||
|
||||
// Parse the obj file
|
||||
return readLines(objPath, parseLine)
|
||||
.then(function() {
|
||||
// Unload resources
|
||||
positions = undefined;
|
||||
normals = undefined;
|
||||
uvs = undefined;
|
||||
|
||||
// Load materials and images
|
||||
return finishLoading(nodes, mtlPaths, objPath);
|
||||
});
|
||||
}
|
||||
|
||||
function processObj(objFile, info, materials, images) {
|
||||
return new Promise(function(resolve) {
|
||||
// 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;
|
||||
|
||||
var vertexArray = [];
|
||||
|
||||
var positions = [];
|
||||
var normals = [];
|
||||
var uvs = [];
|
||||
|
||||
var positionMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE];
|
||||
var positionMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE];
|
||||
|
||||
var hasNormals = info.hasNormals;
|
||||
var hasUVs = info.hasUVs;
|
||||
|
||||
var materialGroups = {}; // Map material to index array
|
||||
var currentIndexArray;
|
||||
|
||||
// Switch to the material-specific index array, or create it if it doesn't exist
|
||||
function useMaterial(material) {
|
||||
if (!defined(materials[material])) {
|
||||
useDefaultMaterial();
|
||||
} else {
|
||||
currentIndexArray = materialGroups[material];
|
||||
if (!defined(currentIndexArray)) {
|
||||
currentIndexArray = [];
|
||||
materialGroups[material] = currentIndexArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useDefaultMaterial() {
|
||||
var defaultMaterial = 'czmDefaultMat';
|
||||
if (!defined(materials[defaultMaterial])) {
|
||||
materials[defaultMaterial] = Material.getDefault();
|
||||
}
|
||||
useMaterial(defaultMaterial);
|
||||
}
|
||||
|
||||
var materialsLength = Object.keys(materials).length;
|
||||
if (materialsLength === 0) {
|
||||
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 = 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]);
|
||||
positionMax[0] = Math.max(px, positionMax[0]);
|
||||
positionMax[1] = Math.max(py, positionMax[1]);
|
||||
positionMax[2] = Math.max(pz, positionMax[2]);
|
||||
vertexArray.push(px, py, pz);
|
||||
|
||||
// Normals
|
||||
if (hasNormals) {
|
||||
var ni = getOffset(n, normals, 3);
|
||||
var nx = normals[ni + 0];
|
||||
var ny = normals[ni + 1];
|
||||
var nz = normals[ni + 2];
|
||||
vertexArray.push(nx, ny, nz);
|
||||
}
|
||||
|
||||
// UVs
|
||||
if (hasUVs) {
|
||||
if (defined(u)) {
|
||||
var ui = getOffset(u, uvs, 2);
|
||||
var ux = uvs[ui + 0];
|
||||
var uy = uvs[ui + 1];
|
||||
// Flip y so 0.0 is the bottom of the image
|
||||
uy = 1.0 - uy;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addVertex(v, p, u, n) {
|
||||
var index = vertexCache[v];
|
||||
if (!defined(index)) {
|
||||
index = vertexCount++;
|
||||
vertexCache[v] = index;
|
||||
createVertex(p, u, n);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
function addFace(v1, p1, u1, n1, v2, p2, u2, n2, v3, p3, u3, n3, v4, p4, u4, n4) {
|
||||
var index1 = addVertex(v1, p1, u1, n1);
|
||||
var index2 = addVertex(v2, p2, u2, n2);
|
||||
var index3 = addVertex(v3, p3, u3, n3);
|
||||
|
||||
currentIndexArray.push(index1);
|
||||
currentIndexArray.push(index2);
|
||||
currentIndexArray.push(index3);
|
||||
|
||||
// Triangulate if the face is a quad
|
||||
if (defined(v4)) {
|
||||
var index4 = addVertex(v4, p4, u4, n4);
|
||||
currentIndexArray.push(index1);
|
||||
currentIndexArray.push(index3);
|
||||
currentIndexArray.push(index4);
|
||||
}
|
||||
}
|
||||
|
||||
// v float float float
|
||||
var vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
|
||||
|
||||
// vn float float float
|
||||
var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
|
||||
|
||||
// vt float float
|
||||
var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
|
||||
|
||||
// f vertex vertex vertex ...
|
||||
var facePattern1 = /f( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)?\/?/;
|
||||
|
||||
// f vertex/uv vertex/uv vertex/uv ...
|
||||
var facePattern2 = /f( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)?/;
|
||||
|
||||
// f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
|
||||
var facePattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/;
|
||||
|
||||
// f vertex//normal vertex//normal vertex//normal ...
|
||||
var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/;
|
||||
|
||||
var stream = byline(fs.createReadStream(objFile, {encoding: 'utf8'}));
|
||||
stream.on('data', function (line) {
|
||||
line = line.trim();
|
||||
var result;
|
||||
if ((line.length === 0) || (line.charAt(0) === '#')) {
|
||||
// Don't process empty lines or comments
|
||||
} else if ((result = vertexPattern.exec(line)) !== null) {
|
||||
positions.push(
|
||||
parseFloat(result[1]),
|
||||
parseFloat(result[2]),
|
||||
parseFloat(result[3])
|
||||
);
|
||||
} else if ((result = normalPattern.exec(line) ) !== null) {
|
||||
var nx = parseFloat(result[1]);
|
||||
var ny = parseFloat(result[2]);
|
||||
var nz = parseFloat(result[3]);
|
||||
var normal = Cartesian3.normalize(new Cartesian3(nx, ny, nz), new Cartesian3());
|
||||
normals.push(normal.x, normal.y, normal.z);
|
||||
} else if ((result = uvPattern.exec(line)) !== null) {
|
||||
uvs.push(
|
||||
parseFloat(result[1]),
|
||||
parseFloat(result[2])
|
||||
);
|
||||
} else if ((result = facePattern1.exec(line)) !== null) {
|
||||
addFace(
|
||||
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], 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(
|
||||
result[1], result[2], result[3], result[4],
|
||||
result[5], result[6], result[7], result[8],
|
||||
result[9], result[10], result[11], result[12],
|
||||
result[13], result[14], result[15], result[16]
|
||||
);
|
||||
} else if ((result = facePattern4.exec(line)) !== null) {
|
||||
addFace(
|
||||
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();
|
||||
useMaterial(materialName);
|
||||
}
|
||||
function finishLoading(nodes, mtlPaths, objPath) {
|
||||
nodes = cleanNodes(nodes);
|
||||
if (nodes.length === 0) {
|
||||
throw new RuntimeError(objPath + ' does not have any geometry data');
|
||||
}
|
||||
return loadMaterials(mtlPaths, objPath)
|
||||
.then(function(materials) {
|
||||
var imagePaths = getImagePaths(materials);
|
||||
return loadImages(imagePaths, objPath)
|
||||
.then(function(images) {
|
||||
return {
|
||||
nodes : nodes,
|
||||
materials : materials,
|
||||
images : images
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stream.on('end', function () {
|
||||
resolve({
|
||||
vertexCount: vertexCount,
|
||||
vertexArray: vertexArray,
|
||||
positionMin: positionMin,
|
||||
positionMax: positionMax,
|
||||
hasUVs: hasUVs,
|
||||
hasNormals: hasNormals,
|
||||
materialGroups: materialGroups,
|
||||
materials: materials,
|
||||
images: images
|
||||
function getAbsolutePath(mtlPath, objPath) {
|
||||
if (!path.isAbsolute(mtlPath)) {
|
||||
mtlPath = path.join(path.dirname(objPath), mtlPath);
|
||||
}
|
||||
return mtlPath;
|
||||
}
|
||||
|
||||
function loadMaterials(mtlPaths, objPath) {
|
||||
var materials = {};
|
||||
return Promise.map(mtlPaths, function(mtlPath) {
|
||||
mtlPath = getAbsolutePath(mtlPath, objPath);
|
||||
return loadMtl(mtlPath)
|
||||
.then(function(materialsInMtl) {
|
||||
materials = combine(materials, materialsInMtl);
|
||||
});
|
||||
});
|
||||
}).then(function() {
|
||||
return materials;
|
||||
});
|
||||
}
|
||||
|
||||
function getImages(inputPath, materials) {
|
||||
// Collect all the image files from the materials
|
||||
var images = [];
|
||||
function loadImages(imagePaths) {
|
||||
var images = {};
|
||||
return Promise.map(imagePaths, function(imagePath) {
|
||||
return loadImage(imagePath)
|
||||
.then(function(image) {
|
||||
if (defined(image)) {
|
||||
images[imagePath] = image;
|
||||
}
|
||||
});
|
||||
}).then(function() {
|
||||
return images;
|
||||
});
|
||||
}
|
||||
|
||||
function getImagePaths(materials) {
|
||||
var imagePaths = [];
|
||||
for (var name in materials) {
|
||||
if (materials.hasOwnProperty(name)) {
|
||||
var material = materials[name];
|
||||
if (defined(material.ambientColorMap) && (images.indexOf(material.ambientColorMap) === -1)) {
|
||||
images.push(material.ambientColorMap);
|
||||
if (defined(material.ambientColorMap) && imagePaths.indexOf(material.ambientColorMap) === -1) {
|
||||
imagePaths.push(material.ambientColorMap);
|
||||
}
|
||||
if (defined(material.diffuseColorMap) && (images.indexOf(material.diffuseColorMap) === -1)) {
|
||||
images.push(material.diffuseColorMap);
|
||||
if (defined(material.diffuseColorMap) && imagePaths.indexOf(material.diffuseColorMap) === -1) {
|
||||
imagePaths.push(material.diffuseColorMap);
|
||||
}
|
||||
if (defined(material.emissionColorMap) && (images.indexOf(material.emissionColorMap) === -1)) {
|
||||
images.push(material.emissionColorMap);
|
||||
if (defined(material.emissionColorMap) && imagePaths.indexOf(material.emissionColorMap) === -1) {
|
||||
imagePaths.push(material.emissionColorMap);
|
||||
}
|
||||
if (defined(material.specularColorMap) && (images.indexOf(material.specularColorMap) === -1)) {
|
||||
images.push(material.specularColorMap);
|
||||
if (defined(material.specularColorMap) && imagePaths.indexOf(material.specularColorMap) === -1) {
|
||||
imagePaths.push(material.specularColorMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
return imagePaths;
|
||||
}
|
||||
|
||||
// Load the image files
|
||||
var promises = [];
|
||||
var imagesInfo = {};
|
||||
var imagesLength = images.length;
|
||||
for (var i = 0; i < imagesLength; i++) {
|
||||
var imagePath = images[i];
|
||||
if (!path.isAbsolute(imagePath)) {
|
||||
imagePath = path.join(inputPath, imagePath);
|
||||
function removeEmptyPrimitives(primitives) {
|
||||
var final = [];
|
||||
var primitivesLength = primitives.length;
|
||||
for (var i = 0; i < primitivesLength; ++i) {
|
||||
var primitive = primitives[i];
|
||||
if (primitive.indices.length > 0) {
|
||||
final.push(primitive);
|
||||
}
|
||||
promises.push(loadImage(imagePath));
|
||||
}
|
||||
return Promise.all(promises)
|
||||
.then(function(imageInfoArray) {
|
||||
var imageInfoArrayLength = imageInfoArray.length;
|
||||
for (var j = 0; j < imageInfoArrayLength; j++) {
|
||||
var image = images[j];
|
||||
var imageInfo = imageInfoArray[j];
|
||||
imagesInfo[image] = imageInfo;
|
||||
}
|
||||
return imagesInfo;
|
||||
});
|
||||
return final;
|
||||
}
|
||||
|
||||
function getMaterials(mtlPath, hasMaterialGroups) {
|
||||
if (hasMaterialGroups && defined(mtlPath)) {
|
||||
return Material.parse(mtlPath);
|
||||
function removeEmptyMeshes(meshes) {
|
||||
var final = [];
|
||||
var meshesLength = meshes.length;
|
||||
for (var i = 0; i < meshesLength; ++i) {
|
||||
var mesh = meshes[i];
|
||||
mesh.primitives = removeEmptyPrimitives(mesh.primitives);
|
||||
if ((mesh.primitives.length > 0) && (mesh.positions.length > 0)) {
|
||||
final.push(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
return final;
|
||||
}
|
||||
|
||||
function getObjInfo(objFile, inputPath) {
|
||||
var mtlPath;
|
||||
var materials;
|
||||
var info;
|
||||
var hasMaterialGroups = false;
|
||||
var hasPositions = false;
|
||||
var hasNormals = false;
|
||||
var hasUVs = false;
|
||||
return new Promise(function(resolve, reject) {
|
||||
var stream = byline(fs.createReadStream(objFile, {encoding: 'utf8'}));
|
||||
stream.on('data', 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);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', function(err) {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
stream.on('end', function () {
|
||||
if (!hasPositions) {
|
||||
reject(new Error('Could not process OBJ file, no positions.'));
|
||||
}
|
||||
info = {
|
||||
hasNormals: hasNormals,
|
||||
hasUVs: hasUVs
|
||||
};
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
return getMaterials(mtlPath, hasMaterialGroups);
|
||||
})
|
||||
.then(function(returnedMaterials) {
|
||||
materials = returnedMaterials;
|
||||
return getImages(inputPath, materials);
|
||||
})
|
||||
.then(function(images) {
|
||||
return {
|
||||
info : info,
|
||||
materials : materials,
|
||||
images : images
|
||||
};
|
||||
});
|
||||
function meshesHaveNames(meshes) {
|
||||
var meshesLength = meshes.length;
|
||||
for (var i = 0; i < meshesLength; ++i) {
|
||||
if (defined(meshes[i].name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function removeEmptyNodes(nodes) {
|
||||
var final = [];
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
var node = nodes[i];
|
||||
var meshes = removeEmptyMeshes(node.meshes);
|
||||
if (meshes.length === 0) {
|
||||
continue;
|
||||
}
|
||||
node.meshes = meshes;
|
||||
if (!defined(node.name) && meshesHaveNames(meshes)) {
|
||||
// If the obj has groups (g) but not object groups (o) then convert meshes to nodes
|
||||
var meshesLength = meshes.length;
|
||||
for (var j = 0; j < meshesLength; ++j) {
|
||||
var mesh = meshes[j];
|
||||
var convertedNode = new Node();
|
||||
convertedNode.name = mesh.name;
|
||||
convertedNode.meshes = [mesh];
|
||||
final.push(convertedNode);
|
||||
}
|
||||
} else {
|
||||
final.push(node);
|
||||
}
|
||||
}
|
||||
return final;
|
||||
}
|
||||
|
||||
function setDefaultNames(items, defaultName, usedNames) {
|
||||
var itemsLength = items.length;
|
||||
for (var i = 0; i < itemsLength; ++i) {
|
||||
var item = items[i];
|
||||
var name = defaultValue(item.name, defaultName);
|
||||
var occurrences = usedNames[name];
|
||||
if (defined(occurrences)) {
|
||||
usedNames[name]++;
|
||||
name = name + '_' + occurrences;
|
||||
} else {
|
||||
usedNames[name] = 1;
|
||||
}
|
||||
item.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
function setDefaults(nodes) {
|
||||
var usedNames = {};
|
||||
setDefaultNames(nodes, 'Node', usedNames);
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
var node = nodes[i];
|
||||
setDefaultNames(node.meshes, node.name + '-Mesh', usedNames);
|
||||
}
|
||||
}
|
||||
|
||||
function cleanNodes(nodes) {
|
||||
nodes = removeEmptyNodes(nodes);
|
||||
setDefaults(nodes);
|
||||
return nodes;
|
||||
}
|
||||
|
27
lib/readLines.js
Normal file
@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
var eventStream = require('event-stream');
|
||||
var fsExtra = require('fs-extra');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
module.exports = readLines;
|
||||
|
||||
/**
|
||||
* Read a file line-by-line.
|
||||
*
|
||||
* @param {String} path Path to the file.
|
||||
* @param {Function} callback Function to call when reading each line.
|
||||
* @returns {Promise} A promise when the reader is finished.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function readLines(path, callback) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fsExtra.createReadStream(path)
|
||||
.on('error', reject)
|
||||
.on('end', resolve)
|
||||
.pipe(eventStream.split())
|
||||
.pipe(eventStream.mapSync(function (line) {
|
||||
callback(line);
|
||||
}));
|
||||
});
|
||||
}
|
41
package.json
@ -6,45 +6,44 @@
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Analytical Graphics, Inc., and Contributors",
|
||||
"url": "https://github.com/AnalyticalGraphicsInc/OBJ2GLTF/graphs/contributors"
|
||||
"url": "https://github.com/AnalyticalGraphicsInc/obj2gltf/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"obj",
|
||||
"gltf"
|
||||
],
|
||||
"homepage": "https://github.com/AnalyticalGraphicsInc/OBJ2GLTF",
|
||||
"homepage": "https://github.com/AnalyticalGraphicsInc/obj2gltf",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:AnalyticalGraphicsInc/OBJ2GLTF.git"
|
||||
"url": "git@github.com:AnalyticalGraphicsInc/obj2gltf.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/AnalyticalGraphicsInc/OBJ2GLTF/issues"
|
||||
"url": "https://github.com/AnalyticalGraphicsInc/obj2gltf/issues"
|
||||
},
|
||||
"main": "index.js",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "2.1.2",
|
||||
"bluebird": "3.4.6",
|
||||
"byline": "5.0.0",
|
||||
"cesium": "1.26.0",
|
||||
"fs-extra": "0.30.0",
|
||||
"gltf-pipeline": "0.1.0-alpha8",
|
||||
"yargs": "6.3.0"
|
||||
"bluebird": "^3.4.7",
|
||||
"cesium": "^1.31.0",
|
||||
"event-stream": "^3.3.4",
|
||||
"fs-extra": "^2.0.0",
|
||||
"gltf-pipeline": "^0.1.0-alpha11",
|
||||
"yargs": "^7.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gulp": "3.9.1",
|
||||
"gulp-jshint": "2.0.2",
|
||||
"istanbul": "0.4.5",
|
||||
"jasmine": "2.5.2",
|
||||
"jasmine-spec-reporter": "2.7.0",
|
||||
"jshint": "2.9.4",
|
||||
"jshint-stylish": "2.2.1",
|
||||
"open": "0.0.5",
|
||||
"requirejs": "2.3.2",
|
||||
"typings": "1.4.0"
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-jshint": "^2.0.4",
|
||||
"istanbul": "^0.4.5",
|
||||
"jasmine": "^2.5.3",
|
||||
"jasmine-spec-reporter": "^3.2.0",
|
||||
"jshint": "^2.9.4",
|
||||
"jshint-stylish": "^2.2.1",
|
||||
"open": "^0.0.5",
|
||||
"requirejs": "^2.3.3",
|
||||
"typings": "^2.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "typings install",
|
||||
|
Before Width: | Height: | Size: 22 KiB |
BIN
specs/data/box-complex-material/alpha.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
specs/data/box-complex-material/ambient.gif
Normal file
After Width: | Height: | Size: 3.9 KiB |
20
specs/data/box-complex-material/box-complex-material.mtl
Normal file
@ -0,0 +1,20 @@
|
||||
# Blender MTL File: 'None'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.200000 0.200000 0.200000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.100000 0.100000 0.100000
|
||||
Ni 1.000000
|
||||
d 0.900000
|
||||
Tr 0.900000
|
||||
map_Ka ambient.gif
|
||||
map_Ke emission.jpg
|
||||
map_Kd diffuse.png
|
||||
map_Ks specular.jpeg
|
||||
map_Ns shininess.png
|
||||
map_Bump bump.png
|
||||
map_d alpha.png
|
||||
illum 2
|
@ -1,7 +1,7 @@
|
||||
# Blender v2.77 (sub 0) OBJ File: 'BoxTextured.blend'
|
||||
# Blender v2.78 (sub 0) OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib BoxTextured.mtl
|
||||
o Cube_Cube.001
|
||||
mtllib box-complex-material.mtl
|
||||
o Cube
|
||||
v -1.000000 -1.000000 1.000000
|
||||
v -1.000000 1.000000 1.000000
|
||||
v -1.000000 -1.000000 -1.000000
|
||||
@ -26,9 +26,9 @@ vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
@ -36,11 +36,11 @@ 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 Textured
|
||||
usemtl Material
|
||||
s off
|
||||
f 2/1/1 4/2/1 3/3/1 1/4/1
|
||||
f 4/5/2 8/6/2 7/7/2 3/8/2
|
||||
f 8/9/3 6/10/3 5/11/3 7/12/3
|
||||
f 6/13/4 2/14/4 1/15/4 5/16/4
|
||||
f 1/17/5 3/18/5 7/7/5 5/16/5
|
||||
f 6/13/6 8/6/6 4/19/6 2/20/6
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
BIN
specs/data/box-complex-material/bump.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
specs/data/box-complex-material/diffuse.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
specs/data/box-complex-material/emission.jpg
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
specs/data/box-complex-material/shininess.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
specs/data/box-complex-material/specular.jpeg
Normal file
After Width: | Height: | Size: 5.5 KiB |
32
specs/data/box-groups/box-groups.mtl
Normal file
@ -0,0 +1,32 @@
|
||||
# Blender MTL File: 'box-objects.blend'
|
||||
# Material Count: 3
|
||||
|
||||
newmtl Blue
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.000000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Green
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.640000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Red
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.000000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
132
specs/data/box-groups/box-groups.obj
Normal file
@ -0,0 +1,132 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box-objects.blend'
|
||||
# www.blender.org
|
||||
mtllib box-groups.mtl
|
||||
g CubeBlue
|
||||
v -1.000000 -1.000000 -4.000000
|
||||
v -1.000000 1.000000 -4.000000
|
||||
v -1.000000 -1.000000 -6.000000
|
||||
v -1.000000 1.000000 -6.000000
|
||||
v 1.000000 -1.000000 -4.000000
|
||||
v 1.000000 1.000000 -4.000000
|
||||
v 1.000000 -1.000000 -6.000000
|
||||
v 1.000000 1.000000 -6.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Blue
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
||||
g CubeGreen
|
||||
v 4.000000 -1.000000 1.000000
|
||||
v 4.000000 1.000000 1.000000
|
||||
v 4.000000 -1.000000 -1.000000
|
||||
v 4.000000 1.000000 -1.000000
|
||||
v 6.000000 -1.000000 1.000000
|
||||
v 6.000000 1.000000 1.000000
|
||||
v 6.000000 -1.000000 -1.000000
|
||||
v 6.000000 1.000000 -1.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Green
|
||||
s off
|
||||
f 9/21/7 10/22/7 12/23/7 11/24/7
|
||||
f 11/25/8 12/26/8 16/27/8 15/28/8
|
||||
f 15/29/9 16/30/9 14/31/9 13/32/9
|
||||
f 13/33/10 14/34/10 10/35/10 9/36/10
|
||||
f 11/25/11 15/37/11 13/38/11 9/36/11
|
||||
f 16/39/12 12/26/12 10/35/12 14/40/12
|
||||
g CubeRed
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Red
|
||||
s off
|
||||
f 17/41/13 18/42/13 20/43/13 19/44/13
|
||||
f 19/45/14 20/46/14 24/47/14 23/48/14
|
||||
f 23/49/15 24/50/15 22/51/15 21/52/15
|
||||
f 21/53/16 22/54/16 18/55/16 17/56/16
|
||||
f 19/45/17 23/57/17 21/58/17 17/56/17
|
||||
f 24/59/18 20/46/18 18/55/18 22/60/18
|
46
specs/data/box-missing-mtllib/box-missing-mtllib.obj
Normal file
@ -0,0 +1,46 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib box.mtl
|
||||
o Cube
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Material
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
@ -1,13 +1,13 @@
|
||||
# Blender MTL File: 'BoxTextured.blend'
|
||||
# Blender MTL File: 'box.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Textured
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd CesiumLogoFlat.png
|
||||
map_Kd cesium.png
|
46
specs/data/box-missing-texture/box-missing-texture.obj
Normal file
@ -0,0 +1,46 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box.blend'
|
||||
# www.blender.org
|
||||
mtllib box-missing-texture.mtl
|
||||
o Cube
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Material
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
12
specs/data/box-mtllib/box-mtllib-blue.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'box-multiple-materials.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Blue
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.000000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
12
specs/data/box-mtllib/box-mtllib-green.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'box-multiple-materials.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Green
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.640000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
12
specs/data/box-mtllib/box-mtllib-red.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'box-multiple-materials.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Red
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.000000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
50
specs/data/box-mtllib/box-mtllib.obj
Normal file
@ -0,0 +1,50 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box-multiple-materials.blend'
|
||||
# www.blender.org
|
||||
mtllib box-mtllib-red.mtl
|
||||
mtllib box-mtllib-green.mtl box-mtllib-blue.mtl
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
usemtl Red
|
||||
f 3/1/1 7/2/1 5/3/1 1/4/1
|
||||
usemtl Green
|
||||
f 1/9/3 2/10/3 4/11/3 3/12/3
|
||||
usemtl Blue
|
||||
f 3/1/5 4/6/5 8/17/5 7/18/5
|
||||
usemtl Red
|
||||
f 8/5/2 4/6/2 2/7/2 6/8/2
|
||||
usemtl Green
|
||||
f 7/13/4 8/14/4 6/15/4 5/16/4
|
||||
usemtl Blue
|
||||
f 5/19/6 6/20/6 2/7/6 1/4/6
|
32
specs/data/box-multiple-materials/box-multiple-materials.mtl
Normal file
@ -0,0 +1,32 @@
|
||||
# Blender MTL File: 'box-multiple-materials.blend'
|
||||
# Material Count: 3
|
||||
|
||||
newmtl Blue
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.000000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Green
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.640000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Red
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.000000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
49
specs/data/box-multiple-materials/box-multiple-materials.obj
Normal file
@ -0,0 +1,49 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box-multiple-materials.blend'
|
||||
# www.blender.org
|
||||
mtllib box-multiple-materials.mtl
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
usemtl Red
|
||||
f 3/1/1 7/2/1 5/3/1 1/4/1
|
||||
usemtl Green
|
||||
f 1/9/3 2/10/3 4/11/3 3/12/3
|
||||
usemtl Blue
|
||||
f 3/1/5 4/6/5 8/17/5 7/18/5
|
||||
usemtl Red
|
||||
f 8/5/2 4/6/2 2/7/2 6/8/2
|
||||
usemtl Green
|
||||
f 7/13/4 8/14/4 6/15/4 5/16/4
|
||||
usemtl Blue
|
||||
f 5/19/6 6/20/6 2/7/6 1/4/6
|
12
specs/data/box-negative-indices/box-negative-indices.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'box.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
20
specs/data/box-negative-indices/box-negative-indices.obj
Normal file
@ -0,0 +1,20 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box.blend'
|
||||
# www.blender.org
|
||||
mtllib box-negative-indices.mtl
|
||||
o Cube
|
||||
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
|
||||
s off
|
||||
f -8 -7 -5 -6
|
||||
f -6 -5 -1 -2
|
||||
f -2 -1 -3 -4
|
||||
f -4 -3 -7 -8
|
||||
f -6 -2 -4 -8
|
||||
f -1 -5 -7 -3
|
125
specs/data/box-no-materials/box-no-materials.obj
Normal file
@ -0,0 +1,125 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box-objects.blend'
|
||||
# www.blender.org
|
||||
v -1.000000 -1.000000 -4.000000
|
||||
v -1.000000 1.000000 -4.000000
|
||||
v -1.000000 -1.000000 -6.000000
|
||||
v -1.000000 1.000000 -6.000000
|
||||
v 1.000000 -1.000000 -4.000000
|
||||
v 1.000000 1.000000 -4.000000
|
||||
v 1.000000 -1.000000 -6.000000
|
||||
v 1.000000 1.000000 -6.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
||||
v 4.000000 -1.000000 1.000000
|
||||
v 4.000000 1.000000 1.000000
|
||||
v 4.000000 -1.000000 -1.000000
|
||||
v 4.000000 1.000000 -1.000000
|
||||
v 6.000000 -1.000000 1.000000
|
||||
v 6.000000 1.000000 1.000000
|
||||
v 6.000000 -1.000000 -1.000000
|
||||
v 6.000000 1.000000 -1.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
s off
|
||||
f 9/21/7 10/22/7 12/23/7 11/24/7
|
||||
f 11/25/8 12/26/8 16/27/8 15/28/8
|
||||
f 15/29/9 16/30/9 14/31/9 13/32/9
|
||||
f 13/33/10 14/34/10 10/35/10 9/36/10
|
||||
f 11/25/11 15/37/11 13/38/11 9/36/11
|
||||
f 16/39/12 12/26/12 10/35/12 14/40/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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
s off
|
||||
f 17/41/13 18/42/13 20/43/13 19/44/13
|
||||
f 19/45/14 20/46/14 24/47/14 23/48/14
|
||||
f 23/49/15 24/50/15 22/51/15 21/52/15
|
||||
f 21/53/16 22/54/16 18/55/16 17/56/16
|
||||
f 19/45/17 23/57/17 21/58/17 17/56/17
|
||||
f 24/59/18 20/46/18 18/55/18 22/60/18
|
12
specs/data/box-normals/box-normals.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'box.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
26
specs/data/box-normals/box-normals.obj
Normal file
@ -0,0 +1,26 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box.blend'
|
||||
# www.blender.org
|
||||
mtllib box-normals.mtl
|
||||
o Cube
|
||||
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
|
||||
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 Material
|
||||
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
|
@ -0,0 +1,486 @@
|
||||
{
|
||||
"accessors": {
|
||||
"accessor_0": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 0,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-6
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
-4
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
"accessor_1": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 288,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
"accessor_2": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 576,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC2"
|
||||
},
|
||||
"accessor_3": {
|
||||
"bufferView": "bufferView_index",
|
||||
"byteOffset": 0,
|
||||
"byteStride": 0,
|
||||
"componentType": 5123,
|
||||
"count": 18,
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
11
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
"accessor_4": {
|
||||
"bufferView": "bufferView_index",
|
||||
"byteOffset": 36,
|
||||
"byteStride": 0,
|
||||
"componentType": 5123,
|
||||
"count": 18,
|
||||
"min": [
|
||||
12
|
||||
],
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
"accessor_5": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 768,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
4,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"max": [
|
||||
6,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
"accessor_6": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 1056,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
"accessor_7": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 1344,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC2"
|
||||
},
|
||||
"accessor_8": {
|
||||
"bufferView": "bufferView_index",
|
||||
"byteOffset": 72,
|
||||
"byteStride": 0,
|
||||
"componentType": 5123,
|
||||
"count": 18,
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
11
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
"accessor_9": {
|
||||
"bufferView": "bufferView_index",
|
||||
"byteOffset": 108,
|
||||
"byteStride": 0,
|
||||
"componentType": 5123,
|
||||
"count": 18,
|
||||
"min": [
|
||||
12
|
||||
],
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
"accessor_10": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 1536,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
"accessor_11": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 1824,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
"accessor_12": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 2112,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC2"
|
||||
},
|
||||
"accessor_13": {
|
||||
"bufferView": "bufferView_index",
|
||||
"byteOffset": 144,
|
||||
"byteStride": 0,
|
||||
"componentType": 5123,
|
||||
"count": 18,
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
11
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
"accessor_14": {
|
||||
"bufferView": "bufferView_index",
|
||||
"byteOffset": 180,
|
||||
"byteStride": 0,
|
||||
"componentType": 5123,
|
||||
"count": 18,
|
||||
"min": [
|
||||
12
|
||||
],
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"type": "SCALAR"
|
||||
}
|
||||
},
|
||||
"asset": {
|
||||
"generator": "obj2gltf",
|
||||
"profile": {
|
||||
"api": "WebGL",
|
||||
"version": "1.0"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"buffers": {
|
||||
"buffer": {
|
||||
"byteLength": 2520,
|
||||
"uri": "data:application/octet-stream;base64,AACAvwAAgL8AAIDAAACAvwAAgD8AAIDAAACAvwAAgD8AAMDAAACAvwAAgL8AAMDAAACAvwAAgL8AAMDAAACAvwAAgD8AAMDAAACAPwAAgD8AAMDAAACAPwAAgL8AAMDAAACAPwAAgL8AAMDAAACAPwAAgD8AAMDAAACAPwAAgD8AAIDAAACAPwAAgL8AAIDAAACAPwAAgL8AAIDAAACAPwAAgD8AAIDAAACAvwAAgD8AAIDAAACAvwAAgL8AAIDAAACAvwAAgL8AAMDAAACAPwAAgL8AAMDAAACAPwAAgL8AAIDAAACAvwAAgL8AAIDAAACAPwAAgD8AAMDAAACAvwAAgD8AAMDAAACAvwAAgD8AAIDAAACAPwAAgD8AAIDAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAACAQAAAgL8AAIA/AACAQAAAgD8AAIA/AACAQAAAgD8AAIC/AACAQAAAgL8AAIC/AACAQAAAgL8AAIC/AACAQAAAgD8AAIC/AADAQAAAgD8AAIC/AADAQAAAgL8AAIC/AADAQAAAgL8AAIC/AADAQAAAgD8AAIC/AADAQAAAgD8AAIA/AADAQAAAgL8AAIA/AADAQAAAgL8AAIA/AADAQAAAgD8AAIA/AACAQAAAgD8AAIA/AACAQAAAgL8AAIA/AACAQAAAgL8AAIC/AADAQAAAgL8AAIC/AADAQAAAgL8AAIA/AACAQAAAgL8AAIA/AADAQAAAgD8AAIC/AACAQAAAgD8AAIC/AACAQAAAgD8AAIA/AADAQAAAgD8AAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIA/AACAvwAAgL8AAIA/AACAPwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIA/AACAPwAAgD8AAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcA"
|
||||
}
|
||||
},
|
||||
"bufferViews": {
|
||||
"bufferView_vertex": {
|
||||
"buffer": "buffer",
|
||||
"byteLength": 2304,
|
||||
"byteOffset": 0,
|
||||
"target": 34962
|
||||
},
|
||||
"bufferView_index": {
|
||||
"buffer": "buffer",
|
||||
"byteLength": 216,
|
||||
"byteOffset": 2304,
|
||||
"target": 34963
|
||||
}
|
||||
},
|
||||
"extensionsUsed": [
|
||||
"KHR_materials_common"
|
||||
],
|
||||
"images": {},
|
||||
"materials": {
|
||||
"Blue": {
|
||||
"extensions": {
|
||||
"KHR_materials_common": {
|
||||
"technique": "PHONG",
|
||||
"values": {
|
||||
"ambient": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"diffuse": [
|
||||
0,
|
||||
0,
|
||||
0.64,
|
||||
1
|
||||
],
|
||||
"emission": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"specular": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
1
|
||||
],
|
||||
"shininess": 96.078431,
|
||||
"transparency": 1,
|
||||
"transparent": false,
|
||||
"doubleSided": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Green": {
|
||||
"extensions": {
|
||||
"KHR_materials_common": {
|
||||
"technique": "PHONG",
|
||||
"values": {
|
||||
"ambient": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"diffuse": [
|
||||
0,
|
||||
0.64,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"emission": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"specular": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
1
|
||||
],
|
||||
"shininess": 96.078431,
|
||||
"transparency": 1,
|
||||
"transparent": false,
|
||||
"doubleSided": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Red": {
|
||||
"extensions": {
|
||||
"KHR_materials_common": {
|
||||
"technique": "PHONG",
|
||||
"values": {
|
||||
"ambient": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"diffuse": [
|
||||
0.64,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"emission": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"specular": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
1
|
||||
],
|
||||
"shininess": 96.078431,
|
||||
"transparency": 1,
|
||||
"transparent": false,
|
||||
"doubleSided": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"meshes": {
|
||||
"CubeBlue_CubeBlue_Blue": {
|
||||
"name": "CubeBlue_CubeBlue_Blue",
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": "accessor_0",
|
||||
"NORMAL": "accessor_1",
|
||||
"TEXCOORD_0": "accessor_2"
|
||||
},
|
||||
"indices": "accessor_3",
|
||||
"material": "Blue",
|
||||
"mode": 4
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": "accessor_0",
|
||||
"NORMAL": "accessor_1",
|
||||
"TEXCOORD_0": "accessor_2"
|
||||
},
|
||||
"indices": "accessor_4",
|
||||
"material": "Green",
|
||||
"mode": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"CubeGreen_CubeGreen_Green": {
|
||||
"name": "CubeGreen_CubeGreen_Green",
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": "accessor_5",
|
||||
"NORMAL": "accessor_6",
|
||||
"TEXCOORD_0": "accessor_7"
|
||||
},
|
||||
"indices": "accessor_8",
|
||||
"material": "Green",
|
||||
"mode": 4
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": "accessor_5",
|
||||
"NORMAL": "accessor_6",
|
||||
"TEXCOORD_0": "accessor_7"
|
||||
},
|
||||
"indices": "accessor_9",
|
||||
"material": "Red",
|
||||
"mode": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"CubeRed_CubeRed_Red": {
|
||||
"name": "CubeRed_CubeRed_Red",
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": "accessor_10",
|
||||
"NORMAL": "accessor_11",
|
||||
"TEXCOORD_0": "accessor_12"
|
||||
},
|
||||
"indices": "accessor_13",
|
||||
"material": "Red",
|
||||
"mode": 4
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": "accessor_10",
|
||||
"NORMAL": "accessor_11",
|
||||
"TEXCOORD_0": "accessor_12"
|
||||
},
|
||||
"indices": "accessor_14",
|
||||
"material": "Blue",
|
||||
"mode": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
"Cube": {
|
||||
"name": "Cube",
|
||||
"meshes": [
|
||||
"CubeBlue_CubeBlue_Blue",
|
||||
"CubeGreen_CubeGreen_Green",
|
||||
"CubeRed_CubeRed_Red"
|
||||
]
|
||||
}
|
||||
},
|
||||
"samplers": {},
|
||||
"scene": "scene",
|
||||
"scenes": {
|
||||
"scene": {
|
||||
"nodes": [
|
||||
"Cube"
|
||||
]
|
||||
}
|
||||
},
|
||||
"textures": {}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
# Blender MTL File: 'box-objects.blend'
|
||||
# Material Count: 3
|
||||
|
||||
newmtl Blue
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.000000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Green
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.640000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Red
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.000000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
@ -0,0 +1,133 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box-objects.blend'
|
||||
# www.blender.org
|
||||
mtllib box-objects-groups-materials.mtl
|
||||
o Cube
|
||||
v -1.000000 -1.000000 -4.000000
|
||||
v -1.000000 1.000000 -4.000000
|
||||
v -1.000000 -1.000000 -6.000000
|
||||
v -1.000000 1.000000 -6.000000
|
||||
v 1.000000 -1.000000 -4.000000
|
||||
v 1.000000 1.000000 -4.000000
|
||||
v 1.000000 -1.000000 -6.000000
|
||||
v 1.000000 1.000000 -6.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
g CubeBlue_CubeBlue_Blue
|
||||
usemtl Blue
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
usemtl Green
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
||||
v 4.000000 -1.000000 1.000000
|
||||
v 4.000000 1.000000 1.000000
|
||||
v 4.000000 -1.000000 -1.000000
|
||||
v 4.000000 1.000000 -1.000000
|
||||
v 6.000000 -1.000000 1.000000
|
||||
v 6.000000 1.000000 1.000000
|
||||
v 6.000000 -1.000000 -1.000000
|
||||
v 6.000000 1.000000 -1.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
g CubeGreen_CubeGreen_Green
|
||||
usemtl Green
|
||||
f 9/21/7 10/22/7 12/23/7 11/24/7
|
||||
f 11/25/8 12/26/8 16/27/8 15/28/8
|
||||
f 15/29/9 16/30/9 14/31/9 13/32/9
|
||||
usemtl Red
|
||||
f 13/33/10 14/34/10 10/35/10 9/36/10
|
||||
f 11/25/11 15/37/11 13/38/11 9/36/11
|
||||
f 16/39/12 12/26/12 10/35/12 14/40/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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
g CubeRed_CubeRed_Red
|
||||
usemtl Red
|
||||
f 17/41/13 18/42/13 20/43/13 19/44/13
|
||||
f 19/45/14 20/46/14 24/47/14 23/48/14
|
||||
f 23/49/15 24/50/15 22/51/15 21/52/15
|
||||
usemtl Blue
|
||||
f 21/53/16 22/54/16 18/55/16 17/56/16
|
||||
f 19/45/17 23/57/17 21/58/17 17/56/17
|
||||
f 24/59/18 20/46/18 18/55/18 22/60/18
|
32
specs/data/box-objects-groups/box-objects-groups.mtl
Normal file
@ -0,0 +1,32 @@
|
||||
# Blender MTL File: 'box-objects.blend'
|
||||
# Material Count: 3
|
||||
|
||||
newmtl Blue
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.000000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Green
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.640000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Red
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.000000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
135
specs/data/box-objects-groups/box-objects-groups.obj
Normal file
@ -0,0 +1,135 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box-objects.blend'
|
||||
# www.blender.org
|
||||
mtllib box-objects-groups.mtl
|
||||
o CubeBlue
|
||||
v -1.000000 -1.000000 -4.000000
|
||||
v -1.000000 1.000000 -4.000000
|
||||
v -1.000000 -1.000000 -6.000000
|
||||
v -1.000000 1.000000 -6.000000
|
||||
v 1.000000 -1.000000 -4.000000
|
||||
v 1.000000 1.000000 -4.000000
|
||||
v 1.000000 -1.000000 -6.000000
|
||||
v 1.000000 1.000000 -6.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
g CubeBlue_CubeBlue_Blue
|
||||
usemtl Blue
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
||||
o CubeGreen
|
||||
v 4.000000 -1.000000 1.000000
|
||||
v 4.000000 1.000000 1.000000
|
||||
v 4.000000 -1.000000 -1.000000
|
||||
v 4.000000 1.000000 -1.000000
|
||||
v 6.000000 -1.000000 1.000000
|
||||
v 6.000000 1.000000 1.000000
|
||||
v 6.000000 -1.000000 -1.000000
|
||||
v 6.000000 1.000000 -1.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
g CubeGreen_CubeGreen_Green
|
||||
usemtl Green
|
||||
s off
|
||||
f 9/21/7 10/22/7 12/23/7 11/24/7
|
||||
f 11/25/8 12/26/8 16/27/8 15/28/8
|
||||
f 15/29/9 16/30/9 14/31/9 13/32/9
|
||||
f 13/33/10 14/34/10 10/35/10 9/36/10
|
||||
f 11/25/11 15/37/11 13/38/11 9/36/11
|
||||
f 16/39/12 12/26/12 10/35/12 14/40/12
|
||||
o CubeRed
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
g CubeRed_CubeRed_Red
|
||||
usemtl Red
|
||||
s off
|
||||
f 17/41/13 18/42/13 20/43/13 19/44/13
|
||||
f 19/45/14 20/46/14 24/47/14 23/48/14
|
||||
f 23/49/15 24/50/15 22/51/15 21/52/15
|
||||
f 21/53/16 22/54/16 18/55/16 17/56/16
|
||||
f 19/45/17 23/57/17 21/58/17 17/56/17
|
||||
f 24/59/18 20/46/18 18/55/18 22/60/18
|
32
specs/data/box-objects/box-objects.mtl
Normal file
@ -0,0 +1,32 @@
|
||||
# Blender MTL File: 'box-objects.blend'
|
||||
# Material Count: 3
|
||||
|
||||
newmtl Blue
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.000000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Green
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.640000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Red
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.000000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
132
specs/data/box-objects/box-objects.obj
Normal file
@ -0,0 +1,132 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box-objects.blend'
|
||||
# www.blender.org
|
||||
mtllib box-objects.mtl
|
||||
o CubeBlue
|
||||
v -1.000000 -1.000000 -4.000000
|
||||
v -1.000000 1.000000 -4.000000
|
||||
v -1.000000 -1.000000 -6.000000
|
||||
v -1.000000 1.000000 -6.000000
|
||||
v 1.000000 -1.000000 -4.000000
|
||||
v 1.000000 1.000000 -4.000000
|
||||
v 1.000000 -1.000000 -6.000000
|
||||
v 1.000000 1.000000 -6.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Blue
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
||||
o CubeGreen
|
||||
v 4.000000 -1.000000 1.000000
|
||||
v 4.000000 1.000000 1.000000
|
||||
v 4.000000 -1.000000 -1.000000
|
||||
v 4.000000 1.000000 -1.000000
|
||||
v 6.000000 -1.000000 1.000000
|
||||
v 6.000000 1.000000 1.000000
|
||||
v 6.000000 -1.000000 -1.000000
|
||||
v 6.000000 1.000000 -1.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Green
|
||||
s off
|
||||
f 9/21/7 10/22/7 12/23/7 11/24/7
|
||||
f 11/25/8 12/26/8 16/27/8 15/28/8
|
||||
f 15/29/9 16/30/9 14/31/9 13/32/9
|
||||
f 13/33/10 14/34/10 10/35/10 9/36/10
|
||||
f 11/25/11 15/37/11 13/38/11 9/36/11
|
||||
f 16/39/12 12/26/12 10/35/12 14/40/12
|
||||
o CubeRed
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Red
|
||||
s off
|
||||
f 17/41/13 18/42/13 20/43/13 19/44/13
|
||||
f 19/45/14 20/46/14 24/47/14 23/48/14
|
||||
f 23/49/15 24/50/15 22/51/15 21/52/15
|
||||
f 21/53/16 22/54/16 18/55/16 17/56/16
|
||||
f 19/45/17 23/57/17 21/58/17 17/56/17
|
||||
f 24/59/18 20/46/18 18/55/18 22/60/18
|
12
specs/data/box-positions-only/box-positions-only.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'box.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
20
specs/data/box-positions-only/box-positions-only.obj
Normal file
@ -0,0 +1,20 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box.blend'
|
||||
# www.blender.org
|
||||
mtllib box-positions-only.mtl
|
||||
o Cube
|
||||
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
|
||||
s off
|
||||
f 1 2 4 3
|
||||
f 3 4 8 7
|
||||
f 7 8 6 5
|
||||
f 5 6 2 1
|
||||
f 3 7 5 1
|
||||
f 8 4 2 6
|
46
specs/data/box-subdirectories/box-textured.obj
Normal file
@ -0,0 +1,46 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box.blend'
|
||||
# www.blender.org
|
||||
mtllib materials/box-textured.mtl
|
||||
o Cube
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Material
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
13
specs/data/box-subdirectories/materials/box-textured.mtl
Normal file
@ -0,0 +1,13 @@
|
||||
# Blender MTL File: 'box.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd images/cesium.png
|
BIN
specs/data/box-subdirectories/materials/images/cesium.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
13
specs/data/box-textured/box-textured.mtl
Normal file
@ -0,0 +1,13 @@
|
||||
# Blender MTL File: 'box.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd cesium.png
|
46
specs/data/box-textured/box-textured.obj
Normal file
@ -0,0 +1,46 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box.blend'
|
||||
# www.blender.org
|
||||
mtllib box-textured.mtl
|
||||
o Cube
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Material
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
BIN
specs/data/box-textured/cesium.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
12
specs/data/box-triangles/box-triangles.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'box.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
46
specs/data/box-triangles/box-triangles.obj
Normal file
@ -0,0 +1,46 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box.blend'
|
||||
# www.blender.org
|
||||
mtllib box-triangles.mtl
|
||||
o Cube
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Material
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
12
specs/data/box-uncleaned/box-uncleaned.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'None'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
52
specs/data/box-uncleaned/box-uncleaned.obj
Normal file
@ -0,0 +1,52 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib box-uncleaned.mtl
|
||||
o Cube
|
||||
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
|
||||
g Cube
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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
|
||||
o Cube
|
||||
g Cube
|
||||
usemtl Material
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
||||
g Cube
|
||||
usemtl Material
|
||||
o Cube
|
32
specs/data/box-usemtl/box-usemtl.mtl
Normal file
@ -0,0 +1,32 @@
|
||||
# Blender MTL File: 'box-objects.blend'
|
||||
# Material Count: 3
|
||||
|
||||
newmtl Blue
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.000000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Green
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.000000 0.640000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Red
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.000000 0.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
129
specs/data/box-usemtl/box-usemtl.obj
Normal file
@ -0,0 +1,129 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box-objects.blend'
|
||||
# www.blender.org
|
||||
mtllib box-usemtl.mtl
|
||||
v -1.000000 -1.000000 -4.000000
|
||||
v -1.000000 1.000000 -4.000000
|
||||
v -1.000000 -1.000000 -6.000000
|
||||
v -1.000000 1.000000 -6.000000
|
||||
v 1.000000 -1.000000 -4.000000
|
||||
v 1.000000 1.000000 -4.000000
|
||||
v 1.000000 -1.000000 -6.000000
|
||||
v 1.000000 1.000000 -6.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Blue
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
||||
v 4.000000 -1.000000 1.000000
|
||||
v 4.000000 1.000000 1.000000
|
||||
v 4.000000 -1.000000 -1.000000
|
||||
v 4.000000 1.000000 -1.000000
|
||||
v 6.000000 -1.000000 1.000000
|
||||
v 6.000000 1.000000 1.000000
|
||||
v 6.000000 -1.000000 -1.000000
|
||||
v 6.000000 1.000000 -1.000000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Green
|
||||
s off
|
||||
f 9/21/7 10/22/7 12/23/7 11/24/7
|
||||
f 11/25/8 12/26/8 16/27/8 15/28/8
|
||||
f 15/29/9 16/30/9 14/31/9 13/32/9
|
||||
f 13/33/10 14/34/10 10/35/10 9/36/10
|
||||
f 11/25/11 15/37/11 13/38/11 9/36/11
|
||||
f 16/39/12 12/26/12 10/35/12 14/40/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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Red
|
||||
s off
|
||||
f 17/41/13 18/42/13 20/43/13 19/44/13
|
||||
f 19/45/14 20/46/14 24/47/14 23/48/14
|
||||
f 23/49/15 24/50/15 22/51/15 21/52/15
|
||||
f 21/53/16 22/54/16 18/55/16 17/56/16
|
||||
f 19/45/17 23/57/17 21/58/17 17/56/17
|
||||
f 24/59/18 20/46/18 18/55/18 22/60/18
|
12
specs/data/box-uvs/box-uvs.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'box.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
40
specs/data/box-uvs/box-uvs.obj
Normal file
@ -0,0 +1,40 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: 'box.blend'
|
||||
# www.blender.org
|
||||
mtllib box-uvs.mtl
|
||||
o Cube
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
usemtl Material
|
||||
s off
|
||||
f 1/1 2/2 4/3 3/4
|
||||
f 3/5 4/6 8/7 7/8
|
||||
f 7/9 8/10 6/11 5/12
|
||||
f 5/13 6/14 2/15 1/16
|
||||
f 3/5 7/17 5/18 1/16
|
||||
f 8/19 4/6 2/15 6/20
|
176
specs/data/box/box.gltf
Normal file
@ -0,0 +1,176 @@
|
||||
{
|
||||
"accessors": {
|
||||
"accessor_0": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 0,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
"accessor_1": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 288,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
"accessor_2": {
|
||||
"bufferView": "bufferView_vertex",
|
||||
"byteOffset": 576,
|
||||
"byteStride": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"min": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"type": "VEC2"
|
||||
},
|
||||
"accessor_3": {
|
||||
"bufferView": "bufferView_index",
|
||||
"byteOffset": 0,
|
||||
"byteStride": 0,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"type": "SCALAR"
|
||||
}
|
||||
},
|
||||
"asset": {
|
||||
"generator": "obj2gltf",
|
||||
"profile": {
|
||||
"api": "WebGL",
|
||||
"version": "1.0"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"buffers": {
|
||||
"buffer": {
|
||||
"byteLength": 840,
|
||||
"uri": "data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIA/AACAvwAAgL8AAIA/AACAPwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIA/AACAPwAAgD8AAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcA"
|
||||
}
|
||||
},
|
||||
"bufferViews": {
|
||||
"bufferView_vertex": {
|
||||
"buffer": "buffer",
|
||||
"byteLength": 768,
|
||||
"byteOffset": 0,
|
||||
"target": 34962
|
||||
},
|
||||
"bufferView_index": {
|
||||
"buffer": "buffer",
|
||||
"byteLength": 72,
|
||||
"byteOffset": 768,
|
||||
"target": 34963
|
||||
}
|
||||
},
|
||||
"extensionsUsed": [
|
||||
"KHR_materials_common"
|
||||
],
|
||||
"images": {},
|
||||
"materials": {
|
||||
"Material": {
|
||||
"extensions": {
|
||||
"KHR_materials_common": {
|
||||
"technique": "PHONG",
|
||||
"values": {
|
||||
"ambient": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"diffuse": [
|
||||
0.64,
|
||||
0.64,
|
||||
0.64,
|
||||
1
|
||||
],
|
||||
"emission": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"specular": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
1
|
||||
],
|
||||
"shininess": 96.078431,
|
||||
"transparency": 1,
|
||||
"transparent": false,
|
||||
"doubleSided": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"meshes": {
|
||||
"Cube-Mesh": {
|
||||
"name": "Cube-Mesh",
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": "accessor_0",
|
||||
"NORMAL": "accessor_1",
|
||||
"TEXCOORD_0": "accessor_2"
|
||||
},
|
||||
"indices": "accessor_3",
|
||||
"material": "Material",
|
||||
"mode": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
"Cube": {
|
||||
"name": "Cube",
|
||||
"meshes": [
|
||||
"Cube-Mesh"
|
||||
]
|
||||
}
|
||||
},
|
||||
"samplers": {},
|
||||
"scene": "scene",
|
||||
"scenes": {
|
||||
"scene": {
|
||||
"nodes": [
|
||||
"Cube"
|
||||
]
|
||||
}
|
||||
},
|
||||
"textures": {}
|
||||
}
|
12
specs/data/box/box.mtl
Normal file
@ -0,0 +1,12 @@
|
||||
# Blender MTL File: 'None'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
46
specs/data/box/box.obj
Normal file
@ -0,0 +1,46 @@
|
||||
# Blender v2.78 (sub 0) OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib box.mtl
|
||||
o Cube
|
||||
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
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 1.0000
|
||||
vt 1.0000 0.0000
|
||||
vt 1.0000 1.0000
|
||||
vt 0.0000 0.0000
|
||||
vt 0.0000 1.0000
|
||||
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 Material
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||
f 8/19/6 4/6/6 2/15/6 6/20/6
|
@ -1,23 +1,123 @@
|
||||
'use strict';
|
||||
var Promise = require('bluebird');
|
||||
|
||||
var fsExtra = require('fs-extra');
|
||||
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
||||
var path = require('path');
|
||||
var convert = require('../../lib/convert');
|
||||
|
||||
var objFile = './specs/data/BoxTextured/BoxTextured.obj';
|
||||
var gltfFile = './specs/data/BoxTextured/BoxTextured.gltf';
|
||||
var objPath = 'specs/data/box-textured/box-textured.obj';
|
||||
var gltfPath = 'specs/data/box-textured/box-textured.gltf';
|
||||
var glbPath = 'specs/data/box-textured/box-textured.glb';
|
||||
|
||||
describe('convert', function() {
|
||||
it('converts an obj to gltf', function(done) {
|
||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk').and.callFake(function(gltf, outputPath, options, callback) {
|
||||
return;
|
||||
});
|
||||
expect(convert(objFile, gltfFile, {})
|
||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
expect(convert(objPath, gltfPath)
|
||||
.then(function() {
|
||||
var args = spy.calls.first().args;
|
||||
expect(args[0]).toBeDefined();
|
||||
expect(path.normalize(args[1])).toEqual(path.normalize(gltfFile));
|
||||
var gltf = args[0];
|
||||
var outputPath = args[1];
|
||||
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(convert(objPath, gltfPath)
|
||||
.then(function() {
|
||||
var args = spy.calls.first().args;
|
||||
var options = args[2];
|
||||
expect(options).toEqual({
|
||||
createDirectory : false,
|
||||
basePath : path.dirname(objPath),
|
||||
binary : false,
|
||||
embed : true,
|
||||
embedImage : true,
|
||||
encodeNormals : false,
|
||||
quantize : false,
|
||||
compressTextureCoordinates : false,
|
||||
aoOptions : undefined,
|
||||
smoothNormals : false,
|
||||
optimizeForCesium : false,
|
||||
textureCompressionOptions : undefined,
|
||||
preserve : true
|
||||
});
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('sets options', function(done) {
|
||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
var textureCompressionOptions = {
|
||||
format : 'dxt1',
|
||||
quality : 10
|
||||
};
|
||||
var options = {
|
||||
binary : true,
|
||||
separate : true,
|
||||
separateTextures : true,
|
||||
compress : true,
|
||||
optimize : true,
|
||||
generateNormals : true,
|
||||
ao : true,
|
||||
optimizeForCesium : true,
|
||||
textureCompressionOptions : textureCompressionOptions
|
||||
};
|
||||
|
||||
expect(convert(objPath, gltfPath, options)
|
||||
.then(function() {
|
||||
var args = spy.calls.first().args;
|
||||
var options = args[2];
|
||||
expect(options).toEqual({
|
||||
createDirectory : false,
|
||||
basePath : path.dirname(objPath),
|
||||
binary : true,
|
||||
embed : false,
|
||||
embedImage : false,
|
||||
encodeNormals : true,
|
||||
quantize : true,
|
||||
compressTextureCoordinates : true,
|
||||
aoOptions : {},
|
||||
smoothNormals : true,
|
||||
optimizeForCesium : true,
|
||||
textureCompressionOptions : textureCompressionOptions,
|
||||
preserve : false
|
||||
});
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('saves as binary if gltfPath has a .glb extension', function(done) {
|
||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
expect(convert(objPath, glbPath)
|
||||
.then(function() {
|
||||
var args = spy.calls.first().args;
|
||||
var options = args[2];
|
||||
expect(options.binary).toBe(true);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('bypassPipeline flag bypasses gltf-pipeline', function(done) {
|
||||
var spy1 = spyOn(convert, '_outputJson');
|
||||
var spy2 = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||
var options = {
|
||||
bypassPipeline : true
|
||||
};
|
||||
expect(convert(objPath, gltfPath, options)
|
||||
.then(function() {
|
||||
expect(spy1.calls.count()).toBe(1);
|
||||
expect(spy2.calls.count()).toBe(0);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('throws if objPath is undefined', function() {
|
||||
expect(function() {
|
||||
convert(undefined, gltfPath);
|
||||
}).toThrowDeveloperError();
|
||||
});
|
||||
|
||||
it('throws if gltfPath is undefined', function() {
|
||||
expect(function() {
|
||||
convert(objPath, undefined);
|
||||
}).toThrowDeveloperError();
|
||||
});
|
||||
});
|
||||
|
355
specs/lib/gltfSpec.js
Normal file
@ -0,0 +1,355 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var fsExtra = require('fs-extra');
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
var clone = require('../../lib/clone.js');
|
||||
var createGltf = require('../../lib/gltf.js');
|
||||
var loadImage = require('../../lib/image.js');
|
||||
var loadObj = require('../../lib/obj.js');
|
||||
|
||||
var WebGLConstants = Cesium.WebGLConstants;
|
||||
|
||||
var fsExtraReadJson = Promise.promisify(fsExtra.readJson);
|
||||
|
||||
var boxObjUrl = 'specs/data/box/box.obj';
|
||||
var groupObjUrl = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj';
|
||||
var boxGltfUrl = 'specs/data/box/box.gltf';
|
||||
var groupGltfUrl = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.gltf';
|
||||
var diffuseTextureUrl = 'specs/data/box-textured/cesium.png';
|
||||
var transparentDiffuseTextureUrl = 'specs/data/box-complex-material/diffuse.png';
|
||||
|
||||
describe('gltf', function() {
|
||||
var boxObjData;
|
||||
var groupObjData;
|
||||
var boxGltf;
|
||||
var groupGltf;
|
||||
var diffuseTexture;
|
||||
var transparentDiffuseTexture;
|
||||
|
||||
beforeAll(function(done) {
|
||||
return Promise.all([
|
||||
loadObj(boxObjUrl)
|
||||
.then(function(data) {
|
||||
boxObjData = data;
|
||||
}),
|
||||
loadObj(groupObjUrl)
|
||||
.then(function(data) {
|
||||
groupObjData = data;
|
||||
}),
|
||||
fsExtraReadJson(boxGltfUrl)
|
||||
.then(function(gltf) {
|
||||
boxGltf = gltf;
|
||||
}),
|
||||
fsExtraReadJson(groupGltfUrl)
|
||||
.then(function(gltf) {
|
||||
groupGltf = gltf;
|
||||
}),
|
||||
loadImage(diffuseTextureUrl)
|
||||
.then(function(image) {
|
||||
diffuseTexture = image;
|
||||
}),
|
||||
loadImage(transparentDiffuseTextureUrl)
|
||||
.then(function(image) {
|
||||
transparentDiffuseTexture = image;
|
||||
})
|
||||
]).then(done);
|
||||
});
|
||||
|
||||
it('simple gltf', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
var gltf = createGltf(objData);
|
||||
expect(gltf).toEqual(boxGltf);
|
||||
});
|
||||
|
||||
it('multiple nodes, meshes, and primitives', function() {
|
||||
var objData = clone(groupObjData, true);
|
||||
var gltf = createGltf(objData);
|
||||
expect(gltf).toEqual(groupGltf);
|
||||
|
||||
expect(Object.keys(gltf.materials).length).toBe(3);
|
||||
expect(Object.keys(gltf.nodes).length).toBe(1);
|
||||
expect(Object.keys(gltf.meshes).length).toBe(3);
|
||||
|
||||
// Check for two primitives in each mesh
|
||||
for (var id in gltf.meshes) {
|
||||
if (gltf.meshes.hasOwnProperty(id)) {
|
||||
var mesh = gltf.meshes[id];
|
||||
expect(mesh.primitives.length).toBe(2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('sets default material values', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.materials.Material = {};
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var material = gltf.materials.Material;
|
||||
var kmc = material.extensions.KHR_materials_common;
|
||||
var values = kmc.values;
|
||||
|
||||
expect(kmc.technique).toBe('LAMBERT');
|
||||
expect(values.ambient).toEqual([0.0, 0.0, 0.0, 1]);
|
||||
expect(values.diffuse).toEqual([0.5, 0.5, 0.5, 1]);
|
||||
expect(values.emission).toEqual([0.0, 0.0, 0.0, 1]);
|
||||
expect(values.specular).toEqual([0.0, 0.0, 0.0, 1]);
|
||||
expect(values.shininess).toEqual(0.0);
|
||||
});
|
||||
|
||||
it('sets material for diffuse texture', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.materials.Material = {
|
||||
diffuseColorMap : diffuseTextureUrl
|
||||
};
|
||||
objData.images[diffuseTextureUrl] = diffuseTexture;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
var texture = gltf.textures.texture_cesium;
|
||||
var image = gltf.images.cesium;
|
||||
|
||||
expect(kmc.technique).toBe('LAMBERT');
|
||||
expect(kmc.values.diffuse).toEqual('texture_cesium');
|
||||
expect(kmc.values.transparency).toBe(1.0);
|
||||
expect(kmc.values.transparent).toBe(false);
|
||||
expect(kmc.values.doubleSided).toBe(false);
|
||||
|
||||
expect(texture).toEqual({
|
||||
format : WebGLConstants.RGB,
|
||||
internalFormat : WebGLConstants.RGB,
|
||||
sampler : 'sampler',
|
||||
source : 'cesium',
|
||||
target : WebGLConstants.TEXTURE_2D,
|
||||
type : WebGLConstants.UNSIGNED_BYTE
|
||||
});
|
||||
|
||||
expect(image).toBeDefined();
|
||||
expect(image.name).toBe('cesium');
|
||||
expect(image.uri.indexOf('data:image/png;base64,') >= 0).toBe(true);
|
||||
|
||||
expect(gltf.samplers.sampler).toEqual({
|
||||
magFilter : WebGLConstants.LINEAR,
|
||||
minFilter : WebGLConstants.LINEAR,
|
||||
wrapS : WebGLConstants.REPEAT,
|
||||
wrapT : WebGLConstants.REPEAT
|
||||
});
|
||||
});
|
||||
|
||||
it('sets material for alpha less than 1', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.materials.Material = {
|
||||
alpha : 0.4
|
||||
};
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 0.4]);
|
||||
expect(kmc.values.transparency).toBe(1.0);
|
||||
expect(kmc.values.transparent).toBe(true);
|
||||
expect(kmc.values.doubleSided).toBe(true);
|
||||
});
|
||||
|
||||
it('sets material for diffuse texture and alpha less than 1', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.materials.Material = {
|
||||
diffuseColorMap : diffuseTextureUrl,
|
||||
alpha : 0.4
|
||||
};
|
||||
objData.images[diffuseTextureUrl] = diffuseTexture;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toEqual('texture_cesium');
|
||||
expect(kmc.values.transparency).toBe(0.4);
|
||||
expect(kmc.values.transparent).toBe(true);
|
||||
expect(kmc.values.doubleSided).toBe(true);
|
||||
});
|
||||
|
||||
it('sets material for transparent diffuse texture', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.materials.Material = {
|
||||
diffuseColorMap : transparentDiffuseTextureUrl
|
||||
};
|
||||
objData.images[transparentDiffuseTextureUrl] = transparentDiffuseTexture;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toBe('texture_diffuse');
|
||||
expect(kmc.values.transparency).toBe(1.0);
|
||||
expect(kmc.values.transparent).toBe(true);
|
||||
expect(kmc.values.doubleSided).toBe(true);
|
||||
});
|
||||
|
||||
it('sets material for specular', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.materials.Material = {
|
||||
specularColor : [0.1, 0.1, 0.2, 1],
|
||||
specularShininess : 0.1
|
||||
};
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.technique).toBe('PHONG');
|
||||
expect(kmc.values.specular).toEqual([0.1, 0.1, 0.2, 1]);
|
||||
expect(kmc.values.shininess).toEqual(0.1);
|
||||
});
|
||||
|
||||
it('sets constant material when there are no normals', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.nodes[0].meshes[0].normals.length = 0;
|
||||
objData.materials.Material = {
|
||||
diffuseColorMap : diffuseTextureUrl
|
||||
};
|
||||
objData.images[diffuseTextureUrl] = diffuseTexture;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.technique).toBe('CONSTANT');
|
||||
expect(kmc.values.emission).toEqual('texture_cesium');
|
||||
});
|
||||
|
||||
it('sets default material when texture is missing', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.materials.Material = {
|
||||
diffuseColorMap : diffuseTextureUrl
|
||||
};
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]);
|
||||
});
|
||||
|
||||
it('uses default material (1)', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.nodes[0].meshes[0].primitives[0].material = undefined;
|
||||
|
||||
// Creates a material called "default"
|
||||
var gltf = createGltf(objData);
|
||||
expect(gltf.materials.default).toBeDefined();
|
||||
var kmc = gltf.materials.default.extensions.KHR_materials_common;
|
||||
expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]);
|
||||
});
|
||||
|
||||
it('uses default material (2)', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.materials = {};
|
||||
|
||||
// Uses the original name of the material
|
||||
var gltf = createGltf(objData);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]);
|
||||
});
|
||||
|
||||
it('handles material used with and without normals', function() {
|
||||
// Two meshes - one with normals, and one without
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.nodes.push(clone(objData.nodes[0], true));
|
||||
objData.nodes[1].meshes[0].normals.length = 0;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var kmc1 = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
var kmc2 = gltf.materials.Material_constant.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc1.technique).toBe('PHONG');
|
||||
expect(kmc2.technique).toBe('CONSTANT');
|
||||
|
||||
// Now test in a different order
|
||||
objData = clone(boxObjData, true);
|
||||
objData.nodes.push(clone(objData.nodes[0], true));
|
||||
objData.nodes[0].meshes[0].normals.length = 0;
|
||||
|
||||
gltf = createGltf(objData);
|
||||
kmc1 = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
kmc2 = gltf.materials.Material_shaded.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc1.technique).toBe('CONSTANT');
|
||||
expect(kmc2.technique).toBe('PHONG');
|
||||
});
|
||||
|
||||
it('runs without normals', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.nodes[0].meshes[0].normals.length = 0;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
|
||||
expect(attributes.POSITION).toBeDefined();
|
||||
expect(attributes.NORMAL).toBeUndefined();
|
||||
expect(attributes.TEXCOORD_0).toBeDefined();
|
||||
});
|
||||
|
||||
it('runs without uvs', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.nodes[0].meshes[0].uvs.length = 0;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
|
||||
expect(attributes.POSITION).toBeDefined();
|
||||
expect(attributes.NORMAL).toBeDefined();
|
||||
expect(attributes.TEXCOORD_0).toBeUndefined();
|
||||
});
|
||||
|
||||
it('runs without uvs and normals', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
objData.nodes[0].meshes[0].normals.length = 0;
|
||||
objData.nodes[0].meshes[0].uvs.length = 0;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
|
||||
expect(attributes.POSITION).toBeDefined();
|
||||
expect(attributes.NORMAL).toBeUndefined();
|
||||
expect(attributes.TEXCOORD_0).toBeUndefined();
|
||||
});
|
||||
|
||||
function expandObjData(objData, duplicatesLength) {
|
||||
var mesh = objData.nodes[0].meshes[0];
|
||||
var indices = mesh.primitives[0].indices;
|
||||
var positions = mesh.positions;
|
||||
var normals = mesh.normals;
|
||||
var uvs = mesh.uvs;
|
||||
|
||||
var indicesLength = indices.length;
|
||||
var vertexCount = positions.length / 3;
|
||||
|
||||
for (var i = 1; i < duplicatesLength; ++i) {
|
||||
for (var j = 0; j < vertexCount; ++j) {
|
||||
positions.push(0.0);
|
||||
positions.push(0.0);
|
||||
positions.push(0.0);
|
||||
normals.push(0.0);
|
||||
normals.push(0.0);
|
||||
normals.push(0.0);
|
||||
uvs.push(0.0);
|
||||
uvs.push(0.0);
|
||||
}
|
||||
for (var k = 0; k < indicesLength; ++k) {
|
||||
indices.push(indices.get(k) + vertexCount * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it('detects need to use uint32 indices', function() {
|
||||
var objData = clone(boxObjData, true);
|
||||
expandObjData(objData, 2731); // Right above 65536 limit
|
||||
var mesh = objData.nodes[0].meshes[0];
|
||||
var indicesLength = mesh.primitives[0].indices.length;
|
||||
var vertexCount = mesh.positions.length / 3;
|
||||
|
||||
var gltf = createGltf(objData);
|
||||
var primitive = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0];
|
||||
var indicesAccessor = gltf.accessors[primitive.indices];
|
||||
expect(indicesAccessor.count).toBe(indicesLength);
|
||||
expect(indicesAccessor.max[0]).toBe(vertexCount - 1);
|
||||
expect(indicesAccessor.componentType).toBe(WebGLConstants.UNSIGNED_INT);
|
||||
|
||||
var positionAccessor = gltf.accessors[primitive.attributes.POSITION];
|
||||
expect(positionAccessor.count).toBe(vertexCount);
|
||||
});
|
||||
});
|
91
specs/lib/imageSpec.js
Normal file
@ -0,0 +1,91 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var path = require('path');
|
||||
var loadImage = require('../../lib/image.js');
|
||||
|
||||
var WebGLConstants = Cesium.WebGLConstants;
|
||||
|
||||
var pngImage = 'specs/data/box-complex-material/shininess.png';
|
||||
var jpgImage = 'specs/data/box-complex-material/emission.jpg';
|
||||
var jpegImage = 'specs/data/box-complex-material/specular.jpeg';
|
||||
var gifImage = 'specs/data/box-complex-material/ambient.gif';
|
||||
var grayscaleImage = 'specs/data/box-complex-material/alpha.png';
|
||||
var transparentImage = 'specs/data/box-complex-material/diffuse.png';
|
||||
var invalidImage = 'invalid.png';
|
||||
|
||||
describe('image', function() {
|
||||
it('loads png image', function(done) {
|
||||
expect(loadImage(pngImage)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.channels).toBe(3);
|
||||
expect(info.data).toBeDefined();
|
||||
expect(info.uri.indexOf('data:image/png') === 0).toBe(true);
|
||||
expect(info.format).toBe(WebGLConstants.RGB);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads jpg image', function(done) {
|
||||
expect(loadImage(jpgImage)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.channels).toBe(3);
|
||||
expect(info.data).toBeDefined();
|
||||
expect(info.uri.indexOf('data:image/jpeg') === 0).toBe(true);
|
||||
expect(info.format).toBe(WebGLConstants.RGB);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads jpeg image', function(done) {
|
||||
expect(loadImage(jpegImage)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.channels).toBe(3);
|
||||
expect(info.data).toBeDefined();
|
||||
expect(info.uri.indexOf('data:image/jpeg') === 0).toBe(true);
|
||||
expect(info.format).toBe(WebGLConstants.RGB);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads gif image', function(done) {
|
||||
expect(loadImage(gifImage)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.channels).toBe(3);
|
||||
expect(info.data).toBeDefined();
|
||||
expect(info.uri.indexOf('data:image/gif') === 0).toBe(true);
|
||||
expect(info.format).toBe(WebGLConstants.RGB);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads grayscale image', function(done) {
|
||||
expect(loadImage(grayscaleImage)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.channels).toBe(1);
|
||||
expect(info.data).toBeDefined();
|
||||
expect(info.uri.indexOf('data:image/png') === 0).toBe(true);
|
||||
expect(info.format).toBe(WebGLConstants.ALPHA);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads transparentImage image', function(done) {
|
||||
expect(loadImage(transparentImage)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(true);
|
||||
expect(info.channels).toBe(4);
|
||||
expect(info.data).toBeDefined();
|
||||
expect(info.uri.indexOf('data:image/png') === 0).toBe(true);
|
||||
expect(info.format).toBe(WebGLConstants.RGBA);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('handles invalid image file', function(done) {
|
||||
spyOn(console, 'log');
|
||||
expect(loadImage(invalidImage)
|
||||
.then(function(image) {
|
||||
expect(image).toBeUndefined();
|
||||
expect(console.log.calls.argsFor(0)[0].indexOf('Could not read image file') >= 0).toBe(true);
|
||||
}), done).toResolve();
|
||||
});
|
||||
});
|
53
specs/lib/mtlSpec.js
Normal file
@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
var path = require('path');
|
||||
var loadMtl = require('../../lib/mtl.js');
|
||||
|
||||
var complexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.mtl';
|
||||
var multipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.mtl';
|
||||
var invalidMaterialUrl = 'invalid.mtl';
|
||||
|
||||
function getImagePath(objPath, relativePath) {
|
||||
return path.normalize(path.join(path.dirname(objPath), relativePath));
|
||||
}
|
||||
|
||||
describe('mtl', function() {
|
||||
it('loads complex material', function(done) {
|
||||
expect(loadMtl(complexMaterialUrl)
|
||||
.then(function(materials) {
|
||||
var material = materials.Material;
|
||||
expect(material).toBeDefined();
|
||||
expect(material.ambientColor).toEqual([0.2, 0.2, 0.2, 1.0]);
|
||||
expect(material.emissionColor).toEqual([0.1, 0.1, 0.1, 1.0]);
|
||||
expect(material.diffuseColor).toEqual([0.64, 0.64, 0.64, 1.0]);
|
||||
expect(material.specularColor).toEqual([0.5, 0.5, 0.5, 1.0]);
|
||||
expect(material.specularShininess).toEqual(96.078431);
|
||||
expect(material.alpha).toEqual(0.9);
|
||||
expect(material.ambientColorMap).toEqual(getImagePath(complexMaterialUrl, 'ambient.gif'));
|
||||
expect(material.emissionColorMap).toEqual(getImagePath(complexMaterialUrl, 'emission.jpg'));
|
||||
expect(material.diffuseColorMap).toEqual(getImagePath(complexMaterialUrl, 'diffuse.png'));
|
||||
expect(material.specularColorMap).toEqual(getImagePath(complexMaterialUrl, 'specular.jpeg'));
|
||||
expect(material.specularShininessMap).toEqual(getImagePath(complexMaterialUrl, 'shininess.png'));
|
||||
expect(material.normalMap).toEqual(getImagePath(complexMaterialUrl, 'bump.png'));
|
||||
expect(material.alphaMap).toEqual(getImagePath(complexMaterialUrl, 'alpha.png'));
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads mtl with multiple materials', function(done) {
|
||||
expect(loadMtl(multipleMaterialsUrl)
|
||||
.then(function(materials) {
|
||||
expect(Object.keys(materials).length).toBe(3);
|
||||
expect(materials.Red.diffuseColor).toEqual([0.64, 0.0, 0.0, 1.0]);
|
||||
expect(materials.Green.diffuseColor).toEqual([0.0, 0.64, 0.0, 1.0]);
|
||||
expect(materials.Blue.diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('handles invalid mtl file', function(done) {
|
||||
spyOn(console, 'log');
|
||||
expect(loadMtl(invalidMaterialUrl)
|
||||
.then(function(materials) {
|
||||
expect(materials).toEqual({});
|
||||
expect(console.log.calls.argsFor(0)[0].indexOf('Could not read material file') >= 0).toBe(true);
|
||||
}), done).toResolve();
|
||||
});
|
||||
});
|
328
specs/lib/objSpec.js
Normal file
@ -0,0 +1,328 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
var loadObj = require('../../lib/obj.js');
|
||||
|
||||
var RuntimeError = Cesium.RuntimeError;
|
||||
|
||||
var objUrl = 'specs/data/box/box.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';
|
||||
var objNegativeIndicesUrl = 'specs/data/box-negative-indices/box-negative-indices.obj';
|
||||
var objTrianglesUrl = 'specs/data/box-triangles/box-triangles.obj';
|
||||
var objObjectsUrl = 'specs/data/box-objects/box-objects.obj';
|
||||
var objGroupsUrl = 'specs/data/box-groups/box-groups.obj';
|
||||
var objObjectsGroupsUrl = 'specs/data/box-objects-groups/box-objects-groups.obj';
|
||||
var objUsemtlUrl = 'specs/data/box-usemtl/box-usemtl.obj';
|
||||
var objNoMaterialsUrl = 'specs/data/box-no-materials/box-no-materials.obj';
|
||||
var objMultipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.obj';
|
||||
var objUncleanedUrl = 'specs/data/box-uncleaned/box-uncleaned.obj';
|
||||
var objMtllibUrl = 'specs/data/box-mtllib/box-mtllib.obj';
|
||||
var objMissingMtllibUrl = 'specs/data/box-missing-mtllib/box-missing-mtllib.obj';
|
||||
var objTexturedUrl = 'specs/data/box-textured/box-textured.obj';
|
||||
var objMissingTextureUrl = 'specs/data/box-missing-texture/box-missing-texture.obj';
|
||||
var objSubdirectoriesUrl = 'specs/data/box-subdirectories/box-textured.obj';
|
||||
var objComplexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.obj';
|
||||
var objInvalidContentsUrl = 'specs/data/box/box.mtl';
|
||||
var objInvalidUrl = 'invalid.obj';
|
||||
|
||||
function getMeshes(data) {
|
||||
var meshes = [];
|
||||
var nodes = data.nodes;
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
meshes = meshes.concat(nodes[i].meshes);
|
||||
}
|
||||
return meshes;
|
||||
}
|
||||
|
||||
function getPrimitives(data) {
|
||||
var primitives = [];
|
||||
var nodes = data.nodes;
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
var meshes = nodes[i].meshes;
|
||||
var meshesLength = meshes.length;
|
||||
for (var j = 0; j < meshesLength; ++j) {
|
||||
primitives = primitives.concat(meshes[j].primitives);
|
||||
}
|
||||
}
|
||||
return primitives;
|
||||
}
|
||||
|
||||
function getImagePath(objPath, relativePath) {
|
||||
return path.normalize(path.join(path.dirname(objPath), relativePath));
|
||||
}
|
||||
|
||||
describe('obj', function() {
|
||||
it('loads obj with positions, normals, and uvs', function(done) {
|
||||
expect(loadObj(objUrl)
|
||||
.then(function(data) {
|
||||
var images = data.images;
|
||||
var materials = data.materials;
|
||||
var nodes = data.nodes;
|
||||
var meshes = getMeshes(data);
|
||||
var primitives = getPrimitives(data);
|
||||
|
||||
expect(Object.keys(images).length).toBe(0);
|
||||
expect(materials.Material).toBeDefined();
|
||||
expect(nodes.length).toBe(1);
|
||||
expect(meshes.length).toBe(1);
|
||||
expect(primitives.length).toBe(1);
|
||||
|
||||
var node = nodes[0];
|
||||
var mesh = meshes[0];
|
||||
var primitive = primitives[0];
|
||||
|
||||
expect(node.name).toBe('Cube');
|
||||
expect(mesh.name).toBe('Cube-Mesh');
|
||||
expect(mesh.positions.length / 3).toBe(24);
|
||||
expect(mesh.normals.length / 3).toBe(24);
|
||||
expect(mesh.uvs.length / 2).toBe(24);
|
||||
expect(primitive.indices.length).toBe(36);
|
||||
expect(primitive.material).toBe('Material');
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with normals', function(done) {
|
||||
expect(loadObj(objNormalsUrl)
|
||||
.then(function(data) {
|
||||
var mesh = getMeshes(data)[0];
|
||||
expect(mesh.positions.length / 3).toBe(24);
|
||||
expect(mesh.normals.length / 3).toBe(24);
|
||||
expect(mesh.uvs.length / 2).toBe(0);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with uvs', function(done) {
|
||||
expect(loadObj(objUvsUrl)
|
||||
.then(function(data) {
|
||||
var mesh = getMeshes(data)[0];
|
||||
expect(mesh.positions.length / 3).toBe(20);
|
||||
expect(mesh.normals.length / 3).toBe(0);
|
||||
expect(mesh.uvs.length / 2).toBe(20);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with negative indices', function(done) {
|
||||
expect(Promise.all([
|
||||
loadObj(objPositionsOnlyUrl),
|
||||
loadObj(objNegativeIndicesUrl)
|
||||
])
|
||||
.then(function(results) {
|
||||
var positionsReference = getMeshes(results[0])[0].positions.toFloatBuffer();
|
||||
var positions = getMeshes(results[1])[0].positions.toFloatBuffer();
|
||||
expect(positions).toEqual(positionsReference);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with triangle faces', function(done) {
|
||||
expect(loadObj(objTrianglesUrl)
|
||||
.then(function(data) {
|
||||
var mesh = getMeshes(data)[0];
|
||||
var primitive = getPrimitives(data)[0];
|
||||
expect(mesh.positions.length / 3).toBe(24);
|
||||
expect(primitive.indices.length).toBe(36);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with triangle faces', function(done) {
|
||||
expect(loadObj(objTrianglesUrl)
|
||||
.then(function(data) {
|
||||
var mesh = getMeshes(data)[0];
|
||||
var primitive = getPrimitives(data)[0];
|
||||
expect(mesh.positions.length / 3).toBe(24);
|
||||
expect(primitive.indices.length).toBe(36);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with objects', function(done) {
|
||||
expect(loadObj(objObjectsUrl)
|
||||
.then(function(data) {
|
||||
var nodes = data.nodes;
|
||||
expect(nodes.length).toBe(3);
|
||||
expect(nodes[0].name).toBe('CubeBlue');
|
||||
expect(nodes[1].name).toBe('CubeGreen');
|
||||
expect(nodes[2].name).toBe('CubeRed');
|
||||
|
||||
var primitives = getPrimitives(data);
|
||||
expect(primitives.length).toBe(3);
|
||||
expect(primitives[0].material).toBe('Blue');
|
||||
expect(primitives[1].material).toBe('Green');
|
||||
expect(primitives[2].material).toBe('Red');
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with groups', function(done) {
|
||||
expect(loadObj(objGroupsUrl)
|
||||
.then(function(data) {
|
||||
var nodes = data.nodes;
|
||||
expect(nodes.length).toBe(3);
|
||||
expect(nodes[0].name).toBe('CubeBlue');
|
||||
expect(nodes[1].name).toBe('CubeGreen');
|
||||
expect(nodes[2].name).toBe('CubeRed');
|
||||
|
||||
var primitives = getPrimitives(data);
|
||||
expect(primitives.length).toBe(3);
|
||||
expect(primitives[0].material).toBe('Blue');
|
||||
expect(primitives[1].material).toBe('Green');
|
||||
expect(primitives[2].material).toBe('Red');
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with objects and groups', function(done) {
|
||||
expect(loadObj(objObjectsGroupsUrl)
|
||||
.then(function(data) {
|
||||
var nodes = data.nodes;
|
||||
expect(nodes.length).toBe(3);
|
||||
expect(nodes[0].name).toBe('CubeBlue');
|
||||
expect(nodes[1].name).toBe('CubeGreen');
|
||||
expect(nodes[2].name).toBe('CubeRed');
|
||||
|
||||
var meshes = getMeshes(data);
|
||||
expect(meshes.length).toBe(3);
|
||||
expect(meshes[0].name).toBe('CubeBlue_CubeBlue_Blue');
|
||||
expect(meshes[1].name).toBe('CubeGreen_CubeGreen_Green');
|
||||
expect(meshes[2].name).toBe('CubeRed_CubeRed_Red');
|
||||
|
||||
var primitives = getPrimitives(data);
|
||||
expect(primitives.length).toBe(3);
|
||||
expect(primitives[0].material).toBe('Blue');
|
||||
expect(primitives[1].material).toBe('Green');
|
||||
expect(primitives[2].material).toBe('Red');
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with usemtl only', function(done) {
|
||||
expect(loadObj(objUsemtlUrl)
|
||||
.then(function(data) {
|
||||
var nodes = data.nodes;
|
||||
expect(nodes.length).toBe(1);
|
||||
expect(nodes[0].name).toBe('Node'); // default name
|
||||
|
||||
var meshes = getMeshes(data);
|
||||
expect(meshes.length).toBe(1);
|
||||
expect(meshes[0].name).toBe('Node-Mesh');
|
||||
|
||||
var primitives = getPrimitives(data);
|
||||
expect(primitives.length).toBe(3);
|
||||
expect(primitives[0].material).toBe('Blue');
|
||||
expect(primitives[1].material).toBe('Green');
|
||||
expect(primitives[2].material).toBe('Red');
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with no materials', function(done) {
|
||||
expect(loadObj(objNoMaterialsUrl)
|
||||
.then(function(data) {
|
||||
var nodes = data.nodes;
|
||||
expect(nodes.length).toBe(1);
|
||||
expect(nodes[0].name).toBe('Node'); // default name
|
||||
|
||||
var primitives = getPrimitives(data);
|
||||
expect(primitives.length).toBe(1);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with multiple materials', function(done) {
|
||||
// The usemtl markers are interleaved, but should condense to just three primitives
|
||||
expect(loadObj(objMultipleMaterialsUrl)
|
||||
.then(function(data) {
|
||||
var nodes = data.nodes;
|
||||
expect(nodes.length).toBe(1);
|
||||
|
||||
var primitives = getPrimitives(data);
|
||||
expect(primitives.length).toBe(3);
|
||||
|
||||
expect(primitives[0].indices.length).toBe(12);
|
||||
expect(primitives[1].indices.length).toBe(12);
|
||||
expect(primitives[2].indices.length).toBe(12);
|
||||
expect(primitives[0].material).toBe('Red');
|
||||
expect(primitives[1].material).toBe('Green');
|
||||
expect(primitives[2].material).toBe('Blue');
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj uncleaned', function(done) {
|
||||
// Obj with extraneous o, g, and usemtl lines
|
||||
// Also tests handling of o and g lines with the same names
|
||||
expect(loadObj(objUncleanedUrl)
|
||||
.then(function(data) {
|
||||
var nodes = data.nodes;
|
||||
var meshes = getMeshes(data);
|
||||
var primitives = getPrimitives(data);
|
||||
|
||||
expect(nodes.length).toBe(1);
|
||||
expect(meshes.length).toBe(1);
|
||||
expect(primitives.length).toBe(1);
|
||||
|
||||
expect(nodes[0].name).toBe('Cube');
|
||||
expect(meshes[0].name).toBe('Cube_1');
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with multiple mtllibs', function(done) {
|
||||
expect(loadObj(objMtllibUrl)
|
||||
.then(function(data) {
|
||||
var materials = data.materials;
|
||||
expect(Object.keys(materials).length).toBe(3);
|
||||
expect(materials.Red.diffuseColor).toEqual([0.64, 0.0, 0.0, 1.0]);
|
||||
expect(materials.Green.diffuseColor).toEqual([0.0, 0.64, 0.0, 1.0]);
|
||||
expect(materials.Blue.diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with missing mtllib', function(done) {
|
||||
spyOn(console, 'log');
|
||||
expect(loadObj(objMissingMtllibUrl)
|
||||
.then(function(data) {
|
||||
expect(data.materials).toEqual({});
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with texture', function(done) {
|
||||
expect(loadObj(objTexturedUrl)
|
||||
.then(function(data) {
|
||||
var imagePath = getImagePath(objTexturedUrl, 'cesium.png');
|
||||
expect(data.images[imagePath]).toBeDefined();
|
||||
expect(data.materials.Material.diffuseColorMap).toEqual(imagePath);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with missing texture', function(done) {
|
||||
spyOn(console, 'log');
|
||||
expect(loadObj(objMissingTextureUrl)
|
||||
.then(function(data) {
|
||||
var imagePath = getImagePath(objMissingTextureUrl, 'cesium.png');
|
||||
expect(data.images[imagePath]).toBeUndefined();
|
||||
expect(data.materials.Material.diffuseColorMap).toEqual(imagePath);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with subdirectories', function(done) {
|
||||
expect(loadObj(objSubdirectoriesUrl)
|
||||
.then(function(data) {
|
||||
var imagePath = getImagePath(objSubdirectoriesUrl, path.join('materials', 'images', 'cesium.png'));
|
||||
expect(data.images[imagePath]).toBeDefined();
|
||||
expect(data.materials.Material.diffuseColorMap).toEqual(imagePath);
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('loads obj with complex material', function(done) {
|
||||
expect(loadObj(objComplexMaterialUrl)
|
||||
.then(function(data) {
|
||||
var images = data.images;
|
||||
expect(Object.keys(images).length).toBe(4); // Only ambient, diffuse, emission, and specular maps are supported by the converter
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
||||
it('does not process file with invalid contents', function(done) {
|
||||
expect(loadObj(objInvalidContentsUrl), done).toRejectWith(RuntimeError);
|
||||
});
|
||||
|
||||
it('throw when reading invalid file', function(done) {
|
||||
expect(loadObj(objInvalidUrl), done).toRejectWith(Error);
|
||||
});
|
||||
});
|