Refactor and tests
10
.gitignore
vendored
@ -2,14 +2,16 @@
|
|||||||
node_modules
|
node_modules
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
|
# TypeScript definitions
|
||||||
|
typings
|
||||||
|
|
||||||
# WebStorm user-specific
|
# WebStorm user-specific
|
||||||
.idea/workspace.xml
|
.idea/workspace.xml
|
||||||
.idea/tasks.xml
|
.idea/tasks.xml
|
||||||
|
|
||||||
# TypeScript definitions
|
|
||||||
typings
|
|
||||||
|
|
||||||
# Generate data
|
# Generate data
|
||||||
test
|
|
||||||
coverage
|
coverage
|
||||||
|
doc
|
||||||
|
output
|
||||||
|
test
|
||||||
*.tgz
|
*.tgz
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<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>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -2,6 +2,7 @@
|
|||||||
/doc
|
/doc
|
||||||
/specs
|
/specs
|
||||||
/test
|
/test
|
||||||
|
/output
|
||||||
/typings
|
/typings
|
||||||
/coverage
|
/coverage
|
||||||
.jshintrc
|
.jshintrc
|
||||||
|
@ -4,3 +4,8 @@ node_js:
|
|||||||
script:
|
script:
|
||||||
- npm run jsHint -- --failTaskOnError
|
- npm run jsHint -- --failTaskOnError
|
||||||
- npm run test -- --failTaskOnError --suppressPassed
|
- 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.
|
obj2gltf includes the following third-party code.
|
||||||
|
|
||||||
### async
|
### bluebird
|
||||||
|
|
||||||
https://www.npmjs.com/package/async
|
> The MIT License (MIT)
|
||||||
|
>
|
||||||
> Copyright (c) 2010-2016 Caolan McMahon
|
> Copyright (c) 2013-2015 Petka Antonov
|
||||||
>
|
>
|
||||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
> of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
> in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
> copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
> furnished to do so, subject to the following conditions:
|
||||||
>
|
>
|
||||||
> The above copyright notice and this permission notice shall be included in
|
> 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
|
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
> 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
|
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
> 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.
|
|
||||||
|
|
||||||
### Cesium
|
### Cesium
|
||||||
|
|
||||||
@ -73,6 +49,35 @@ http://cesiumjs.org/
|
|||||||
|
|
||||||
See https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md
|
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
|
### fs-extra
|
||||||
|
|
||||||
https://www.npmjs.com/package/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
|
> 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
|
> 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
|
> 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:
|
> 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 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
|
> 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
|
> 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,
|
> 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.
|
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
### gltf-pipeline
|
### gltf-pipeline
|
||||||
|
|
||||||
@ -112,24 +117,24 @@ https://www.npmjs.com/package/yargs
|
|||||||
The command-line tool uses yargs.
|
The command-line tool uses yargs.
|
||||||
|
|
||||||
> Copyright 2010 James Halliday (mail@substack.net)
|
> 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:
|
> This project is free software released under the MIT/X11 license:
|
||||||
>
|
>
|
||||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
> of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
> in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
> copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
> furnished to do so, subject to the following conditions:
|
||||||
>
|
>
|
||||||
> The above copyright notice and this permission notice shall be included in
|
> 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
|
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
> 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
|
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
> THE SOFTWARE.
|
||||||
|
18
README.md
@ -13,7 +13,7 @@ Using obj2gltf as a library:
|
|||||||
var obj2gltf = require('obj2gltf');
|
var obj2gltf = require('obj2gltf');
|
||||||
var convert = obj2gltf.convert;
|
var convert = obj2gltf.convert;
|
||||||
var options = {
|
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)
|
convert('model.obj', 'model.gltf', options)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@ -30,20 +30,24 @@ Using obj2gltf as a command-line tool:
|
|||||||
|
|
||||||
`node bin/obj2gltf.js -i model.obj -o model.gltf -s`
|
`node bin/obj2gltf.js -i model.obj -o model.gltf -s`
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
###Command line flags:
|
###Command line flags:
|
||||||
|
|
||||||
|Flag|Description|Required|
|
|Flag|Description|Required|
|
||||||
|----|-----------|--------|
|
|----|-----------|--------|
|
||||||
|`-i`|Path to the input OBJ file.| :white_check_mark: Yes|
|
|`-h`|Display help.|No|
|
||||||
|`-o`|Directory or filename for the exported glTF file.|No|
|
|`-i`|Path to the obj file.| :white_check_mark: Yes|
|
||||||
|`-b`|Output binary glTF.|No, default `false`|
|
|`-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`|
|
|`-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`|
|
|`-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`|
|
|`--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
|
## Contributions
|
||||||
|
|
||||||
@ -53,5 +57,5 @@ Pull requests are appreciated. Please use the same [Contributor License Agreeme
|
|||||||
|
|
||||||
Developed by the Cesium team.
|
Developed by the Cesium team.
|
||||||
<p align="center">
|
<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>
|
</p>
|
||||||
|
143
bin/obj2gltf.js
@ -1,53 +1,116 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
"use strict";
|
'use strict';
|
||||||
var argv = require('yargs').argv;
|
|
||||||
var Cesium = require('cesium');
|
var Cesium = require('cesium');
|
||||||
var defined = Cesium.defined;
|
var path = require('path');
|
||||||
var defaultValue = Cesium.defaultValue;
|
var yargs = require('yargs');
|
||||||
var convert = require('../lib/convert');
|
var convert = require('../lib/convert');
|
||||||
|
|
||||||
if (process.argv.length < 3 || defined(argv.h) || defined(argv.help)) {
|
var defaultValue = Cesium.defaultValue;
|
||||||
console.log('Usage: ./bin/obj2gltf.js [INPUT] [OPTIONS]');
|
var defined = Cesium.defined;
|
||||||
console.log(' -i, --input Path to obj file');
|
|
||||||
console.log(' -o, --output Directory or filename for the exported glTF file');
|
var args = process.argv;
|
||||||
console.log(' -b, --binary Output binary glTF');
|
args = args.slice(2, args.length);
|
||||||
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.');
|
var argv = yargs
|
||||||
console.log(' -c --compress Quantize positions, compress texture coordinates, and oct-encode normals.');
|
.usage('Usage: node $0 -i inputPath -o outputPath')
|
||||||
console.log(' -h, --help Display this help');
|
.example('node $0 -i ./specs/data/box/box.obj -o box.gltf')
|
||||||
console.log(' --ao Apply ambient occlusion to the converted model');
|
.help('h')
|
||||||
console.log(' --cesium Optimize the glTF for Cesium by using the sun as a default light source.');
|
.alias('h', 'help')
|
||||||
process.exit(0);
|
.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));
|
if (!defined(gltfPath)) {
|
||||||
var outputPath = defaultValue(argv._[1], defaultValue(argv.o, argv.output));
|
var extension = argv.b ? '.glb' : '.gltf';
|
||||||
var binary = defaultValue(defaultValue(argv.b, argv.binary), false);
|
var modelName = path.basename(objPath, path.extname(objPath));
|
||||||
var separate = defaultValue(defaultValue(argv.s, argv.separate), false);
|
gltfPath = path.join(path.dirname(objPath), modelName + extension);
|
||||||
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.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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');
|
console.time('Total');
|
||||||
|
|
||||||
var options = {
|
convert(objPath, gltfPath, options)
|
||||||
binary : binary,
|
|
||||||
embed : !separate,
|
|
||||||
embedImage : !separateImage,
|
|
||||||
compress : compress,
|
|
||||||
ao : ao,
|
|
||||||
optimizeForCesium : optimizeForCesium
|
|
||||||
};
|
|
||||||
|
|
||||||
convert(objFile, outputPath, options)
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
console.timeEnd('Total');
|
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 gulp = require('gulp');
|
||||||
var gulpJshint = require('gulp-jshint');
|
var gulpJshint = require('gulp-jshint');
|
||||||
var Jasmine = require('jasmine');
|
var Jasmine = require('jasmine');
|
||||||
var JasmineSpecReporter = require('jasmine-spec-reporter');
|
var JasmineSpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
||||||
var open = require('open');
|
var open = require('open');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var yargs = require('yargs');
|
var yargs = require('yargs');
|
||||||
@ -20,8 +20,8 @@ var environmentSeparator = process.platform === 'win32' ? ';' : ':';
|
|||||||
var nodeBinaries = path.join(__dirname, 'node_modules', '.bin');
|
var nodeBinaries = path.join(__dirname, 'node_modules', '.bin');
|
||||||
process.env.PATH += environmentSeparator + nodeBinaries;
|
process.env.PATH += environmentSeparator + nodeBinaries;
|
||||||
|
|
||||||
var jsHintFiles = ['**/*.js', '!node_modules/**', '!coverage/**'];
|
var jsHintFiles = ['**/*.js', '!node_modules/**', '!coverage/**', '!doc/**'];
|
||||||
var specFiles = ['**/*.js', '!node_modules/**', '!coverage/**'];
|
var specFiles = ['**/*.js', '!node_modules/**', '!coverage/**', '!doc/**', '!bin/**'];
|
||||||
|
|
||||||
gulp.task('jsHint', function () {
|
gulp.task('jsHint', function () {
|
||||||
var stream = gulp.src(jsHintFiles)
|
var stream = gulp.src(jsHintFiles)
|
||||||
@ -53,8 +53,8 @@ gulp.task('test', function (done) {
|
|||||||
|
|
||||||
gulp.task('test-watch', function () {
|
gulp.task('test-watch', function () {
|
||||||
gulp.watch(specFiles).on('change', function () {
|
gulp.watch(specFiles).on('change', function () {
|
||||||
//We can't simply depend on the test task because Jasmine
|
// We can't simply depend on the test task because Jasmine
|
||||||
//does not like being run multiple times in the same process.
|
// does not like being run multiple times in the same process.
|
||||||
try {
|
try {
|
||||||
child_process.execSync('jasmine JASMINE_CONFIG_PATH=specs/jasmine.json', {
|
child_process.execSync('jasmine JASMINE_CONFIG_PATH=specs/jasmine.json', {
|
||||||
stdio: [process.stdin, process.stdout, process.stderr]
|
stdio: [process.stdin, process.stdout, process.stderr]
|
||||||
@ -71,7 +71,7 @@ gulp.task('coverage', function () {
|
|||||||
' cover' +
|
' cover' +
|
||||||
' --include-all-sources' +
|
' --include-all-sources' +
|
||||||
' --dir coverage' +
|
' --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' +
|
' node_modules/jasmine/bin/jasmine.js' +
|
||||||
' JASMINE_CONFIG_PATH=specs/jasmine.json', {
|
' JASMINE_CONFIG_PATH=specs/jasmine.json', {
|
||||||
stdio: [process.stdin, process.stdout, process.stderr]
|
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";
|
'use strict';
|
||||||
var path = require('path');
|
|
||||||
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
|
||||||
var parseObj = require('./obj');
|
|
||||||
var createGltf = require('./gltf');
|
|
||||||
var Cesium = require('cesium');
|
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 defaultValue = Cesium.defaultValue;
|
||||||
|
var defined = Cesium.defined;
|
||||||
|
var DeveloperError = Cesium.DeveloperError;
|
||||||
|
|
||||||
module.exports = convert;
|
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 binary = defaultValue(options.binary, false);
|
||||||
var embed = defaultValue(options.embed, true);
|
var separate = defaultValue(options.separate, false);
|
||||||
var embedImage = defaultValue(options.embedImage, true);
|
var separateTextures = defaultValue(options.separateTextures, false);
|
||||||
var compress = defaultValue(options.compress, 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 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)) {
|
if (!defined(objPath)) {
|
||||||
throw new Error('objFile is required');
|
throw new DeveloperError('objPath is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!defined(outputPath)) {
|
if (!defined(gltfPath)) {
|
||||||
outputPath = path.dirname(objFile);
|
throw new DeveloperError('gltfPath is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputPath = path.dirname(objFile);
|
var basePath = path.dirname(objPath);
|
||||||
var modelName = path.basename(objFile, '.obj');
|
var modelName = path.basename(objPath, path.extname(objPath));
|
||||||
|
var extension = path.extname(gltfPath);
|
||||||
var extension = path.extname(outputPath);
|
if (extension === '.glb') {
|
||||||
if (extension !== '') {
|
binary = true;
|
||||||
modelName = path.basename(outputPath, extension);
|
|
||||||
outputPath = path.dirname(outputPath);
|
|
||||||
}
|
}
|
||||||
|
gltfPath = path.join(path.dirname(gltfPath), modelName + extension);
|
||||||
|
|
||||||
extension = binary ? '.glb' : '.gltf';
|
var aoOptions = ao ? {} : undefined;
|
||||||
var gltfFile = path.join(outputPath, modelName + extension);
|
|
||||||
|
|
||||||
return parseObj(objFile, inputPath)
|
var pipelineOptions = {
|
||||||
.then(function(data) {
|
createDirectory : false,
|
||||||
return createGltf(data, inputPath, modelName);
|
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) {
|
.then(function(gltf) {
|
||||||
var aoOptions = ao ? {} : undefined;
|
return saveExternalBuffer(gltf, gltfPath);
|
||||||
var options = {
|
})
|
||||||
binary: binary,
|
.then(function(gltf) {
|
||||||
embed: embed,
|
if (bypassPipeline) {
|
||||||
embedImage: embedImage,
|
return convert._outputJson(gltfPath, gltf);
|
||||||
encodeNormals: compress,
|
} else {
|
||||||
quantize: compress,
|
return GltfPipeline.processJSONToDisk(gltf, gltfPath, pipelineOptions);
|
||||||
aoOptions: aoOptions,
|
}
|
||||||
optimizeForCesium : optimizeForCesium,
|
});
|
||||||
createDirectory: false,
|
}
|
||||||
basePath: inputPath
|
|
||||||
};
|
/**
|
||||||
return GltfPipeline.processJSONToDisk(gltf, gltfFile, options);
|
* 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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
548
lib/gltf.js
@ -1,148 +1,37 @@
|
|||||||
"use strict";
|
'use strict';
|
||||||
var Cesium = require('cesium');
|
var Cesium = require('cesium');
|
||||||
var Promise = require('bluebird');
|
|
||||||
var fs = require('fs-extra');
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
var defined = Cesium.defined;
|
var defined = Cesium.defined;
|
||||||
var defaultValue = Cesium.defaultValue;
|
var defaultValue = Cesium.defaultValue;
|
||||||
var WebGLConstants = Cesium.WebGLConstants;
|
var WebGLConstants = Cesium.WebGLConstants;
|
||||||
|
|
||||||
var fsWriteFile = Promise.promisify(fs.writeFile);
|
|
||||||
|
|
||||||
module.exports = createGltf;
|
module.exports = createGltf;
|
||||||
|
|
||||||
function createGltf(data, inputPath, modelName) {
|
/**
|
||||||
var vertexCount = data.vertexCount;
|
* Create a glTF from obj data.
|
||||||
var vertexArray = data.vertexArray;
|
*
|
||||||
var positionMin = data.positionMin;
|
* @param {Object} objData Output of obj.js, containing an array of nodes containing geometry information, materials, and images.
|
||||||
var positionMax = data.positionMax;
|
* @returns {Object} A glTF asset with the KHR_materials_common extension.
|
||||||
var hasUVs = data.hasUVs;
|
*
|
||||||
var hasNormals = data.hasNormals;
|
* @private
|
||||||
var materialGroups = data.materialGroups;
|
*/
|
||||||
var materials = data.materials;
|
function createGltf(objData) {
|
||||||
var images = data.images;
|
var nodes = objData.nodes;
|
||||||
|
var materials = objData.materials;
|
||||||
var i, j, name;
|
var images = objData.images;
|
||||||
|
var sceneId = 'scene';
|
||||||
var sizeOfFloat32 = 4;
|
var samplerId = 'sampler';
|
||||||
var sizeOfUint32 = 4;
|
var bufferId = 'buffer';
|
||||||
var sizeOfUint16 = 2;
|
var vertexBufferViewId = 'bufferView_vertex';
|
||||||
|
var indexBufferViewId = 'bufferView_index';
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
var gltf = {
|
var gltf = {
|
||||||
accessors : {},
|
accessors : {},
|
||||||
asset : {},
|
asset : {},
|
||||||
buffers : {},
|
buffers : {},
|
||||||
bufferViews : {},
|
bufferViews : {},
|
||||||
|
extensionsUsed : ['KHR_materials_common'],
|
||||||
images : {},
|
images : {},
|
||||||
materials : {},
|
materials : {},
|
||||||
meshes : {},
|
meshes : {},
|
||||||
@ -154,174 +43,97 @@ function createGltf(data, inputPath, modelName) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
gltf.asset = {
|
gltf.asset = {
|
||||||
"generator": "OBJ2GLTF",
|
generator : 'obj2gltf',
|
||||||
"premultipliedAlpha": true,
|
profile : {
|
||||||
"profile": {
|
api : 'WebGL',
|
||||||
"api": "WebGL",
|
version : '1.0'
|
||||||
"version": "1.0"
|
|
||||||
},
|
},
|
||||||
"version": 1
|
version: '1.0'
|
||||||
};
|
};
|
||||||
|
|
||||||
gltf.scenes[sceneId] = {
|
gltf.scenes[sceneId] = {
|
||||||
nodes : [nodeId]
|
nodes : []
|
||||||
};
|
};
|
||||||
|
|
||||||
gltf.nodes[nodeId] = {
|
function getImageId(imagePath) {
|
||||||
children : [],
|
return path.basename(imagePath, path.extname(imagePath));
|
||||||
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
|
function getTextureId(imagePath) {
|
||||||
|
if (!defined(imagePath) || !defined(images[imagePath])) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return 'texture_' + getImageId(imagePath);
|
||||||
|
}
|
||||||
|
|
||||||
var bufferSeparate = false;
|
function createMaterial(material, hasNormals) {
|
||||||
var bufferUri;
|
var ambient = defaultValue(defaultValue(getTextureId(material.ambientColorMap), material.ambientColor), [0, 0, 0, 1]);
|
||||||
if (buffer.length > 201326580) {
|
var diffuse = defaultValue(defaultValue(getTextureId(material.diffuseColorMap), material.diffuseColor), [0.5, 0.5, 0.5, 1]);
|
||||||
// toString fails for buffers larger than ~192MB. Instead save the buffer to a .bin file.
|
var emission = defaultValue(defaultValue(getTextureId(material.emissionColorMap), material.emissionColor), [0, 0, 0, 1]);
|
||||||
// Source: https://github.com/nodejs/node/issues/4266
|
var specular = defaultValue(defaultValue(getTextureId(material.specularColorMap), material.specularColor), [0, 0, 0, 1]);
|
||||||
bufferSeparate = true;
|
var alpha = defaultValue(defaultValue(material.alpha), 1.0);
|
||||||
bufferUri = modelName + '.bin';
|
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 {
|
} else {
|
||||||
bufferUri = 'data:application/octet-stream;base64,' + buffer.toString('base64');
|
diffuse[3] = alpha;
|
||||||
|
transparent = diffuse[3] < 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
gltf.buffers[bufferId] = {
|
var doubleSided = transparent;
|
||||||
byteLength : bufferByteLength,
|
|
||||||
type : 'arraybuffer',
|
|
||||||
uri : bufferUri
|
|
||||||
};
|
|
||||||
|
|
||||||
gltf.bufferViews[bufferViewVertexId] = {
|
if (!hasNormals) {
|
||||||
buffer : bufferId,
|
// Constant technique only factors in ambient and emission sources - set emission to diffuse
|
||||||
byteLength : vertexArrayByteLength,
|
emission = diffuse;
|
||||||
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 technique = hasNormals ? (hasSpecular ? 'PHONG' : 'LAMBERT') : 'CONSTANT';
|
||||||
var primitive = primitives[i];
|
return {
|
||||||
gltf.accessors[getAccessorIndexId(i)] = {
|
extensions : {
|
||||||
bufferView : bufferViewIndexId,
|
KHR_materials_common : {
|
||||||
byteOffset : primitive.indexOffset * indexComponentSize,
|
technique : technique,
|
||||||
byteStride : 0,
|
values : {
|
||||||
componentType : indexComponentType,
|
ambient : ambient,
|
||||||
count : primitive.indexCount,
|
diffuse : diffuse,
|
||||||
type : 'SCALAR'
|
emission : emission,
|
||||||
|
specular : specular,
|
||||||
|
shininess : shininess,
|
||||||
|
transparency : transparency,
|
||||||
|
transparent : transparent,
|
||||||
|
doubleSided : doubleSided
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
gltf.accessors[accessorPositionId] = {
|
if (Object.keys(images).length > 0) {
|
||||||
bufferView : bufferViewVertexId,
|
gltf.samplers[samplerId] = {
|
||||||
byteOffset : positionByteOffset,
|
magFilter : WebGLConstants.LINEAR,
|
||||||
byteStride : vertexByteStride,
|
minFilter : WebGLConstants.LINEAR,
|
||||||
componentType : WebGLConstants.FLOAT,
|
wrapS : WebGLConstants.REPEAT,
|
||||||
count : vertexCount,
|
wrapT : WebGLConstants.REPEAT
|
||||||
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) {
|
for (var imagePath in images) {
|
||||||
gltf.accessors[accessorUVId] = {
|
if (images.hasOwnProperty(imagePath)) {
|
||||||
bufferView : bufferViewVertexId,
|
var image = images[imagePath];
|
||||||
byteOffset : uvByteOffset,
|
var imageId = getImageId(imagePath);
|
||||||
byteStride : vertexByteStride,
|
var textureId = getTextureId(imagePath);
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
gltf.images[imageId] = {
|
gltf.images[imageId] = {
|
||||||
|
name : imageId,
|
||||||
uri : image.uri
|
uri : image.uri
|
||||||
};
|
};
|
||||||
gltf.textures[textureId] = {
|
gltf.textures[textureId] = {
|
||||||
format : format,
|
format : image.format,
|
||||||
internalFormat : format,
|
internalFormat : image.format,
|
||||||
sampler : samplerId,
|
sampler : samplerId,
|
||||||
source : imageId,
|
source : imageId,
|
||||||
target : WebGLConstants.TEXTURE_2D,
|
target : WebGLConstants.TEXTURE_2D,
|
||||||
@ -330,9 +142,191 @@ function createGltf(data, inputPath, modelName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bufferSeparate) {
|
var vertexBuffers = [];
|
||||||
var bufferPath = path.join(inputPath, modelName + '.bin');
|
var vertexByteOffset = 0;
|
||||||
return fsWriteFile(bufferPath, buffer);
|
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;
|
return gltf;
|
||||||
}
|
}
|
||||||
|
90
lib/image.js
@ -1,12 +1,55 @@
|
|||||||
"use strict";
|
'use strict';
|
||||||
var Promise = require('bluebird');
|
var Cesium = require('cesium');
|
||||||
var fs = require('fs-extra');
|
var fs = require('fs-extra');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var Promise = require('bluebird');
|
||||||
|
|
||||||
var fsReadFile = Promise.promisify(fs.readFile);
|
var fsReadFile = Promise.promisify(fs.readFile);
|
||||||
|
|
||||||
|
var WebGLConstants = Cesium.WebGLConstants;
|
||||||
|
|
||||||
module.exports = loadImage;
|
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) {
|
function getChannels(colorType) {
|
||||||
switch (colorType) {
|
switch (colorType) {
|
||||||
case 0: // greyscale
|
case 0: // greyscale
|
||||||
@ -24,40 +67,27 @@ function getChannels(colorType) {
|
|||||||
|
|
||||||
function getUriType(extension) {
|
function getUriType(extension) {
|
||||||
switch (extension) {
|
switch (extension) {
|
||||||
case 'png':
|
case '.png':
|
||||||
return 'data:image/png';
|
return 'data:image/png';
|
||||||
case 'jpg':
|
case '.jpg':
|
||||||
|
case '.jpeg':
|
||||||
return 'data:image/jpeg';
|
return 'data:image/jpeg';
|
||||||
case 'jpeg':
|
case '.gif':
|
||||||
return 'data:image/jpeg';
|
|
||||||
case 'gif':
|
|
||||||
return 'data:image/gif';
|
return 'data:image/gif';
|
||||||
default:
|
default:
|
||||||
return 'data:image/' + extension;
|
return 'data:image/' + extension.slice(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadImage(imagePath) {
|
function getFormat(channels) {
|
||||||
return fsReadFile(imagePath)
|
switch (channels) {
|
||||||
.then(function(data) {
|
case 1:
|
||||||
var extension = path.extname(imagePath).slice(1);
|
return WebGLConstants.ALPHA;
|
||||||
var uriType = getUriType(extension);
|
case 2:
|
||||||
var uri = uriType + ';base64,' + data.toString('base64');
|
return WebGLConstants.LUMINANCE_ALPHA;
|
||||||
|
case 3:
|
||||||
var info = {
|
return WebGLConstants.RGB;
|
||||||
transparent: false,
|
case 4:
|
||||||
channels: 3,
|
return WebGLConstants.RGBA;
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
100
lib/mtl.js
@ -1,53 +1,44 @@
|
|||||||
"use strict";
|
'use strict';
|
||||||
var Promise = require('bluebird');
|
var path = require('path');
|
||||||
var fs = require('fs-extra');
|
var readLines = require('./readLines');
|
||||||
var defined = require('cesium').defined;
|
|
||||||
|
|
||||||
var fsReadFile = Promise.promisify(fs.readFile);
|
module.exports = loadMtl;
|
||||||
|
|
||||||
module.exports = {
|
function Material() {
|
||||||
getDefault : getDefault,
|
this.ambientColor = undefined; // Ka
|
||||||
parse : parse
|
this.emissionColor = undefined; // Ke
|
||||||
};
|
this.diffuseColor = undefined; // Kd
|
||||||
|
this.specularColor = undefined; // Ks
|
||||||
function createMaterial() {
|
this.specularShininess = undefined; // Ns
|
||||||
return {
|
this.alpha = undefined; // d / Tr
|
||||||
ambientColor : undefined, // Ka
|
this.ambientColorMap = undefined; // map_Ka
|
||||||
emissionColor : undefined, // Ke
|
this.emissionColorMap = undefined; // map_Ke
|
||||||
diffuseColor : undefined, // Kd
|
this.diffuseColorMap = undefined; // map_Kd
|
||||||
specularColor : undefined, // Ks
|
this.specularColorMap = undefined; // map_Ks
|
||||||
specularShininess : undefined, // Ns
|
this.specularShininessMap = undefined; // map_Ns
|
||||||
alpha : undefined, // d / Tr
|
this.normalMap = undefined; // map_Bump
|
||||||
ambientColorMap : undefined, // map_Ka
|
this.alphaMap = undefined; // map_d
|
||||||
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 getDefault() {
|
/**
|
||||||
var material = createMaterial();
|
* Parse an mtl file.
|
||||||
material.diffuseColor = [0.5, 0.5, 0.5, 1.0];
|
*
|
||||||
return material;
|
* @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.
|
||||||
|
*
|
||||||
function parse(mtlPath) {
|
* @private
|
||||||
return fsReadFile(mtlPath, 'utf8')
|
*/
|
||||||
.then(function (contents) {
|
function loadMtl(mtlPath) {
|
||||||
var materials = {};
|
|
||||||
var material;
|
var material;
|
||||||
var values;
|
var values;
|
||||||
var value;
|
var value;
|
||||||
var lines = contents.split('\n');
|
var materials = {};
|
||||||
var length = lines.length;
|
|
||||||
for (var i = 0; i < length; ++i) {
|
function parseLine(line) {
|
||||||
var line = lines[i].trim();
|
line = line.trim();
|
||||||
if (/^newmtl /i.test(line)) {
|
if (/^newmtl /i.test(line)) {
|
||||||
var name = line.substring(7).trim();
|
var name = line.substring(7).trim();
|
||||||
material = createMaterial();
|
material = new Material();
|
||||||
materials[name] = material;
|
materials[name] = material;
|
||||||
} else if (/^Ka /i.test(line)) {
|
} else if (/^Ka /i.test(line)) {
|
||||||
values = line.substring(3).trim().split(' ');
|
values = line.substring(3).trim().split(' ');
|
||||||
@ -91,24 +82,24 @@ function parse(mtlPath) {
|
|||||||
value = line.substring(3).trim();
|
value = line.substring(3).trim();
|
||||||
material.alpha = parseFloat(value);
|
material.alpha = parseFloat(value);
|
||||||
} else if (/^map_Ka /i.test(line)) {
|
} else if (/^map_Ka /i.test(line)) {
|
||||||
material.ambientColorMap = line.substring(7).trim();
|
material.ambientColorMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||||
} else if (/^map_Ke /i.test(line)) {
|
} else if (/^map_Ke /i.test(line)) {
|
||||||
material.emissionColorMap = line.substring(7).trim();
|
material.emissionColorMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||||
} else if (/^map_Kd /i.test(line)) {
|
} else if (/^map_Kd /i.test(line)) {
|
||||||
material.diffuseColorMap = line.substring(7).trim();
|
material.diffuseColorMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||||
} else if (/^map_Ks /i.test(line)) {
|
} else if (/^map_Ks /i.test(line)) {
|
||||||
material.specularColorMap = line.substring(7).trim();
|
material.specularColorMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||||
} else if (/^map_Ns /i.test(line)) {
|
} else if (/^map_Ns /i.test(line)) {
|
||||||
material.specularShininessMap = line.substring(7).trim();
|
material.specularShininessMap = getAbsolutePath(line.substring(7).trim(), mtlPath);
|
||||||
} else if (/^map_Bump /i.test(line)) {
|
} else if (/^map_Bump /i.test(line)) {
|
||||||
material.normalMap = line.substring(9).trim();
|
material.normalMap = getAbsolutePath(line.substring(9).trim(), mtlPath);
|
||||||
} else if (/^map_d /i.test(line)) {
|
} else if (/^map_d /i.test(line)) {
|
||||||
material.alphaMap = line.substring(6).trim();
|
material.alphaMap = getAbsolutePath(line.substring(6).trim(), mtlPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (defined(material.alpha)) {
|
|
||||||
material.diffuseColor[3] = material.alpha;
|
return readLines(mtlPath, parseLine)
|
||||||
}
|
.then(function() {
|
||||||
return materials;
|
return materials;
|
||||||
})
|
})
|
||||||
.catch(function() {
|
.catch(function() {
|
||||||
@ -116,3 +107,10 @@ function parse(mtlPath) {
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAbsolutePath(imagePath, mtlPath) {
|
||||||
|
if (!path.isAbsolute(imagePath)) {
|
||||||
|
imagePath = path.join(path.dirname(mtlPath), imagePath);
|
||||||
|
}
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
590
lib/obj.js
@ -1,125 +1,167 @@
|
|||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
var Cesium = require('cesium');
|
var Cesium = require('cesium');
|
||||||
var Promise = require('bluebird');
|
|
||||||
var byline = require('byline');
|
|
||||||
var fs = require('fs-extra');
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var Promise = require('bluebird');
|
||||||
|
|
||||||
|
var ArrayStorage = require('./ArrayStorage');
|
||||||
var loadImage = require('./image');
|
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 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) {
|
function Node() {
|
||||||
return getObjInfo(objFile, inputPath)
|
this.name = undefined;
|
||||||
.then(function(result) {
|
this.meshes = [];
|
||||||
var info = result.info;
|
|
||||||
var materials = result.materials;
|
|
||||||
var images = result.images;
|
|
||||||
return processObj(objFile, info, materials, images);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function processObj(objFile, info, materials, images) {
|
function Mesh() {
|
||||||
return new Promise(function(resolve) {
|
this.name = undefined;
|
||||||
// A vertex is specified by indexes into each of the attribute arrays,
|
this.primitives = [];
|
||||||
// but these indexes may be different. This maps the separate indexes to a single index.
|
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 vertexCache = {};
|
||||||
|
var vertexCacheLimit = 1000000;
|
||||||
|
var vertexCacheCount = 0;
|
||||||
var vertexCount = 0;
|
var vertexCount = 0;
|
||||||
|
|
||||||
var vertexArray = [];
|
// All mtl paths seen in the obj
|
||||||
|
var mtlPaths = [];
|
||||||
|
|
||||||
var positions = [];
|
function getName(name) {
|
||||||
var normals = [];
|
return (name === '' ? undefined : name);
|
||||||
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() {
|
function addNode(name) {
|
||||||
var defaultMaterial = 'czmDefaultMat';
|
node = new Node();
|
||||||
if (!defined(materials[defaultMaterial])) {
|
node.name = getName(name);
|
||||||
materials[defaultMaterial] = Material.getDefault();
|
nodes.push(node);
|
||||||
}
|
addMesh();
|
||||||
useMaterial(defaultMaterial);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var materialsLength = Object.keys(materials).length;
|
function addMesh(name) {
|
||||||
if (materialsLength === 0) {
|
mesh = new Mesh();
|
||||||
useDefaultMaterial();
|
mesh.name = getName(name);
|
||||||
|
node.meshes.push(mesh);
|
||||||
|
addPrimitive();
|
||||||
|
|
||||||
|
// Clear the vertex cache for each new mesh
|
||||||
|
vertexCache = {};
|
||||||
|
vertexCacheCount = 0;
|
||||||
|
vertexCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOffset(a, data, components) {
|
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);
|
var i = parseInt(a);
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
// Negative vertex indexes reference the vertices immediately above it
|
// Negative vertex indexes reference the vertices immediately above it
|
||||||
return (data.length / components + i) * components;
|
return (attributeData.length / components + i) * components;
|
||||||
}
|
}
|
||||||
return (i - 1) * components;
|
return (i - 1) * components;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVertex(p, u, n) {
|
function createVertex(p, u, n) {
|
||||||
// Positions
|
// Positions
|
||||||
|
if (defined(p)) {
|
||||||
var pi = getOffset(p, positions, 3);
|
var pi = getOffset(p, positions, 3);
|
||||||
var px = positions[pi + 0];
|
var px = positions.get(pi + 0);
|
||||||
var py = positions[pi + 1];
|
var py = positions.get(pi + 1);
|
||||||
var pz = positions[pi + 2];
|
var pz = positions.get(pi + 2);
|
||||||
|
mesh.positions.push(px);
|
||||||
positionMin[0] = Math.min(px, positionMin[0]);
|
mesh.positions.push(py);
|
||||||
positionMin[1] = Math.min(py, positionMin[1]);
|
mesh.positions.push(pz);
|
||||||
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
|
// Normals
|
||||||
if (hasNormals) {
|
if (defined(n)) {
|
||||||
var ni = getOffset(n, normals, 3);
|
var ni = getOffset(n, normals, 3);
|
||||||
var nx = normals[ni + 0];
|
var nx = normals.get(ni + 0);
|
||||||
var ny = normals[ni + 1];
|
var ny = normals.get(ni + 1);
|
||||||
var nz = normals[ni + 2];
|
var nz = normals.get(ni + 2);
|
||||||
vertexArray.push(nx, ny, nz);
|
mesh.normals.push(nx);
|
||||||
|
mesh.normals.push(ny);
|
||||||
|
mesh.normals.push(nz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UVs
|
// UVs
|
||||||
if (hasUVs) {
|
|
||||||
if (defined(u)) {
|
if (defined(u)) {
|
||||||
var ui = getOffset(u, uvs, 2);
|
var ui = getOffset(u, uvs, 2);
|
||||||
var ux = uvs[ui + 0];
|
var ux = uvs.get(ui + 0);
|
||||||
var uy = uvs[ui + 1];
|
var uy = uvs.get(ui + 1);
|
||||||
// Flip y so 0.0 is the bottom of the image
|
mesh.uvs.push(ux);
|
||||||
uy = 1.0 - uy;
|
mesh.uvs.push(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,8 +171,15 @@ function processObj(objFile, info, materials, images) {
|
|||||||
index = vertexCount++;
|
index = vertexCount++;
|
||||||
vertexCache[v] = index;
|
vertexCache[v] = index;
|
||||||
createVertex(p, u, n);
|
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;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,63 +188,48 @@ function processObj(objFile, info, materials, images) {
|
|||||||
var index2 = addVertex(v2, p2, u2, n2);
|
var index2 = addVertex(v2, p2, u2, n2);
|
||||||
var index3 = addVertex(v3, p3, u3, n3);
|
var index3 = addVertex(v3, p3, u3, n3);
|
||||||
|
|
||||||
currentIndexArray.push(index1);
|
primitive.indices.push(index1);
|
||||||
currentIndexArray.push(index2);
|
primitive.indices.push(index2);
|
||||||
currentIndexArray.push(index3);
|
primitive.indices.push(index3);
|
||||||
|
|
||||||
// Triangulate if the face is a quad
|
// Triangulate if the face is a quad
|
||||||
if (defined(v4)) {
|
if (defined(v4)) {
|
||||||
var index4 = addVertex(v4, p4, u4, n4);
|
var index4 = addVertex(v4, p4, u4, n4);
|
||||||
currentIndexArray.push(index1);
|
primitive.indices.push(index1);
|
||||||
currentIndexArray.push(index3);
|
primitive.indices.push(index3);
|
||||||
currentIndexArray.push(index4);
|
primitive.indices.push(index4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// v float float float
|
function parseLine(line) {
|
||||||
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();
|
line = line.trim();
|
||||||
var result;
|
var result;
|
||||||
|
|
||||||
if ((line.length === 0) || (line.charAt(0) === '#')) {
|
if ((line.length === 0) || (line.charAt(0) === '#')) {
|
||||||
// Don't process empty lines or comments
|
// 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) {
|
} else if ((result = vertexPattern.exec(line)) !== null) {
|
||||||
positions.push(
|
positions.push(parseFloat(result[1]));
|
||||||
parseFloat(result[1]),
|
positions.push(parseFloat(result[2]));
|
||||||
parseFloat(result[2]),
|
positions.push(parseFloat(result[3]));
|
||||||
parseFloat(result[3])
|
|
||||||
);
|
|
||||||
} else if ((result = normalPattern.exec(line) ) !== null) {
|
} else if ((result = normalPattern.exec(line) ) !== null) {
|
||||||
var nx = parseFloat(result[1]);
|
normals.push(parseFloat(result[1]));
|
||||||
var ny = parseFloat(result[2]);
|
normals.push(parseFloat(result[2]));
|
||||||
var nz = parseFloat(result[3]);
|
normals.push(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) {
|
} else if ((result = uvPattern.exec(line)) !== null) {
|
||||||
uvs.push(
|
uvs.push(parseFloat(result[1]));
|
||||||
parseFloat(result[1]),
|
uvs.push(1.0 - parseFloat(result[2])); // Flip y so 0.0 is the bottom of the image
|
||||||
parseFloat(result[2])
|
|
||||||
);
|
|
||||||
} else if ((result = facePattern1.exec(line)) !== null) {
|
} else if ((result = facePattern1.exec(line)) !== null) {
|
||||||
addFace(
|
addFace(
|
||||||
result[1], result[1], undefined, undefined,
|
result[1], result[1], undefined, undefined,
|
||||||
@ -224,142 +258,190 @@ function processObj(objFile, info, materials, images) {
|
|||||||
result[7], result[8], undefined, result[9],
|
result[7], result[8], undefined, result[9],
|
||||||
result[10], result[11], undefined, result[12]
|
result[10], result[11], undefined, result[12]
|
||||||
);
|
);
|
||||||
} else if (/^usemtl /.test(line)) {
|
|
||||||
var materialName = line.substring(7).trim();
|
|
||||||
useMaterial(materialName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stream.on('end', function () {
|
|
||||||
resolve({
|
|
||||||
vertexCount: vertexCount,
|
|
||||||
vertexArray: vertexArray,
|
|
||||||
positionMin: positionMin,
|
|
||||||
positionMax: positionMax,
|
|
||||||
hasUVs: hasUVs,
|
|
||||||
hasNormals: hasNormals,
|
|
||||||
materialGroups: materialGroups,
|
|
||||||
materials: materials,
|
|
||||||
images: images
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getImages(inputPath, materials) {
|
|
||||||
// Collect all the image files from the materials
|
|
||||||
var images = [];
|
|
||||||
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.diffuseColorMap) && (images.indexOf(material.diffuseColorMap) === -1)) {
|
|
||||||
images.push(material.diffuseColorMap);
|
|
||||||
}
|
|
||||||
if (defined(material.emissionColorMap) && (images.indexOf(material.emissionColorMap) === -1)) {
|
|
||||||
images.push(material.emissionColorMap);
|
|
||||||
}
|
|
||||||
if (defined(material.specularColorMap) && (images.indexOf(material.specularColorMap) === -1)) {
|
|
||||||
images.push(material.specularColorMap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the image files
|
// Create a default node in case there are no o/g/usemtl lines in the obj
|
||||||
var promises = [];
|
addNode();
|
||||||
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);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMaterials(mtlPath, hasMaterialGroups) {
|
// Parse the obj file
|
||||||
if (hasMaterialGroups && defined(mtlPath)) {
|
return readLines(objPath, parseLine)
|
||||||
return Material.parse(mtlPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
.then(function() {
|
||||||
return getMaterials(mtlPath, hasMaterialGroups);
|
// Unload resources
|
||||||
})
|
positions = undefined;
|
||||||
.then(function(returnedMaterials) {
|
normals = undefined;
|
||||||
materials = returnedMaterials;
|
uvs = undefined;
|
||||||
return getImages(inputPath, materials);
|
|
||||||
})
|
// Load materials and images
|
||||||
|
return finishLoading(nodes, mtlPaths, objPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
.then(function(images) {
|
||||||
return {
|
return {
|
||||||
info : info,
|
nodes : nodes,
|
||||||
materials : materials,
|
materials : materials,
|
||||||
images : images
|
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 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) && imagePaths.indexOf(material.ambientColorMap) === -1) {
|
||||||
|
imagePaths.push(material.ambientColorMap);
|
||||||
|
}
|
||||||
|
if (defined(material.diffuseColorMap) && imagePaths.indexOf(material.diffuseColorMap) === -1) {
|
||||||
|
imagePaths.push(material.diffuseColorMap);
|
||||||
|
}
|
||||||
|
if (defined(material.emissionColorMap) && imagePaths.indexOf(material.emissionColorMap) === -1) {
|
||||||
|
imagePaths.push(material.emissionColorMap);
|
||||||
|
}
|
||||||
|
if (defined(material.specularColorMap) && imagePaths.indexOf(material.specularColorMap) === -1) {
|
||||||
|
imagePaths.push(material.specularColorMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imagePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return final;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 final;
|
||||||
|
}
|
||||||
|
|
||||||
|
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": [
|
"contributors": [
|
||||||
{
|
{
|
||||||
"name": "Analytical Graphics, Inc., and Contributors",
|
"name": "Analytical Graphics, Inc., and Contributors",
|
||||||
"url": "https://github.com/AnalyticalGraphicsInc/OBJ2GLTF/graphs/contributors"
|
"url": "https://github.com/AnalyticalGraphicsInc/obj2gltf/graphs/contributors"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"obj",
|
"obj",
|
||||||
"gltf"
|
"gltf"
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/AnalyticalGraphicsInc/OBJ2GLTF",
|
"homepage": "https://github.com/AnalyticalGraphicsInc/obj2gltf",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git@github.com:AnalyticalGraphicsInc/OBJ2GLTF.git"
|
"url": "git@github.com:AnalyticalGraphicsInc/obj2gltf.git"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/AnalyticalGraphicsInc/OBJ2GLTF/issues"
|
"url": "https://github.com/AnalyticalGraphicsInc/obj2gltf/issues"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0.0"
|
"node": ">=4.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "2.1.2",
|
"bluebird": "^3.4.7",
|
||||||
"bluebird": "3.4.6",
|
"cesium": "^1.31.0",
|
||||||
"byline": "5.0.0",
|
"event-stream": "^3.3.4",
|
||||||
"cesium": "1.26.0",
|
"fs-extra": "^2.0.0",
|
||||||
"fs-extra": "0.30.0",
|
"gltf-pipeline": "^0.1.0-alpha11",
|
||||||
"gltf-pipeline": "0.1.0-alpha8",
|
"yargs": "^7.0.1"
|
||||||
"yargs": "6.3.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gulp": "3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-jshint": "2.0.2",
|
"gulp-jshint": "^2.0.4",
|
||||||
"istanbul": "0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"jasmine": "2.5.2",
|
"jasmine": "^2.5.3",
|
||||||
"jasmine-spec-reporter": "2.7.0",
|
"jasmine-spec-reporter": "^3.2.0",
|
||||||
"jshint": "2.9.4",
|
"jshint": "^2.9.4",
|
||||||
"jshint-stylish": "2.2.1",
|
"jshint-stylish": "^2.2.1",
|
||||||
"open": "0.0.5",
|
"open": "^0.0.5",
|
||||||
"requirejs": "2.3.2",
|
"requirejs": "^2.3.3",
|
||||||
"typings": "1.4.0"
|
"typings": "^2.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublish": "typings install",
|
"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
|
# www.blender.org
|
||||||
mtllib BoxTextured.mtl
|
mtllib box-complex-material.mtl
|
||||||
o Cube_Cube.001
|
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
|
||||||
@ -26,9 +26,9 @@ vt 0.0000 0.0000
|
|||||||
vt 1.0000 0.0000
|
vt 1.0000 0.0000
|
||||||
vt 1.0000 1.0000
|
vt 1.0000 1.0000
|
||||||
vt 0.0000 1.0000
|
vt 0.0000 1.0000
|
||||||
vt 0.0000 0.0000
|
|
||||||
vt 1.0000 0.0000
|
vt 1.0000 0.0000
|
||||||
vt 1.0000 1.0000
|
vt 1.0000 1.0000
|
||||||
|
vt 0.0000 0.0000
|
||||||
vt 0.0000 1.0000
|
vt 0.0000 1.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
|
||||||
@ -36,11 +36,11 @@ vn 1.0000 0.0000 0.0000
|
|||||||
vn 0.0000 0.0000 1.0000
|
vn 0.0000 0.0000 1.0000
|
||||||
vn 0.0000 -1.0000 0.0000
|
vn 0.0000 -1.0000 0.0000
|
||||||
vn 0.0000 1.0000 0.0000
|
vn 0.0000 1.0000 0.0000
|
||||||
usemtl Textured
|
usemtl Material
|
||||||
s off
|
s off
|
||||||
f 2/1/1 4/2/1 3/3/1 1/4/1
|
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||||
f 4/5/2 8/6/2 7/7/2 3/8/2
|
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||||
f 8/9/3 6/10/3 5/11/3 7/12/3
|
f 7/9/3 8/10/3 6/11/3 5/12/3
|
||||||
f 6/13/4 2/14/4 1/15/4 5/16/4
|
f 5/13/4 6/14/4 2/15/4 1/16/4
|
||||||
f 1/17/5 3/18/5 7/7/5 5/16/5
|
f 3/5/5 7/17/5 5/18/5 1/16/5
|
||||||
f 6/13/6 8/6/6 4/19/6 2/20/6
|
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
|
# Material Count: 1
|
||||||
|
|
||||||
newmtl Textured
|
newmtl Material
|
||||||
Ns 96.078431
|
Ns 96.078431
|
||||||
Ka 1.000000 1.000000 1.000000
|
Ka 0.000000 0.000000 0.000000
|
||||||
Kd 0.640000 0.640000 0.640000
|
Kd 0.640000 0.640000 0.640000
|
||||||
Ks 0.500000 0.500000 0.500000
|
Ks 0.500000 0.500000 0.500000
|
||||||
Ke 0.000000 0.000000 0.000000
|
Ke 0.000000 0.000000 0.000000
|
||||||
Ni 1.000000
|
Ni 1.000000
|
||||||
d 1.000000
|
d 1.000000
|
||||||
illum 2
|
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';
|
'use strict';
|
||||||
var Promise = require('bluebird');
|
var fsExtra = require('fs-extra');
|
||||||
|
|
||||||
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var convert = require('../../lib/convert');
|
var convert = require('../../lib/convert');
|
||||||
|
|
||||||
var objFile = './specs/data/BoxTextured/BoxTextured.obj';
|
var objPath = 'specs/data/box-textured/box-textured.obj';
|
||||||
var gltfFile = './specs/data/BoxTextured/BoxTextured.gltf';
|
var gltfPath = 'specs/data/box-textured/box-textured.gltf';
|
||||||
|
var glbPath = 'specs/data/box-textured/box-textured.glb';
|
||||||
|
|
||||||
describe('convert', function() {
|
describe('convert', function() {
|
||||||
it('converts an obj to gltf', function(done) {
|
it('converts an obj to gltf', function(done) {
|
||||||
var spy = spyOn(GltfPipeline, 'processJSONToDisk').and.callFake(function(gltf, outputPath, options, callback) {
|
var spy = spyOn(GltfPipeline, 'processJSONToDisk');
|
||||||
return;
|
expect(convert(objPath, gltfPath)
|
||||||
});
|
|
||||||
expect(convert(objFile, gltfFile, {})
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
var args = spy.calls.first().args;
|
var args = spy.calls.first().args;
|
||||||
expect(args[0]).toBeDefined();
|
var gltf = args[0];
|
||||||
expect(path.normalize(args[1])).toEqual(path.normalize(gltfFile));
|
var outputPath = args[1];
|
||||||
|
expect(path.normalize(outputPath)).toEqual(path.normalize(gltfPath));
|
||||||
|
expect(gltf).toBeDefined();
|
||||||
|
expect(gltf.images.cesium).toBeDefined();
|
||||||
}), done).toResolve();
|
}), 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);
|
||||||
|
});
|
||||||
|
});
|