diff --git a/.gitignore b/.gitignore index 31dde5f..c6533aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ -**/node_modules +node_modules +npm-debug.log +.idea/workspace.xml +.idea/tasks.xml .DS_Store Thumbs.db diff --git a/.idea/OBJ2GLTF.iml b/.idea/OBJ2GLTF.iml new file mode 100644 index 0000000..e14bba9 --- /dev/null +++ b/.idea/OBJ2GLTF.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..b45162c --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/libraries/OBJ2GLTF_node_modules.xml b/.idea/libraries/OBJ2GLTF_node_modules.xml new file mode 100644 index 0000000..7737ba8 --- /dev/null +++ b/.idea/libraries/OBJ2GLTF_node_modules.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..72abef0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..eb8cfab --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..52f4a1f --- /dev/null +++ b/.jshintrc @@ -0,0 +1,60 @@ +{ + "bitwise": false, + "camelcase": false, + "curly": true, + "eqeqeq": true, + "forin": true, + "freeze": true, + "immed": true, + "latedef": "nofunc", + "noarg": true, + "noempty": true, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "quotmark": false, + "undef": true, + "unused": "strict", + "strict": true, + "asi": false, + "boss": false, + "debug": false, + "eqnull": false, + "moz": false, + "evil": false, + "expr": false, + "funcscope": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": false, + "multistr": true, + "noyield": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "shadow": false, + "sub": false, + "supernew": false, + "validthis": false, + "browser": false, + "browserify": false, + "couch": false, + "devel": true, + "dojo": false, + "jasmine": false, + "jquery": false, + "mocha": true, + "mootools": false, + "node": true, + "nonstandard": false, + "prototypejs": false, + "qunit": false, + "rhino": false, + "shelljs": false, + "worker": false, + "wsh": false, + "yui": false +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..42dd344 --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +node_modules +npm-debug.log +.idea +.DS_Store +Thumbs.db +.npmignore +gulpfile.js +.jshintrc diff --git a/README.md b/README.md index b0ddfc3..3ae00a0 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ Run `node bin/obj2gltf.js` and pass it the path to an OBJ file. |`-i`|Path to the input OBJ file.| :white_check_mark: Yes| |`-o`|Directory or filename for the exported glTF file.|No| |`-e`|Embed glTF resources, including images, into the exported glTF file.|No, default `false`| -|`-t`|Shading technique. Possible values are `lambert`, `phong`, `blinn`, and `constant`. The shading technique is typically determined by the MTL file, but this allows more explicit control.|No| |`-h`|Display help|No| ###Examples: diff --git a/bin/obj2gltf.js b/bin/obj2gltf.js index 1418b20..78f7e50 100644 --- a/bin/obj2gltf.js +++ b/bin/obj2gltf.js @@ -1,67 +1,37 @@ #!/usr/bin/env node "use strict"; -var fs = require('fs'); -var path = require('path'); -var argv = require('minimist')(process.argv.slice(2)); -var parseObj = require('../lib/obj'); -var createGltf = require('../lib/gltf'); -var util = require('../lib/util'); -var defined = util.defined; -var defaultValue = util.defaultValue; +var argv = require('yargs').argv; +var Cesium = require('Cesium'); +var defined = Cesium.defined; +var defaultValue = Cesium.defaultValue; +var convert = require('../lib/convert'); -// TODO : support zlib -// TODO : support binary export if (process.argv.length < 3 || defined(argv.h) || defined(argv.help)) { - console.log('Usage: ./bin/obj2gltf.js [INPUT] [OPTIONS]\n'); + console.log('Usage: ./bin/obj2gltf.js [INPUT] [OPTIONS]'); console.log(' -i, --input Path to obj file'); console.log(' -o, --output Directory or filename for the exported glTF file'); console.log(' -b, --binary Output binary glTF'); console.log(' -e, --embed Embed glTF resources into a single file'); - console.log(' -t, --technique Shading technique. Possible values are lambert, phong, blinn, constant'); console.log(' -h, --help Display this help'); process.exit(0); } var objFile = defaultValue(argv._[0], defaultValue(argv.i, argv.input)); var outputPath = defaultValue(argv._[1], defaultValue(argv.o, argv.output)); -var binary = defaultValue(defaultValue(argv.b, argv.binary), false); -var embed = defaultValue(defaultValue(argv.e, argv.embed), false); -var technique = defaultValue(argv.t, argv.technique); +var binary = defaultValue(argv.b, argv.binary); +var embed = defaultValue(argv.e, argv.embed); if (!defined(objFile)) { - console.error('-i or --input argument is required. See --help for details.'); - process.exit(1); + throw new Error('-i or --input argument is required. See --help for details.'); } -if (!defined(outputPath)) { - outputPath = path.dirname(objFile); -} +console.time('Total'); -if (defined(technique)) { - technique = technique.toUpperCase(); - if ((technique !== 'LAMBERT') && (technique !== 'PHONG') && (technique !== 'BLINN') && (technique !== 'CONSTANT')) { - console.log('Unrecognized technique \'' + technique + '\'. Using default instead.'); - } -} +var options = { + binary : binary, + embed : embed +}; -var inputPath = path.dirname(objFile); -var modelName = path.basename(objFile, '.obj'); - -var outputIsGltf = /.gltf$/.test(outputPath); -if (outputIsGltf) { - modelName = path.basename(outputPath, '.gltf'); - outputPath = path.dirname(outputPath); -} - -fs.mkdir(outputPath, function(){ - console.time('Total'); - console.time('Parse Obj'); - parseObj(objFile, inputPath, function(data) { - console.timeEnd('Parse Obj'); - console.time('Create glTF'); - createGltf(data, modelName, inputPath, outputPath, binary, embed, technique, function() { - console.timeEnd('Create glTF'); - console.timeEnd('Total'); - }); - }); +convert(objFile, outputPath, options, function() { + console.timeEnd('Total'); }); diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..efb6f69 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,25 @@ +'use strict'; + +var jshint = require('gulp-jshint'); +var gulp = require('gulp'); + +var jsHintFiles = ['index.js', 'bin/*.js', 'lib/*.js']; + +gulp.task('default', ['jsHint']); + +gulp.task('jsHint', function() { + return gulp.src(jsHintFiles) + .pipe(jshint.extract('auto')) + .pipe(jshint()) + .pipe(jshint.reporter('jshint-stylish')) + .pipe(jshint.reporter('fail')); +}); + +gulp.task('jsHint-watch', function() { + gulp.watch(jsHintFiles).on('change', function(event) { + gulp.src(event.path) + .pipe(jshint.extract('auto')) + .pipe(jshint()) + .pipe(jshint.reporter('jshint-stylish')); + }); +}); diff --git a/index.js b/index.js new file mode 100644 index 0000000..326e68e --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +module.exports = { + convert : require('./lib/convert') +}; diff --git a/lib/WebGLConstants.js b/lib/WebGLConstants.js deleted file mode 100644 index 80098a2..0000000 --- a/lib/WebGLConstants.js +++ /dev/null @@ -1,300 +0,0 @@ -"use strict"; -module.exports = { - DEPTH_BUFFER_BIT : 0x00000100, - STENCIL_BUFFER_BIT : 0x00000400, - COLOR_BUFFER_BIT : 0x00004000, - POINTS : 0x0000, - LINES : 0x0001, - LINE_LOOP : 0x0002, - LINE_STRIP : 0x0003, - TRIANGLES : 0x0004, - TRIANGLE_STRIP : 0x0005, - TRIANGLE_FAN : 0x0006, - ZERO : 0, - ONE : 1, - SRC_COLOR : 0x0300, - ONE_MINUS_SRC_COLOR : 0x0301, - SRC_ALPHA : 0x0302, - ONE_MINUS_SRC_ALPHA : 0x0303, - DST_ALPHA : 0x0304, - ONE_MINUS_DST_ALPHA : 0x0305, - DST_COLOR : 0x0306, - ONE_MINUS_DST_COLOR : 0x0307, - SRC_ALPHA_SATURATE : 0x0308, - FUNC_ADD : 0x8006, - BLEND_EQUATION : 0x8009, - BLEND_EQUATION_RGB : 0x8009, /* same as BLEND_EQUATION */ - BLEND_EQUATION_ALPHA : 0x883D, - FUNC_SUBTRACT : 0x800A, - FUNC_REVERSE_SUBTRACT : 0x800B, - BLEND_DST_RGB : 0x80C8, - BLEND_SRC_RGB : 0x80C9, - BLEND_DST_ALPHA : 0x80CA, - BLEND_SRC_ALPHA : 0x80CB, - CONSTANT_COLOR : 0x8001, - ONE_MINUS_CONSTANT_COLOR : 0x8002, - CONSTANT_ALPHA : 0x8003, - ONE_MINUS_CONSTANT_ALPHA : 0x8004, - BLEND_COLOR : 0x8005, - ARRAY_BUFFER : 0x8892, - ELEMENT_ARRAY_BUFFER : 0x8893, - ARRAY_BUFFER_BINDING : 0x8894, - ELEMENT_ARRAY_BUFFER_BINDING : 0x8895, - STREAM_DRAW : 0x88E0, - STATIC_DRAW : 0x88E4, - DYNAMIC_DRAW : 0x88E8, - BUFFER_SIZE : 0x8764, - BUFFER_USAGE : 0x8765, - CURRENT_VERTEX_ATTRIB : 0x8626, - FRONT : 0x0404, - BACK : 0x0405, - FRONT_AND_BACK : 0x0408, - CULL_FACE : 0x0B44, - BLEND : 0x0BE2, - DITHER : 0x0BD0, - STENCIL_TEST : 0x0B90, - DEPTH_TEST : 0x0B71, - SCISSOR_TEST : 0x0C11, - POLYGON_OFFSET_FILL : 0x8037, - SAMPLE_ALPHA_TO_COVERAGE : 0x809E, - SAMPLE_COVERAGE : 0x80A0, - NO_ERROR : 0, - INVALID_ENUM : 0x0500, - INVALID_VALUE : 0x0501, - INVALID_OPERATION : 0x0502, - OUT_OF_MEMORY : 0x0505, - CW : 0x0900, - CCW : 0x0901, - LINE_WIDTH : 0x0B21, - ALIASED_POINT_SIZE_RANGE : 0x846D, - ALIASED_LINE_WIDTH_RANGE : 0x846E, - CULL_FACE_MODE : 0x0B45, - FRONT_FACE : 0x0B46, - DEPTH_RANGE : 0x0B70, - DEPTH_WRITEMASK : 0x0B72, - DEPTH_CLEAR_VALUE : 0x0B73, - DEPTH_FUNC : 0x0B74, - STENCIL_CLEAR_VALUE : 0x0B91, - STENCIL_FUNC : 0x0B92, - STENCIL_FAIL : 0x0B94, - STENCIL_PASS_DEPTH_FAIL : 0x0B95, - STENCIL_PASS_DEPTH_PASS : 0x0B96, - STENCIL_REF : 0x0B97, - STENCIL_VALUE_MASK : 0x0B93, - STENCIL_WRITEMASK : 0x0B98, - STENCIL_BACK_FUNC : 0x8800, - STENCIL_BACK_FAIL : 0x8801, - STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802, - STENCIL_BACK_PASS_DEPTH_PASS : 0x8803, - STENCIL_BACK_REF : 0x8CA3, - STENCIL_BACK_VALUE_MASK : 0x8CA4, - STENCIL_BACK_WRITEMASK : 0x8CA5, - VIEWPORT : 0x0BA2, - SCISSOR_BOX : 0x0C10, - COLOR_CLEAR_VALUE : 0x0C22, - COLOR_WRITEMASK : 0x0C23, - UNPACK_ALIGNMENT : 0x0CF5, - PACK_ALIGNMENT : 0x0D05, - MAX_TEXTURE_SIZE : 0x0D33, - MAX_VIEWPORT_DIMS : 0x0D3A, - SUBPIXEL_BITS : 0x0D50, - RED_BITS : 0x0D52, - GREEN_BITS : 0x0D53, - BLUE_BITS : 0x0D54, - ALPHA_BITS : 0x0D55, - DEPTH_BITS : 0x0D56, - STENCIL_BITS : 0x0D57, - POLYGON_OFFSET_UNITS : 0x2A00, - POLYGON_OFFSET_FACTOR : 0x8038, - TEXTURE_BINDING_2D : 0x8069, - SAMPLE_BUFFERS : 0x80A8, - SAMPLES : 0x80A9, - SAMPLE_COVERAGE_VALUE : 0x80AA, - SAMPLE_COVERAGE_INVERT : 0x80AB, - COMPRESSED_TEXTURE_FORMATS : 0x86A3, - DONT_CARE : 0x1100, - FASTEST : 0x1101, - NICEST : 0x1102, - GENERATE_MIPMAP_HINT : 0x8192, - BYTE : 0x1400, - UNSIGNED_BYTE : 0x1401, - SHORT : 0x1402, - UNSIGNED_SHORT : 0x1403, - INT : 0x1404, - UNSIGNED_INT : 0x1405, - FLOAT : 0x1406, - DEPTH_COMPONENT : 0x1902, - ALPHA : 0x1906, - RGB : 0x1907, - RGBA : 0x1908, - LUMINANCE : 0x1909, - LUMINANCE_ALPHA : 0x190A, - UNSIGNED_SHORT_4_4_4_4 : 0x8033, - UNSIGNED_SHORT_5_5_5_1 : 0x8034, - UNSIGNED_SHORT_5_6_5 : 0x8363, - FRAGMENT_SHADER : 0x8B30, - VERTEX_SHADER : 0x8B31, - MAX_VERTEX_ATTRIBS : 0x8869, - MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB, - MAX_VARYING_VECTORS : 0x8DFC, - MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D, - MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C, - MAX_TEXTURE_IMAGE_UNITS : 0x8872, - MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD, - SHADER_TYPE : 0x8B4F, - DELETE_STATUS : 0x8B80, - LINK_STATUS : 0x8B82, - VALIDATE_STATUS : 0x8B83, - ATTACHED_SHADERS : 0x8B85, - ACTIVE_UNIFORMS : 0x8B86, - ACTIVE_ATTRIBUTES : 0x8B89, - SHADING_LANGUAGE_VERSION : 0x8B8C, - CURRENT_PROGRAM : 0x8B8D, - NEVER : 0x0200, - LESS : 0x0201, - EQUAL : 0x0202, - LEQUAL : 0x0203, - GREATER : 0x0204, - NOTEQUAL : 0x0205, - GEQUAL : 0x0206, - ALWAYS : 0x0207, - KEEP : 0x1E00, - REPLACE : 0x1E01, - INCR : 0x1E02, - DECR : 0x1E03, - INVERT : 0x150A, - INCR_WRAP : 0x8507, - DECR_WRAP : 0x8508, - VENDOR : 0x1F00, - RENDERER : 0x1F01, - VERSION : 0x1F02, - NEAREST : 0x2600, - LINEAR : 0x2601, - NEAREST_MIPMAP_NEAREST : 0x2700, - LINEAR_MIPMAP_NEAREST : 0x2701, - NEAREST_MIPMAP_LINEAR : 0x2702, - LINEAR_MIPMAP_LINEAR : 0x2703, - TEXTURE_MAG_FILTER : 0x2800, - TEXTURE_MIN_FILTER : 0x2801, - TEXTURE_WRAP_S : 0x2802, - TEXTURE_WRAP_T : 0x2803, - TEXTURE_2D : 0x0DE1, - TEXTURE : 0x1702, - TEXTURE_CUBE_MAP : 0x8513, - TEXTURE_BINDING_CUBE_MAP : 0x8514, - TEXTURE_CUBE_MAP_POSITIVE_X : 0x8515, - TEXTURE_CUBE_MAP_NEGATIVE_X : 0x8516, - TEXTURE_CUBE_MAP_POSITIVE_Y : 0x8517, - TEXTURE_CUBE_MAP_NEGATIVE_Y : 0x8518, - TEXTURE_CUBE_MAP_POSITIVE_Z : 0x8519, - TEXTURE_CUBE_MAP_NEGATIVE_Z : 0x851A, - MAX_CUBE_MAP_TEXTURE_SIZE : 0x851C, - TEXTURE0 : 0x84C0, - TEXTURE1 : 0x84C1, - TEXTURE2 : 0x84C2, - TEXTURE3 : 0x84C3, - TEXTURE4 : 0x84C4, - TEXTURE5 : 0x84C5, - TEXTURE6 : 0x84C6, - TEXTURE7 : 0x84C7, - TEXTURE8 : 0x84C8, - TEXTURE9 : 0x84C9, - TEXTURE10 : 0x84CA, - TEXTURE11 : 0x84CB, - TEXTURE12 : 0x84CC, - TEXTURE13 : 0x84CD, - TEXTURE14 : 0x84CE, - TEXTURE15 : 0x84CF, - TEXTURE16 : 0x84D0, - TEXTURE17 : 0x84D1, - TEXTURE18 : 0x84D2, - TEXTURE19 : 0x84D3, - TEXTURE20 : 0x84D4, - TEXTURE21 : 0x84D5, - TEXTURE22 : 0x84D6, - TEXTURE23 : 0x84D7, - TEXTURE24 : 0x84D8, - TEXTURE25 : 0x84D9, - TEXTURE26 : 0x84DA, - TEXTURE27 : 0x84DB, - TEXTURE28 : 0x84DC, - TEXTURE29 : 0x84DD, - TEXTURE30 : 0x84DE, - TEXTURE31 : 0x84DF, - ACTIVE_TEXTURE : 0x84E0, - REPEAT : 0x2901, - CLAMP_TO_EDGE : 0x812F, - MIRRORED_REPEAT : 0x8370, - FLOAT_VEC2 : 0x8B50, - FLOAT_VEC3 : 0x8B51, - FLOAT_VEC4 : 0x8B52, - INT_VEC2 : 0x8B53, - INT_VEC3 : 0x8B54, - INT_VEC4 : 0x8B55, - BOOL : 0x8B56, - BOOL_VEC2 : 0x8B57, - BOOL_VEC3 : 0x8B58, - BOOL_VEC4 : 0x8B59, - FLOAT_MAT2 : 0x8B5A, - FLOAT_MAT3 : 0x8B5B, - FLOAT_MAT4 : 0x8B5C, - SAMPLER_2D : 0x8B5E, - SAMPLER_CUBE : 0x8B60, - VERTEX_ATTRIB_ARRAY_ENABLED : 0x8622, - VERTEX_ATTRIB_ARRAY_SIZE : 0x8623, - VERTEX_ATTRIB_ARRAY_STRIDE : 0x8624, - VERTEX_ATTRIB_ARRAY_TYPE : 0x8625, - VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A, - VERTEX_ATTRIB_ARRAY_POINTER : 0x8645, - VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F, - IMPLEMENTATION_COLOR_READ_TYPE : 0x8B9A, - IMPLEMENTATION_COLOR_READ_FORMAT : 0x8B9B, - COMPILE_STATUS : 0x8B81, - LOW_FLOAT : 0x8DF0, - MEDIUM_FLOAT : 0x8DF1, - HIGH_FLOAT : 0x8DF2, - LOW_INT : 0x8DF3, - MEDIUM_INT : 0x8DF4, - HIGH_INT : 0x8DF5, - FRAMEBUFFER : 0x8D40, - RENDERBUFFER : 0x8D41, - RGBA4 : 0x8056, - RGB5_A1 : 0x8057, - RGB565 : 0x8D62, - DEPTH_COMPONENT16 : 0x81A5, - STENCIL_INDEX : 0x1901, - STENCIL_INDEX8 : 0x8D48, - DEPTH_STENCIL : 0x84F9, - RENDERBUFFER_WIDTH : 0x8D42, - RENDERBUFFER_HEIGHT : 0x8D43, - RENDERBUFFER_INTERNAL_FORMAT : 0x8D44, - RENDERBUFFER_RED_SIZE : 0x8D50, - RENDERBUFFER_GREEN_SIZE : 0x8D51, - RENDERBUFFER_BLUE_SIZE : 0x8D52, - RENDERBUFFER_ALPHA_SIZE : 0x8D53, - RENDERBUFFER_DEPTH_SIZE : 0x8D54, - RENDERBUFFER_STENCIL_SIZE : 0x8D55, - FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 0x8CD0, - FRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 0x8CD1, - FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 0x8CD2, - FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3, - COLOR_ATTACHMENT0 : 0x8CE0, - DEPTH_ATTACHMENT : 0x8D00, - STENCIL_ATTACHMENT : 0x8D20, - DEPTH_STENCIL_ATTACHMENT : 0x821A, - NONE : 0, - FRAMEBUFFER_COMPLETE : 0x8CD5, - FRAMEBUFFER_INCOMPLETE_ATTACHMENT : 0x8CD6, - FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7, - FRAMEBUFFER_INCOMPLETE_DIMENSIONS : 0x8CD9, - FRAMEBUFFER_UNSUPPORTED : 0x8CDD, - FRAMEBUFFER_BINDING : 0x8CA6, - RENDERBUFFER_BINDING : 0x8CA7, - MAX_RENDERBUFFER_SIZE : 0x84E8, - INVALID_FRAMEBUFFER_OPERATION : 0x0506, - UNPACK_FLIP_Y_WEBGL : 0x9240, - UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241, - CONTEXT_LOST_WEBGL : 0x9242, - UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243, - BROWSER_DEFAULT_WEBGL : 0x9244 -}; diff --git a/lib/convert.js b/lib/convert.js new file mode 100644 index 0000000..0d19efd --- /dev/null +++ b/lib/convert.js @@ -0,0 +1,51 @@ +"use strict"; +var path = require('path'); +var gltfPipeline = require('gltf-pipeline').gltfPipeline; +var parseObj = require('./obj'); +var createGltf = require('./gltf'); +var Cesium = require('cesium'); +var defined = Cesium.defined; +var defaultValue = Cesium.defaultValue; + +module.exports = convert; + +function convert(objFile, outputPath, options, done) { + var binary = defaultValue(options.binary, false); + var embed = defaultValue(options.embed, true); + + if (!defined(objFile)) { + throw new Error('objFile is required'); + } + + if (!defined(outputPath)) { + outputPath = path.dirname(objFile); + } + + var inputPath = path.dirname(objFile); + var modelName = path.basename(objFile, '.obj'); + + var extension = path.extname(outputPath); + if (extension !== '') { + modelName = path.basename(outputPath, extension); + outputPath = path.dirname(outputPath); + } + + extension = binary ? '.glb' : '.gltf'; + var gltfFile = path.join(outputPath, modelName + extension); + + parseObj(objFile, inputPath, function(data) { + createGltf(data, modelName, function(gltf) { + var options = { + binary : binary, + embed : embed, + createDirectory : false + }; + gltfPipeline.processJSONToDisk(gltf, gltfFile, options, function(error) { + if (error) { + throw error; + } + done(); + }); + }); + }); +} diff --git a/lib/gltf.js b/lib/gltf.js index 001eb61..4200947 100644 --- a/lib/gltf.js +++ b/lib/gltf.js @@ -1,281 +1,221 @@ "use strict"; -var fs = require('fs'); -var fsExtra = require('fs-extra'); var path = require('path'); -var async = require('async'); -var zlib = require('zlib'); -var util = require('./util'); -var defined = util.defined; -var defaultValue = util.defaultValue; -var imageInfo = require('./image'); -var WebGLConstants = require('./WebGLConstants'); -var modelMaterialsCommon = require('./modelMaterialsCommon'); +var Cesium = require('cesium'); +var defined = Cesium.defined; +var defaultValue = Cesium.defaultValue; +var WebGLConstants = Cesium.WebGLConstants; module.exports = createGltf; -function getImages(inputPath, outputPath, embed, materials, done) { - 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); - } - } - } - - var imagesInfo = {}; - async.each(images, function (image, callback) { - var imagePath = image; - if (!path.isAbsolute(imagePath)) { - imagePath = path.join(inputPath, image); - } - var baseName = path.basename(image); - var copyPath = path.join(outputPath, baseName); - imageInfo(imagePath, function(info) { - var uri; - if (embed) { - uri = 'data:application/octet-stream;base64,' + info.data.toString('base64'); - } else { - uri = baseName; - } - - imagesInfo[image] = { - transparent : info.transparent, - channels : info.channels, - uri : uri - }; - - if (embed) { - callback(); - } else if (path.relative(imagePath, copyPath) !== '') { - fsExtra.copy(imagePath, copyPath, {clobber : true}, function (err) { - if (err) { - throw err; - } - callback(); - }); - } - }); - }, function (err) { - if (err) { - throw err; - } - done(imagesInfo); - }); -} - -function createGltf(data, modelName, inputPath, outputPath, binary, embed, technique, done) { +function createGltf(data, modelName, done) { var vertexCount = data.vertexCount; var vertexArray = data.vertexArray; var positionMin = data.positionMin; var positionMax = data.positionMax; var hasUVs = data.hasUVs; + var hasNormals = data.hasNormals; var materialGroups = data.materialGroups; var materials = data.materials; + var images = data.images; - getImages(inputPath, outputPath, embed, materials, function(images) { - var i, j, name; + var i, j, name; - var sizeOfFloat32 = 4; - var sizeOfUint32 = 4; - var sizeOfUint16 = 2; + var sizeOfFloat32 = 4; + var sizeOfUint32 = 4; + var sizeOfUint16 = 2; - var indexComponentType; - var indexComponentSize; + 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; - } + // 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; + // 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; - for (j = 0; j < indexCount; ++j) { - if (indexComponentSize === sizeOfUint16) { - buffer.writeUInt16LE(indexArray[j], byteOffset); - } else { - buffer.writeUInt32LE(indexArray[j], byteOffset); - } - byteOffset += indexComponentSize; + 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 = sizeOfFloat32 * 3; - var uvByteOffset = sizeOfFloat32 * 6; - var vertexByteStride = hasUVs ? sizeOfFloat32 * 8 : sizeOfFloat32 * 6; + var positionByteOffset = 0; + var normalByteOffset = 0; + var uvByteOffset = 0; + var vertexByteStride = 0; - var binaryRelPath = modelName + '.bin'; - var binaryPath = path.join(outputPath, binaryRelPath); - var bufferId = 'buffer_' + modelName; - 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'; + 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; + } - function getAccessorIndexId(i) { - return 'accessor_index_' + i; + var bufferId = 'buffer_' + modelName; + 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 getMaterialId(material) { - return 'material_' + material; - } + function getImageId(image) { + return 'image_' + path.basename(image).substr(0, image.lastIndexOf('.')); + } - function getTextureId(image) { - if (!defined(image)) { - return undefined; - } - return 'texture_' + path.basename(image).substr(0, image.lastIndexOf('.')); - } + var gltf = { + accessors : {}, + asset : {}, + buffers : {}, + bufferViews : {}, + images : {}, + materials : {}, + meshes : {}, + nodes : {}, + samplers : {}, + scene : sceneId, + scenes : {}, + textures : {} + }; - function getImageId(image) { - return 'image_' + path.basename(image).substr(0, image.lastIndexOf('.')); - } + gltf.asset = { + "generator": "OBJ2GLTF", + "premultipliedAlpha": true, + "profile": { + "api": "WebGL", + "version": "1.0" + }, + "version": 1 + }; - var gltf = { - accessors : {}, - asset : {}, - buffers : {}, - bufferViews : {}, - extensionsUsed : ['KHR_materials_common'], - images : {}, - materials : {}, - meshes : {}, - nodes : {}, - samplers : {}, - scene : sceneId, - scenes : {}, - textures : {} + gltf.scenes[sceneId] = { + nodes : [nodeId] + }; + + gltf.nodes[nodeId] = { + children : [], + matrix : [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + meshes : [meshId], + name : modelName + }; + + gltf.samplers[samplerId] = {}; // Use default values + + var bufferUri = 'data:application/octet-stream;base64,' + buffer.toString('base64'); + + gltf.buffers[bufferId] = { + byteLength : bufferByteLength, + type : 'arraybuffer', + uri : bufferUri + }; + + gltf.bufferViews[bufferViewVertexId] = { + buffer : bufferId, + byteLength : vertexArrayByteLength, + byteOffset : 0, + target : WebGLConstants.ARRAY_BUFFER + }; + gltf.bufferViews[bufferViewIndexId] = { + buffer : bufferId, + byteLength : indexArrayByteLength, + byteOffset : vertexArrayByteLength, + target : WebGLConstants.ELEMENT_ARRAY_BUFFER + }; + + for (i = 0; i < primitivesLength; ++i) { + var primitive = primitives[i]; + gltf.accessors[getAccessorIndexId(i)] = { + bufferView : bufferViewIndexId, + byteOffset : primitive.indexOffset * indexComponentSize, + byteStride : 0, + componentType : indexComponentType, + count : primitive.indexCount, + type : 'SCALAR' }; + } - gltf.asset = { - "generator": "OBJ2GLTF", - "premultipliedAlpha": true, - "profile": { - "api": "WebGL", - "version": "1.0" - }, - "version": 1 - }; - - gltf.scenes[sceneId] = { - nodes : [nodeId] - }; - - gltf.nodes[nodeId] = { - children : [], - matrix : [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], - meshes : [meshId], - name : modelName - }; - - gltf.samplers[samplerId] = {}; // Use default values - - var bufferUri; - if (embed) { - bufferUri = 'data:application/octet-stream;base64,' + buffer.toString('base64'); - } else { - bufferUri = binaryRelPath; - } - - gltf.buffers[bufferId] = { - byteLength : bufferByteLength, - type : 'arraybuffer', - uri : bufferUri - }; - - gltf.bufferViews[bufferViewVertexId] = { - buffer : bufferId, - byteLength : vertexArrayByteLength, - byteOffset : 0, - target : WebGLConstants.ARRAY_BUFFER - }; - gltf.bufferViews[bufferViewIndexId] = { - buffer : bufferId, - byteLength : indexArrayByteLength, - byteOffset : vertexArrayByteLength, - target : WebGLConstants.ELEMENT_ARRAY_BUFFER - }; - - for (i = 0; i < primitivesLength; ++i) { - var primitive = primitives[i]; - gltf.accessors[getAccessorIndexId(i)] = { - bufferView : bufferViewIndexId, - byteOffset : primitive.indexOffset * indexComponentSize, - byteStride : 0, - componentType : indexComponentType, - count : primitive.indexCount, - type : 'SCALAR' - }; - } - - gltf.accessors[accessorPositionId] = { - bufferView : bufferViewVertexId, - byteOffset : positionByteOffset, - byteStride : vertexByteStride, - componentType : WebGLConstants.FLOAT, - count : vertexCount, - min : positionMin, - max : positionMax, - type : 'VEC3' - }; + gltf.accessors[accessorPositionId] = { + bufferView : bufferViewVertexId, + byteOffset : positionByteOffset, + byteStride : vertexByteStride, + componentType : WebGLConstants.FLOAT, + count : vertexCount, + min : positionMin, + max : positionMax, + type : 'VEC3' + }; + if (hasNormals) { gltf.accessors[accessorNormalId] = { bufferView : bufferViewVertexId, byteOffset : normalByteOffset, @@ -284,150 +224,97 @@ function createGltf(data, modelName, inputPath, outputPath, binary, embed, techn count : vertexCount, type : 'VEC3' }; + } - if (hasUVs) { - gltf.accessors[accessorUVId] = { - bufferView : bufferViewVertexId, - byteOffset : uvByteOffset, - byteStride : vertexByteStride, - componentType : WebGLConstants.FLOAT, - count : vertexCount, - type : 'VEC2' - }; - } - - var gltfPrimitives = []; - gltf.meshes[meshId] = { - name : modelName, - primitives : gltfPrimitives + if (hasUVs) { + gltf.accessors[accessorUVId] = { + bufferView : bufferViewVertexId, + byteOffset : uvByteOffset, + byteStride : vertexByteStride, + componentType : WebGLConstants.FLOAT, + count : vertexCount, + type : 'VEC2' }; + } - var gltfAttributes = {}; - gltfAttributes.POSITION = accessorPositionId; + 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; - } + } + 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 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] = { - uri : image.uri - }; - gltf.textures[textureId] = { - format : format, - internalFormat : format, - sampler : samplerId, - source : imageId, - target : WebGLConstants.TEXTURE_2D, - type : WebGLConstants.UNSIGNED_BYTE - }; - } - } - - for (i = 0; i < primitivesLength; ++i) { - var materialName = primitives[i].material; - var material = materials[materialName]; - var materialId = getMaterialId(materialName); - - // Get shading technique - var shadingTechnique = technique; - var specularColor = defaultValue(material.specularColor, [0, 0, 0, 1]); - var specularShininess = material.specularShininess; - var hasSpecularColor = (specularColor[0] > 0) || (specularColor[1] > 0) || (specularColor[2] > 0); - var hasSpecularColorMap = defined(material.specularColorMap); - if (defined(shadingTechnique)) { - if ((shadingTechnique === 'PHONG') || (shadingTechnique === 'BLINN')) { - if (!defined(specularShininess)) { - specularShininess = 10.0; - } - if (!hasSpecularColor) { - specularColor[0] = specularColor[1] = specularColor[2] = 0.5; - } - } - } else { - shadingTechnique = 'BLINN'; - if (!hasSpecularColorMap && !hasSpecularColor) { - shadingTechnique = 'LAMBERT'; - } - } + 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(getTextureId(material.specularColorMap), specularColor), - shininess : defaultValue(specularShininess, 0.0), - transparency : defaultValue(material.alpha, 1.0) + specular : defaultValue(defaultValue(getTextureId(material.specularColorMap), material.specularColor), [0, 0, 0, 1]), + shininess : defaultValue(material.specularShininess, 0.0) }; - // If an image is transparent, set transparency to 0.99 to force alpha path - var diffuseColorMap = material.diffuseColorMap; - if (defined(diffuseColorMap) && images[diffuseColorMap].transparent) { - values.transparency = 0.99 * (values.transparency || 1.0); - } - gltf.materials[materialId] = { - name : materialName, - extensions : { - KHR_materials_common: { - technique: shadingTechnique, - values: values - } - } + name: name, + values: values }; } + } - // Generate techniques, shaders, and programs - gltf = modelMaterialsCommon(gltf); + 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; + } - // Save .gltf file - var gltfPath = path.join(outputPath, modelName) + '.gltf'; - var gltfString = JSON.stringify(gltf, null, 4); - fs.writeFile(gltfPath, gltfString, function(error) { - if (error) { - throw error; - } - if (embed) { - done(); - } else { - // Save .bin file - fs.writeFile(binaryPath, buffer, function(error) { - if (error) { - throw error; - } - done(); - }); - } - }); - }); + gltf.images[imageId] = { + uri : image.uri + }; + gltf.textures[textureId] = { + format : format, + internalFormat : format, + sampler : samplerId, + source : imageId, + target : WebGLConstants.TEXTURE_2D, + type : WebGLConstants.UNSIGNED_BYTE + }; + } + } + + done(gltf); } diff --git a/lib/image.js b/lib/image.js index d435b83..bc72268 100644 --- a/lib/image.js +++ b/lib/image.js @@ -1,8 +1,8 @@ "use strict"; -var fs = require('fs'); +var fs = require('fs-extra'); var path = require('path'); -module.exports = imageInfo; +module.exports = loadImage; function getChannels(colorType) { switch (colorType) { @@ -19,10 +19,10 @@ function getChannels(colorType) { } } -function imageInfo(imagePath, done) { - fs.readFile(imagePath, function(err, data) { - if (err) { - throw(err); +function loadImage(imagePath, done) { + fs.readFile(imagePath, function(error, data) { + if (error) { + throw(error); } var info = { diff --git a/lib/modelMaterialsCommon.js b/lib/modelMaterialsCommon.js deleted file mode 100644 index 4ed1942..0000000 --- a/lib/modelMaterialsCommon.js +++ /dev/null @@ -1,750 +0,0 @@ -"use strict"; -var WebGLConstants = require('./WebGLConstants'); -var util = require('./util'); -var defined = util.defined; -var defaultValue = util.defaultValue; - -module.exports = modelMaterialsCommon; - -function webGLConstantToGlslType(webGLValue) { - switch(webGLValue) { - case WebGLConstants.FLOAT: - return 'float'; - case WebGLConstants.FLOAT_VEC2: - return 'vec2'; - case WebGLConstants.FLOAT_VEC3: - return 'vec3'; - case WebGLConstants.FLOAT_VEC4: - return 'vec4'; - case WebGLConstants.FLOAT_MAT2: - return 'mat2'; - case WebGLConstants.FLOAT_MAT3: - return 'mat3'; - case WebGLConstants.FLOAT_MAT4: - return 'mat4'; - case WebGLConstants.SAMPLER_2D: - return 'sampler2D'; - } -} - -function generateLightParameters(gltf) { - var result = {}; - - var lights; - if (defined(gltf.extensions) && defined(gltf.extensions.KHR_materials_common)) { - lights = gltf.extensions.KHR_materials_common.lights; - } - - if (defined(lights)) { - // Figure out which node references the light - var nodes = gltf.nodes; - for (var nodeName in nodes) { - if (nodes.hasOwnProperty(nodeName)) { - var node = nodes[nodeName]; - if (defined(node.extensions) && defined(node.extensions.KHR_materials_common)) { - var nodeLightId = node.extensions.KHR_materials_common.light; - if (defined(nodeLightId) && defined(lights[nodeLightId])) { - lights[nodeLightId].node = nodeName; - } - delete node.extensions.KHR_materials_common; - } - } - } - - // Add light parameters to result - var lightCount = 0; - for(var lightName in lights) { - if (lights.hasOwnProperty(lightName)) { - var light = lights[lightName]; - var lightType = light.type; - if ((lightType !== 'ambient') && !defined(light.node)) { - delete lights[lightName]; - continue; - } - var lightBaseName = 'light' + lightCount.toString(); - light.baseName = lightBaseName; - switch(lightType) { - case 'ambient': - var ambient = light.ambient; - result[lightBaseName + 'Color'] = { - type: WebGLConstants.FLOAT_VEC3, - value: ambient.color - }; - break; - case 'directional': - var directional = light.directional; - result[lightBaseName + 'Color'] = - { - type: WebGLConstants.FLOAT_VEC3, - value: directional.color - }; - if (defined(light.node)) { - result[lightBaseName + 'Transform'] = - { - node: light.node, - semantic: 'MODELVIEW', - type: WebGLConstants.FLOAT_MAT4 - }; - } - break; - case 'point': - var point = light.point; - result[lightBaseName + 'Color'] = - { - type: WebGLConstants.FLOAT_VEC3, - value: point.color - }; - if (defined(light.node)) { - result[lightBaseName + 'Transform'] = - { - node: light.node, - semantic: 'MODELVIEW', - type: WebGLConstants.FLOAT_MAT4 - }; - } - result[lightBaseName + 'Attenuation'] = - { - type: WebGLConstants.FLOAT_VEC3, - value: [point.constantAttenuation, point.linearAttenuation, point.quadraticAttenuation] - }; - break; - case 'spot': - var spot = light.spot; - result[lightBaseName + 'Color'] = - { - type: WebGLConstants.FLOAT_VEC3, - value: spot.color - }; - if (defined(light.node)) { - result[lightBaseName + 'Transform'] = - { - node: light.node, - semantic: 'MODELVIEW', - type: WebGLConstants.FLOAT_MAT4 - }; - result[lightBaseName + 'InverseTransform'] = { - node: light.node, - semantic: 'MODELVIEWINVERSE', - type: WebGLConstants.FLOAT_MAT4, - useInFragment: true - }; - } - result[lightBaseName + 'Attenuation'] = - { - type: WebGLConstants.FLOAT_VEC3, - value: [spot.constantAttenuation, spot.linearAttenuation, spot.quadraticAttenuation] - }; - - result[lightBaseName + 'FallOff'] = - { - type: WebGLConstants.FLOAT_VEC2, - value: [spot.fallOffAngle, spot.fallOffExponent] - }; - break; - } - ++lightCount; - } - } - } - - return result; -} - -function getNextId(dictionary, baseName, startingCount) { - var count = defaultValue(startingCount, 0); - var nextId; - do { - nextId = baseName + (count++).toString(); - } while(defined(dictionary[nextId])); - - return nextId; -} - -var techniqueCount = 0; -var vertexShaderCount = 0; -var fragmentShaderCount = 0; -var programCount = 0; -function generateTechnique(gltf, khrMaterialsCommon, attributes, lightParameters, jointCount) { - var techniques = gltf.techniques; - var shaders = gltf.shaders; - var programs = gltf.programs; - attributes = defaultValue(attributes, []); - var attributesCount = attributes.length; - var lightingModel = khrMaterialsCommon.technique.toUpperCase(); - var lights; - if (defined(gltf.extensions) && defined(gltf.extensions.KHR_materials_common)) { - lights = gltf.extensions.KHR_materials_common.lights; - } - var hasSkinning = (jointCount > 0); - var values = khrMaterialsCommon.values; - var isDoubleSided = values.doubleSided; - delete values.doubleSided; - - var vertexShader = 'precision highp float;\n'; - var fragmentShader = 'precision highp float;\n'; - - // Generate IDs for our new objects - var techniqueId = getNextId(techniques, 'technique', techniqueCount); - var vertexShaderId = getNextId(shaders, 'vertexShader', vertexShaderCount); - var fragmentShaderId = getNextId(shaders, 'fragmentShader', fragmentShaderCount); - var programId = getNextId(programs, 'program', programCount); - - // Add techniques - var lowerCase; - var techniqueAttributes = {}; - for (var i=0;i 0) { - lowerCase = name.toLowerCase(); - techniqueParameters[lowerCase] = { - type: typeValue - }; - } - } - } - - // Copy light parameters into technique parameters - if (defined(lightParameters)) { - for (var lightParamName in lightParameters) { - if (lightParameters.hasOwnProperty(lightParamName)) { - techniqueParameters[lightParamName] = lightParameters[lightParamName]; - } - } - } - - // Generate uniforms object before attributes are added - var techniqueUniforms = {}; - for (var paramName in techniqueParameters) { - if (techniqueParameters.hasOwnProperty(paramName)) { - var param = techniqueParameters[paramName]; - techniqueUniforms['u_' + paramName] = paramName; - var arraySize = defined(param.count) ? '['+param.count+']' : ''; - if (((param.type !== WebGLConstants.FLOAT_MAT3) && (param.type !== WebGLConstants.FLOAT_MAT4)) || - param.useInFragment) { - fragmentShader += 'uniform ' + webGLConstantToGlslType(param.type) + ' u_' + paramName + arraySize + ';\n'; - delete param.useInFragment; - } - else { - vertexShader += 'uniform ' + webGLConstantToGlslType(param.type) + ' u_' + paramName + arraySize + ';\n'; - } - } - } - - // Add attributes with semantics - var vertexShaderMain = ''; - if (hasSkinning) { - vertexShaderMain += ' mat4 skinMat = a_weight.x * u_jointMatrix[int(a_joint.x)];\n'; - vertexShaderMain += ' skinMat += a_weight.y * u_jointMatrix[int(a_joint.y)];\n'; - vertexShaderMain += ' skinMat += a_weight.z * u_jointMatrix[int(a_joint.z)];\n'; - vertexShaderMain += ' skinMat += a_weight.w * u_jointMatrix[int(a_joint.w)];\n'; - } - - // TODO: Handle multiple texture coordinates for now we just use 1 - var v_texcoord; - for (i=0;i 0) { - lowerCase = attribute.toLowerCase(); - techniqueParameters[lowerCase] = { - semantic: attribute, - type: typeValue - }; - } - } - - var hasNormals = defined(techniqueParameters.normal); - var hasSpecular = hasNormals && ((lightingModel === 'BLINN') || (lightingModel === 'PHONG')) && - defined(techniqueParameters.specular) && defined(techniqueParameters.shininess); - - // Generate lighting code blocks - var hasNonAmbientLights = false; - var hasAmbientLights = false; - var fragmentLightingBlock = ''; - for (var lightName in lights) { - if (lights.hasOwnProperty(lightName)) { - var light = lights[lightName]; - var lightType = light.type.toLowerCase(); - var lightBaseName = light.baseName; - fragmentLightingBlock += ' {\n'; - var lightColorName = 'u_' + lightBaseName + 'Color'; - var varyingName; - if(lightType === 'ambient') { - hasAmbientLights = true; - fragmentLightingBlock += ' ambientLight += ' + lightColorName + ';\n'; - } - else if (hasNormals && (lightingModel !== 'CONSTANT')) { - hasNonAmbientLights = true; - varyingName = 'v_' + lightBaseName + 'Direction'; - vertexShader += 'varying vec3 ' + varyingName + ';\n'; - fragmentShader += 'varying vec3 ' + varyingName + ';\n'; - - if (lightType === 'directional') { - vertexShaderMain += ' ' + varyingName + ' = mat3(u_' + lightBaseName + 'Transform) * vec3(0.,0.,1.);\n'; - fragmentLightingBlock += ' float attenuation = 1.0;\n'; - } - else { - vertexShaderMain += ' ' + varyingName + ' = u_' + lightBaseName + 'Transform[3].xyz - pos.xyz;\n'; - fragmentLightingBlock += ' float range = length(' + varyingName + ');\n'; - fragmentLightingBlock += ' float attenuation = 1.0 / (u_' + lightBaseName + 'Attenuation.x + '; - fragmentLightingBlock += '(u_' + lightBaseName + 'Attenuation.y * range) + '; - fragmentLightingBlock += '(u_' + lightBaseName + 'Attenuation.z * range * range));\n'; - } - - fragmentLightingBlock += ' vec3 l = normalize(' + varyingName + ');\n'; - - if (lightType === 'spot') { - fragmentLightingBlock += ' vec4 spotPosition = u_' + lightBaseName + 'InverseTransform * vec4(v_positionEC, 1.0);\n'; - fragmentLightingBlock += ' float cosAngle = dot(vec3(0.0,0.0,-1.0), normalize(spotPosition.xyz));\n'; - fragmentLightingBlock += ' if (cosAngle < cos(u_' + lightBaseName + 'FallOff.x * 0.5))\n'; - fragmentLightingBlock += ' {\n'; - fragmentLightingBlock += ' attenuation *= max(0.0, pow(cosAngle, u_' + lightBaseName + 'FallOff.y));\n'; - } - - fragmentLightingBlock += ' diffuseLight += ' + lightColorName + '* max(dot(normal,l), 0.) * attenuation;\n'; - - if (hasSpecular) { - if (lightingModel === 'BLINN') { - fragmentLightingBlock += ' vec3 h = normalize(l + viewDir);\n'; - fragmentLightingBlock += ' float specularIntensity = max(0., pow(max(dot(normal, h), 0.), u_shininess)) * attenuation;\n'; - } - else { // PHONG - fragmentLightingBlock += ' vec3 reflectDir = reflect(-l, normal);\n'; - fragmentLightingBlock += ' float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess)) * attenuation;\n'; - } - fragmentLightingBlock += ' specularLight += ' + lightColorName + ' * specularIntensity;\n'; - } - - if (lightType === 'spot') { - fragmentLightingBlock += ' }\n'; - } - } - fragmentLightingBlock += ' }\n'; - } - } - - if (!hasAmbientLights) { - // Add an ambient light if we don't have one - fragmentLightingBlock += ' ambientLight += vec3(0.1, 0.1, 0.1);\n'; - } - - if (!hasNonAmbientLights && (lightingModel !== 'CONSTANT')) { - fragmentLightingBlock += ' vec3 l = normalize(czm_sunDirectionEC);\n'; - fragmentLightingBlock += ' diffuseLight += vec3(1.0, 1.0, 1.0) * max(dot(normal,l), 0.);\n'; - - if (hasSpecular) { - if (lightingModel === 'BLINN') { - fragmentLightingBlock += ' vec3 h = normalize(l + viewDir);\n'; - fragmentLightingBlock += ' float specularIntensity = max(0., pow(max(dot(normal, h), 0.), u_shininess));\n'; - } - else { // PHONG - fragmentLightingBlock += ' vec3 reflectDir = reflect(-l, normal);\n'; - fragmentLightingBlock += ' float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess));\n'; - } - - fragmentLightingBlock += ' specularLight += vec3(1.0, 1.0, 1.0) * specularIntensity;\n'; - } - } - - vertexShader += 'void main(void) {\n'; - vertexShader += vertexShaderMain; - vertexShader += '}\n'; - - fragmentShader += 'void main(void) {\n'; - var colorCreationBlock = ' vec3 color = vec3(0.0, 0.0, 0.0);\n'; - if (hasNormals) { - fragmentShader += ' vec3 normal = normalize(v_normal);\n'; - if (isDoubleSided) { - fragmentShader += ' if (gl_FrontFacing == false)\n'; - fragmentShader += ' {\n'; - fragmentShader += ' normal = -normal;\n'; - fragmentShader += ' }\n'; - } - } - - var finalColorComputation; - if (lightingModel !== 'CONSTANT') { - if (defined(techniqueParameters.diffuse)) { - if (techniqueParameters.diffuse.type === WebGLConstants.SAMPLER_2D) { - fragmentShader += ' vec4 diffuse = texture2D(u_diffuse, ' + v_texcoord + ');\n'; - } - else { - fragmentShader += ' vec4 diffuse = u_diffuse;\n'; - } - fragmentShader += ' vec3 diffuseLight = vec3(0.0, 0.0, 0.0);\n'; - colorCreationBlock += ' color += diffuse.rgb * diffuseLight;\n'; - } - - if (hasSpecular) { - if (techniqueParameters.specular.type === WebGLConstants.SAMPLER_2D) { - fragmentShader += ' vec3 specular = texture2D(u_specular, ' + v_texcoord + ').rgb;\n'; - } - else { - fragmentShader += ' vec3 specular = u_specular.rgb;\n'; - } - fragmentShader += ' vec3 specularLight = vec3(0.0, 0.0, 0.0);\n'; - colorCreationBlock += ' color += specular * specularLight;\n'; - } - - if (defined(techniqueParameters.transparency)) { - finalColorComputation = ' gl_FragColor = vec4(color * diffuse.a, diffuse.a * u_transparency);\n'; - } - else { - finalColorComputation = ' gl_FragColor = vec4(color * diffuse.a, diffuse.a);\n'; - } - } - else { - if (defined(techniqueParameters.transparency)) { - finalColorComputation = ' gl_FragColor = vec4(color, u_transparency);\n'; - } - else { - finalColorComputation = ' gl_FragColor = vec4(color, 1.0);\n'; - } - } - - if (defined(techniqueParameters.emission)) { - if (techniqueParameters.emission.type === WebGLConstants.SAMPLER_2D) { - fragmentShader += ' vec3 emission = texture2D(u_emission, ' + v_texcoord + ');\n'; - } - else { - fragmentShader += ' vec3 emission = u_emission.rgb;\n'; - } - colorCreationBlock += ' color += emission;\n'; - } - - if (defined(techniqueParameters.ambient)) { - if (techniqueParameters.ambient.type === WebGLConstants.SAMPLER_2D) { - fragmentShader += ' vec3 ambient = texture2D(u_ambient, ' + v_texcoord + ');\n'; - } - else { - fragmentShader += ' vec3 ambient = u_ambient.rgb;\n'; - } - } - else { - fragmentShader += ' vec3 ambient = diffuse.rgb;\n'; - } - fragmentShader += ' vec3 viewDir = -normalize(v_positionEC);\n'; - fragmentShader += ' vec3 ambientLight = vec3(0.0, 0.0, 0.0);\n'; - colorCreationBlock += ' color += ambient * ambientLight;\n'; - - // Add in light computations - fragmentShader += fragmentLightingBlock; - - fragmentShader += colorCreationBlock; - fragmentShader += finalColorComputation; - fragmentShader += '}\n'; - - // TODO: Handle texture transparency - var techniqueStates; - if (hasAlpha) { - techniqueStates = { - enable: [ - WebGLConstants.DEPTH_TEST, - WebGLConstants.BLEND - ], - depthMask: false, - functions: { - "blendEquationSeparate": [ - WebGLConstants.FUNC_ADD, - WebGLConstants.FUNC_ADD - ], - "blendFuncSeparate": [ - WebGLConstants.ONE, - WebGLConstants.ONE_MINUS_SRC_ALPHA, - WebGLConstants.ONE, - WebGLConstants.ONE_MINUS_SRC_ALPHA - ] - } - }; - } - else { - techniqueStates = { - enable: [ - WebGLConstants.CULL_FACE, - WebGLConstants.DEPTH_TEST - ] - }; - } - techniques[techniqueId] = { - attributes: techniqueAttributes, - parameters: techniqueParameters, - program: programId, - states: techniqueStates, - uniforms: techniqueUniforms - }; - - // Add shaders - shaders[vertexShaderId] = { - type: WebGLConstants.VERTEX_SHADER, - uri: '', - extras: { - source: vertexShader - } - }; - shaders[fragmentShaderId] = { - type: WebGLConstants.FRAGMENT_SHADER, - uri: '', - extras: { - source: fragmentShader - } - }; - - // Add program - var programAttributes = []; - for (i=0;i