├── README.md ├── .gitignore ├── assets ├── gradient │ ├── gradient.bin │ ├── gradient0VS.glsl │ ├── gradient0FS.glsl │ └── gradient.json └── picking │ ├── pickingFS.glsl │ ├── pickingVs.glsl │ └── picking.json ├── ui ├── scene-viewer.reel │ ├── scene-viewer.css │ ├── scene-viewer.html │ └── scene-viewer.js └── view.reel │ ├── view.css │ ├── view.html │ └── view.meta ├── runtime ├── web.config ├── scene.meta ├── node.meta ├── component-3d.meta ├── material.meta ├── base.js ├── camera.js ├── resource-loader.js ├── technique.js ├── builtin-assets.js ├── scene-resource-loader.js ├── resource-description.js ├── glTF-scene.js ├── node-wrapper.js ├── primitive.js ├── mesh.js ├── transform-helper.js ├── scene-helper.js ├── skin.js ├── glTF-material.js ├── scene.js ├── mesh-resource-loader.js ├── projection.js ├── animation-manager.js ├── transform.js ├── material.js ├── node.js ├── glTF-parser.js ├── scene-renderer.js └── glTF-node.js ├── package.json └── controllers └── camera-controller.js /README.md: -------------------------------------------------------------------------------- 1 | mjs-volume 2 | ========== 3 | 4 | MontageJS 3D Components 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .DS_Store 3 | .idea 4 | node_modules 5 | npm-debug.log 6 | *$py.class 7 | console.log -------------------------------------------------------------------------------- /assets/gradient/gradient.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emackey/mjs-volume/master/assets/gradient/gradient.bin -------------------------------------------------------------------------------- /assets/picking/pickingFS.glsl: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec4 u_pickingColor; 3 | void main(void) { 4 | gl_FragColor = u_pickingColor; 5 | } 6 | -------------------------------------------------------------------------------- /ui/scene-viewer.reel/scene-viewer.css: -------------------------------------------------------------------------------- 1 | .SceneViewer { 2 | -webkit-box-flex: 1; 3 | -ms-flex: 1; 4 | flex: 1; 5 | display: -webkit-box; 6 | display: -ms-flex; 7 | display: flex; 8 | height: 100%; 9 | } 10 | -------------------------------------------------------------------------------- /assets/picking/pickingVs.glsl: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | attribute vec3 a_position; 3 | uniform mat4 u_worldviewMatrix; 4 | uniform mat4 u_projectionMatrix; 5 | void main(void) { 6 | vec4 pos = u_worldviewMatrix * vec4(a_position,1.0); 7 | gl_Position = u_projectionMatrix * pos; 8 | } 9 | -------------------------------------------------------------------------------- /ui/view.reel/view.css: -------------------------------------------------------------------------------- 1 | .View { 2 | -webkit-box-flex: 1; 3 | -ms-flex: 1; 4 | flex: 1; 5 | display: -webkit-box; 6 | display: -ms-flex; 7 | display: flex; 8 | } 9 | 10 | .View-canvas { 11 | -webkit-box-flex: 1; 12 | -ms-flex: 1; 13 | flex: 1; 14 | } -------------------------------------------------------------------------------- /assets/gradient/gradient0VS.glsl: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | attribute vec3 a_position; 3 | varying vec3 v_position; 4 | uniform mat4 u_projectionMatrix; 5 | void main(void) 6 | { 7 | v_position = a_position; 8 | gl_Position = u_projectionMatrix * vec4(a_position, 1.); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /assets/gradient/gradient0FS.glsl: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | varying vec3 v_position; 3 | uniform vec3 u_color0; 4 | uniform vec3 u_color1; 5 | void main(void) { 6 | float intensity = (v_position.y / 2.) + 0.5; 7 | 8 | vec3 color = mix(u_color0, u_color1, intensity); 9 | 10 | gl_FragColor = vec4(color,.7); 11 | } 12 | -------------------------------------------------------------------------------- /runtime/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"mjs-volume", 3 | "version":"0.1.0", 4 | "dependencies": { 5 | "montage": "0.13.7", 6 | "digit": "0.3.1", 7 | "matte": "0.1.1", 8 | "q" : "0.9.6", 9 | "collections": "~0.2.1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/fabrobinet/mjs-volume.git" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ui/scene-viewer.reel/scene-viewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 |
24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /runtime/scene.meta: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "path_property": { 5 | "prototype": "montage/core/meta/property-blueprint", 6 | "properties": { 7 | "name": "path", 8 | "valueType": "string", 9 | "blueprint": { 10 | "@": "root" 11 | } 12 | } 13 | }, 14 | 15 | "root": { 16 | "prototype": "montage/core/meta/module-blueprint", 17 | "properties": { 18 | "name": "Scene", 19 | "propertyBlueprints": [ 20 | { 21 | "@": "path_property" 22 | } 23 | ], 24 | "propertyBlueprintGroups": { 25 | "Scene": [ 26 | { 27 | "@": "path_property" 28 | } 29 | ] 30 | }, 31 | "blueprintModule": { 32 | "%": "runtime/scene.meta" 33 | }, 34 | "exportName": "Scene", 35 | "module": { 36 | "%": "runtime/scene" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /runtime/node.meta: -------------------------------------------------------------------------------- 1 | { 2 | "hidden_property": { 3 | "prototype": "montage/core/meta/property-blueprint", 4 | "properties": { 5 | "name": "hidden", 6 | "valueType": "boolean", 7 | "blueprint": { 8 | "@": "root" 9 | } 10 | } 11 | }, 12 | 13 | "blueprint_parent": { 14 | "prototype": "montage/core/meta/blueprint-reference", 15 | "properties": { 16 | "valueReference": { 17 | "blueprintName": "Component3D", 18 | "blueprintModule": { 19 | "%": "runtime/component-3d.meta" 20 | }, 21 | "prototypeName": "Component3D" 22 | } 23 | } 24 | }, 25 | 26 | "root": { 27 | "prototype": "montage/core/meta/module-blueprint", 28 | "properties": { 29 | "parent": {"@": "blueprint_parent"}, 30 | "name": "Node", 31 | "propertyBlueprints": [ 32 | { 33 | "@": "hidden_property" 34 | } 35 | ], 36 | "propertyBlueprintGroups": { 37 | "Node": [ 38 | { 39 | "@": "hidden_property" 40 | } 41 | ] 42 | }, 43 | "blueprintModule": { 44 | "%": "runtime/node.meta" 45 | }, 46 | "exportName": "Node", 47 | "module": { 48 | "%": "runtime/node" 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /runtime/component-3d.meta: -------------------------------------------------------------------------------- 1 | { 2 | "scene_property": { 3 | "prototype": "montage/core/meta/property-blueprint", 4 | "properties": { 5 | "name": "scene", 6 | "valueType": "object", 7 | "blueprint": { 8 | "@": "root" 9 | } 10 | } 11 | }, 12 | 13 | "id_property": { 14 | "prototype": "montage/core/meta/property-blueprint", 15 | "properties": { 16 | "name": "id", 17 | "valueType": "string", 18 | "blueprint": { 19 | "@": "root" 20 | } 21 | } 22 | }, 23 | 24 | "root": { 25 | "prototype": "montage/core/meta/module-blueprint", 26 | "properties": { 27 | "name": "Component3D", 28 | "propertyBlueprints": [ 29 | { 30 | "@": "id_property" 31 | }, 32 | { 33 | "@": "scene_property" 34 | } 35 | ], 36 | "propertyBlueprintGroups": { 37 | "Component3D": [ 38 | { 39 | "@": "id_property" 40 | }, 41 | { 42 | "@": "scene_property" 43 | } 44 | ] 45 | }, 46 | "blueprintModule": { 47 | "%": "runtime/component-3d.meta" 48 | }, 49 | "exportName": "Component3D", 50 | "module": { 51 | "%": "runtime/component-3d" 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /runtime/material.meta: -------------------------------------------------------------------------------- 1 | { 2 | "image_property": { 3 | "prototype": "montage/core/meta/property-blueprint", 4 | "properties": { 5 | "name": "image", 6 | "valueType": "string", 7 | "blueprint": { 8 | "@": "root" 9 | } 10 | } 11 | }, 12 | 13 | "opacity_property": { 14 | "prototype": "montage/core/meta/property-blueprint", 15 | "properties": { 16 | "name": "opacity", 17 | "valueType": "number", 18 | "blueprint": { 19 | "@": "root" 20 | } 21 | } 22 | }, 23 | 24 | "blueprint_parent": { 25 | "prototype": "montage/core/meta/blueprint-reference", 26 | "properties": { 27 | "valueReference": { 28 | "blueprintName": "Component3D", 29 | "blueprintModule": { 30 | "%": "runtime/component-3d.meta" 31 | }, 32 | "prototypeName": "Component3D" 33 | } 34 | } 35 | }, 36 | 37 | "root": { 38 | "prototype": "montage/core/meta/module-blueprint", 39 | "properties": { 40 | "parent": {"@": "blueprint_parent"}, 41 | "name": "Material", 42 | "propertyBlueprints": [ 43 | { 44 | "@": "image_property" 45 | }, 46 | { 47 | "@": "opacity_property" 48 | } 49 | ], 50 | "propertyBlueprintGroups": { 51 | "Material": [ 52 | { 53 | "@": "image_property" 54 | }, 55 | { 56 | "@": "opacity_property" 57 | } 58 | ] 59 | }, 60 | "blueprintModule": { 61 | "%": "runtime/material.meta" 62 | }, 63 | "exportName": "Material", 64 | "module": { 65 | "%": "runtime/material" 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /runtime/base.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the Motorola Mobility, Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | exports.Base = Object.create(Object.prototype, { 27 | 28 | _properties: { value: null, writable: true }, 29 | 30 | properties: { 31 | get: function() { 32 | return this._properties; 33 | }, 34 | set: function(value) { 35 | this._properties = value; 36 | } 37 | }, 38 | 39 | __Base_init: { 40 | value: function() { 41 | this._properties = {}; 42 | } 43 | } 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /runtime/camera.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the Motorola Mobility, Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | var Base = require("runtime/base").Base; 28 | 29 | exports.Camera = Object.create(Base, { 30 | 31 | _projection: { value: null, writable: true }, 32 | 33 | projection: { 34 | get: function() { 35 | return this._projection; 36 | }, 37 | set: function(value) { 38 | this._projection = value; 39 | } 40 | }, 41 | 42 | init: { 43 | value: function() { 44 | this.__Base_init(); 45 | return this; 46 | } 47 | } 48 | 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /assets/picking/picking.json: -------------------------------------------------------------------------------- 1 | { 2 | "profile": "WebGL 1.0", 3 | "programs": { 4 | "program_0": { 5 | "fragmentShader": "technique1FS", 6 | "vertexShader": "technique1VS" 7 | } 8 | }, 9 | "renderBuffers" : { 10 | "depthBuffer" : { 11 | } 12 | }, 13 | "shaders": { 14 | "technique1FS": { 15 | "path": "pickingFS.glsl" 16 | }, 17 | "technique1VS": { 18 | "path": "pickingVS.glsl" 19 | } 20 | }, 21 | "techniques": { 22 | "pickingTechnique": { 23 | "parameters": { 24 | "pickingColor": { 25 | "type": 35665 26 | }, 27 | "position": { 28 | "semantic": "POSITION", 29 | "type": 35665 30 | }, 31 | "projectionMatrix": { 32 | "semantic": "PROJECTION", 33 | "type": 35676 34 | }, 35 | "worldViewMatrix": { 36 | "semantic": "MODELVIEW", 37 | "type": 35676 38 | } 39 | }, 40 | "pass": "defaultPass", 41 | "passes": { 42 | "defaultPass": { 43 | "instanceProgram": { 44 | "attributes": { 45 | "a_position": "position" 46 | }, 47 | "program": "program_0", 48 | "uniforms": { 49 | "u_pickingColor": "pickingColor", 50 | "u_projectionMatrix": "projectionMatrix", 51 | "u_worldviewMatrix": "worldViewMatrix" 52 | } 53 | }, 54 | "states": { 55 | "blendEnable": 0, 56 | "cullFaceEnable": 1, 57 | "depthMask": 1, 58 | "depthTestEnable": 1, 59 | "extras" : { 60 | "comment" : "we ignore states here, we will use state used by the material associated with the primitive being drawn" 61 | } 62 | } 63 | } 64 | } 65 | } 66 | }, 67 | "version": "0.3" 68 | } -------------------------------------------------------------------------------- /ui/view.reel/view.html: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | 36 | 37 | 52 | 53 | 54 | 55 |
56 | 57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /ui/view.reel/view.meta: -------------------------------------------------------------------------------- 1 | { 2 | "scene_property": { 3 | "prototype": "montage/core/meta/property-blueprint", 4 | "properties": { 5 | "name": "scene", 6 | "valueType": "object", 7 | "blueprint": { 8 | "@": "root" 9 | } 10 | } 11 | }, 12 | 13 | "viewPoint_property": { 14 | "prototype": "montage/core/meta/property-blueprint", 15 | "properties": { 16 | "name": "viewPoint", 17 | "valueType": "object", 18 | "blueprint": { 19 | "@": "root" 20 | } 21 | } 22 | }, 23 | 24 | "width_property": { 25 | "prototype": "montage/core/meta/property-blueprint", 26 | "properties": { 27 | "name": "width", 28 | "valueType": "number", 29 | "blueprint": { 30 | "@": "root" 31 | } 32 | } 33 | }, 34 | "height_property": { 35 | "prototype": "montage/core/meta/property-blueprint", 36 | "properties": { 37 | "name": "height", 38 | "valueType": "number", 39 | "blueprint": { 40 | "@": "root" 41 | } 42 | } 43 | }, 44 | 45 | "root": { 46 | "prototype": "montage/core/meta/module-blueprint", 47 | "properties": { 48 | "name": "View", 49 | "propertyBlueprints": [ 50 | { 51 | "@": "scene_property" 52 | }, 53 | { 54 | "@": "viewPoint_property" 55 | }, 56 | { 57 | "@": "width_property" 58 | }, 59 | { 60 | "@": "height_property" 61 | } 62 | ], 63 | "propertyBlueprintGroups": { 64 | "Main": [ 65 | { 66 | "@": "scene_property" 67 | }, 68 | { 69 | "@": "viewPoint_property" 70 | }, 71 | { 72 | "@": "width_property" 73 | }, 74 | { 75 | "@": "height_property" 76 | } 77 | ] 78 | }, 79 | "blueprintModule": { 80 | "%": "ui/view/view.meta" 81 | }, 82 | "exportName": "View", 83 | "module": { 84 | "%": "ui/view.reel" 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /runtime/resource-loader.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | exports.ResourceLoader = Object.create(Object.prototype, { 25 | 26 | webGLRenderer: { value: null, writable:true}, 27 | 28 | delegate: { value: null, writable:true}, 29 | 30 | _trackedIds: { value: null, writable:true }, 31 | 32 | _addTrackedId: { 33 | value: function(trackedId) { 34 | //console.log("add tracked resource:" + trackedId); 35 | 36 | if (this._trackedIds == null) { 37 | this._trackedIds = {}; 38 | } 39 | // if (this._trackedIds[trackedId] == null) 40 | // console.log("add track"+trackedId); 41 | this._trackedIds[trackedId] = true; 42 | } 43 | }, 44 | 45 | _removeTrackedId: { 46 | value: function(trackedId) { 47 | //console.log("remove track"+trackedId); 48 | if (this._trackedIds[trackedId] == null) { 49 | //we are listening to all request so we ignore this for now. 50 | //console.log("MeshLoader: inconsistency error") 51 | } else { 52 | //console.log("loaded:"+trackedId); 53 | delete this._trackedIds[trackedId]; 54 | } 55 | } 56 | } 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /runtime/technique.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | var Base = require("runtime/base").Base; 25 | 26 | exports.Technique = Object.create(Base, { 27 | 28 | _parameters: { value: null, writable: true }, 29 | 30 | _passName: { value: null, writable: true }, 31 | 32 | _passes: { value: null, writable: true }, 33 | 34 | init: { 35 | value: function() { 36 | this.__Base_init(); 37 | this.passes = {}; 38 | return this; 39 | } 40 | }, 41 | 42 | parameters: { 43 | get: function() { 44 | return this._parameters; 45 | }, 46 | set: function(value) { 47 | this._parameters = value; 48 | } 49 | }, 50 | 51 | passName: { 52 | get: function() { 53 | return this._passName; 54 | }, 55 | set: function(value) { 56 | if (this._passName != value) { 57 | this._passName = value; 58 | } 59 | } 60 | }, 61 | 62 | rootPass: { 63 | get: function() { 64 | return this._passes[this.passName]; 65 | } 66 | }, 67 | 68 | passesDidChange: { 69 | value: function() { 70 | //update the @pass when passes is changed. 71 | //For convenience set to null if there are multiple passes or to the only pass contained when there is just a single one. 72 | var passesNames = Object.keys(this.passes); 73 | this.passName = (passesNames.length == 1) ? passesNames[0] : null; 74 | } 75 | }, 76 | 77 | passes: { 78 | get: function() { 79 | return this._passes; 80 | }, 81 | set: function(value) { 82 | if (this._passes != value) { 83 | this._passes = value; 84 | this.passesDidChange(); 85 | } 86 | } 87 | }, 88 | 89 | execute: { 90 | value: function(webGLRenderer, time, options) { 91 | webGLRenderer.resetStates(); 92 | this.rootPass.execute(webGLRenderer, time, options); 93 | } 94 | } 95 | 96 | }); 97 | 98 | -------------------------------------------------------------------------------- /runtime/builtin-assets.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | var Q = require("q"); 25 | var RuntimeTFLoader = require("runtime/runtime-tf-loader").RuntimeTFLoader; 26 | 27 | exports.BuiltInAssets = Object.create(Object.prototype, { 28 | 29 | _deferredForName: { value: {}, writable: true }, 30 | 31 | _assetInfos: { value: {}, writable: true }, 32 | 33 | registerBuiltinAssetsIfNeeded: { 34 | value: function() { 35 | var pickingLocation = require.location + "assets/picking/picking.json"; 36 | var gradientLocation = require.location + "assets/gradient/gradient.json"; 37 | 38 | this._assetInfos["pickingTechnique"] = { "location" : pickingLocation, "options" : {"ids": ["pickingTechnique"]} }; 39 | this._assetInfos["gradient"] = { "location" : gradientLocation }; 40 | } 41 | }, 42 | 43 | assetInfosForName: { 44 | value: function(name) { 45 | this.registerBuiltinAssetsIfNeeded(); 46 | return this._assetInfos[name]; 47 | } 48 | }, 49 | 50 | assetWithName: { 51 | value: function(assetName, options) { 52 | var deferred = this._deferredForName[assetName]; 53 | if (!deferred) { 54 | deferred = Q.defer(); 55 | this._deferredForName[assetName] = deferred; 56 | var readerDelegate = {}; 57 | readerDelegate.loadCompleted = function (asset) { 58 | deferred.resolve(asset); 59 | }.bind(this); 60 | 61 | var loader = Object.create(RuntimeTFLoader); 62 | var assetInfos = this.assetInfosForName(assetName); 63 | if (!assetInfos) { 64 | deferred.reject("ERROR:"+assetName+" isn't registered as a built-in asset"); 65 | } else { 66 | loader.initWithPath(assetInfos.location); 67 | loader.delegate = readerDelegate; 68 | loader.load(null, assetInfos.options); 69 | } 70 | } 71 | 72 | return deferred.promise; 73 | } 74 | } 75 | 76 | }); 77 | -------------------------------------------------------------------------------- /runtime/scene-resource-loader.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | var ResourceLoader = require("runtime/resource-loader").ResourceLoader; 25 | var MeshResourceLoader = require("runtime/mesh-resource-loader").MeshResourceLoader; 26 | 27 | exports.SceneResourceLoader = Object.create(ResourceLoader, { 28 | 29 | _scene: { value:null, writable: true }, 30 | 31 | scene: { 32 | get: function() { 33 | return this._scene; 34 | }, 35 | set: function(value) { 36 | this._scene = value; 37 | } 38 | }, 39 | 40 | meshesDidLoad: { 41 | value: function() { 42 | if (this.delegate) { 43 | this.delegate.sceneResourcesDidPrepare(this.scene); 44 | } 45 | } 46 | }, 47 | 48 | loadScene: { 49 | value: function() { 50 | var self = this; 51 | if (this.scene) { 52 | var node = this.scene.rootNode; 53 | var meshes = {}; 54 | var meshesSet = []; 55 | node.apply( function(node, parent, parentTransform) { 56 | if (node.meshes) { 57 | if (node.meshes.length) { 58 | node.meshes.forEach( function(mesh) { 59 | if (meshes[mesh.id] == null) { 60 | meshesSet.push(mesh); 61 | meshes[mesh.id] = mesh; 62 | } 63 | }, this); 64 | } 65 | } 66 | return null; 67 | }, true, null); 68 | 69 | var meshResourceLoader = Object.create(MeshResourceLoader).init(meshesSet, self.webGLRenderer, this); 70 | meshResourceLoader.loadMeshes(); 71 | } 72 | } 73 | }, 74 | 75 | init: { 76 | value: function(scene, webGLRenderer, delegate) { 77 | if (delegate) 78 | this.delegate = delegate; 79 | this.webGLRenderer = webGLRenderer; 80 | this.scene = scene; 81 | webGLRenderer.resourceManager.observers.push(this); 82 | return this; 83 | } 84 | } 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /runtime/resource-description.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the Motorola Mobility, Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | var global = window; 28 | 29 | (function (root, factory) { 30 | if (typeof exports === 'object') { 31 | // Node. Does not work with strict CommonJS, but 32 | // only CommonJS-like enviroments that support module.exports, 33 | // like Node. 34 | factory(module.exports); 35 | } else if (typeof define === 'function' && define.amd) { 36 | // AMD. Register as an anonymous module. 37 | define([], function () { 38 | return factory(root); 39 | }); 40 | } else { 41 | // Browser globals 42 | factory(root); 43 | } 44 | }(this, function (root) { 45 | var ResourceDescription = Object.create(Object.prototype, { 46 | 47 | _description: { value: null, writable: true }, 48 | 49 | _id: { value: null, writable: true }, 50 | 51 | _type: { value: null, writable: true }, 52 | 53 | init: { 54 | enumerable: true, 55 | value: function(id, description) { 56 | this._id = id; 57 | this._description = description; 58 | return this; 59 | } 60 | }, 61 | 62 | description: { 63 | enumerable: true, 64 | get: function() { 65 | return this._description; 66 | } 67 | }, 68 | 69 | id: { 70 | enumerable: true, 71 | get: function() { 72 | return this._id; 73 | }, 74 | set: function(value) { 75 | this._id = value; 76 | } 77 | }, 78 | 79 | type: { 80 | enumerable: true, 81 | get: function() { 82 | return this._type; 83 | }, 84 | set: function(value) { 85 | this._type = value; 86 | } 87 | } 88 | 89 | }); 90 | 91 | if(root) { 92 | root.ResourceDescription = ResourceDescription; 93 | } 94 | 95 | return ResourceDescription; 96 | 97 | })); -------------------------------------------------------------------------------- /runtime/glTF-scene.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | var Montage = require("montage").Montage; 25 | var glTFNode = require("runtime/glTF-node").glTFNode; 26 | 27 | exports.glTFScene = Montage.specialize( { 28 | 29 | constructor: { 30 | value: function glTFScene() { 31 | this.super(); 32 | } 33 | }, 34 | 35 | _rootNode: { value : null, writable: true }, 36 | 37 | rootNode: { 38 | get: function() { 39 | return this._rootNode; 40 | }, 41 | set: function(value) { 42 | this._rootNode = value; 43 | } 44 | }, 45 | 46 | _id: { value: null, writable: true }, 47 | 48 | id: { 49 | get: function() { 50 | return this._id; 51 | }, 52 | set: function(value) { 53 | this._id = value; 54 | } 55 | }, 56 | 57 | baseURL: { value: null, writable:true }, 58 | 59 | _animationManager: { value: null, writable: true }, 60 | 61 | animationManager: { 62 | get: function() { 63 | return this._animationManager; 64 | }, 65 | set: function(value) { 66 | this._animationManager = value; 67 | } 68 | }, 69 | 70 | init: { 71 | value: function() { 72 | this.rootNode = Object.create(glTFNode); 73 | this.rootNode.initWithID(); 74 | return this; 75 | } 76 | }, 77 | 78 | startTime: { 79 | get: function() { 80 | var startTime = 0; 81 | if (this.animationManager) { 82 | return this.animationManager.startTime; 83 | } 84 | return startTime; 85 | } 86 | }, 87 | 88 | endTime: { 89 | get: function() { 90 | var endTime = -1; 91 | if (this.animationManager) { 92 | return this.animationManager.endTime; 93 | } 94 | return endTime; 95 | } 96 | }, 97 | 98 | _name: { 99 | value: null, 100 | writable: true 101 | }, 102 | 103 | name: { 104 | enumerable: true, 105 | get: function() { 106 | return this._name; 107 | }, 108 | set: function(value) { 109 | this._name = value; 110 | } 111 | } 112 | }); 113 | -------------------------------------------------------------------------------- /runtime/node-wrapper.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | require("runtime/dependencies/gl-matrix"); 25 | var Utilities = require("runtime/utilities").Utilities; 26 | var TransformHelper = require("runtime/transform-helper").TransformHelper; 27 | 28 | exports.NodeWrapper = Object.create(Object.prototype, { 29 | 30 | _transformHelper: { value: null, writable: true }, 31 | 32 | node: { 33 | get: function() { 34 | return this._transformHelper.node; 35 | } 36 | }, 37 | 38 | init: { 39 | value: function(node) { 40 | this._transformHelper = Object.create(TransformHelper).init(); 41 | this._transformHelper.node = node; 42 | return this; 43 | } 44 | }, 45 | 46 | viewPointWillChange: { 47 | value: function(node, prev, transform) { 48 | } 49 | }, 50 | 51 | viewPointDidChange: { 52 | value: function() { 53 | this._transformHelper.viewPoint = this._scenePassRenderer.viewPoint; 54 | } 55 | }, 56 | 57 | viewPointMatrixDidUpdate: { 58 | value: function() { 59 | this._transformHelper.transformDidUpdate(); 60 | } 61 | }, 62 | 63 | //------- 64 | 65 | scenePassRenderer: { 66 | get: function() { 67 | return this._scenePassRenderer; 68 | }, 69 | set: function(value) { 70 | if (this._scenePassRenderer != value) { 71 | if (this._scenePassRenderer) { 72 | this._scenePassRenderer.removeObserver(this) 73 | } 74 | 75 | this._scenePassRenderer = value; 76 | this._transformHelper.viewMatrix = value._viewPointMatrix; 77 | 78 | if (this._scenePassRenderer) { 79 | this._scenePassRenderer.addObserver(this); 80 | } 81 | } 82 | } 83 | }, 84 | 85 | worldMatrix: { 86 | get: function() { 87 | return this.node.worldMatrix; 88 | } 89 | }, 90 | 91 | worldViewMatrix: { 92 | get: function() { 93 | return this._transformHelper.worldViewMatrix; 94 | } 95 | }, 96 | 97 | worldViewInverseTransposeMatrix: { 98 | get: function() { 99 | return this._transformHelper.worldViewInverseTransposeMatrix; 100 | } 101 | } 102 | 103 | }); 104 | 105 | -------------------------------------------------------------------------------- /runtime/primitive.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the Motorola Mobility, Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | require("runtime/dependencies/gl-matrix"); 28 | var Utilities = require("runtime/utilities").Utilities; 29 | 30 | exports.Primitive = Object.create(Object.prototype, { 31 | 32 | _attributesCount: { 33 | enumerable: false, 34 | value: 0, 35 | writable: true 36 | }, 37 | 38 | attributesCount : { 39 | enumerable: false, 40 | get: function() { 41 | return this._attributesCount; 42 | } 43 | }, 44 | 45 | init: { 46 | value: function() { 47 | this.step = 0; 48 | this.semantics = {}; 49 | return this; 50 | } 51 | }, 52 | 53 | _semantics: { 54 | enumerable: false, 55 | value: null, 56 | writable: true 57 | }, 58 | 59 | semantics: { 60 | enumerable: true, 61 | get: function() { 62 | return this._semantics; 63 | }, 64 | set: function(value) { 65 | this._semantics = value; 66 | } 67 | }, 68 | 69 | //since semantics/set got simplified we should get rid of this eventually 70 | addVertexAttribute: { 71 | enumerable: false, 72 | value: function(vertexAttribute) { 73 | if (vertexAttribute.semantic === "POSITION") { 74 | var bbox = null; 75 | var accessor = vertexAttribute.attribute; 76 | if (accessor.min && accessor.max) { 77 | bbox = [ accessor.min, accessor.max]; 78 | } 79 | this.boundingBox = bbox; 80 | } 81 | 82 | if (!this.semantics[vertexAttribute.semantics]) { 83 | this.semantics[vertexAttribute.semantic] = vertexAttribute.attribute; 84 | this._attributesCount++; 85 | } 86 | } 87 | }, 88 | 89 | _computeBBOXIfNeeded: { 90 | enumerable: false, 91 | value: function() { 92 | } 93 | }, 94 | 95 | _boundingBox: { 96 | value: null, 97 | writable: true 98 | }, 99 | 100 | boundingBox: { 101 | enumerable: true, 102 | get: function() { 103 | this._computeBBOXIfNeeded(); 104 | return this._boundingBox; 105 | }, 106 | // we let the possibility to override by hand the bounding volume. 107 | set: function(value) { 108 | this._boundingBox = value; 109 | } 110 | }, 111 | 112 | _indices: { enumerable: false, value: null, writable: true }, 113 | 114 | indices: { 115 | get: function() { 116 | return this._indices; 117 | }, 118 | set: function(value) { 119 | this._indices = value; 120 | } 121 | }, 122 | 123 | _material: { enumerable: false, value: null, writable: true }, 124 | 125 | material: { 126 | get: function() { 127 | return this._material; 128 | }, 129 | set: function(value) { 130 | this._material = value; 131 | } 132 | } 133 | 134 | }); 135 | -------------------------------------------------------------------------------- /runtime/mesh.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the Motorola Mobility, Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | require("runtime/dependencies/gl-matrix"); 28 | var Base = require("runtime/base").Base; 29 | var Utilities = require("runtime/utilities").Utilities; 30 | 31 | exports.Mesh = Object.create(Base, { 32 | 33 | PRIMITIVES: { value: "primitives" }, 34 | 35 | _primitives: { 36 | value: null, 37 | writable: true 38 | }, 39 | 40 | primitives: { 41 | enumerable: true, 42 | get: function() { 43 | return this._primitives; 44 | }, 45 | set: function(value) { 46 | this._primitives = value; 47 | } 48 | }, 49 | 50 | //theses 2 propreties are just for the demo... 51 | step: { value: 0, writable:true }, 52 | loadedPrimitivesCount: { value: 0, writable:true }, 53 | 54 | loaded: { 55 | get: function() { 56 | return (this.loadedPrimitivesCount === this.primitives.length); 57 | } 58 | }, 59 | 60 | _id: { 61 | value: null, 62 | writable: true 63 | }, 64 | 65 | id: { 66 | enumerable: true, 67 | get: function() { 68 | return this._id; 69 | }, 70 | set: function(value) { 71 | this._id = value; 72 | } 73 | }, 74 | 75 | _name: { 76 | value: null, 77 | writable: true 78 | }, 79 | 80 | name: { 81 | enumerable: true, 82 | get: function() { 83 | return this._name; 84 | }, 85 | set: function(value) { 86 | this._name = value; 87 | } 88 | }, 89 | 90 | _computeBBOXIfNeeded: { 91 | enumerable: false, 92 | value: function() { 93 | if ( (!this._boundingBox) && this.primitives) { 94 | var count = this.primitives.length; 95 | if (count > 0) { 96 | var bbox = this.primitives[0].boundingBox; 97 | if (bbox) { 98 | var i; 99 | for (i = 1 ; i < count ; i++) { 100 | if (this.primitives[i].boundingBox) { //it could be not here here as we are loading everything asynchronously 101 | bbox = Utilities.mergeBBox(bbox, this.primitives[i].boundingBox); 102 | } 103 | } 104 | this._boundingBox = bbox; 105 | } 106 | } 107 | } 108 | } 109 | }, 110 | 111 | _boundingBox: { 112 | value: null, 113 | writable: true 114 | }, 115 | 116 | boundingBox: { 117 | enumerable: true, 118 | get: function() { 119 | this._computeBBOXIfNeeded(); 120 | return this._boundingBox; 121 | }, 122 | // we let the possibility to override by hand the bounding volume. 123 | set: function(value) { 124 | this._boundingBox = value; 125 | } 126 | }, 127 | 128 | init: { 129 | value: function() { 130 | this.__Base_init(); 131 | this._primitives = []; 132 | return this; 133 | } 134 | } 135 | }); 136 | -------------------------------------------------------------------------------- /runtime/transform-helper.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Fabrice ROBINET 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | require("runtime/dependencies/gl-matrix"); 25 | var Base = require("runtime/base").Base; 26 | var Utilities = require("runtime/utilities").Utilities; 27 | 28 | var TransformHelper = exports.TransformHelper = Object.create(Object.prototype, { 29 | 30 | _viewMatrix: { value: null, writable: true }, 31 | 32 | _worldViewMatrix: { value: null, writable: true }, 33 | 34 | _worldViewInverseTransposeMatrix: { value: null, writable: true }, 35 | 36 | _dirty: { value: true, writable: true }, 37 | 38 | _id: { value: 0, writable: true }, 39 | 40 | //node observer for the viewPoint.transform and node.transform 41 | transformWillChange: { 42 | value: function(node, prev, transform) { 43 | this._dirty = true; 44 | } 45 | }, 46 | 47 | transformDidChange: { 48 | value: function(node) { 49 | } 50 | }, 51 | 52 | _node: { value: null, writable: true }, 53 | 54 | node: { 55 | set: function(value) { 56 | if (this._node != value) { 57 | if (this._node) { 58 | this._node.transform.removeObserver(this); 59 | } 60 | this._node = value; 61 | if (this._node) { 62 | this._node.transform.addObserver(this); 63 | } 64 | this._dirty = true; 65 | } 66 | }, 67 | get: function() { 68 | return this._node; 69 | } 70 | }, 71 | 72 | _viewMatrix: { value: null, writable: true }, 73 | 74 | viewMatrix: { 75 | set: function(value) { 76 | this._viewMatrix = value; 77 | this._dirty = true; 78 | }, 79 | get: function() { 80 | return this._viewMatrix; 81 | } 82 | }, 83 | 84 | transformDidUpdate: { 85 | value: function(transform) { 86 | this._dirty = true; 87 | } 88 | }, 89 | 90 | updateMatricesIfNeeded: { 91 | value: function() { 92 | if (this._dirty) { 93 | mat4.multiply(this._viewMatrix, this._node.worldMatrix, this._worldViewMatrix); 94 | mat4.toInverseMat3(this._worldViewMatrix, this._worldViewInverseTransposeMatrix); 95 | mat3.transpose(this._worldViewInverseTransposeMatrix); 96 | this._dirty = false; 97 | } 98 | } 99 | }, 100 | 101 | worldViewMatrix: { 102 | get: function() { 103 | if (this._dirty) { //add this non-strictly necessary test to potentially prevent a function call 104 | this.updateMatricesIfNeeded(); 105 | } 106 | return this._worldViewMatrix; 107 | } 108 | }, 109 | 110 | worldViewInverseTransposeMatrix: { 111 | get: function() { 112 | if (this._dirty) { //add this non-strictly necessary test to potentially prevent a function call 113 | this.updateMatricesIfNeeded(); 114 | } 115 | return this._worldViewInverseTransposeMatrix; 116 | } 117 | }, 118 | 119 | init: { 120 | value: function() { 121 | this._viewMatrix = mat4.identity(); 122 | this._worldViewMatrix = mat4.identity(); 123 | this._worldViewInverseTransposeMatrix = mat3.identity(); 124 | return this; 125 | } 126 | } 127 | 128 | }); 129 | -------------------------------------------------------------------------------- /runtime/scene-helper.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | require("runtime/dependencies/gl-matrix"); 25 | var Montage = require("montage").Montage; 26 | var Uuid = require("montage/core/uuid").Uuid; 27 | var glTFScene = require("runtime/glTF-scene").glTFScene; 28 | var glTFNode = require("runtime/glTF-node").glTFNode; 29 | var Scene = require("runtime/scene").Scene; 30 | var Node = require("runtime/node").Node; 31 | var SceneRenderer = require("runtime/scene-renderer").SceneRenderer; 32 | var glTFMaterial = require("runtime/glTF-material").glTFMaterial; 33 | var Utilities = require("runtime/utilities").Utilities; 34 | var Projection = require("runtime/projection").Projection; 35 | var Camera = require("runtime/camera").Camera; 36 | var BBox = require("runtime/utilities").BBox; 37 | 38 | var SceneHelper = exports.SceneHelper = Object.create(Object.prototype, { 39 | 40 | getGLTFViewPoints: { 41 | value: function(scene) { 42 | var viewPoints = []; 43 | var node = scene.glTFElement.rootNode; 44 | node.apply( function(node, parent, parentTransform) { 45 | if (node.cameras) { 46 | if (node.cameras.length) 47 | viewPoints = viewPoints.concat(node); 48 | } 49 | return null; 50 | }, true, null); 51 | return viewPoints; 52 | } 53 | }, 54 | 55 | //we don't want to cache this to avoid synchronization here, so we don't want to call it often either :) 56 | getViewPoints: { 57 | value: function(scene) { 58 | var viewPoints = this.getGLTFViewPoints(scene); 59 | var m3dNodes = []; 60 | viewPoints.forEach( function(viewPoint) { 61 | var m3dNode = Montage.create(Node); 62 | m3dNode.scene = scene; 63 | //FIXME: should have probably used baseId here 64 | m3dNode.id = viewPoint.baseId; 65 | m3dNodes.push(m3dNode); 66 | }, this); 67 | 68 | return m3dNodes; 69 | } 70 | }, 71 | 72 | createGLTFNodeIncludingCamera: { 73 | value: function(cameraName) { 74 | //TODO: make that a default projection method 75 | var projection = Object.create(Projection); 76 | projection.initWithDescription( { 77 | "type":"perspective", 78 | "perspective" : { 79 | "yfov":45, 80 | "aspectRatio":1, 81 | "znear":0.1, 82 | "zfar":100 83 | } 84 | }); 85 | 86 | //create camera 87 | var camera = Object.create(Camera).init(); 88 | camera.projection = projection; 89 | //create node to hold the camera 90 | var cameraNode = Object.create(glTFNode).init(); 91 | camera.name = cameraNode.name = cameraName; 92 | cameraNode.id = Uuid.generate(); 93 | cameraNode.baseId = cameraNode.id; 94 | cameraNode.cameras.push(camera); 95 | return cameraNode; 96 | } 97 | }, 98 | 99 | createNodeIncludingCamera: { 100 | value: function(cameraName, m3dScene) { 101 | var cameraNode = SceneHelper.createGLTFNodeIncludingCamera(cameraName); 102 | var scene = m3dScene.glTFElement; 103 | scene.ids[cameraNode.baseId] = cameraNode; 104 | var m3dNode = Montage.create(Node); 105 | m3dNode.scene = m3dScene; 106 | m3dNode.id = cameraNode.baseId; 107 | return m3dNode; 108 | } 109 | } 110 | 111 | }); 112 | -------------------------------------------------------------------------------- /assets/gradient/gradient.json: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "generator": "hand-written" 4 | }, 5 | "accessors": { 6 | "attribute_22": { 7 | "bufferView": "bufferView_28", 8 | "byteOffset": 0, 9 | "byteStride": 12, 10 | "count": 4, 11 | "max": [ 12 | 0.5, 13 | 0.5, 14 | 0.5 15 | ], 16 | "min": [ 17 | -0.5, 18 | -0.5, 19 | -0.5 20 | ], 21 | "type": 35665 22 | }, 23 | "indices_20": { 24 | "bufferView": "bufferView_29", 25 | "byteOffset": 0, 26 | "count": 6, 27 | "type": 5123 28 | } 29 | 30 | }, 31 | "bufferViews": { 32 | "bufferView_28": { 33 | "buffer": "gradient.bin", 34 | "byteLength": 48, 35 | "byteOffset": 0, 36 | "target": 34962 37 | }, 38 | "bufferView_29": { 39 | "buffer": "gradient.bin", 40 | "byteLength": 12, 41 | "byteOffset": 48, 42 | "target": 34963 43 | } 44 | }, 45 | "buffers": { 46 | "gradient.bin": { 47 | "byteLength": 60, 48 | "path": "gradient.bin" 49 | } 50 | }, 51 | "cameras": { 52 | "camera_0": { 53 | "type": "orthographic", 54 | "orthographic" : { 55 | "xmag": 1, 56 | "ymag": 1, 57 | "zfar": 1000, 58 | "znear": 0 59 | } 60 | } 61 | }, 62 | "materials": { 63 | "material.0": { 64 | "instanceTechnique": { 65 | "technique": "technique1" 66 | }, 67 | "name": "blinn3" 68 | } 69 | }, 70 | "meshes": { 71 | "quad_mesh": { 72 | "name": "quad_mesh", 73 | "primitives": [ 74 | { 75 | "indices": "indices_20", 76 | "material": "material.0", 77 | "primitive": 4, 78 | "attributes": { 79 | "POSITION": "attribute_22" 80 | } 81 | } 82 | ] 83 | } 84 | }, 85 | "nodes": { 86 | "node_0": { 87 | "meshes": [ 88 | "quad_mesh" 89 | ], 90 | "name": "quad" 91 | }, 92 | "node_1": { 93 | "camera": "camera_0", 94 | "name": "camera1" 95 | } 96 | }, 97 | "profile": "WebGL 1.0", 98 | "programs": { 99 | "program_0": { 100 | "fragmentShader": "gradient0FS", 101 | "vertexShader": "gradient0VS" 102 | } 103 | }, 104 | "scene": "defaultScene", 105 | "scenes": { 106 | "defaultScene": { 107 | "nodes": [ 108 | "node_0", 109 | "node_1" 110 | ] 111 | } 112 | }, 113 | "shaders": { 114 | "gradient0FS": { 115 | "path": "gradient0FS.glsl" 116 | }, 117 | "gradient0VS": { 118 | "path": "gradient0VS.glsl" 119 | } 120 | }, 121 | "techniques": { 122 | "technique1": { 123 | "parameters": { 124 | "color0": { 125 | "type": 35665, 126 | "value": [ 127 | 1, 128 | 1, 129 | 1 130 | ] 131 | }, 132 | "color1": { 133 | "type": 35665, 134 | "value": [ 135 | 0, 136 | 0, 137 | 0 138 | ] 139 | }, 140 | "position": { 141 | "semantic": "POSITION", 142 | "type": 35665 143 | }, 144 | "projectionMatrix": { 145 | "semantic": "PROJECTION", 146 | "type": 35676 147 | } 148 | }, 149 | "pass": "gradientPass", 150 | "passes": { 151 | "gradientPass": { 152 | "instanceProgram": { 153 | "attributes": { 154 | "a_position": "position" 155 | }, 156 | "program": "program_0", 157 | "uniforms": { 158 | "u_color0": "color0", 159 | "u_color1": "color1", 160 | "u_projectionMatrix": "projectionMatrix" 161 | } 162 | }, 163 | "states": { 164 | "blendEnable": 1, 165 | "blendEquation": 32774, 166 | "blendFunc": { 167 | "dfactor": 771, 168 | "sfactor": 770 169 | }, 170 | "cullFaceEnable": 0, 171 | "depthMask": 0, 172 | "depthTestEnable": 0 173 | } 174 | } 175 | } 176 | } 177 | } 178 | } -------------------------------------------------------------------------------- /runtime/skin.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013,Fabrice Robinet. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | require("runtime/dependencies/gl-matrix"); 25 | var Base = require("runtime/base").Base; 26 | var Transform = require("runtime/transform").Transform; 27 | var Utilities = require("runtime/utilities").Utilities; 28 | 29 | exports.Skin = Object.create(Object.prototype, { 30 | 31 | jointsIds: { value: null, writable: true }, 32 | 33 | nodesForSkeleton: { value: null, writable: true }, 34 | 35 | bindShapeMatrix: { value: null, writable: true }, 36 | 37 | inverseBindMatricesDescription: { value: null, writable: true }, 38 | 39 | matricesForSkeleton: { value: null, writable: true }, 40 | 41 | sources: { value: null, writable: true }, 42 | 43 | init: { 44 | value: function() { 45 | this.jointsIds = []; 46 | this.nodesForSkeleton = {}; 47 | this.matricesForSkeleton = {}; 48 | this.sources = []; 49 | return this; 50 | } 51 | }, 52 | 53 | inverseBindMatricesDelegate: { 54 | value: { 55 | handleError: function(errorCode, info) { 56 | console.log("ERROR:vertexAttributeBufferDelegate:"+errorCode+" :"+info); 57 | }, 58 | 59 | convert: function (source, resource, ctx) { 60 | return new Float32Array(resource); 61 | }, 62 | 63 | resourceAvailable: function (glResource, ctx) { 64 | } 65 | } 66 | }, 67 | 68 | process: { 69 | value: function(node, resourceManager) { 70 | var skeletons = Object.keys(this.nodesForSkeleton); 71 | 72 | var objectSpace = mat4.create(); 73 | mat4.inverse(node.worldMatrix, objectSpace); 74 | 75 | skeletons.forEach(function(skeleton) { 76 | var nodes = this.nodesForSkeleton[skeleton]; 77 | var matrices = this.matricesForSkeleton[skeleton]; 78 | if (!matrices) { 79 | var length = 16 * this.jointsIds.length; 80 | matrices = new Float32Array(length); 81 | this.matricesForSkeleton[skeleton] = matrices; 82 | var identity = mat4.identity(); 83 | for (var i = 0 ; i < length ; i++) { 84 | matrices[i] = identity[i % 16]; 85 | } 86 | } 87 | var inverseBindMatrices = resourceManager.getResource(this.inverseBindMatricesDescription, this.inverseBindMatricesDelegate); 88 | if (inverseBindMatrices) { 89 | this.sources.forEach(function(source) { 90 | //FIXME: assume mesh here but it could be morph (later..) 91 | var mesh = source; 92 | 93 | var BSM = this.bindShapeMatrix; 94 | var jointsCount = this.jointsIds.length; 95 | var IBM = mat4.create(); 96 | for (var i = 0; i < jointsCount ; i++) { 97 | for (var j = 0; j < 16 ; j++) { 98 | IBM[j] = inverseBindMatrices[(i * 16) + j]; 99 | } 100 | 101 | var JM = nodes[i].worldMatrix; 102 | var destMat = mat4.identity(); 103 | mat4.multiply(destMat, objectSpace); 104 | mat4.multiply(destMat, JM); 105 | mat4.multiply(destMat, IBM); 106 | mat4.multiply(destMat, BSM); 107 | for (var j = 0; j < 16 ; j++) { 108 | matrices[(i*16)+j] = destMat[j]; 109 | } 110 | } 111 | 112 | mesh.primitives.forEach(function(primitive) { 113 | if (primitive.material.parameters) { 114 | primitive.material.parameters["jointMat"].value = matrices; 115 | } 116 | }, this) 117 | }, this); 118 | } 119 | }, this); 120 | } 121 | } 122 | 123 | }); 124 | -------------------------------------------------------------------------------- /runtime/glTF-material.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the Motorola Mobility, Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | require("runtime/dependencies/gl-matrix"); 28 | var Base = require("runtime/base").Base; 29 | var Technique = require("runtime/technique").Technique; 30 | var Pass = require("runtime/pass").Pass; 31 | var GLSLProgram = require("runtime/glsl-program").GLSLProgram; 32 | var ResourceDescription = require("runtime/resource-description").ResourceDescription; 33 | 34 | exports.glTFMaterial = Object.create(Base, { 35 | 36 | /* 37 | animationDuration: { value: 0.7, writable: false}, 38 | animationDelegate: { value: null, writable: true}, 39 | _techniqueForID: { value: {}, writable: true }, 40 | inputWillChange: { 41 | value: function(name, value) { 42 | } 43 | }, 44 | 45 | //update the technique when change parameters 46 | //the combinatory possibilties could explode here, and should not be handled that way for future devs.. 47 | inputDidChange: { 48 | value: function(name) { 49 | 50 | } 51 | }, 52 | 53 | _addInputPropertyIfNeeded: { 54 | value: function(property) { 55 | var privateName = "_" + property; 56 | var self = this; 57 | if (this.inputs.hasOwnProperty(property) === false) { 58 | Object.defineProperty(this.inputs, privateName, { writable:true , value: null }); 59 | 60 | Object.defineProperty(this.inputs, property, { 61 | get: function() { return self.inputs[privateName]; }, 62 | set: function(value) { 63 | var currentValue = self.inputs[property]; 64 | self.inputWillChange.call(self, property, value); 65 | if ((property === "diffuseTexture")||(property === "reflectionTexture")) { 66 | if (typeof value === "string") { 67 | var texture = value; 68 | var imageResource = Object.create(ResourceDescription).init(texture, { path: texture }); 69 | imageResource.type = "image"; 70 | value = imageResource; 71 | } else if (value !== null) { 72 | var image = value; 73 | //check if the uuid that we potentially stored is here 74 | var uuid = image["__uuid"]; 75 | if (!uuid) { 76 | image["__uuid"] = image.src ? src.split("/").join("_") : Uuid.generate(); 77 | } 78 | 79 | var imageResource = Object.create(ResourceDescription).init(uuid, { "image": image }); 80 | imageResource.type = "image"; 81 | value = imageResource; 82 | } 83 | } 84 | self.inputs[privateName] = value; 85 | self.inputDidChange.call(self, property); 86 | } 87 | }); 88 | } 89 | } 90 | }, 91 | 92 | _prepareInputsProperties: { 93 | value: function() { 94 | var properties = ["diffuseColor", "diffuseTexture", "transparency", "reflectionTexture", "reflectionIntensity", "shininess", "specularColor"]; 95 | properties.forEach( function(property) { 96 | this._addInputPropertyIfNeeded(property); 97 | }, this); 98 | } 99 | }, 100 | */ 101 | 102 | techniqueDidChange: { 103 | value: function() { 104 | //first thing when a technique is changed check for symbols. 105 | //these symbols will provides the names of the parameters to be tracked. 106 | 107 | } 108 | }, 109 | 110 | _technique: { value: null, writable: true }, 111 | 112 | technique: { 113 | enumerable: false, 114 | get: function() { 115 | return this._technique; 116 | }, 117 | set: function(value) { 118 | if (this._technique !== value) { 119 | this._technique = value; 120 | this.techniqueDidChange(); 121 | } 122 | } 123 | }, 124 | 125 | _parameters: { value: null, writable: true }, 126 | 127 | parameters: { 128 | enumerable: false, 129 | get: function() { 130 | return this._parameters; 131 | }, 132 | set: function(value) { 133 | this._parameters = value; 134 | } 135 | }, 136 | 137 | init: { 138 | value: function(id) { 139 | this.id = id; 140 | this.__Base_init(); 141 | this._parameters = {}; 142 | return this; 143 | } 144 | } 145 | 146 | }); -------------------------------------------------------------------------------- /runtime/scene.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | var Montage = require("montage").Montage; 25 | var Node = require("runtime/node").Node; 26 | var RuntimeTFLoader = require("runtime/runtime-tf-loader").RuntimeTFLoader; 27 | var URL = require("montage/core/url"); 28 | var SceneResourceLoader = require("runtime/scene-resource-loader").SceneResourceLoader; 29 | var Q = require("q"); 30 | var Target = require("montage/core/target").Target; 31 | 32 | exports.Scene = Target.specialize( { 33 | 34 | constructor: { 35 | value: function Scene() { 36 | this.super(); 37 | } 38 | }, 39 | 40 | _resourcesLoaded: { value: false, writable: true }, 41 | 42 | _rootNode: { value: null, writable: true }, 43 | 44 | rootNode: { 45 | get: function() { 46 | if (this.status === "loaded") { 47 | if (this._rootNode == null) { 48 | this._rootNode = Montage.create(Node); 49 | this._rootNode.scene = this; 50 | this._rootNode.id = this.glTFElement.rootNode.id; 51 | } 52 | } 53 | return this._rootNode; 54 | } 55 | }, 56 | 57 | sceneResourcesDidPrepare: { 58 | value: function() { 59 | if (!this._resourcesLoaded) { 60 | if (this._prepareToRenderDefer) { 61 | this._prepareToRenderDefer.resolve(); 62 | } 63 | this._resourcesLoaded = true; 64 | //FIXME: we should pass { scene:scene webGLContext:webGLContext 65 | //only problem now is pass the webGLContext through the promise properly 66 | this.dispatchEventNamed("resourcesDidLoad", true, false, this); 67 | this.status = "loaded"; 68 | } 69 | } 70 | }, 71 | 72 | isLoaded: { 73 | value: function() { 74 | return this.status == "loaded"; 75 | } 76 | }, 77 | 78 | status: { value: 0, writable:true}, 79 | 80 | path: { 81 | set: function(value) { 82 | //Work-around until montage implements textfield that do not send continous input.. 83 | if (value) { 84 | if (value.indexOf(".json") === -1) 85 | return; 86 | 87 | var URLObject = URL.parse(value); 88 | if (!URLObject.scheme) { 89 | var packages = Object.keys(require.packages); 90 | //HACK: for demo, packages[0] is guaranted to be the entry point 91 | value = URL.resolve(packages[0], value); 92 | } 93 | } 94 | 95 | if (value !== this._path) { 96 | var self = this; 97 | var readerDelegate = {}; 98 | readerDelegate.loadCompleted = function (scene) { 99 | this.totalBufferSize = loader.totalBufferSize; 100 | this.glTFElement = scene; 101 | this.status = "loaded"; 102 | console.log("scene loaded:"+this._path); 103 | }.bind(this); 104 | 105 | if (value) { 106 | var loader = Object.create(RuntimeTFLoader); 107 | this.status = "loading"; 108 | loader.initWithPath(value); 109 | loader.delegate = readerDelegate; 110 | loader.load(null /* userInfo */, null /* options */); 111 | } else { 112 | this.scene = null; 113 | } 114 | 115 | this._path = value; 116 | } 117 | }, 118 | 119 | get: function() { 120 | return this._path; 121 | } 122 | }, 123 | 124 | _prepareToRenderDefer: { value: null, writable: true }, 125 | 126 | /* 127 | This method doesn't need to be called directly if the rendering is done via a view. 128 | */ 129 | prepareToRender: { 130 | value: function(webGLRenderer) { 131 | if (this._prepareToRenderDefer == null) { 132 | this._prepareToRenderDefer = Q.defer(); 133 | var sceneResourceLoader = Object.create(SceneResourceLoader).init(this.glTFElement, webGLRenderer, this); 134 | sceneResourceLoader.loadScene(); 135 | } 136 | 137 | return this._prepareToRenderDefer.promise; 138 | } 139 | }, 140 | 141 | _styleSheets: { value: null, writable: true }, 142 | 143 | styleSheets: { 144 | get: function() { 145 | return this._styleSheets; 146 | }, 147 | 148 | set: function(value) { 149 | this._styleSheets = value; 150 | } 151 | }, 152 | 153 | init: { 154 | value:function(glTFElement) { 155 | if (glTFElement) { 156 | this.glTFElement = glTFElement; 157 | this.status = "loaded"; 158 | } 159 | return this; 160 | } 161 | } 162 | 163 | }); 164 | -------------------------------------------------------------------------------- /ui/scene-viewer.reel/scene-viewer.js: -------------------------------------------------------------------------------- 1 | var Montage = require("montage").Montage; 2 | var Component = require("montage/ui/component").Component; 3 | var Utilities = require("runtime/utilities").Utilities; 4 | var SceneHelper = require("runtime/scene-helper").SceneHelper; 5 | 6 | exports.SceneViewer = Component.specialize({ 7 | 8 | /** 9 | * The Scene Object 10 | * @type {Object} 11 | */ 12 | scene: { 13 | get: function() { 14 | return this._scene; 15 | }, 16 | set: function(value) { 17 | if (value != this._scene) { 18 | this._scene = value; 19 | this.sceneDidChange(); 20 | } 21 | } 22 | }, 23 | 24 | /** 25 | * View that performs rendering of the Scene 26 | * @type {Object} 27 | */ 28 | sceneView: { 29 | get: function() { 30 | return this.templateObjects ? this.templateObjects.sceneView : null; 31 | } 32 | }, 33 | 34 | /** 35 | * If true the viewer will automatically switch from one animated viewPoint to another 36 | * @type {boolean} 37 | * @default true 38 | */ 39 | automaticallyCyclesThroughViewPoints: { value: true, writable: true }, 40 | 41 | play: { 42 | value: function() { 43 | if (this.sceneView) { 44 | this.sceneView.play(); 45 | } 46 | } 47 | }, 48 | 49 | pause: { 50 | value: function() { 51 | if (this.sceneView) { 52 | this.sceneView.pause(); 53 | } 54 | } 55 | }, 56 | 57 | stop: { 58 | value: function() { 59 | if (this.sceneView) { 60 | this.sceneView.stop(); 61 | } 62 | } 63 | }, 64 | 65 | /* private */ 66 | 67 | _scene: { value: null, writable: true }, 68 | 69 | constructor: { 70 | value: function SceneViewer () { 71 | this.super(); 72 | } 73 | }, 74 | 75 | /* internal montage framework + callbacks from various delegates */ 76 | 77 | _sceneDidLoad: { 78 | value: function(scene) { 79 | if (scene.glTFElement) { 80 | if (this.scene.glTFElement.animationManager) { 81 | if (this.scene.glTFElement.animationManager) { 82 | this.scene.glTFElement.animationManager.delegate = this; 83 | } 84 | } 85 | } 86 | } 87 | }, 88 | 89 | handleStatusChange: { 90 | value: function(status, key, object) { 91 | if (status === "loaded") { 92 | this._sceneDidLoad(object); 93 | //Work-around 94 | var self = this; 95 | setTimeout(function() { 96 | self.scene.removeOwnPropertyChangeListener("status", self); 97 | }, 1); 98 | 99 | } 100 | } 101 | }, 102 | 103 | sceneDidChange: { 104 | value: function() { 105 | if (this.scene) { 106 | if (this.scene.isLoaded()) { 107 | this._sceneDidLoad(this.scene); 108 | } else { 109 | this.scene.addOwnPropertyChangeListener("status", this); 110 | } 111 | 112 | } 113 | if (this.sceneView) { 114 | this.sceneView.scene = this.scene; 115 | } 116 | } 117 | }, 118 | 119 | sceneTimeWillChange: { 120 | value: function(animation, upcomingSceneTime) { 121 | 122 | } 123 | }, 124 | 125 | sceneTimeDidChange: { 126 | value: function(animation) { 127 | 128 | if (this.scene == null) 129 | return; 130 | if (this.scene.glTFElement == null) { 131 | return; 132 | } 133 | 134 | var endTime = this.scene.glTFElement.endTime; 135 | if ((endTime !== -1) && (this.sceneView != null)) { 136 | var animationManager = this.scene.glTFElement.animationManager; 137 | if (animationManager.sceneTime / 1000. > endTime) { 138 | if (this.automaticallyCyclesThroughViewPoints == true) { 139 | var viewPointIndex = this.sceneView._viewPointIndex; //_viewPointIndex is private in view, we could actually put/access this info from scene 140 | var viewPoints = SceneHelper.getViewPoints(this.scene); 141 | if (viewPoints.length > 0) { 142 | var nextViewPoint; 143 | var checkIdx = 0; 144 | do { 145 | animationManager.sceneTime = 0; 146 | checkIdx++; 147 | viewPointIndex = ++viewPointIndex % viewPoints.length; 148 | nextViewPoint = viewPoints[viewPointIndex]; 149 | } while ((checkIdx < viewPoints.length) && (animationManager.nodeHasAnimatedAncestor(nextViewPoint.glTFElement) == false)); 150 | this.sceneView.viewPoint = nextViewPoint; 151 | } 152 | } 153 | } 154 | } 155 | } 156 | }, 157 | 158 | templateDidLoad:{ 159 | value:function () { 160 | //we ensure that we'll have the scene propagated to view by calling sceneDidChange() in templateDidLoad 161 | this.sceneDidChange(); 162 | this.needsDraw = true; 163 | this.sceneView.needsDraw = true; 164 | 165 | } 166 | }, 167 | 168 | enterDocument: { 169 | value: function(firstTime) { 170 | window.addEventListener("resize", this, true); 171 | } 172 | }, 173 | 174 | exitDocument: { 175 | value: function() { 176 | window.removeEventListener("resize", this, true); 177 | } 178 | }, 179 | 180 | draw: { 181 | value: function() { 182 | } 183 | }, 184 | 185 | willDraw: { 186 | value: function() { 187 | if (this.sceneView) { 188 | this.sceneView.needsDraw = true; 189 | } 190 | } 191 | }, 192 | 193 | /* dom */ 194 | 195 | captureResize: { 196 | value: function(evt) { 197 | this.needsDraw = true; 198 | 199 | var w = this.element.offsetWidth; 200 | var h = this.element.offsetHeight; 201 | 202 | this.sceneView.width = w; 203 | this.sceneView.height = h; 204 | this.sceneView.needsDraw = true; 205 | 206 | } 207 | } 208 | 209 | }); 210 | -------------------------------------------------------------------------------- /runtime/mesh-resource-loader.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | var ResourceLoader = require("runtime/resource-loader").ResourceLoader; 25 | 26 | exports.MeshResourceLoader = Object.create(ResourceLoader, { 27 | 28 | meshes: { value: null, writable:true}, 29 | 30 | fireMeshesDidLoadIfNeeded: { 31 | value: function() { 32 | 33 | var ids = Object.keys(this._trackedIds); 34 | if (ids) { 35 | if (ids.length == 0) { 36 | if (this.delegate) { 37 | if (this.delegate.meshesDidLoad) { 38 | //FIXME: stop being an observer here 39 | this.delegate.meshesDidLoad(this.meshes); 40 | } 41 | } 42 | } 43 | } 44 | } 45 | }, 46 | 47 | resourceAvailable: { 48 | value: function(resourceId) { 49 | //console.log("resource available:" + resourceId); 50 | 51 | this._removeTrackedId(resourceId); 52 | this.fireMeshesDidLoadIfNeeded(); 53 | } 54 | }, 55 | 56 | //load and upload to VRAM 57 | _fetchResources: { 58 | value: function(delegate, resources, ctx) { 59 | var webGLContext = this.webGLRenderer.webGLContext; 60 | 61 | //Load images an upload texture 62 | var resourceIds = Object.keys(resources); 63 | resourceIds.forEach(function(resourceId) { 64 | //FIXME: handle the case of vertexBuffer who expect the resource to be passed as context 65 | //We want here to change the resource manager to prevent this 66 | //convert/resourveAvailable method of delegate should have resource has first argument 67 | var resource = this.webGLRenderer.resourceManager.getResource(resources[resourceId], delegate, webGLContext); 68 | if (resource == null) { 69 | this._addTrackedId(resourceId); 70 | } 71 | }, this); 72 | } 73 | }, 74 | 75 | _fetchAllResources: { 76 | value: function(resources) { 77 | var webGLContext = this.webGLRenderer.webGLContext; 78 | 79 | this._fetchResources(this.webGLRenderer.vertexAttributeBufferDelegate, resources.vertexBuffers, true); 80 | this._fetchResources(this.webGLRenderer.indicesDelegate, resources.allIndices); 81 | this._fetchResources(this.webGLRenderer.textureDelegate, resources.textures); 82 | this._fetchResources(this.webGLRenderer.programDelegate, resources.programs); 83 | 84 | //attempt a call to fireMeshesDidLoadIfNeeded just in case if nothing has to be fetched. 85 | //this might be more efficient to not go through the callback and return a bool here. 86 | this.fireMeshesDidLoadIfNeeded(); 87 | } 88 | }, 89 | 90 | _trackProgramsFromMaterial: { 91 | value: function(material, programs) { 92 | var technique = material.technique; 93 | if (technique) { 94 | for (var passId in technique.passes) { 95 | var pass = technique.passes[passId]; 96 | var instanceProgram = pass.instanceProgram; 97 | if (instanceProgram) { 98 | programs[instanceProgram.program.id] = instanceProgram.program; 99 | this._addTrackedId(instanceProgram.program.id); 100 | } 101 | } 102 | } 103 | } 104 | }, 105 | 106 | _trackTexturesFromMaterial: { 107 | value: function(material, textures) { 108 | var parameters = material.parameters; 109 | if (parameters) { 110 | var parametersKeys = Object.keys(parameters); 111 | if (parametersKeys.length > 0) { 112 | parametersKeys.forEach(function(parameterKey) { 113 | var parameter = parameters[parameterKey]; 114 | if (parameter) { 115 | if (parameter.value) { 116 | var value = parameter.value; 117 | if ((value.type === "texture")) { 118 | textures[value.id] = value; 119 | this._addTrackedId(value.id); 120 | } 121 | } 122 | } 123 | }, this); 124 | } 125 | } 126 | } 127 | }, 128 | 129 | 130 | _trackIndicesFromPrimitive: { 131 | value: function(primitive, allIndices) { 132 | var indices = primitive.indices; 133 | allIndices[indices.id] = indices; 134 | this._addTrackedId(indices.id); 135 | } 136 | }, 137 | 138 | _trackVertexBuffersFromPrimitive: { 139 | value: function(primitive, vertexBuffers) { 140 | for (var semantic in primitive.semantics) { 141 | var vertexBuffer = primitive.semantics[semantic]; 142 | vertexBuffers[vertexBuffer.id] = vertexBuffer; 143 | this._addTrackedId(vertexBuffer.id); 144 | } 145 | } 146 | }, 147 | 148 | _trackMesh: { 149 | value: function(mesh, resources, webGLRenderer) { 150 | 151 | mesh.primitives.forEach( function(primitive) { 152 | this._trackTexturesFromMaterial(primitive.material, resources.textures); 153 | this._trackIndicesFromPrimitive(primitive, resources.allIndices); 154 | this._trackVertexBuffersFromPrimitive(primitive, resources.vertexBuffers); 155 | this._trackProgramsFromMaterial(primitive.material, resources.programs); 156 | 157 | }, this); 158 | 159 | } 160 | }, 161 | 162 | _trackMeshes: { 163 | value: function(resources) { 164 | this.meshes.forEach( function(mesh) { 165 | this._trackMesh(mesh, resources, this.webGLRenderer); 166 | }, this); 167 | } 168 | }, 169 | 170 | loadMeshes: { 171 | value: function() { 172 | var resources = {}; 173 | //+ animations ? maybe not required in the pass of this 174 | //+ programs 175 | 176 | resources.textures = {}; 177 | resources.allIndices = {}; 178 | resources.vertexBuffers = {}; 179 | resources.programs = {}; 180 | 181 | this._trackMeshes(resources); 182 | this._fetchAllResources(resources, this.webGLRenderer); 183 | } 184 | }, 185 | 186 | init: { 187 | value: function(meshes, webGLRenderer, delegate) { 188 | this.delegate = delegate; 189 | this.webGLRenderer = webGLRenderer; 190 | this.meshes = meshes; 191 | webGLRenderer.resourceManager.observers.push(this); 192 | return this; 193 | } 194 | } 195 | 196 | }); 197 | -------------------------------------------------------------------------------- /controllers/camera-controller.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Fabrice ROBINET 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | require("runtime/dependencies/gl-matrix"); 25 | var Utilities = require("runtime/utilities").Utilities; 26 | var Transform = require("runtime/transform").Transform; 27 | var Montage = require("montage").Montage; 28 | 29 | exports.CameraController = Montage.specialize( { 30 | 31 | // Montage 32 | constructor: { 33 | value: function View() { 34 | this.super(); 35 | 36 | this._lastPosition = [0 ,0]; 37 | } 38 | }, 39 | 40 | _viewPoint: { value: null, writable: true}, 41 | 42 | viewPoint: { 43 | get: function() { 44 | return this._viewPoint; 45 | }, 46 | set: function(value) { 47 | if (this._viewPoint != value) { 48 | this._viewPoint = value; 49 | this.zoomStep = 0; 50 | } 51 | } 52 | }, 53 | 54 | _node: { value: null, writable: true}, 55 | 56 | zoomStep: { value: 0, writable: true }, 57 | 58 | node: { 59 | get: function() { 60 | return this._node; 61 | }, 62 | set: function(value) { 63 | if (this._node != value) { 64 | this._node = value; 65 | this.zoomStep = 0; 66 | } 67 | } 68 | }, 69 | 70 | _lastPosition: { value: null, writable: true }, 71 | 72 | _transform: { value: null, writable: true }, 73 | 74 | _axisUp: { value: null, writable: true }, 75 | 76 | zoom: { 77 | value: function(event) { 78 | if (this.moving) 79 | return; 80 | 81 | var self = this; 82 | var direction = vec3.create(); 83 | var eye = vec3.create(this.viewPoint.glTFElement.transform.translation); 84 | 85 | var targetPosition; 86 | var rootNode = this.node.glTFElement; 87 | var sceneBBox = rootNode.getBoundingBox(true); 88 | targetPosition = [ 89 | (sceneBBox[0][0] + sceneBBox[1][0]) / 2, 90 | (sceneBBox[0][1] + sceneBBox[1][1]) / 2, 91 | (sceneBBox[0][2] + sceneBBox[1][2]) / 2]; 92 | 93 | if (this.zoomStep == 0) { 94 | var lg = vec3.createFrom(sceneBBox[1][0] - sceneBBox[0][0], sceneBBox[1][1] - sceneBBox[0][1], sceneBBox[1][2] - sceneBBox[0][2]) 95 | this.zoomStep = 0.0001 * vec3.length(lg); 96 | 97 | } 98 | 99 | direction[0] = targetPosition[0] - eye[0]; 100 | direction[1] = targetPosition[1] - eye[1]; 101 | direction[2] = targetPosition[2] - eye[2]; 102 | vec3.normalize(direction); 103 | 104 | eye[0] += this.zoomStep * direction[0] * event.wheelDeltaY; 105 | eye[1] += this.zoomStep * direction[1] * event.wheelDeltaY; 106 | eye[2] += this.zoomStep * direction[2] * event.wheelDeltaY; 107 | 108 | this.viewPoint.glTFElement.transform.translation = eye; 109 | } 110 | }, 111 | 112 | translate: { 113 | value: function(event) { 114 | this._transform.matrix = this.viewPoint.glTFElement.worldMatrix; 115 | if (this.moving == false) 116 | return; 117 | 118 | var xDelta = event.translateX - this._lastPosition[0]; 119 | var yDelta = event.translateY - this._lastPosition[1]; 120 | 121 | this._lastPosition[0] = event.translateX; 122 | this._lastPosition[1] = event.translateY; 123 | 124 | xDelta *= 0.05; 125 | yDelta *= -0.05; 126 | 127 | //if (this._axisUp == null) { 128 | this._axisUp = vec3.createFrom(0, 1, 0); 129 | mat4.rotateVec3(this._transform.matrix, this._axisUp); 130 | //} 131 | var hasTarget = false; 132 | var targetPosition; 133 | if (hasTarget == false) { 134 | var rootNode = this.node.glTFElement; 135 | var sceneBBox = rootNode.getBoundingBox(true); 136 | targetPosition = [ 137 | (sceneBBox[0][0] + sceneBBox[1][0]) / 2, 138 | (sceneBBox[0][1] + sceneBBox[1][1]) / 2, 139 | (sceneBBox[0][2] + sceneBBox[1][2]) / 2]; 140 | } 141 | var direction = vec3.create(); 142 | var eye = vec3.create(this._transform.translation); 143 | 144 | direction[0] = targetPosition[0] - eye[0]; 145 | direction[1] = targetPosition[1] - eye[1]; 146 | direction[2] = targetPosition[2] - eye[2]; 147 | 148 | var axisUpAdjusted = vec3.create(this._axisUp); 149 | var right = vec3.create(); 150 | vec3.normalize(direction); 151 | vec3.cross(direction, this._axisUp, right); 152 | vec3.normalize(right); 153 | vec3.cross(direction, right, axisUpAdjusted); 154 | vec3.normalize(axisUpAdjusted); 155 | 156 | var cameraMat = mat4.identity(); 157 | 158 | var ratio = 0; 159 | if (Math.abs(yDelta) > Math.abs(xDelta)) { 160 | ratio = Math.abs(yDelta) / Math.abs(xDelta); 161 | } else { 162 | ratio = Math.abs(xDelta) / Math.abs(yDelta); 163 | } 164 | 165 | if (ratio > 0.5) { 166 | mat4.rotate(cameraMat, xDelta, axisUpAdjusted); 167 | mat4.rotate(cameraMat, yDelta, right); 168 | } else 169 | if (Math.abs(yDelta) > Math.abs(xDelta)) 170 | mat4.rotate(cameraMat, yDelta, right); 171 | else 172 | mat4.rotate(cameraMat, xDelta, axisUpAdjusted); 173 | 174 | eye[0] -= targetPosition[0]; 175 | eye[1] -= targetPosition[1]; 176 | eye[2] -= targetPosition[2]; 177 | 178 | mat4.rotateVec3(cameraMat, eye); 179 | 180 | eye[0] += targetPosition[0]; 181 | eye[1] += targetPosition[1]; 182 | eye[2] += targetPosition[2]; 183 | 184 | var rotationMatrix = mat4.identity(); 185 | mat4.multiply3(cameraMat, this._transform.matrix, rotationMatrix); 186 | 187 | var translationMatrix = mat4.identity(); 188 | mat4.translate(translationMatrix, eye); 189 | 190 | var finalMat = mat4.identity(); 191 | mat4.multiply(translationMatrix, rotationMatrix, finalMat); 192 | this.viewPoint.glTFElement.transform.matrix = finalMat; 193 | } 194 | }, 195 | 196 | beginTranslate: { 197 | value: function(event) { 198 | this.moving = true; 199 | if (this._transform == null) { 200 | this._transform = Object.create(Transform).init(); 201 | } 202 | this._transform.matrix = this.viewPoint.glTFElement.worldMatrix; 203 | } 204 | }, 205 | 206 | endTranslate: { 207 | value: function(event) { 208 | this.moving = false; 209 | 210 | this._axisUp = null; 211 | } 212 | } 213 | 214 | }); 215 | -------------------------------------------------------------------------------- /runtime/projection.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the Motorola Mobility, Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | require("runtime/dependencies/gl-matrix"); 28 | var Base = require("runtime/base").Base; 29 | 30 | exports.Projection = Object.create(Base, { 31 | 32 | _matrix: { value: null, writable: true }, 33 | _type: { value: null, writable: true }, 34 | _xfov: { value: 0, writable: true }, 35 | _yfov: { value: 0, writable: true }, 36 | _xmag: { value: 0, writable: true }, 37 | _ymag: { value: 0, writable: true }, 38 | _znear: { value: 0, writable: true }, 39 | _zfar: { value: 0, writable: true }, 40 | _aspectRatio: { value: 0, writable: true }, 41 | 42 | _dirtyFlag: { value : false, writable: true}, 43 | 44 | projection: { 45 | get: function() { 46 | return this._type; 47 | }, 48 | set: function(value) { 49 | if (this._type !== value) { 50 | this._type = value; 51 | this._dirtyFlag = true; 52 | } 53 | } 54 | }, 55 | 56 | xfov: { 57 | get: function() { 58 | return this._xfov; 59 | }, 60 | set: function(value) { 61 | if (this._xfov !== value) { 62 | this._xfov = value; 63 | this._dirtyFlag = true; 64 | } 65 | } 66 | }, 67 | 68 | yfov: { 69 | get: function() { 70 | return this._yfov; 71 | }, 72 | set: function(value) { 73 | if (this._yfov !== value) { 74 | this._yfov = value; 75 | this._dirtyFlag = true; 76 | } 77 | } 78 | }, 79 | 80 | xmag: { 81 | get: function() { 82 | return this._xmag; 83 | }, 84 | set: function(value) { 85 | if (this._xmag !== value) { 86 | this._xmag = value; 87 | this._dirtyFlag = true; 88 | } 89 | } 90 | }, 91 | 92 | ymag: { 93 | get: function() { 94 | return this._ymag; 95 | }, 96 | set: function(value) { 97 | if (this._ymag !== value) { 98 | this._ymag = value; 99 | this._dirtyFlag = true; 100 | } 101 | } 102 | }, 103 | 104 | znear: { 105 | get: function() { 106 | return this._znear; 107 | }, 108 | set: function(value) { 109 | if (this._znear !== value) { 110 | this._znear = value; 111 | this._dirtyFlag = true; 112 | } 113 | } 114 | }, 115 | 116 | zfar: { 117 | get: function() { 118 | return this._zfar; 119 | }, 120 | set: function(value) { 121 | if (this._zfar !== value) { 122 | this._zfar = value; 123 | this._dirtyFlag = true; 124 | } 125 | } 126 | }, 127 | 128 | aspectRatio: { 129 | get: function() { 130 | return this._aspectRatio; 131 | }, 132 | set: function(value) { 133 | var matrix = this.matrix; 134 | if (matrix) { 135 | if (this.yfov) { 136 | matrix[0] = this._scaleX / value ; 137 | } else if (this.xfov) { 138 | matrix[5] = this._scaleY * value ; 139 | 140 | } 141 | } 142 | this._aspectRatio = value; 143 | } 144 | }, 145 | 146 | _scaleX : { value: 0, writable: true }, 147 | _scaleY : { value: 0, writable: true }, 148 | 149 | matrix: { 150 | get: function() { 151 | if (this._dirtyFlag) 152 | { 153 | if (this.projection === "perspective") { 154 | var degToRadians = 3.14159265359 / 360.0; 155 | 156 | var scaleX = 0; 157 | var scaleY = 0; 158 | if (this.yfov) { 159 | scaleY = 1./Math.tan(this.yfov * degToRadians); 160 | } 161 | if (this.xfov) { 162 | scaleX = 1./Math.tan(this.xfov * degToRadians); 163 | } else { 164 | scaleX = scaleY; 165 | } 166 | if (scaleY == 0) { 167 | scaleY = scaleX; 168 | } 169 | this._scaleX = scaleX; 170 | this._scaleY = scaleY; 171 | this._matrix = mat4.create(); 172 | 173 | this._matrix[0] = scaleX; 174 | this._matrix[1] = 0.0; 175 | this._matrix[2] = 0.0; 176 | this._matrix[3] = 0.0; 177 | 178 | this._matrix[4] = 0.0; 179 | this._matrix[5]= scaleY; 180 | this._matrix[6] = 0.0; 181 | this._matrix[7] = 0.0; 182 | 183 | this._matrix[8] = 0.0; 184 | this._matrix[9] = 0.0; 185 | this._matrix[10] = (this.zfar + this.znear) / (this.znear - this.zfar); 186 | this._matrix[11] = -1.0; 187 | 188 | this._matrix[12] = 0.0; 189 | this._matrix[13] = 0.0; 190 | this._matrix[14] = (2.0 * this.zfar * this.znear) / (this.znear - this.zfar); 191 | this._matrix[15] = 0.0; 192 | 193 | } else if (this.projection === "orthographic") { 194 | this._matrix = mat4.ortho(-this.xmag, this.xmag, -this.ymag, this.ymag, this.znear, this.zfar); 195 | } else { 196 | console.log("WARNING: unhandled camera type:"+type) 197 | } 198 | 199 | this._dirtyFlag = false; 200 | } 201 | return this._matrix; 202 | }, 203 | set: function(value ) { 204 | this._matrix = value; 205 | } 206 | }, 207 | 208 | 209 | initWithDescription: { 210 | value: function(description) { 211 | this.__Base_init(); 212 | this.projection = description.type; 213 | description = description[this.projection]; 214 | this.xfov = description.xfov ? description.xfov : 0; 215 | this.yfov = description.yfov ? description.yfov : 0; 216 | this.xmag = description.xmag ? description.xmag : 1; 217 | this.ymag = description.ymag ? description.ymag : 1; 218 | this.znear = description.znear != null ? description.znear : 1; 219 | this.zfar = description.zfar != null ? description.zfar : 100; 220 | this.aspectRatio = description.aspect_ratio ? description.aspect_ratio : 0; //deprecate this one 221 | if (!this.aspectRatio) 222 | this.aspectRatio = description.aspectRatio ? description.aspectRatio : 0; 223 | this._dirtyFlag = true; 224 | } 225 | }, 226 | 227 | init: { 228 | value: function() { 229 | this.__Base_init(); 230 | 231 | this.projection = null; 232 | this.xfov = 0; 233 | this.yfov = 0; 234 | this.xmag = 1; 235 | this.ymag = 1; 236 | this.znear = 1; 237 | this.zfar = 100; 238 | this.aspectRatio = 4./3; 239 | this._dirtyFlag = true; 240 | } 241 | } 242 | }); 243 | 244 | -------------------------------------------------------------------------------- /runtime/animation-manager.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice ROBINET 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | require("runtime/dependencies/gl-matrix"); 25 | var Base = require("runtime/base").Base; 26 | var KeyframeAnimation = require("runtime/animation").KeyframeAnimation; 27 | var BasicAnimation = require("runtime/animation").BasicAnimation; 28 | var Animation = require("runtime/animation").Animation; 29 | 30 | exports.AnimationManager = Object.create(Base, { 31 | 32 | startTime: { 33 | get: function() { 34 | if (this.animations) { 35 | if (this.animations.length > 0) { 36 | var startTime = this.animations[0].startTime; 37 | for (var i = 1 ; i < this.animations.length ; i++ ) { 38 | if (this.animations[i].startTime < startTime) { 39 | startTime = this.animations[i].startTime; 40 | } 41 | } 42 | return startTime; 43 | } 44 | return 0; 45 | } 46 | } 47 | }, 48 | 49 | endTime: { 50 | get: function() { 51 | if (this.animations) { 52 | if (this.animations.length > 0) { 53 | var endTime = this.animations[0].endTime; 54 | for (var i = 1 ; i < this.animations.length ; i++ ) { 55 | if (this.animations[i].endTime > endTime) { 56 | endTime = this.animations[i].endTime; 57 | } 58 | } 59 | return endTime; 60 | } 61 | return -1; 62 | } 63 | } 64 | }, 65 | 66 | _animations: { value: null, writable: true }, 67 | 68 | animations: { 69 | get: function() { 70 | return this._animations; 71 | }, 72 | set: function(value) { 73 | if (this._animations != value) { 74 | this._animations = value; 75 | } 76 | } 77 | }, 78 | 79 | _sceneTime: { value: 0, writable: true }, 80 | 81 | sceneTime: { 82 | get: function() { 83 | return this._sceneTime; 84 | }, 85 | set: function(value) { 86 | if (this._delegate) { 87 | if (this._delegate.sceneTimeWillChange) { 88 | this._delegate.sceneTimeWillChange(this, value); 89 | } 90 | } 91 | this._sceneTime = value; 92 | if (this._delegate) { 93 | if (this._delegate.sceneTimeDidChange) { 94 | this._delegate.sceneTimeDidChange(this); 95 | } 96 | } 97 | } 98 | }, 99 | 100 | _delegate: { value: 0, writable: true }, 101 | 102 | delegate: { 103 | get: function() { 104 | return this._delegate; 105 | }, 106 | set: function(value) { 107 | this._delegate = value; 108 | } 109 | }, 110 | 111 | targets: { 112 | get: function() { 113 | var targets = []; 114 | if (this._animations != null) { 115 | this._animations.forEach(function(animation) { 116 | if (animation.type == Animation.KEYFRAME) { 117 | animation.channels.forEach(function(channel) { 118 | targets.push(channel.target.id); 119 | }, this); 120 | } else { 121 | if (animation.type == Animation.BASIC) { 122 | } 123 | } 124 | 125 | }, this); 126 | } 127 | return targets; 128 | } 129 | }, 130 | 131 | hasAnimation: { 132 | value: function(targetUID, targets) { 133 | //it is a forEach, because eventually we will return all the animations for a given target. 134 | var animated = false; 135 | if (this._animations == null) 136 | return false; 137 | if (targets == null) 138 | targets = this.targets; 139 | 140 | return targets.indexOf(targetUID) !== -1; 141 | } 142 | }, 143 | 144 | nodeHasAnimatedAncestor: { 145 | value: function(node) { 146 | do { 147 | if (this.hasAnimation(node.id)) { 148 | return true; 149 | } 150 | node = node.parent; 151 | } while (node != null); 152 | return false; 153 | } 154 | }, 155 | 156 | //will be deprecated 157 | updateTargetsAtTime: { 158 | value: function(time, resourceManager) { 159 | if (this.animations) { 160 | this.animations.forEach( function(animation) { 161 | //FIXME: unify this - could just use a method called evaluate 162 | if (animation.type == Animation.KEYFRAME) { 163 | animation.updateTargetsAtTime(time, resourceManager); 164 | } else if (animation.type == Animation.BASIC) { 165 | //animation._evaluateAtTime(time); 166 | } 167 | }, this); 168 | } 169 | } 170 | }, 171 | 172 | evaluateAtTime: { 173 | value: function(time, resourceManager) { 174 | if (this._activeAnimations) { 175 | 176 | this._activeAnimations.forEach( function(animation) { 177 | //FIXME: unify this - could just use a method called evaluate 178 | if (animation.type == Animation.KEYFRAME) { 179 | //animation.updateTargetsAtTime(time, resourceManager); 180 | } else if (animation.type == Animation.BASIC) { 181 | animation._evaluateAtTime(time); 182 | } 183 | }, this); 184 | } 185 | } 186 | }, 187 | 188 | hasActiveAnimations: { 189 | value: function() { 190 | return this._activeAnimations.length > 0; 191 | } 192 | }, 193 | 194 | _activeAnimations: { value: 0 , writable: true }, 195 | 196 | _removeAnimationAtIndex: { 197 | value: function(index) { 198 | var animation = this._activeAnimations[index]; 199 | //animation._evaluateAtTime(Date.now()); 200 | if (animation.delegate) { 201 | animation.delegate.animationDidStop(animation); 202 | } 203 | this._activeAnimations.splice(index, 1); 204 | } 205 | }, 206 | 207 | playAnimation: { 208 | value: function(animation) { 209 | var self = this; 210 | this._activeAnimations.push(animation); 211 | if (animation.delegate) 212 | animation.delegate.animationDidStart(animation); 213 | setTimeout(function() { 214 | //animation may have been removed 215 | var index = self._activeAnimations.indexOf(animation); 216 | if (index !== -1) { 217 | self._removeAnimationAtIndex(index); 218 | } 219 | }, animation.duration); 220 | } 221 | }, 222 | 223 | init: { 224 | value: function() { 225 | this.__Base_init(); 226 | this.animations = []; 227 | this._activeAnimations = []; 228 | return this; 229 | } 230 | }, 231 | 232 | removeAnimationWithTargetAndPath: { 233 | value: function(target, path) { 234 | if (this._activeAnimations) { 235 | for (var i = 0 ; i < this._activeAnimations.length ; i++) { 236 | var animation = this._activeAnimations[i]; 237 | if ((animation.target === target) && (animation.path == path)) { 238 | this._removeAnimationAtIndex(i); 239 | return; 240 | } 241 | } 242 | } 243 | } 244 | } 245 | 246 | }); 247 | -------------------------------------------------------------------------------- /runtime/transform.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Fabrice ROBINET 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | require("runtime/dependencies/gl-matrix"); 25 | var Base = require("runtime/base").Base; 26 | var Utilities = require("runtime/utilities").Utilities; 27 | 28 | //FIXME: add decomposition to be able to add getters in TRS 29 | var Transform = exports.Transform = Object.create(Base, { 30 | _matrix: { value: null, writable: true }, 31 | 32 | _dirty: { value: true, writable: true }, 33 | _dirtyAffines: { value: true, writable: true }, 34 | 35 | _translation: { value: null, writable: true }, 36 | _rotation: { value: null, writable: true }, 37 | _scale: { value: null, writable: true }, 38 | 39 | _id: { value: 0, writable: true }, 40 | 41 | _fireTransformDidUpdate: { 42 | value: function(flag) { 43 | if (this._observers) { 44 | for (var i = 0 ; i < this._observers.length ; i++) { 45 | this._observers[i].transformDidUpdate(this); 46 | } 47 | } 48 | } 49 | }, 50 | 51 | _updateDirtyFlag: { 52 | value: function(flag) { 53 | this._dirty = flag; 54 | this._fireTransformDidUpdate(); 55 | } 56 | }, 57 | 58 | interpolateToTransform: { 59 | value: function(to, step, destination) { 60 | step = Utilities.easeOut(step); 61 | this._rebuildAffinesIfNeeded(); 62 | to._rebuildAffinesIfNeeded(); 63 | 64 | Utilities.interpolateVec(this._translation, to._translation, step, destination._translation); 65 | Utilities.interpolateVec(this._scale, to._scale, step, destination._scale); 66 | Utilities.inverpolateAxisAngle(this._rotation, to._rotation, step, destination._rotation); 67 | //FIXME:breaks encapsulation 68 | destination._updateDirtyFlag(true); 69 | } 70 | }, 71 | 72 | matrix: { 73 | get: function() { 74 | if (this._dirty) { 75 | if (this._matrix == null) { 76 | this._matrix = mat4.create(); 77 | } 78 | 79 | if (this._intermediateMatrices == null) { 80 | this._intermediateMatrices = []; 81 | 82 | this._intermediateMatrices.push(mat4.identity()); //idx: 0 tmp 83 | this._intermediateMatrices.push(mat4.identity()); //idx: 1 tr 84 | this._intermediateMatrices.push(mat4.identity()); //idx: 2 scale 85 | this._intermediateMatrices.push(mat4.identity()); //idx: 3 rotation 86 | } 87 | 88 | mat4.identity(this._matrix); 89 | mat4.identity(this._intermediateMatrices[0]); 90 | 91 | mat4.set(this._intermediateMatrices[0], this._intermediateMatrices[1]); //tr 92 | mat4.set(this._intermediateMatrices[0], this._intermediateMatrices[2]); //scale 93 | mat4.set(this._intermediateMatrices[0], this._intermediateMatrices[3]); //rotation 94 | 95 | mat4.translate(this._intermediateMatrices[1], this._translation); 96 | mat4.scale(this._intermediateMatrices[2], this._scale); 97 | quat4.toMat4(this._rotation, this._intermediateMatrices[3]); 98 | 99 | mat4.multiply(this._matrix, this._intermediateMatrices[1]); 100 | mat4.multiply(this._matrix, this._intermediateMatrices[2]); 101 | mat4.multiply(this._matrix, this._intermediateMatrices[3]); 102 | 103 | //we can be silent about this one (not use this._updateDirtyFlag(false)) 104 | this._dirty = false; 105 | } 106 | 107 | return this._matrix; 108 | }, 109 | set: function(value ) { 110 | if (this._matrix == null) { 111 | this._matrix = mat4.create(); 112 | } 113 | 114 | mat4.set(value, this._matrix); 115 | this._updateDirtyFlag(false); 116 | this._dirtyAffines = true; 117 | } 118 | }, 119 | 120 | _rebuildAffinesIfNeeded: { 121 | value: function() { 122 | if (this._dirtyAffines === true) { 123 | Utilities.decomposeMat4(this.matrix, this._translation, this._rotation, this._scale); 124 | this._dirtyAffines = false; 125 | } 126 | } 127 | }, 128 | 129 | translation : { 130 | set: function(value ) { 131 | this._translation = value; 132 | this._updateDirtyFlag(true); 133 | }, get: function(value) { 134 | this._rebuildAffinesIfNeeded(); 135 | return this._translation; 136 | } 137 | }, 138 | 139 | rotation : { 140 | set: function(value ) { 141 | this._rotation = value; 142 | this._updateDirtyFlag(true); 143 | }, get: function(value) { 144 | this._rebuildAffinesIfNeeded(); 145 | return this._rotation; 146 | } 147 | }, 148 | 149 | scale : { 150 | set: function(value ) { 151 | this._scale = value; 152 | this._updateDirtyFlag(true); 153 | }, get: function(value) { 154 | this._rebuildAffinesIfNeeded(); 155 | return this._scale; 156 | } 157 | }, 158 | 159 | _commonInit: { 160 | value: function() { 161 | this.translation = vec3.createFrom(0,0,0); 162 | this.rotation = vec4.createFrom(0,0,0,0); 163 | this.scale = vec3.createFrom(1,1,1); 164 | this.matrix = mat4.identity(); 165 | this._id = Transform.bumpId(); 166 | } 167 | }, 168 | 169 | initWithDescription: { 170 | value: function(description) { 171 | this._commonInit(); 172 | 173 | if (description.matrix) { 174 | this.matrix = mat4.create(description.matrix); 175 | } else if (description.translation || description.rotation || description.scale) { 176 | this.translation = description.translation ? vec3.create(description.translation) : vec3.createFrom(0,0,0); 177 | var r = description.rotation; 178 | this.rotation = r ? quat4.fromAngleAxis(r[3], vec3.createFrom(r[0],r[1],r[2])) : vec4.createFrom(0,0,0,0); 179 | this.scale = description.scale ? vec3.create(description.scale) : vec3.createFrom(1,1,1); 180 | } else { 181 | this.matrix = mat4.identity(); 182 | } 183 | return this; 184 | } 185 | }, 186 | 187 | init: { 188 | value: function() { 189 | this._commonInit(); 190 | return this; 191 | } 192 | }, 193 | 194 | bumpId: { 195 | value: function() { 196 | Transform._id++; 197 | return Transform._id; 198 | } 199 | }, 200 | 201 | copy: { 202 | value: function() { 203 | var transform = Object.create(Transform).init(); 204 | 205 | if (this._translation) { 206 | transform.translation = vec3.createFrom(this._translation[0], this._translation[1], this._translation[2]); 207 | } 208 | 209 | if (this._scale) { 210 | transform.scale = vec3.createFrom(this._scale[0], this._scale[1], this._scale[2]); 211 | } 212 | 213 | if (this._rotation) { 214 | transform.rotation = quat4.create(this._rotation); 215 | } 216 | 217 | transform.matrix = mat4.create(this.matrix); 218 | return transform; 219 | } 220 | }, 221 | 222 | _observers: { value: null, writable: true}, 223 | 224 | addObserver: { 225 | value: function(observer) { 226 | if (this._observers == null) { 227 | this._observers = []; 228 | } 229 | 230 | if (this._observers.indexOf(observer) === -1) { 231 | this._observers.push(observer); 232 | } else { 233 | console.log("WARNING attempt to add 2 times the same observer in transform") 234 | } 235 | } 236 | }, 237 | 238 | removeObserver: { 239 | value: function(observer) { 240 | if (this._observers) { 241 | var index = this._observers.indexOf(observer); 242 | if (index !== -1) { 243 | this._observers.splice(index, 1); 244 | } 245 | } 246 | } 247 | } 248 | 249 | }); 250 | -------------------------------------------------------------------------------- /runtime/material.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | var Montage = require("montage").Montage; 25 | var Component3D = require("runtime/component-3d").Component3D; 26 | var BasicAnimation = require("runtime/animation").BasicAnimation; 27 | 28 | exports.Material = Component3D.specialize( { 29 | 30 | constructor: { 31 | value: function Material() { 32 | this.super(); 33 | 34 | this.addRangeAtPathChangeListener("filterColor", this, "handleFilterColorChange"); 35 | this.addOwnPropertyChangeListener("glTFElement", this); 36 | this.addOwnPropertyChangeListener("image", this); 37 | this.addOwnPropertyChangeListener("opacity", this); 38 | } 39 | }, 40 | 41 | filterColor: { value: [1,1,1,1]}, 42 | 43 | _originalOpacity: { value: 1, writable: true }, 44 | 45 | handleGlTFElementChange: { 46 | value: function() { 47 | this.handleFilterColorChange(); 48 | this.handleImageChange(); 49 | 50 | this._originalOpacity = this._opacity; 51 | if (this._opacity == null) { 52 | if (this.glTFElement.parameters["transparency"] != null) { 53 | this._originalOpacity = this._opacity = this.glTFElement.parameters["transparency"].value; 54 | } 55 | } else { 56 | this.handleOpacityChange(); 57 | } 58 | } 59 | }, 60 | 61 | initialValueForStyleableProperty: { 62 | value: function(property) { 63 | if (property == "opacity") { 64 | return this._originalOpacity; 65 | } 66 | } 67 | }, 68 | 69 | handleFilterColorChange: { 70 | value: function(plus, minus, index) { 71 | if (this.glTFElement != null) { 72 | if (this.glTFElement.parameters["filterColor"]) { 73 | this.glTFElement.parameters["filterColor"].value = this.filterColor; 74 | if (this.scene) { 75 | this.scene.dispatchEventNamed("materialUpdate", true, false, this); 76 | } 77 | } 78 | } 79 | } 80 | }, 81 | 82 | handleOpacityChange: { 83 | value: function() { 84 | if (this.glTFElement != null) { 85 | if (this.glTFElement.parameters["transparency"]) { 86 | this.glTFElement.parameters["transparency"].value = this._opacity; 87 | if (this.scene) { 88 | this.scene.dispatchEventNamed("materialUpdate", true, false, this); 89 | } 90 | } 91 | } 92 | } 93 | }, 94 | 95 | handleImageChange: { 96 | value: function() { 97 | if (this.glTFElement != null) { 98 | if (this.glTFElement.parameters["diffuse"]) { 99 | if (this._image) { 100 | var imagePath = this.resolvePathIfNeeded(this._image); 101 | var parameterValue = this.parameterForImagePath(imagePath); 102 | this.glTFElement.parameters["diffuse"] = parameterValue; 103 | if (this.scene) { 104 | this.scene.dispatchEventNamed("textureUpdate", true, false, parameterValue); 105 | } 106 | } 107 | } 108 | } 109 | } 110 | }, 111 | 112 | parameterForImagePath: { 113 | value: function(imagePath) { 114 | 115 | var sampler = { 116 | "magFilter": WebGLRenderingContext.LINEAR, 117 | "minFilter": WebGLRenderingContext.LINEAR, 118 | "type": "sampler", 119 | "wrapS" : WebGLRenderingContext.REPEAT, 120 | "wrapT" : WebGLRenderingContext.REPEAT 121 | }; 122 | 123 | var source = { 124 | "id" : "source-"+ imagePath, 125 | "type" : "image", 126 | "baseId" : "source-"+ imagePath, 127 | "description" : { 128 | "path" : imagePath 129 | } 130 | }; 131 | 132 | var parameterValue = { 133 | "baseId": "texture-" + imagePath, 134 | "id": "texture-" + imagePath, 135 | "format": WebGLRenderingContext.RGBA, 136 | "internalFormat" : WebGLRenderingContext.RGBA, 137 | "sampler" : sampler, 138 | "source" : source, 139 | "type" : "texture", 140 | "target" : WebGLRenderingContext.TEXTURE_2D 141 | }; 142 | 143 | var parameter = { 144 | "parameter": "diffuse", 145 | "value" : parameterValue 146 | }; 147 | 148 | return parameter; 149 | } 150 | }, 151 | 152 | _image: { value: null , writable:true }, 153 | 154 | image: { 155 | set: function(value) { 156 | if (value) { 157 | //FIXME: remove this when we initialized property image with the path in place when the glTFElement comes up 158 | if (value.length == 0) { 159 | return; 160 | } 161 | } else { 162 | return; 163 | } 164 | 165 | var lowerCaseImage = value.toLowerCase(); 166 | if ((lowerCaseImage.indexOf(".jpg") != -1) || (lowerCaseImage.indexOf(".jpeg") != -1) || (lowerCaseImage.indexOf(".png") != -1)) { 167 | if (this._image != value) { 168 | this._image = value; 169 | } 170 | } 171 | }, 172 | get: function() { 173 | return this._image; 174 | } 175 | }, 176 | 177 | _opacity: { value: null, writable:true }, 178 | 179 | animationDidStart: { 180 | value: function(animation) { 181 | } 182 | }, 183 | 184 | animationDidStop: { 185 | value: function(animation) { 186 | } 187 | }, 188 | 189 | animationDidUpdate: { 190 | value: function(animation) { 191 | } 192 | }, 193 | 194 | opacity_animationSetter: { 195 | set: function(value) { 196 | if (this._opacity != value) { 197 | this._opacity = value; 198 | this.handleOpacityChange(); 199 | } 200 | } 201 | }, 202 | 203 | opacity: { 204 | set: function(value) { 205 | if (this._opacity != value) { 206 | //remove animation if any 207 | if (this.glTFElement) { 208 | var animationManager = this.scene.glTFElement.animationManager; 209 | animationManager.removeAnimationWithTargetAndPath(this, "opacity_animationSetter"); 210 | if (this._style) { 211 | if (this._style.transitions) { 212 | var transition = this._style.transitions["opacity"]; 213 | if (transition != null) { 214 | if (transition.duration > 0) { 215 | var opacityAnimation = Object.create(BasicAnimation).init(); 216 | opacityAnimation.path = "opacity_animationSetter"; 217 | opacityAnimation.target = this; 218 | opacityAnimation.delegate = this; 219 | opacityAnimation.from = Number(this._opacity); 220 | opacityAnimation.to = Number(value); 221 | opacityAnimation.duration = transition.duration * 1000; 222 | animationManager.playAnimation(opacityAnimation); 223 | opacityAnimation.animationWasAddedToTarget(); 224 | animationManager.evaluateAtTime(Date.now()); 225 | return; 226 | } 227 | } 228 | } 229 | } 230 | } 231 | 232 | this._opacity = value; 233 | } 234 | }, 235 | get: function() { 236 | return this._opacity; 237 | } 238 | }, 239 | 240 | _stylableProperties: { value: ["opacity"]}, 241 | 242 | styleableProperties: { 243 | get: function() { 244 | return this._stylableProperties; 245 | } 246 | } 247 | 248 | }); 249 | -------------------------------------------------------------------------------- /runtime/node.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | var Montage = require("montage").Montage; 25 | var Component3D = require("runtime/component-3d").Component3D; 26 | var Transform = require("runtime/transform").Transform; 27 | var BasicAnimation = require("runtime/animation").BasicAnimation; 28 | require("runtime/dependencies/gl-matrix"); 29 | 30 | exports.Node = Component3D.specialize( { 31 | 32 | constructor: { 33 | value: function Node() { 34 | this.super(); 35 | //FIXME: these guys are not removed 36 | this._hidden = false; 37 | this._visibility = "visible"; 38 | this._offsetMatrix = mat4.identity(); 39 | 40 | this.addOwnPropertyChangeListener("hidden", this); 41 | this.addOwnPropertyChangeListener("visibility", this); 42 | this.addOwnPropertyChangeListener("offsetMatrix", this); 43 | this.addOwnPropertyChangeListener("originVector", this); 44 | this.addOwnPropertyChangeListener("glTFElement", this); 45 | } 46 | }, 47 | 48 | animationDidStart: { 49 | value: function(animation) { 50 | } 51 | }, 52 | 53 | animationDidStop: { 54 | value: function(animation) { 55 | } 56 | }, 57 | 58 | animationDidUpdate: { 59 | value: function(animation) { 60 | } 61 | }, 62 | 63 | handleGlTFElementChange: { 64 | value: function() { 65 | this.handleHiddenChange(); 66 | this.handleVisibilityChange(); 67 | this.handleOffsetMatrixChange(); 68 | this.handleOriginVectorChange(); 69 | 70 | this._applyCSSPropertyWithValueForState(this.__STYLE_DEFAULT__, "offsetMatrix", this._offsetMatrix); 71 | 72 | } 73 | }, 74 | 75 | handleHiddenChange: { 76 | value: function() { 77 | if (this.glTFElement != null) { 78 | this.glTFElement.hidden = this._hidden; 79 | //FIXME: user a more appropriate name for this, it will just trigger a redraw 80 | this.scene.dispatchEventNamed("materialUpdate", true, false, this); 81 | } 82 | } 83 | }, 84 | 85 | handleVisibilityChange: { 86 | value: function() { 87 | if (this.glTFElement != null) { 88 | this.glTFElement.hidden = this.visibility ? this.visibility === "hidden" : false; 89 | //FIXME: user a more appropriate name for this, it will just trigger a redraw 90 | this.scene.dispatchEventNamed("materialUpdate", true, false, this); 91 | } 92 | } 93 | }, 94 | 95 | offsetMatrix_animationSetter: { 96 | set: function(value) { 97 | this._offsetMatrix = value.matrix; 98 | this.handleOffsetMatrixChange(); 99 | } 100 | }, 101 | 102 | handleOffsetMatrixChange: { 103 | value: function() { 104 | if (this.glTFElement != null) { 105 | //access a private property. not sure yet which name would be the most appropriate yet 106 | this.glTFElement._offsetMatrix = this._offsetMatrix; 107 | //FIXME: user a more appropriate name for this, it will just trigger a redraw 108 | this.scene.dispatchEventNamed("materialUpdate", true, false, this); 109 | } 110 | } 111 | }, 112 | 113 | handleOriginVectorChange: { 114 | value: function() { 115 | if (this.glTFElement != null) { 116 | this.glTFElement._originVector = this._originVector; 117 | //FIXME: user a more appropriate name for this, it will just trigger a redraw 118 | this.scene.dispatchEventNamed("materialUpdate", true, false, this); 119 | } 120 | } 121 | }, 122 | 123 | _hidden: { value: false, writable:true }, 124 | 125 | //deprecated - just kept for the den-demo 126 | hidden: { 127 | set: function(value) { 128 | if (this._hidden != value) { 129 | this._hidden = value; 130 | } 131 | }, 132 | get: function() { 133 | return this._hidden; 134 | } 135 | }, 136 | 137 | _visibility: { value: false, writable:true }, 138 | 139 | visibility: { 140 | set: function(value) { 141 | this._visibility = value; 142 | }, 143 | get: function() { 144 | return this._visibility; 145 | } 146 | }, 147 | 148 | _transform: { value: null, writable:true }, 149 | 150 | transform: { 151 | set: function(value) { 152 | this._transform = value; 153 | }, 154 | get: function() { 155 | return this._transform; 156 | } 157 | }, 158 | 159 | _offsetMatrix: { value: null, writable:true }, 160 | 161 | offsetMatrix: { 162 | set: function(value) { 163 | if (this.glTFElement) { 164 | var animationManager = this.scene.glTFElement.animationManager; 165 | animationManager.removeAnimationWithTargetAndPath(this, "offsetMatrix_animationSetter"); 166 | if (this._style) { 167 | if (this._style.transitions) { 168 | var transition = this._style.transitions["offsetMatrix"]; 169 | if (transition != null) { 170 | if (transition.duration > 0) { 171 | var fromTr = Object.create(Transform).init(); 172 | var toTr = Object.create(Transform).init(); 173 | fromTr.matrix = this._offsetMatrix; 174 | toTr.matrix = value; 175 | var transformAnimation = Object.create(BasicAnimation).init(); 176 | transformAnimation.path = "offsetMatrix_animationSetter"; 177 | transformAnimation.target = this; 178 | transformAnimation.delegate = this; 179 | transformAnimation.from = fromTr; 180 | transformAnimation.to = toTr; 181 | transformAnimation.duration = transition.duration * 1000; 182 | animationManager.playAnimation(transformAnimation); 183 | transformAnimation.animationWasAddedToTarget(); 184 | animationManager.evaluateAtTime(Date.now()); 185 | return; 186 | } 187 | } 188 | } 189 | } 190 | } 191 | 192 | this._offsetMatrix = value; 193 | }, 194 | get: function() { 195 | return this._offsetMatrix; 196 | } 197 | }, 198 | 199 | _originVector: { value: null, writable:true }, 200 | 201 | originVector: { 202 | set: function(value) { 203 | this._originVector = value; 204 | }, 205 | get: function() { 206 | return this._originVector; 207 | } 208 | }, 209 | 210 | _observers: { value: null, writable: true}, 211 | 212 | addObserver: { 213 | value: function(observer) { 214 | if (this._observers == null) { 215 | this._observers = []; 216 | } 217 | 218 | if (this._observers.indexOf(observer) === -1) { 219 | this._observers.push(observer); 220 | } else { 221 | console.log("WARNING attempt to add 2 times the same observer in glTFNode") 222 | } 223 | } 224 | }, 225 | 226 | removeObserver: { 227 | value: function(observer) { 228 | if (this._observers) { 229 | var index = this._observers.indexOf(observer); 230 | if (index !== -1) { 231 | this._observers.splice(index, 1); 232 | } 233 | } 234 | } 235 | }, 236 | 237 | _stylableProperties: { value: ["visibility", "offsetMatrix", "originVector"]}, 238 | 239 | styleableProperties: { 240 | get: function() { 241 | return this._stylableProperties; 242 | } 243 | }, 244 | 245 | initialValueForStyleableProperty: { 246 | value: function(property) { 247 | if (property === "visibility") { 248 | return "visible"; 249 | } else if (property === "offsetMatrix") { 250 | return mat4.identity(); 251 | } else if (property === "originVector") { 252 | return vec3.createFrom(50, 50, 50); 253 | } 254 | } 255 | } 256 | 257 | }); 258 | -------------------------------------------------------------------------------- /runtime/glTF-parser.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Fabrice Robinet 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | /* 25 | The Abstract Loader has two modes: 26 | #1: [static] load all the JSON at once [as of now] 27 | #2: [stream] stream and parse JSON progressively [not yet supported] 28 | 29 | Whatever is the mechanism used to parse the JSON (#1 or #2), 30 | The loader starts by resolving the paths to binaries and referenced json files (by replace the value of the path property with an absolute path if it was relative). 31 | 32 | In case #1: it is guaranteed to call the concrete loader implementation methods in a order that solves the dependencies between the entries. 33 | only the nodes requires an extra pass to set up the hirerarchy. 34 | In case #2: the concrete implementation will have to solve the dependencies. no order is guaranteed. 35 | 36 | When case #1 is used the followed dependency order is: 37 | 38 | scenes -> nodes -> meshes -> materials -> techniques -> shaders 39 | -> buffers 40 | -> cameras 41 | -> lights 42 | 43 | The readers starts with the leafs, i.e: 44 | shaders, techniques, materials, meshes, buffers, cameras, lights, nodes, scenes 45 | 46 | For each called handle method called the client should return true if the next handle can be call right after returning, 47 | or false if a callback on client side will notify the loader that the next handle method can be called. 48 | 49 | */ 50 | var global = window; 51 | (function (root, factory) { 52 | if (typeof exports === 'object') { 53 | // Node. Does not work with strict CommonJS, but 54 | // only CommonJS-like enviroments that support module.exports, 55 | // like Node. 56 | factory(module.exports); 57 | } else if (typeof define === 'function' && define.amd) { 58 | // AMD. Register as an anonymous module. 59 | define([], function () { 60 | return factory(root); 61 | }); 62 | } else { 63 | // Browser globals 64 | factory(root); 65 | } 66 | }(this, function (root) { 67 | "use strict"; 68 | 69 | var categoriesDepsOrder = ["buffers", "bufferViews", "images", "videos", "samplers", "textures", "shaders", "programs", "techniques", "materials", "accessors", "meshes", "cameras", "lights", "skins", "nodes", "scenes", "animations"]; 70 | 71 | var glTFParser = Object.create(Object.prototype, { 72 | 73 | _rootDescription: { value: null, writable: true }, 74 | 75 | rootDescription: { 76 | set: function(value) { 77 | this._rootDescription = value; 78 | }, 79 | get: function() { 80 | return this._rootDescription; 81 | } 82 | }, 83 | 84 | baseURL: { value: null, writable: true }, 85 | 86 | //detect absolute path following the same protocol than window.location 87 | _isAbsolutePath: { 88 | value: function(path) { 89 | var isAbsolutePathRegExp = new RegExp("^"+window.location.protocol, "i"); 90 | 91 | return path.match(isAbsolutePathRegExp) ? true : false; 92 | } 93 | }, 94 | 95 | resolvePathIfNeeded: { 96 | value: function(path) { 97 | if (this._isAbsolutePath(path)) { 98 | return path; 99 | } 100 | 101 | return this.baseURL + path; 102 | } 103 | }, 104 | 105 | _resolvePathsForCategories: { 106 | value: function(categories) { 107 | categories.forEach( function(category) { 108 | var descriptions = this.json[category]; 109 | if (descriptions) { 110 | var descriptionKeys = Object.keys(descriptions); 111 | descriptionKeys.forEach( function(descriptionKey) { 112 | var description = descriptions[descriptionKey]; 113 | description.path = this.resolvePathIfNeeded(description.path); 114 | }, this); 115 | } 116 | }, this); 117 | } 118 | }, 119 | 120 | _json: { 121 | value: null, 122 | writable: true 123 | }, 124 | 125 | json: { 126 | enumerable: true, 127 | get: function() { 128 | return this._json; 129 | }, 130 | set: function(value) { 131 | if (this._json !== value) { 132 | this._json = value; 133 | this._resolvePathsForCategories(["buffers", "shaders", "images", "videos"]); 134 | } 135 | } 136 | }, 137 | 138 | _path: { 139 | value: null, 140 | writable: true 141 | }, 142 | 143 | getEntryDescription: { 144 | value: function (entryID, entryType) { 145 | var entries = null; 146 | 147 | var category = entryType; 148 | entries = this.rootDescription[category]; 149 | if (!entries) { 150 | console.log("ERROR:CANNOT find expected category named:"+category); 151 | return null; 152 | } 153 | 154 | return entries ? entries[entryID] : null; 155 | } 156 | }, 157 | 158 | _stepToNextCategory: { 159 | value: function() { 160 | this._state.categoryIndex = this.getNextCategoryIndex(this._state.categoryIndex + 1); 161 | if (this._state.categoryIndex !== -1) { 162 | this._state.categoryState.index = 0; 163 | return true; 164 | } 165 | 166 | return false; 167 | } 168 | }, 169 | 170 | _stepToNextDescription: { 171 | enumerable: false, 172 | value: function() { 173 | var categoryState = this._state.categoryState; 174 | var keys = categoryState.keys; 175 | if (!keys) { 176 | console.log("INCONSISTENCY ERROR"); 177 | return false; 178 | } 179 | 180 | categoryState.index++; 181 | categoryState.keys = null; 182 | if (categoryState.index >= keys.length) { 183 | return this._stepToNextCategory(); 184 | } 185 | return false; 186 | } 187 | }, 188 | 189 | hasCategory: { 190 | value: function(category) { 191 | return this.rootDescription[category] ? true : false; 192 | } 193 | }, 194 | 195 | _handleState: { 196 | value: function() { 197 | 198 | var methodForType = { 199 | "buffers" : this.handleBuffer, 200 | "bufferViews" : this.handleBufferView, 201 | "shaders" : this.handleShader, 202 | "programs" : this.handleProgram, 203 | "techniques" : this.handleTechnique, 204 | "materials" : this.handleMaterial, 205 | "meshes" : this.handleMesh, 206 | "cameras" : this.handleCamera, 207 | "lights" : this.handleLight, 208 | "nodes" : this.handleNode, 209 | "scenes" : this.handleScene, 210 | "images" : this.handleImage, 211 | "animations" : this.handleAnimation, 212 | "accessors" : this.handleAccessor, 213 | "skins" : this.handleSkin, 214 | "samplers" : this.handleSampler, 215 | "textures" : this.handleTexture, 216 | "videos" : this.handleVideo 217 | 218 | }; 219 | 220 | var success = true; 221 | while (this._state.categoryIndex !== -1) { 222 | var category = categoriesDepsOrder[this._state.categoryIndex]; 223 | var categoryState = this._state.categoryState; 224 | var keys = categoryState.keys; 225 | if (!keys) { 226 | categoryState.keys = keys = Object.keys(this.rootDescription[category]); 227 | if (keys) { 228 | if (keys.length == 0) { 229 | this._stepToNextDescription(); 230 | continue; 231 | } 232 | } 233 | } 234 | 235 | var type = category; 236 | var entryID = keys[categoryState.index]; 237 | var description = this.getEntryDescription(entryID, type); 238 | if (!description) { 239 | if (this.handleError) { 240 | this.handleError("INCONSISTENCY ERROR: no description found for entry "+entryID); 241 | success = false; 242 | break; 243 | } 244 | } else { 245 | 246 | if (methodForType[type]) { 247 | if (methodForType[type].call(this, entryID, description, this._state.userInfo) === false) { 248 | success = false; 249 | break; 250 | } 251 | } 252 | 253 | this._stepToNextDescription(); 254 | } 255 | } 256 | 257 | if (this.handleLoadCompleted) { 258 | this.handleLoadCompleted(success); 259 | } 260 | 261 | } 262 | }, 263 | 264 | _loadJSONIfNeeded: { 265 | enumerable: true, 266 | value: function(callback) { 267 | var self = this; 268 | //FIXME: handle error 269 | if (!this._json) { 270 | var jsonPath = this._path; 271 | var i = jsonPath.lastIndexOf("/"); 272 | this.baseURL = (i !== 0) ? jsonPath.substring(0, i + 1) : ''; 273 | var jsonfile = new XMLHttpRequest(); 274 | jsonfile.open("GET", jsonPath, true); 275 | jsonfile.onreadystatechange = function() { 276 | if (jsonfile.readyState == 4) { 277 | if (jsonfile.status == 200) { 278 | self.json = JSON.parse(jsonfile.responseText); 279 | if (callback) { 280 | callback(self.json); 281 | } 282 | } 283 | } 284 | }; 285 | jsonfile.send(null); 286 | } else { 287 | if (callback) { 288 | callback(this.json); 289 | } 290 | } 291 | } 292 | }, 293 | 294 | /* load JSON and assign it as description to the reader */ 295 | _buildLoader: { 296 | value: function(callback) { 297 | var self = this; 298 | function JSONReady(json) { 299 | self.rootDescription = json; 300 | if (callback) 301 | callback(this); 302 | } 303 | 304 | this._loadJSONIfNeeded(JSONReady); 305 | } 306 | }, 307 | 308 | _state: { value: null, writable: true }, 309 | 310 | _getEntryType: { 311 | value: function(entryID) { 312 | var rootKeys = categoriesDepsOrder; 313 | for (var i = 0 ; i < rootKeys.length ; i++) { 314 | var rootValues = this.rootDescription[rootKeys[i]]; 315 | if (rootValues) { 316 | return rootKeys[i]; 317 | } 318 | } 319 | return null; 320 | } 321 | }, 322 | 323 | getNextCategoryIndex: { 324 | value: function(currentIndex) { 325 | for (var i = currentIndex ; i < categoriesDepsOrder.length ; i++) { 326 | if (this.hasCategory(categoriesDepsOrder[i])) { 327 | return i; 328 | } 329 | } 330 | 331 | return -1; 332 | } 333 | }, 334 | 335 | load: { 336 | enumerable: true, 337 | value: function(userInfo, options) { 338 | var self = this; 339 | this._buildLoader(function loaderReady(reader) { 340 | var startCategory = self.getNextCategoryIndex.call(self,0); 341 | if (startCategory !== -1) { 342 | self._state = { "userInfo" : userInfo, 343 | "options" : options, 344 | "categoryIndex" : startCategory, 345 | "categoryState" : { "index" : "0" } }; 346 | self._handleState(); 347 | } 348 | }); 349 | } 350 | }, 351 | 352 | initWithPath: { 353 | value: function(path) { 354 | this._path = path; 355 | this._json = null; 356 | return this; 357 | } 358 | }, 359 | 360 | //this is meant to be global and common for all instances 361 | _knownURLs: { writable: true, value: {} }, 362 | 363 | //to be invoked by subclass, so that ids can be ensured to not overlap 364 | loaderContext: { 365 | value: function() { 366 | if (typeof this._knownURLs[this._path] === "undefined") { 367 | this._knownURLs[this._path] = Object.keys(this._knownURLs).length; 368 | } 369 | return "__" + this._knownURLs[this._path]; 370 | } 371 | }, 372 | 373 | initWithJSON: { 374 | value: function(json, baseURL) { 375 | this.json = json; 376 | this.baseURL = baseURL; 377 | if (!baseURL) { 378 | console.log("WARNING: no base URL passed to Reader:initWithJSON"); 379 | } 380 | return this; 381 | } 382 | } 383 | 384 | }); 385 | 386 | if(root) { 387 | root.glTFParser = glTFParser; 388 | } 389 | 390 | return glTFParser; 391 | 392 | })); 393 | -------------------------------------------------------------------------------- /runtime/scene-renderer.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Motorola Mobility, Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the Motorola Mobility, Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | require("runtime/dependencies/gl-matrix"); 27 | var Technique = require("runtime/technique").Technique; 28 | var ScenePass = require("runtime/pass").ScenePass; 29 | var BuiltInAssets = require("runtime/builtin-assets").BuiltInAssets; 30 | var o3dgc = require("runtime/dependencies/o3dgc"); 31 | exports.SceneRenderer = Object.create(Object.prototype, { 32 | 33 | loadPickingTechnique: { 34 | value: function() { 35 | var self = this; 36 | var techniquePromise = BuiltInAssets.assetWithName( "pickingTechnique"); 37 | 38 | techniquePromise.then(function (asset) { 39 | self.technique.rootPass.scenePassRenderer.pickingTechnique = asset; 40 | }, function (error) { 41 | }, function (progress) { 42 | }); 43 | } 44 | }, 45 | 46 | createTechniqueIfNeeded: { 47 | value: function() { 48 | if (!this._technique) { 49 | this._technique = Object.create(Technique).init(); 50 | var pass = Object.create(ScenePass).init(); 51 | //there is just one pass, so passName will be automatically set to "defaultPass" 52 | this._technique.passes = { "defaultPass": pass }; 53 | } 54 | } 55 | }, 56 | 57 | _webGLRenderer: { value: null, writable: true }, 58 | 59 | _technique: { value: null, writable: true }, 60 | 61 | technique: { 62 | get: function() { 63 | return this._technique; 64 | }, 65 | set: function(value) { 66 | this._technique = value; 67 | } 68 | }, 69 | 70 | 71 | compressedMeshDelegate: { 72 | value: { 73 | str2ab: function(str) { 74 | var buf = new ArrayBuffer(str.length); 75 | var bufView = new Uint8Array(buf); 76 | for (var i=0, strLen=str.length; i 0) { 107 | ifs.SetCoordIndex(new Uint16Array(bufferIndices, 0, 3 * ifs.GetNCoordIndex())); 108 | } 109 | if (ifs.GetNCoord() > 0) { 110 | ifs.SetCoord(new Float32Array(buffer, shift, 3 * ifs.GetNCoord())); 111 | shift += 12 * ifs.GetNCoord(); 112 | } 113 | if (ifs.GetNNormal() > 0) { 114 | ifs.SetNormal(new Float32Array(buffer, shift, 3 * ifs.GetNNormal())); 115 | shift += 12 * ifs.GetNNormal(); 116 | } 117 | 118 | var numNumFloatAttributes = ifs.GetNumFloatAttributes(); 119 | for (var a = 0; a < numNumFloatAttributes; ++a){ 120 | if (ifs.GetNFloatAttribute(a) > 0) { 121 | ifs.SetFloatAttribute(a, new Float32Array(ifs.GetFloatAttributeDim(a) * ifs.GetNFloatAttribute(a))); 122 | } 123 | } 124 | /* 125 | console.log("Mesh info "); 126 | console.log("\t# coords " + ifs.GetNCoord()); 127 | console.log("\t# normals " + ifs.GetNNormal()); 128 | console.log("\t# texcoords " + ifs.GetNTexCoord()); 129 | console.log("\t# triangles " + ifs.GetNCoordIndex()); 130 | */ 131 | // decode mesh 132 | timer.Tic(); 133 | decoder.DecodePlayload(ifs, bstream); 134 | timer.Toc(); 135 | /* 136 | var size = arrayBuffer.byteLength; 137 | console.log("DecodePlayload time " + timer.GetElapsedTime() + " ms, " + size + " bytes (" + (8.0 * size / ifs.GetNCoord()) + " bpv)"); 138 | console.log("Details"); 139 | var stats = decoder.GetStats(); 140 | console.log("\t CoordIndex " + stats.m_timeCoordIndex + " ms, " + stats.m_streamSizeCoordIndex + " bytes (" + (8.0 * stats.m_streamSizeCoordIndex / ifs.GetNCoord()) + " bpv)"); 141 | console.log("\t Coord " + stats.m_timeCoord + " ms, " + stats.m_streamSizeCoord + " bytes (" + (8.0 * stats.m_streamSizeCoord / ifs.GetNCoord()) + " bpv)"); 142 | console.log("\t Normal " + stats.m_timeNormal + " ms, " + stats.m_streamSizeNormal + " bytes (" + (8.0 * stats.m_streamSizeNormal / ifs.GetNCoord()) + " bpv)"); 143 | console.log("\t TexCoord " + stats.m_timeTexCoord + " ms, " + stats.m_streamSizeTexCoord + " bytes (" + (8.0 * stats.m_streamSizeTexCoord / ifs.GetNCoord()) + " bpv)"); 144 | console.log("\t Color " + stats.m_timeColor + " ms, " + stats.m_streamSizeColor + " bytes (" + (8.0 * stats.m_streamSizeColor / ifs.GetNCoord()) + " bpv)"); 145 | console.log("\t Float Attributes " + stats.m_timeFloatAttribute + " ms, " + stats.m_streamSizeFloatAttribute + " bytes (" + (8.0 * stats.m_streamSizeFloatAttribute / ifs.GetNCoord()) + " bpv)"); 146 | console.log("\t Integer Attributes " + stats.m_timeFloatAttribute + " ms, " + stats.m_streamSizeFloatAttribute + " bytes (" + (8.0 * stats.m_streamSizeFloatAttribute / ifs.GetNCoord()) + " bpv)"); 147 | console.log("\t Reorder " + stats.m_timeReorder + " ms, " + 0 + " bytes (" + 0.0 + " bpv)"); 148 | //SaveOBJ(ifs, fileName); 149 | */ 150 | return ifs; 151 | } 152 | }, 153 | 154 | 155 | handleError: function(errorCode, info) { 156 | console.log("ERROR:vertexAttributeBufferDelegate:"+errorCode+" :"+info); 157 | }, 158 | 159 | decompressAttribsInner_: function(str, inputStart, inputEnd, 160 | output, outputStart, stride, 161 | decodeOffset, decodeScale) { 162 | var prev = 0; 163 | for (var j = inputStart; j < inputEnd; j++) { 164 | var code = str.charCodeAt(j); 165 | prev += (code >> 1) ^ (-(code & 1)); 166 | output[outputStart] = decodeScale * (prev + decodeOffset); 167 | outputStart += stride; 168 | } 169 | }, 170 | 171 | decompressIndices_: function(str, inputStart, numIndices, 172 | output, outputStart) { 173 | var highest = 0; 174 | for (var i = 0; i < numIndices; i++) { 175 | var code = str.charCodeAt(inputStart++); 176 | output[outputStart++] = highest - code; 177 | if (code == 0) { 178 | highest++; 179 | } 180 | } 181 | }, 182 | 183 | decompressMesh: function(str, meshParams, decodeParams, callback) { 184 | // Extract conversion parameters from attribArrays. 185 | var stride = decodeParams.decodeScales.length; 186 | var decodeOffsets = decodeParams.decodeOffsets; 187 | var decodeScales = decodeParams.decodeScales; 188 | var attribStart = meshParams.attribRange[0]; 189 | var numVerts = meshParams.attribRange[1]; 190 | 191 | // Decode attributes. 192 | var inputOffset = attribStart; 193 | var attribsOut = new Float32Array(stride * numVerts); 194 | for (var j = 0; j < stride; j++) { 195 | var end = inputOffset + numVerts; 196 | var decodeScale = decodeScales[j]; 197 | if (decodeScale) { 198 | // Assume if decodeScale is never set, simply ignore the 199 | // attribute. 200 | this.decompressAttribsInner_(str, inputOffset, end, 201 | attribsOut, j, stride, 202 | decodeOffsets[j], decodeScale); 203 | } 204 | inputOffset = end; 205 | } 206 | 207 | var indexStart = meshParams.indexRange[0]; 208 | var numIndices = 3*meshParams.indexRange[1]; 209 | var indicesOut = new Uint16Array(numIndices); 210 | this.decompressIndices_(str, inputOffset, numIndices, indicesOut, 0); 211 | 212 | // Decode bboxen. 213 | /* 214 | var bboxen = undefined; 215 | var bboxOffset = meshParams.bboxes; 216 | if (bboxOffset) { 217 | bboxen = decompressAABBs_(str, bboxOffset, meshParams.names.length, 218 | decodeOffsets, decodeScales); 219 | } 220 | */ 221 | callback(attribsOut, indicesOut, null, meshParams); 222 | }, 223 | 224 | 225 | convert: function (source, resource, ctx) { 226 | var compression = ctx.mesh.compression; 227 | if (compression.type == "won-compression") { 228 | var indexRange = compression.indexRange; 229 | if (indexRange) { 230 | var meshEnd = indexRange[0] + 3 * indexRange[1]; 231 | var callback = null; 232 | this.decompressMesh(resource, compression, compression, 233 | function(attribsOut, indicesOut, bboxen, meshParams) { 234 | ctx.renderer.setupCompressedMesh(ctx.mesh, attribsOut, indicesOut); 235 | }); 236 | } 237 | } else { 238 | var vertexCount = 0; 239 | var mesh = ctx.mesh; 240 | if (compression.compressedData) { 241 | var compressedData = compression.compressedData; 242 | vertexCount = compressedData.verticesCount; 243 | var ifs = this.decode(resource, compressedData.mode === "ascii"); 244 | var indicesShort = ifs.GetCoordIndex(); 245 | var positions = ifs.GetCoord(); 246 | var normals = ifs.GetNNormal() > 0 ? ifs.GetNormal() : null; 247 | ctx.renderer.setupCompressedMesh2(ctx.mesh, vertexCount, positions, normals, ifs, compressedData.floatAttributesIndexes, indicesShort); 248 | } 249 | } 250 | 251 | return resource; 252 | }, 253 | 254 | resourceAvailable: function (glResource, ctx) { 255 | } 256 | } 257 | }, 258 | 259 | scene: { 260 | get: function() { 261 | return this.technique.rootPass.scene; 262 | }, 263 | set: function(value) { 264 | var self = this; 265 | var scene = this.technique.rootPass.scene; 266 | if (scene != value) { 267 | this.technique.rootPass.scene = value; 268 | 269 | this.scene.rootNode.apply( function(node, parent, context) { 270 | if (node.meshes) { 271 | node.meshes.forEach(function (mesh) { 272 | if (mesh.compression) { 273 | var requestType = "text"; 274 | if (mesh.compression.compressedData.mode) { 275 | if (mesh.compression.compressedData.mode == "binary") { 276 | requestType = "arraybuffer"; 277 | } 278 | } 279 | 280 | mesh.compression.compressedData.requestType = requestType; 281 | 282 | self.webGLRenderer.resourceManager.getResource( 283 | mesh.compression.compressedData, 284 | self.compressedMeshDelegate, 285 | { "mesh" : mesh, "renderer" : self.webGLRenderer}); 286 | } 287 | }, this); 288 | } 289 | } , true, null); 290 | 291 | } 292 | } 293 | }, 294 | 295 | webGLRenderer: { 296 | get: function() { 297 | return this._webGLRenderer; 298 | }, 299 | set: function(value) { 300 | this._webGLRenderer = value; 301 | } 302 | }, 303 | 304 | init: { 305 | value: function( webGLRenderer, options) { 306 | this.webGLRenderer = webGLRenderer; 307 | this.createTechniqueIfNeeded(); 308 | this.loadPickingTechnique(); 309 | return this; 310 | } 311 | }, 312 | 313 | render: { 314 | value: function(time, options) { 315 | if (this.technique) 316 | this.technique.execute(this.webGLRenderer, time, options); 317 | } 318 | } 319 | 320 | }); 321 | 322 | -------------------------------------------------------------------------------- /runtime/glTF-node.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Fabrice Robinet 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | require("runtime/dependencies/gl-matrix"); 25 | var Base = require("runtime/base").Base; 26 | var Transform = require("runtime/transform").Transform; 27 | var Utilities = require("runtime/utilities").Utilities; 28 | 29 | var glTFNode = exports.glTFNode = Object.create(Base, { 30 | 31 | //just for glTFNode singleton 32 | currentId: { value: 0, writable:true }, 33 | 34 | bumpId: { 35 | value: function() { 36 | return glTFNode._id++; 37 | } 38 | }, 39 | 40 | _children: { value: null, writable: true }, 41 | 42 | children: { 43 | get: function() { 44 | return this._children; 45 | }, 46 | set: function(value) { 47 | this._children = value; 48 | } 49 | }, 50 | 51 | _id: { value: null, writable: true }, 52 | 53 | id: { 54 | get: function() { 55 | return this._id; 56 | }, 57 | set: function(value) { 58 | this._id = value; 59 | } 60 | }, 61 | 62 | _hidden: { value: null, writable: true }, 63 | 64 | hidden: { 65 | get: function() { 66 | return this._hidden; 67 | }, 68 | set: function(value) { 69 | this._hidden = value; 70 | } 71 | }, 72 | 73 | _computeBBOXIfNeeded: { 74 | enumerable: false, 75 | value: function() { 76 | if (!this._boundingBox) { 77 | var meshes = this._properties["meshes"]; 78 | var count = this.meshes.length; 79 | if (count > 0) { 80 | var bbox = this.meshes[0].boundingBox; 81 | if (bbox) { 82 | var i; 83 | for (i = 1 ; i < count ; i++) { 84 | var aBBox = this.meshes[i].boundingBox; 85 | if (aBBox) { //it could be not here here as we are loading everything asynchronously 86 | bbox = Utilities.mergeBBox(bbox, aBBox); 87 | } 88 | } 89 | this._boundingBox = bbox;//Utilities.transformBBox(bbox, this.transform); 90 | } 91 | } 92 | } 93 | } 94 | }, 95 | 96 | getBoundingBox: { 97 | value: function(includesHierarchy) { 98 | //FIXME: this code is inefficient, BBOX be cached with dirty flags and invalidation (just like for worldMatrix) 99 | if (includesHierarchy) { 100 | var ctx = mat4.identity(); 101 | var hierarchicalBBOX = this.boundingBox; 102 | this.apply( function(node, parent, parentTransform) { 103 | var modelMatrix = mat4.create(); 104 | mat4.multiply( parentTransform, node.transform.matrix, modelMatrix); 105 | if (node.boundingBox) { 106 | var bbox = Utilities.transformBBox(node.boundingBox, modelMatrix); 107 | 108 | if (hierarchicalBBOX) { 109 | if (node.meshes) { 110 | if (node.meshes.length > 0) 111 | hierarchicalBBOX = Utilities.mergeBBox(bbox, hierarchicalBBOX); 112 | } 113 | } else { 114 | hierarchicalBBOX = bbox; 115 | } 116 | } 117 | return modelMatrix; 118 | }, true, ctx); 119 | return hierarchicalBBOX; 120 | } else { 121 | return this.boundingBox; 122 | } 123 | } 124 | }, 125 | 126 | _boundingBox: { 127 | enumerable: false, 128 | value: null, 129 | writable: true 130 | }, 131 | 132 | boundingBox: { 133 | enumerable: true, 134 | get: function() { 135 | this._computeBBOXIfNeeded(); 136 | return this._boundingBox; 137 | }, 138 | // we let the possibility to override by hand the bounding volume. 139 | set: function(value) { 140 | this._boundingBox = value; 141 | } 142 | }, 143 | 144 | meshesDidChange: { 145 | value: function(meshes) { 146 | this._boundingBox = null; //invalidate bounding box 147 | } 148 | }, 149 | 150 | nodesDidChange: { 151 | value: function(nodes) { 152 | } 153 | }, 154 | 155 | _parent: { value: null, writable: true}, 156 | 157 | parent: { 158 | get: function() { 159 | return this._parent; 160 | } 161 | }, 162 | 163 | init: { 164 | value: function() { 165 | this.__Base_init(); 166 | this._children = []; 167 | this.transform = Object.create(Transform).init(); 168 | this._properties["meshes"] = []; 169 | 170 | var self = this; 171 | this._properties["meshes"].push = function(data) { 172 | var result = Array.prototype.push.call(this, data); 173 | self.meshesDidChange(this); 174 | return result; 175 | } 176 | 177 | this._children.push = function(data) { 178 | var result = Array.prototype.push.call(this, data); 179 | data._parent = self; 180 | self.nodesDidChange(this); 181 | return result; 182 | } 183 | 184 | this._properties["cameras"] = []; 185 | this._properties["lights"] = []; 186 | 187 | this._worldMatrixIsDirty = true; 188 | this._worldMatrix = mat4.create(); 189 | 190 | return this; 191 | } 192 | }, 193 | 194 | initWithID: { 195 | value: function(id) { 196 | if (id == null) 197 | this.id = this.id + "-" +this.bumpId(); 198 | else 199 | this.id = id; 200 | return this.init(); 201 | } 202 | }, 203 | 204 | getPropertyArrayNamed: { 205 | value: function(name) { 206 | return this._properties[name] 207 | } 208 | }, 209 | 210 | _observers: { value: null, writable: true}, 211 | 212 | addObserver: { 213 | value: function(observer) { 214 | if (this._observers == null) { 215 | this._observers = []; 216 | } 217 | 218 | if (this._observers.indexOf(observer) === -1) { 219 | this._observers.push(observer); 220 | } else { 221 | console.log("WARNING attempt to add 2 times the same observer in transform") 222 | } 223 | } 224 | }, 225 | 226 | removeObserver: { 227 | value: function(observer) { 228 | if (this._observers) { 229 | var index = this._observers.indexOf(observer); 230 | if (index !== -1) { 231 | this._observers.splice(index, 1); 232 | } 233 | } 234 | } 235 | }, 236 | 237 | _transform: { value: null, writable: true }, 238 | 239 | transform: { 240 | get: function() { 241 | if (this.target) { 242 | var targetWorldMatrix = this.target.worldMatrix; 243 | 244 | var sourcePosition = this._transform.translation; 245 | var targetPosition = vec3.create(); 246 | var up = [0 , 1, 0]; 247 | 248 | Utilities.decomposeMat4(targetWorldMatrix, targetPosition, null, null); 249 | 250 | var lookatmat = mat4.lookAt(sourcePosition, targetPosition, up); 251 | mat4.inverse(lookatmat); 252 | var transform = Object.create(Transform).init(); 253 | transform.matrix = lookatmat; 254 | 255 | this._transform.rotation = transform.rotation; 256 | } 257 | return this._transform; 258 | }, 259 | set: function(value) { 260 | if (this._observers) { 261 | for (var i = 0 ; i < this._observers.length ; i++) { 262 | this._observers[i].transformWillChange(this, this._transform, value); 263 | } 264 | } 265 | 266 | if (this._transform) { 267 | this._transform.removeObserver(this); 268 | } 269 | this._transform = value; 270 | this._invalidateWorldMatrix(); 271 | 272 | if (this._transform) { 273 | this._transform.addObserver(this); 274 | } 275 | 276 | if (this._observers) { 277 | for (var i = 0 ; i < this._observers.length ; i++) { 278 | this._observers[i].transformDidChange(this); 279 | } 280 | } 281 | 282 | } 283 | }, 284 | 285 | meshes: { 286 | get: function() { 287 | return this.getPropertyArrayNamed("meshes"); 288 | }, 289 | set: function(value) { 290 | this._properties["meshes"] = value; 291 | this.meshesDidChange(value); 292 | } 293 | }, 294 | 295 | cameras: { 296 | get: function() { 297 | return this.getPropertyArrayNamed("cameras"); 298 | }, 299 | set: function(value) { 300 | this._properties["cameras"] = value; 301 | } 302 | }, 303 | 304 | lights: { 305 | get: function() { 306 | return this.getPropertyArrayNamed("lights"); 307 | }, 308 | set: function(value) { 309 | this._properties["lights"] = value; 310 | } 311 | }, 312 | 313 | _apply: { 314 | value: function( callback, recurse, parent, ctx) { 315 | 316 | if (callback) { 317 | ctx = callback(this, parent, ctx); 318 | 319 | if (recurse) { 320 | this.children.forEach( function(node) { 321 | node._apply(callback, recurse, this, ctx); 322 | }, this); 323 | } 324 | } 325 | } 326 | }, 327 | 328 | apply: { 329 | value: function( callback, recurse, ctx) { 330 | this._apply(callback, recurse, null, ctx); 331 | } 332 | }, 333 | 334 | //TODO: generalize nodeWithName and apply 335 | _nodeWithName: { 336 | value: function( name) { 337 | if (this.name === name) 338 | return this; 339 | 340 | if (this.children) { 341 | for (var i = 0 ; i < this.children.length ; i++) { 342 | var node = this.children[i]; 343 | var res = node._nodeWithName(name); 344 | if (res) { 345 | return res; 346 | } 347 | } 348 | } 349 | 350 | return null; 351 | } 352 | }, 353 | 354 | nodeWithName: { 355 | value: function(name) { 356 | return this._nodeWithName(name); 357 | } 358 | }, 359 | 360 | _nodeWithID: { 361 | value: function( id) { 362 | if (this.id === id) 363 | return this; 364 | 365 | if (this.children) { 366 | for (var i = 0 ; i < this.children.length ; i++) { 367 | var node = this.children[i]; 368 | var res = node._nodeWithID(id); 369 | if (res) { 370 | return res; 371 | } 372 | } 373 | } 374 | 375 | return null; 376 | } 377 | }, 378 | 379 | nodeWithJointID: { 380 | value: function(id) { 381 | return this._nodeWithJointID(id); 382 | } 383 | }, 384 | 385 | _nodeWithJointID: { 386 | value: function( id) { 387 | if (this.jointId === id) 388 | return this; 389 | 390 | if (this.children) { 391 | for (var i = 0 ; i < this.children.length ; i++) { 392 | var node = this.children[i]; 393 | var res = node._nodeWithJointID(id); 394 | if (res) { 395 | return res; 396 | } 397 | } 398 | } 399 | 400 | return null; 401 | } 402 | }, 403 | 404 | nodeWithID: { 405 | value: function(id) { 406 | return this._nodeWithID(id); 407 | } 408 | }, 409 | 410 | copy: { 411 | value: function(node) { 412 | var node = Object.create(glTFNode).init(); 413 | 414 | node.name = this.name; 415 | if (this.meshes) { 416 | this.meshes.forEach( function(mesh) { 417 | node.meshes.push(mesh); 418 | }, this); 419 | } 420 | if (this.lights) { 421 | this.lights.forEach( function(light) { 422 | node.lights.push(light); 423 | }, this); 424 | } 425 | if (this.cameras) { 426 | this.cameras.forEach( function(camera) { 427 | node.cameras.push(camera); 428 | }, this); 429 | } 430 | 431 | //for copies of nodes coming from a DAG we keep track of the id but still, we want to be different 432 | var postId = this.bumpId(); 433 | node.id = this.id + "-" +postId; 434 | node.baseId = this.baseId + "-" +postId; 435 | node.transform = this.transform.copy(); 436 | 437 | return node; 438 | } 439 | }, 440 | 441 | _worldMatrixIsDirty: { value: true, writable:true }, 442 | 443 | _worldMatrix: { value: null, writable:true }, 444 | 445 | _offsetMatrix: { value: null, writable:true }, 446 | 447 | _originVector: { value: null, writable:true }, 448 | 449 | worldMatrix: { 450 | get: function() { 451 | if (this.parent) { 452 | //TODO: handle case with target ! 453 | if (this._worldMatrixIsDirty || (this.target != null)) { 454 | mat4.multiply(this.parent.worldMatrix, this.transform.matrix, this._worldMatrix); 455 | this._worldMatrixIsDirty = false; 456 | } 457 | if (this._offsetMatrix != null) { 458 | var bbox = this.getBoundingBox(false); 459 | if (bbox != null) { 460 | //this is now just for testing purposes, not optimized * at all * 461 | var inv = mat4.create(); 462 | var res = mat4.identity(); 463 | var res2 = mat4.create(); 464 | 465 | var originVector = vec3.createFrom(0.5, 0.5, 0.5); 466 | if (this._originVector != null) { 467 | originVector[0] = this._originVector[0] / 100.0; 468 | originVector[1] = this._originVector[1] / 100.0; 469 | originVector[2] = this._originVector[2] / 100.0; 470 | } 471 | 472 | var mid = [ 473 | (bbox[0][0] + bbox[1][0]) * originVector[0], 474 | (bbox[0][1] + bbox[1][1]) * originVector[0], 475 | (bbox[0][2] + bbox[1][2]) * originVector[0]]; 476 | 477 | var tr1 = vec3.create(mid); 478 | mat4.translate(res, tr1); 479 | mat4.multiply(res, this._offsetMatrix, res2); 480 | mat4.multiply(this._worldMatrix, res2, res); 481 | tr1[0] = -tr1[0]; 482 | tr1[1] = -tr1[1]; 483 | tr1[2] = -tr1[2]; 484 | mat4.translate(res, tr1); 485 | 486 | return res; 487 | } 488 | } 489 | 490 | return this._worldMatrix; 491 | } else { 492 | this._worldMatrixIsDirty = false; 493 | return this.transform.matrix; 494 | } 495 | } 496 | }, 497 | 498 | _ignoresTransformUpdates: { value: false, writable: true }, 499 | 500 | _invalidateWorldMatrix: { 501 | value: function() { 502 | this._worldMatrixIsDirty = true; 503 | 504 | //hack to tell observer to invalidate themselves 505 | this._ignoresTransformUpdates = true; 506 | this._transform._fireTransformDidUpdate(); 507 | this._ignoresTransformUpdates = false; 508 | 509 | if (this._children) { 510 | for (var i = 0 ; i < this._children.length ; i++) { 511 | this._children[i]._invalidateWorldMatrix(); 512 | } 513 | } 514 | } 515 | }, 516 | 517 | transformDidUpdate: { 518 | value: function() { 519 | if (this._ignoresTransformUpdates === false) 520 | this._invalidateWorldMatrix(); 521 | } 522 | }, 523 | 524 | nodeWithJointID: { 525 | value: function(id) { 526 | return this._nodeWithJointID(id); 527 | } 528 | }, 529 | 530 | nodeWithPropertyNamed: { 531 | value: function( propertyName) { 532 | if (this[propertyName] != null) 533 | return this; 534 | 535 | if (this.children) { 536 | for (var i = 0 ; i < this.children.length ; i++) { 537 | var node = this.children[i]; 538 | var res = node.nodeWithPropertyNamed(propertyName); 539 | if (res) { 540 | return res; 541 | } 542 | } 543 | } 544 | return null; 545 | } 546 | }, 547 | 548 | _target: { value:null , writable:true }, 549 | 550 | target: { 551 | get: function() { 552 | return this._target; 553 | }, 554 | set: function(target) { 555 | this._target = target; 556 | } 557 | } 558 | 559 | }); 560 | --------------------------------------------------------------------------------