diff --git a/Release/XSeen.0.4.5+23_0e9dbd1.js b/Release/XSeen.0.4.5+23_0e9dbd1.js new file mode 100644 index 0000000..4a52c1e --- /dev/null +++ b/Release/XSeen.0.4.5+23_0e9dbd1.js @@ -0,0 +1,14403 @@ +/* + * XSeen V0.4.5+23_0e9dbd1 + * Built Tue Jul 11 08:08:45 2017 + * + +Dual licensed under the MIT and GPL licenses. + +==[MIT]==================================================================== +Copyright (c) 2017, Daly Realism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +==[GPL]==================================================================== + +XSeen - Declarative 3D for HTML + +Copyright (C) 2017, Daly Realism + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +=== COPYRIGHT +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (C) 2017, Daly Realism for XSeen +Copyright, Fraunhofer for X3DOM +Copyright, Mozilla for A-Frame +Copyright, THREE and Khronos for various parts of THREE.js +Copyright (C) 2017, John Carlson for JSON->XML converter (JSONParser.js) + +=== +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + */ +// File: utils/ColladaLoader.js +/** +* @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com +* @author Tony Parisi / http://www.tonyparisi.com/ +*/ + +THREE.ColladaLoader = function () { + + var COLLADA = null; + var scene = null; + var visualScene; + var kinematicsModel; + + var readyCallbackFunc = null; + + var sources = {}; + var images = {}; + var animations = {}; + var controllers = {}; + var geometries = {}; + var materials = {}; + var effects = {}; + var cameras = {}; + var lights = {}; + + var animData; + var kinematics; + var visualScenes; + var kinematicsModels; + var baseUrl; + var morphs; + var skins; + + var flip_uv = true; + var preferredShading = THREE.SmoothShading; + + var options = { + // Force Geometry to always be centered at the local origin of the + // containing Mesh. + centerGeometry: false, + + // Axis conversion is done for geometries, animations, and controllers. + // If we ever pull cameras or lights out of the COLLADA file, they'll + // need extra work. + convertUpAxis: false, + + subdivideFaces: true, + + upAxis: 'Y', + + // For reflective or refractive materials we'll use this cubemap + defaultEnvMap: null + + }; + + var colladaUnit = 1.0; + var colladaUp = 'Y'; + var upConversion = null; + + function load ( url, readyCallback, progressCallback, failCallback ) { + + var length = 0; + + if ( document.implementation && document.implementation.createDocument ) { + + var request = new XMLHttpRequest(); + + request.onreadystatechange = function() { + + if ( request.readyState === 4 ) { + + if ( request.status === 0 || request.status === 200 ) { + + if ( request.response ) { + + readyCallbackFunc = readyCallback; + parse( request.response, undefined, url ); + + } else { + + if ( failCallback ) { + + failCallback( { type: 'error', url: url } ); + + } else { + + console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); + + } + + } + + }else{ + + if( failCallback ){ + + failCallback( { type: 'error', url: url } ); + + }else{ + + console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' ); + + } + + } + + } else if ( request.readyState === 3 ) { + + if ( progressCallback ) { + + if ( length === 0 ) { + + length = request.getResponseHeader( "Content-Length" ); + + } + + progressCallback( { total: length, loaded: request.responseText.length } ); + + } + + } + + }; + + request.open( "GET", url, true ); + request.send( null ); + + } else { + + alert( "Don't know how to parse XML!" ); + + } + + } + + function parse( text, callBack, url ) { + + COLLADA = new DOMParser().parseFromString( text, 'text/xml' ); + callBack = callBack || readyCallbackFunc; + + if ( url !== undefined ) { + + var parts = url.split( '/' ); + parts.pop(); + baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; + + } + + parseAsset(); + setUpConversion(); + images = parseLib( "library_images image", _Image, "image" ); + materials = parseLib( "library_materials material", Material, "material" ); + effects = parseLib( "library_effects effect", Effect, "effect" ); + geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); + cameras = parseLib( "library_cameras camera", Camera, "camera" ); + lights = parseLib( "library_lights light", Light, "light" ); + controllers = parseLib( "library_controllers controller", Controller, "controller" ); + animations = parseLib( "library_animations animation", Animation, "animation" ); + visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); + kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); + + morphs = []; + skins = []; + + visualScene = parseScene(); + scene = new THREE.Group(); + + for ( var i = 0; i < visualScene.nodes.length; i ++ ) { + + scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); + + } + + // unit conversion + scene.scale.multiplyScalar( colladaUnit ); + + createAnimations(); + + kinematicsModel = parseKinematicsModel(); + createKinematics(); + + var result = { + + scene: scene, + morphs: morphs, + skins: skins, + animations: animData, + kinematics: kinematics, + dae: { + images: images, + materials: materials, + cameras: cameras, + lights: lights, + effects: effects, + geometries: geometries, + controllers: controllers, + animations: animations, + visualScenes: visualScenes, + visualScene: visualScene, + scene: visualScene, + kinematicsModels: kinematicsModels, + kinematicsModel: kinematicsModel + } + + }; + + if ( callBack ) { + + callBack( result ); + + } + + return result; + + } + + function setPreferredShading ( shading ) { + + preferredShading = shading; + + } + + function parseAsset () { + + var elements = COLLADA.querySelectorAll('asset'); + + var element = elements[0]; + + if ( element && element.childNodes ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'unit': + + var meter = child.getAttribute( 'meter' ); + + if ( meter ) { + + colladaUnit = parseFloat( meter ); + + } + + break; + + case 'up_axis': + + colladaUp = child.textContent.charAt(0); + break; + + } + + } + + } + + } + + function parseLib ( q, classSpec, prefix ) { + + var elements = COLLADA.querySelectorAll(q); + + var lib = {}; + + var i = 0; + + var elementsLength = elements.length; + + for ( var j = 0; j < elementsLength; j ++ ) { + + var element = elements[j]; + var daeElement = ( new classSpec() ).parse( element ); + + if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); + lib[ daeElement.id ] = daeElement; + + } + + return lib; + + } + + function parseScene() { + + var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; + + if ( sceneElement ) { + + var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); + return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; + + } else { + + return null; + + } + + } + + function parseKinematicsModel() { + + var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; + + if ( kinematicsModelElement ) { + + var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); + return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; + + } else { + + return null; + + } + + } + + function createAnimations() { + + animData = []; + + // fill in the keys + recurseHierarchy( scene ); + + } + + function recurseHierarchy( node ) { + + var n = visualScene.getChildById( node.colladaId, true ), + newData = null; + + if ( n && n.keys ) { + + newData = { + fps: 60, + hierarchy: [ { + node: n, + keys: n.keys, + sids: n.sids + } ], + node: node, + name: 'animation_' + node.name, + length: 0 + }; + + animData.push(newData); + + for ( var i = 0, il = n.keys.length; i < il; i ++ ) { + + newData.length = Math.max( newData.length, n.keys[i].time ); + + } + + } else { + + newData = { + hierarchy: [ { + keys: [], + sids: [] + } ] + } + + } + + for ( var i = 0, il = node.children.length; i < il; i ++ ) { + + var d = recurseHierarchy( node.children[i] ); + + for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { + + newData.hierarchy.push( { + keys: [], + sids: [] + } ); + + } + + } + + return newData; + + } + + function calcAnimationBounds () { + + var start = 1000000; + var end = -start; + var frames = 0; + var ID; + for ( var id in animations ) { + + var animation = animations[ id ]; + ID = ID || animation.id; + for ( var i = 0; i < animation.sampler.length; i ++ ) { + + var sampler = animation.sampler[ i ]; + + sampler.create(); + + start = Math.min( start, sampler.startTime ); + end = Math.max( end, sampler.endTime ); + frames = Math.max( frames, sampler.input.length ); + + } + + } + + return { start:start, end:end, frames:frames,ID:ID }; + + } + + function createMorph ( geometry, ctrl ) { + + var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; + + if ( !morphCtrl || !morphCtrl.morph ) { + + console.log("could not find morph controller!"); + return; + + } + + var morph = morphCtrl.morph; + + for ( var i = 0; i < morph.targets.length; i ++ ) { + + var target_id = morph.targets[ i ]; + var daeGeometry = geometries[ target_id ]; + + if ( !daeGeometry.mesh || + !daeGeometry.mesh.primitives || + !daeGeometry.mesh.primitives.length ) { + continue; + } + + var target = daeGeometry.mesh.primitives[ 0 ].geometry; + + if ( target.vertices.length === geometry.vertices.length ) { + + geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); + + } + + } + + geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); + + } + + function createSkin ( geometry, ctrl, applyBindShape ) { + + var skinCtrl = controllers[ ctrl.url ]; + + if ( !skinCtrl || !skinCtrl.skin ) { + + console.log( "could not find skin controller!" ); + return; + + } + + if ( !ctrl.skeleton || !ctrl.skeleton.length ) { + + console.log( "could not find the skeleton for the skin!" ); + return; + + } + + var skin = skinCtrl.skin; + var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); + var hierarchy = []; + + applyBindShape = applyBindShape !== undefined ? applyBindShape : true; + + var bones = []; + geometry.skinWeights = []; + geometry.skinIndices = []; + + //createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 ); + //createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights ); + + /* + geometry.animation = { + name: 'take_001', + fps: 30, + length: 2, + JIT: true, + hierarchy: hierarchy + }; + */ + + if ( applyBindShape ) { + + for ( var i = 0; i < geometry.vertices.length; i ++ ) { + + geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); + + } + + } + + } + + function setupSkeleton ( node, bones, frame, parent ) { + + node.world = node.world || new THREE.Matrix4(); + node.localworld = node.localworld || new THREE.Matrix4(); + node.world.copy( node.matrix ); + node.localworld.copy( node.matrix ); + + if ( node.channels && node.channels.length ) { + + var channel = node.channels[ 0 ]; + var m = channel.sampler.output[ frame ]; + + if ( m instanceof THREE.Matrix4 ) { + + node.world.copy( m ); + node.localworld.copy(m); + if (frame === 0) + node.matrix.copy(m); + } + + } + + if ( parent ) { + + node.world.multiplyMatrices( parent, node.world ); + + } + + bones.push( node ); + + for ( var i = 0; i < node.nodes.length; i ++ ) { + + setupSkeleton( node.nodes[ i ], bones, frame, node.world ); + + } + + } + + function setupSkinningMatrices ( bones, skin ) { + + // FIXME: this is dumb... + + for ( var i = 0; i < bones.length; i ++ ) { + + var bone = bones[ i ]; + var found = -1; + + if ( bone.type != 'JOINT' ) continue; + + for ( var j = 0; j < skin.joints.length; j ++ ) { + + if ( bone.sid === skin.joints[ j ] ) { + + found = j; + break; + + } + + } + + if ( found >= 0 ) { + + var inv = skin.invBindMatrices[ found ]; + + bone.invBindMatrix = inv; + bone.skinningMatrix = new THREE.Matrix4(); + bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) + bone.animatrix = new THREE.Matrix4(); + + bone.animatrix.copy(bone.localworld); + bone.weights = []; + + for ( var j = 0; j < skin.weights.length; j ++ ) { + + for (var k = 0; k < skin.weights[ j ].length; k ++ ) { + + var w = skin.weights[ j ][ k ]; + + if ( w.joint === found ) { + + bone.weights.push( w ); + + } + + } + + } + + } else { + + console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); + + bone.skinningMatrix = new THREE.Matrix4(); + bone.weights = []; + + } + } + + } + + //Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix + function flattenSkeleton(skeleton) { + + var list = []; + var walk = function(parentid, node, list) { + + var bone = {}; + bone.name = node.sid; + bone.parent = parentid; + bone.matrix = node.matrix; + var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; + bone.matrix.decompose(data[0], data[1], data[2]); + + bone.pos = [ data[0].x,data[0].y,data[0].z ]; + + bone.scl = [ data[2].x,data[2].y,data[2].z ]; + bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; + list.push(bone); + + for (var i in node.nodes) { + + walk(node.sid, node.nodes[i], list); + + } + + }; + + walk(-1, skeleton, list); + return list; + + } + + //Move the vertices into the pose that is proper for the start of the animation + function skinToBindPose(geometry,skeleton,skinController) { + + var bones = []; + setupSkeleton( skeleton, bones, -1 ); + setupSkinningMatrices( bones, skinController.skin ); + var v = new THREE.Vector3(); + var skinned = []; + + for (var i = 0; i < geometry.vertices.length; i ++) { + + skinned.push(new THREE.Vector3()); + + } + + for ( i = 0; i < bones.length; i ++ ) { + + if ( bones[ i ].type != 'JOINT' ) continue; + + for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { + + var w = bones[ i ].weights[ j ]; + var vidx = w.index; + var weight = w.weight; + + var o = geometry.vertices[vidx]; + var s = skinned[vidx]; + + v.x = o.x; + v.y = o.y; + v.z = o.z; + + v.applyMatrix4( bones[i].skinningMatrix ); + + s.x += (v.x * weight); + s.y += (v.y * weight); + s.z += (v.z * weight); + } + + } + + for (var i = 0; i < geometry.vertices.length; i ++) { + + geometry.vertices[i] = skinned[i]; + + } + + } + + function applySkin ( geometry, instanceCtrl, frame ) { + + var skinController = controllers[ instanceCtrl.url ]; + + frame = frame !== undefined ? frame : 40; + + if ( !skinController || !skinController.skin ) { + + console.log( 'ColladaLoader: Could not find skin controller.' ); + return; + + } + + if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { + + console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); + return; + + } + + var animationBounds = calcAnimationBounds(); + var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); + + //flatten the skeleton into a list of bones + var bonelist = flattenSkeleton(skeleton); + var joints = skinController.skin.joints; + + //sort that list so that the order reflects the order in the joint list + var sortedbones = []; + for (var i = 0; i < joints.length; i ++) { + + for (var j = 0; j < bonelist.length; j ++) { + + if (bonelist[j].name === joints[i]) { + + sortedbones[i] = bonelist[j]; + + } + + } + + } + + //hook up the parents by index instead of name + for (var i = 0; i < sortedbones.length; i ++) { + + for (var j = 0; j < sortedbones.length; j ++) { + + if (sortedbones[i].parent === sortedbones[j].name) { + + sortedbones[i].parent = j; + + } + + } + + } + + + var i, j, w, vidx, weight; + var v = new THREE.Vector3(), o, s; + + // move vertices to bind shape + for ( i = 0; i < geometry.vertices.length; i ++ ) { + geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); + } + + var skinIndices = []; + var skinWeights = []; + var weights = skinController.skin.weights; + + // hook up the skin weights + // TODO - this might be a good place to choose greatest 4 weights + for ( var i =0; i < weights.length; i ++ ) { + + var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); + var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); + + skinIndices.push(indicies); + skinWeights.push(weight); + + } + + geometry.skinIndices = skinIndices; + geometry.skinWeights = skinWeights; + geometry.bones = sortedbones; + // process animation, or simply pose the rig if no animation + + //create an animation for the animated bones + //NOTE: this has no effect when using morphtargets + var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; + + for (var j = 0; j < sortedbones.length; j ++) { + + animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); + + } + + console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); + + + + skinToBindPose(geometry, skeleton, skinController); + + + for ( frame = 0; frame < animationBounds.frames; frame ++ ) { + + var bones = []; + var skinned = []; + // process the frame and setup the rig with a fresh + // transform, possibly from the bone's animation channel(s) + + setupSkeleton( skeleton, bones, frame ); + setupSkinningMatrices( bones, skinController.skin ); + + for (var i = 0; i < bones.length; i ++) { + + for (var j = 0; j < animationdata.hierarchy.length; j ++) { + + if (animationdata.hierarchy[j].name === bones[i].sid) { + + var key = {}; + key.time = (frame / 30); + key.matrix = bones[i].animatrix; + + if (frame === 0) + bones[i].matrix = key.matrix; + + var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; + key.matrix.decompose(data[0], data[1], data[2]); + + key.pos = [ data[0].x,data[0].y,data[0].z ]; + + key.scl = [ data[2].x,data[2].y,data[2].z ]; + key.rot = data[1]; + + animationdata.hierarchy[j].keys.push(key); + + } + + } + + } + + geometry.animation = animationdata; + + } + + } + + function createKinematics() { + + if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { + kinematics = undefined; + return; + } + + var jointMap = {}; + + var _addToMap = function( jointIndex, parentVisualElement ) { + + var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); + var colladaNode = visualScene.getChildById( parentVisualElementId, true ); + var joint = kinematicsModel.joints[ jointIndex ]; + + scene.traverse(function( node ) { + + if ( node.colladaId == parentVisualElementId ) { + + jointMap[ jointIndex ] = { + node: node, + transforms: colladaNode.transforms, + joint: joint, + position: joint.zeroPosition + }; + + } + + }); + + }; + + kinematics = { + + joints: kinematicsModel && kinematicsModel.joints, + + getJointValue: function( jointIndex ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + return jointData.position; + + } else { + + console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + }, + + setJointValue: function( jointIndex, value ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + var joint = jointData.joint; + + if ( value > joint.limits.max || value < joint.limits.min ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); + + } else if ( joint.static ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); + + } else { + + var threejsNode = jointData.node; + var axis = joint.axis; + var transforms = jointData.transforms; + + var matrix = new THREE.Matrix4(); + var m1 = new THREE.Matrix4(); + + for (i = 0; i < transforms.length; i ++ ) { + + var transform = transforms[ i ]; + + // kinda ghetto joint detection + + if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { + + // apply actual joint value here + switch ( joint.type ) { + + case 'revolute': + + matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); + break; + + case 'prismatic': + + matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); + break; + + default: + + console.warn( 'setJointValue: unknown joint type: ' + joint.type ); + break; + + } + + } else { + + switch ( transform.type ) { + + case 'matrix': + + matrix.multiply( transform.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); + + break; + + } + } + } + + // apply the matrix to the threejs node + var elementsFloat32Arr = matrix.elements; + var elements = Array.prototype.slice.call( elementsFloat32Arr ); + + var elementsRowMajor = [ + elements[ 0 ], + elements[ 4 ], + elements[ 8 ], + elements[ 12 ], + elements[ 1 ], + elements[ 5 ], + elements[ 9 ], + elements[ 13 ], + elements[ 2 ], + elements[ 6 ], + elements[ 10 ], + elements[ 14 ], + elements[ 3 ], + elements[ 7 ], + elements[ 11 ], + elements[ 15 ] + ]; + + threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); + threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); + + jointMap[ jointIndex ].position = value; + + } + + } else { + + console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + } + + }; + + var element = COLLADA.querySelector('scene instance_kinematics_scene'); + + if ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_joint_axis': + + var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); + var axis = child.querySelector('axis param').textContent; + var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); + var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); + + if ( visualTargetElement ) { + var parentVisualElement = visualTargetElement.parentElement; + _addToMap(jointIndex, parentVisualElement); + } + + break; + + default: + + break; + + } + + } + } + + } + + function createSceneGraph ( node, parent ) { + + var obj = new THREE.Object3D(); + var skinned = false; + var skinController; + var morphController; + var i, j; + + // FIXME: controllers + + for ( i = 0; i < node.controllers.length; i ++ ) { + + var controller = controllers[ node.controllers[ i ].url ]; + + switch ( controller.type ) { + + case 'skin': + + if ( geometries[ controller.skin.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.skin.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + skinned = true; + skinController = node.controllers[ i ]; + + } else if ( controllers[ controller.skin.source ] ) { + + // urgh: controller can be chained + // handle the most basic case... + + var second = controllers[ controller.skin.source ]; + morphController = second; + // skinController = node.controllers[i]; + + if ( second.morph && geometries[ second.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = second.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + + } + + } + + break; + + case 'morph': + + if ( geometries[ controller.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + morphController = node.controllers[ i ]; + + } + + console.log( 'ColladaLoader: Morph-controller partially supported.' ); + + default: + break; + + } + + } + + // geometries + + var double_sided_materials = {}; + + for ( i = 0; i < node.geometries.length; i ++ ) { + + var instance_geometry = node.geometries[i]; + var instance_materials = instance_geometry.instance_material; + var geometry = geometries[ instance_geometry.url ]; + var used_materials = {}; + var used_materials_array = []; + var num_materials = 0; + var first_material; + + if ( geometry ) { + + if ( !geometry.mesh || !geometry.mesh.primitives ) + continue; + + if ( obj.name.length === 0 ) { + + obj.name = geometry.id; + + } + + // collect used fx for this geometry-instance + + if ( instance_materials ) { + + for ( j = 0; j < instance_materials.length; j ++ ) { + + var instance_material = instance_materials[ j ]; + var mat = materials[ instance_material.target ]; + var effect_id = mat.instance_effect.url; + var shader = effects[ effect_id ].shader; + var material3js = shader.material; + + if ( geometry.doubleSided ) { + + if ( !( instance_material.symbol in double_sided_materials ) ) { + + var _copied_material = material3js.clone(); + _copied_material.side = THREE.DoubleSide; + double_sided_materials[ instance_material.symbol ] = _copied_material; + + } + + material3js = double_sided_materials[ instance_material.symbol ]; + + } + + material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; + used_materials[ instance_material.symbol ] = num_materials; + used_materials_array.push( material3js ); + first_material = material3js; + first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; + num_materials ++; + + } + + } + + var mesh; + var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); + var geom = geometry.mesh.geometry3js; + + if ( num_materials > 1 ) { + + material = new THREE.MultiMaterial( used_materials_array ); + + for ( j = 0; j < geom.faces.length; j ++ ) { + + var face = geom.faces[ j ]; + face.materialIndex = used_materials[ face.daeMaterial ] + + } + + } + + if ( skinController !== undefined ) { + + + applySkin( geom, skinController ); + + if ( geom.morphTargets.length > 0 ) { + + material.morphTargets = true; + material.skinning = false; + + } else { + + material.morphTargets = false; + material.skinning = true; + + } + + + mesh = new THREE.SkinnedMesh( geom, material, false ); + + + //mesh.skeleton = skinController.skeleton; + //mesh.skinController = controllers[ skinController.url ]; + //mesh.skinInstanceController = skinController; + mesh.name = 'skin_' + skins.length; + + + + //mesh.animationHandle.setKey(0); + skins.push( mesh ); + + } else if ( morphController !== undefined ) { + + createMorph( geom, morphController ); + + material.morphTargets = true; + + mesh = new THREE.Mesh( geom, material ); + mesh.name = 'morph_' + morphs.length; + + morphs.push( mesh ); + + } else { + + if ( geom.isLineStrip === true ) { + + mesh = new THREE.Line( geom ); + + } else { + + mesh = new THREE.Mesh( geom, material ); + + } + + } + + obj.add(mesh); + + } + + } + + for ( i = 0; i < node.cameras.length; i ++ ) { + + var instance_camera = node.cameras[i]; + var cparams = cameras[instance_camera.url]; + + var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), + parseFloat(cparams.znear), parseFloat(cparams.zfar)); + + obj.add(cam); + } + + for ( i = 0; i < node.lights.length; i ++ ) { + + var light = null; + var instance_light = node.lights[i]; + var lparams = lights[instance_light.url]; + + if ( lparams && lparams.technique ) { + + var color = lparams.color.getHex(); + var intensity = lparams.intensity; + var distance = lparams.distance; + var angle = lparams.falloff_angle; + + switch ( lparams.technique ) { + + case 'directional': + + light = new THREE.DirectionalLight( color, intensity, distance ); + light.position.set(0, 0, 1); + break; + + case 'point': + + light = new THREE.PointLight( color, intensity, distance ); + break; + + case 'spot': + + light = new THREE.SpotLight( color, intensity, distance, angle ); + light.position.set(0, 0, 1); + break; + + case 'ambient': + + light = new THREE.AmbientLight( color ); + break; + + } + + } + + if (light) { + obj.add(light); + } + } + + obj.name = node.name || node.id || ""; + obj.colladaId = node.id || ""; + obj.layer = node.layer || ""; + obj.matrix = node.matrix; + obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); + + if ( options.centerGeometry && obj.geometry ) { + + var delta = obj.geometry.center(); + delta.multiply( obj.scale ); + delta.applyQuaternion( obj.quaternion ); + + obj.position.sub( delta ); + + } + + for ( i = 0; i < node.nodes.length; i ++ ) { + + obj.add( createSceneGraph( node.nodes[i], node ) ); + + } + + return obj; + + } + + function getJointId( skin, id ) { + + for ( var i = 0; i < skin.joints.length; i ++ ) { + + if ( skin.joints[ i ] === id ) { + + return i; + + } + + } + + } + + function getLibraryNode( id ) { + + var nodes = COLLADA.querySelectorAll('library_nodes node'); + + for ( var i = 0; i < nodes.length; i++ ) { + + var attObj = nodes[i].attributes.getNamedItem('id'); + + if ( attObj && attObj.value === id ) { + + return nodes[i]; + + } + + } + + return undefined; + + } + + function getChannelsForNode ( node ) { + + var channels = []; + var startTime = 1000000; + var endTime = -1000000; + + for ( var id in animations ) { + + var animation = animations[id]; + + for ( var i = 0; i < animation.channel.length; i ++ ) { + + var channel = animation.channel[i]; + var sampler = animation.sampler[i]; + var id = channel.target.split('/')[0]; + + if ( id == node.id ) { + + sampler.create(); + channel.sampler = sampler; + startTime = Math.min(startTime, sampler.startTime); + endTime = Math.max(endTime, sampler.endTime); + channels.push(channel); + + } + + } + + } + + if ( channels.length ) { + + node.startTime = startTime; + node.endTime = endTime; + + } + + return channels; + + } + + function calcFrameDuration( node ) { + + var minT = 10000000; + + for ( var i = 0; i < node.channels.length; i ++ ) { + + var sampler = node.channels[i].sampler; + + for ( var j = 0; j < sampler.input.length - 1; j ++ ) { + + var t0 = sampler.input[ j ]; + var t1 = sampler.input[ j + 1 ]; + minT = Math.min( minT, t1 - t0 ); + + } + } + + return minT; + + } + + function calcMatrixAt( node, t ) { + + var animated = {}; + + var i, j; + + for ( i = 0; i < node.channels.length; i ++ ) { + + var channel = node.channels[ i ]; + animated[ channel.sid ] = channel; + + } + + var matrix = new THREE.Matrix4(); + + for ( i = 0; i < node.transforms.length; i ++ ) { + + var transform = node.transforms[ i ]; + var channel = animated[ transform.sid ]; + + if ( channel !== undefined ) { + + var sampler = channel.sampler; + var value; + + for ( j = 0; j < sampler.input.length - 1; j ++ ) { + + if ( sampler.input[ j + 1 ] > t ) { + + value = sampler.output[ j ]; + //console.log(value.flatten) + break; + + } + + } + + if ( value !== undefined ) { + + if ( value instanceof THREE.Matrix4 ) { + + matrix.multiplyMatrices( matrix, value ); + + } else { + + // FIXME: handle other types + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } + + return matrix; + + } + + function bakeAnimations ( node ) { + + if ( node.channels && node.channels.length ) { + + var keys = [], + sids = []; + + for ( var i = 0, il = node.channels.length; i < il; i ++ ) { + + var channel = node.channels[i], + fullSid = channel.fullSid, + sampler = channel.sampler, + input = sampler.input, + transform = node.getTransformBySid( channel.sid ), + member; + + if ( channel.arrIndices ) { + + member = []; + + for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { + + member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); + + } + + } else { + + member = getConvertedMember( channel.member ); + + } + + if ( transform ) { + + if ( sids.indexOf( fullSid ) === -1 ) { + + sids.push( fullSid ); + + } + + for ( var j = 0, jl = input.length; j < jl; j ++ ) { + + var time = input[j], + data = sampler.getData( transform.type, j, member ), + key = findKey( keys, time ); + + if ( !key ) { + + key = new Key( time ); + var timeNdx = findTimeNdx( keys, time ); + keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); + + } + + key.addTarget( fullSid, transform, member, data ); + + } + + } else { + + console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); + + } + + } + + // post process + for ( var i = 0; i < sids.length; i ++ ) { + + var sid = sids[ i ]; + + for ( var j = 0; j < keys.length; j ++ ) { + + var key = keys[ j ]; + + if ( !key.hasTarget( sid ) ) { + + interpolateKeys( keys, key, j, sid ); + + } + + } + + } + + node.keys = keys; + node.sids = sids; + + } + + } + + function findKey ( keys, time) { + + var retVal = null; + + for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { + + var key = keys[i]; + + if ( key.time === time ) { + + retVal = key; + + } else if ( key.time > time ) { + + break; + + } + + } + + return retVal; + + } + + function findTimeNdx ( keys, time) { + + var ndx = -1; + + for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { + + var key = keys[i]; + + if ( key.time >= time ) { + + ndx = i; + + } + + } + + return ndx; + + } + + function interpolateKeys ( keys, key, ndx, fullSid ) { + + var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), + nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); + + if ( prevKey && nextKey ) { + + var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), + prevTarget = prevKey.getTarget( fullSid ), + nextData = nextKey.getTarget( fullSid ).data, + prevData = prevTarget.data, + data; + + if ( prevTarget.type === 'matrix' ) { + + data = prevData; + + } else if ( prevData.length ) { + + data = []; + + for ( var i = 0; i < prevData.length; ++ i ) { + + data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); + + } + + } + + // Get next key with given sid + + function getNextKeyWith( keys, fullSid, ndx ) { + + for ( ; ndx < keys.length; ndx ++ ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + } + + // Get previous key with given sid + + function getPrevKeyWith( keys, fullSid, ndx ) { + + ndx = ndx >= 0 ? ndx : ndx + keys.length; + + for ( ; ndx >= 0; ndx -- ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + } + + function _Image() { + + this.id = ""; + this.init_from = ""; + + } + + _Image.prototype.parse = function(element) { + + this.id = element.getAttribute('id'); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'init_from' ) { + + this.init_from = child.textContent; + + } + + } + + return this; + + }; + + function Controller() { + + this.id = ""; + this.name = ""; + this.type = ""; + this.skin = null; + this.morph = null; + + } + + Controller.prototype.parse = function( element ) { + + this.id = element.getAttribute('id'); + this.name = element.getAttribute('name'); + this.type = "none"; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'skin': + + this.skin = (new Skin()).parse(child); + this.type = child.nodeName; + break; + + case 'morph': + + this.morph = (new Morph()).parse(child); + this.type = child.nodeName; + break; + + default: + break; + + } + } + + return this; + + }; + + function Morph() { + + this.method = null; + this.source = null; + this.targets = null; + this.weights = null; + + } + + Morph.prototype.parse = function( element ) { + + var sources = {}; + var inputs = []; + var i; + + this.method = element.getAttribute( 'method' ); + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + + for ( i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + var source = ( new Source() ).parse( child ); + sources[ source.id ] = source; + break; + + case 'targets': + + inputs = this.parseInputs( child ); + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + for ( i = 0; i < inputs.length; i ++ ) { + + var input = inputs[ i ]; + var source = sources[ input.source ]; + + switch ( input.semantic ) { + + case 'MORPH_TARGET': + + this.targets = source.read(); + break; + + case 'MORPH_WEIGHT': + + this.weights = source.read(); + break; + + default: + break; + + } + } + + return this; + + }; + + Morph.prototype.parseInputs = function(element) { + + var inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( (new Input()).parse(child) ); + break; + + default: + break; + } + } + + return inputs; + + }; + + function Skin() { + + this.source = ""; + this.bindShapeMatrix = null; + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + } + + Skin.prototype.parse = function( element ) { + + var sources = {}; + var joints, weights; + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_shape_matrix': + + var f = _floats(child.textContent); + this.bindShapeMatrix = getConvertedMat4( f ); + break; + + case 'source': + + var src = new Source().parse(child); + sources[ src.id ] = src; + break; + + case 'joints': + + joints = child; + break; + + case 'vertex_weights': + + weights = child; + break; + + default: + + console.log( child.nodeName ); + break; + + } + } + + this.parseJoints( joints, sources ); + this.parseWeights( weights, sources ); + + return this; + + }; + + Skin.prototype.parseJoints = function ( element, sources ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + var input = ( new Input() ).parse( child ); + var source = sources[ input.source ]; + + if ( input.semantic === 'JOINT' ) { + + this.joints = source.read(); + + } else if ( input.semantic === 'INV_BIND_MATRIX' ) { + + this.invBindMatrices = source.read(); + + } + + break; + + default: + break; + } + + } + + }; + + Skin.prototype.parseWeights = function ( element, sources ) { + + var v, vcount, inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( ( new Input() ).parse( child ) ); + break; + + case 'v': + + v = _ints( child.textContent ); + break; + + case 'vcount': + + vcount = _ints( child.textContent ); + break; + + default: + break; + + } + + } + + var index = 0; + + for ( var i = 0; i < vcount.length; i ++ ) { + + var numBones = vcount[i]; + var vertex_weights = []; + + for ( var j = 0; j < numBones; j ++ ) { + + var influence = {}; + + for ( var k = 0; k < inputs.length; k ++ ) { + + var input = inputs[ k ]; + var value = v[ index + input.offset ]; + + switch ( input.semantic ) { + + case 'JOINT': + + influence.joint = value;//this.joints[value]; + break; + + case 'WEIGHT': + + influence.weight = sources[ input.source ].data[ value ]; + break; + + default: + break; + + } + + } + + vertex_weights.push( influence ); + index += inputs.length; + } + + for ( var j = 0; j < vertex_weights.length; j ++ ) { + + vertex_weights[ j ].index = i; + + } + + this.weights.push( vertex_weights ); + + } + + }; + + function VisualScene () { + + this.id = ""; + this.name = ""; + this.nodes = []; + this.scene = new THREE.Group(); + + } + + VisualScene.prototype.getChildById = function( id, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildById( id, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.getChildBySid = function( sid, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.parse = function( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.nodes = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Node() { + + this.id = ""; + this.name = ""; + this.sid = ""; + this.nodes = []; + this.controllers = []; + this.transforms = []; + this.geometries = []; + this.channels = []; + this.matrix = new THREE.Matrix4(); + + } + + Node.prototype.getChannelForTransform = function( transformSid ) { + + for ( var i = 0; i < this.channels.length; i ++ ) { + + var channel = this.channels[i]; + var parts = channel.target.split('/'); + var id = parts.shift(); + var sid = parts.shift(); + var dotSyntax = (sid.indexOf(".") >= 0); + var arrSyntax = (sid.indexOf("(") >= 0); + var arrIndices; + var member; + + if ( dotSyntax ) { + + parts = sid.split("."); + sid = parts.shift(); + member = parts.shift(); + + } else if ( arrSyntax ) { + + arrIndices = sid.split("("); + sid = arrIndices.shift(); + + for ( var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); + + } + + } + + if ( sid === transformSid ) { + + channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; + return channel; + + } + + } + + return null; + + }; + + Node.prototype.getChildById = function ( id, recursive ) { + + if ( this.id === id ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildById( id, recursive ); + + if ( n ) { + + return n; + + } + + } + + } + + return null; + + }; + + Node.prototype.getChildBySid = function ( sid, recursive ) { + + if ( this.sid === sid ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( n ) { + + return n; + + } + + } + } + + return null; + + }; + + Node.prototype.getTransformBySid = function ( sid ) { + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; + + } + + return null; + + }; + + Node.prototype.parse = function( element ) { + + var url; + + this.id = element.getAttribute('id'); + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.type = element.getAttribute('type'); + this.layer = element.getAttribute('layer'); + + this.type = this.type === 'JOINT' ? this.type : 'NODE'; + + this.nodes = []; + this.transforms = []; + this.geometries = []; + this.cameras = []; + this.lights = []; + this.controllers = []; + this.matrix = new THREE.Matrix4(); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + case 'instance_camera': + + this.cameras.push( ( new InstanceCamera() ).parse( child ) ); + break; + + case 'instance_controller': + + this.controllers.push( ( new InstanceController() ).parse( child ) ); + break; + + case 'instance_geometry': + + this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); + break; + + case 'instance_light': + + this.lights.push( ( new InstanceLight() ).parse( child ) ); + break; + + case 'instance_node': + + url = child.getAttribute( 'url' ).replace( /^#/, '' ); + var iNode = getLibraryNode( url ); + + if ( iNode ) { + + this.nodes.push( ( new Node() ).parse( iNode )) ; + + } + + break; + + case 'rotate': + case 'translate': + case 'scale': + case 'matrix': + case 'lookat': + case 'skew': + + this.transforms.push( ( new Transform() ).parse( child ) ); + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + this.channels = getChannelsForNode( this ); + bakeAnimations( this ); + + this.updateMatrix(); + + return this; + + }; + + Node.prototype.updateMatrix = function () { + + this.matrix.identity(); + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + this.transforms[ i ].apply( this.matrix ); + + } + + }; + + function Transform () { + + this.sid = ""; + this.type = ""; + this.data = []; + this.obj = null; + + } + + Transform.prototype.parse = function ( element ) { + + this.sid = element.getAttribute( 'sid' ); + this.type = element.nodeName; + this.data = _floats( element.textContent ); + this.convert(); + + return this; + + }; + + Transform.prototype.convert = function () { + + switch ( this.type ) { + + case 'matrix': + + this.obj = getConvertedMat4( this.data ); + break; + + case 'rotate': + + this.angle = THREE.Math.degToRad( this.data[3] ); + + case 'translate': + + fixCoords( this.data, -1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + case 'scale': + + fixCoords( this.data, 1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + default: + console.log( 'Can not convert Transform of type ' + this.type ); + break; + + } + + }; + + Transform.prototype.apply = function () { + + var m1 = new THREE.Matrix4(); + + return function ( matrix ) { + + switch ( this.type ) { + + case 'matrix': + + matrix.multiply( this.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); + + break; + + case 'scale': + + matrix.scale( this.obj ); + + break; + + } + + }; + + }(); + + Transform.prototype.update = function ( data, member ) { + + var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; + + switch ( this.type ) { + + case 'matrix': + + if ( ! member ) { + + this.obj.copy( data ); + + } else if ( member.length === 1 ) { + + switch ( member[ 0 ] ) { + + case 0: + + this.obj.n11 = data[ 0 ]; + this.obj.n21 = data[ 1 ]; + this.obj.n31 = data[ 2 ]; + this.obj.n41 = data[ 3 ]; + + break; + + case 1: + + this.obj.n12 = data[ 0 ]; + this.obj.n22 = data[ 1 ]; + this.obj.n32 = data[ 2 ]; + this.obj.n42 = data[ 3 ]; + + break; + + case 2: + + this.obj.n13 = data[ 0 ]; + this.obj.n23 = data[ 1 ]; + this.obj.n33 = data[ 2 ]; + this.obj.n43 = data[ 3 ]; + + break; + + case 3: + + this.obj.n14 = data[ 0 ]; + this.obj.n24 = data[ 1 ]; + this.obj.n34 = data[ 2 ]; + this.obj.n44 = data[ 3 ]; + + break; + + } + + } else if ( member.length === 2 ) { + + var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); + this.obj[ propName ] = data; + + } else { + + console.log('Incorrect addressing of matrix in transform.'); + + } + + break; + + case 'translate': + case 'scale': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + break; + + } + + break; + + case 'rotate': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + case 'ANGLE': + + this.angle = THREE.Math.degToRad( data ); + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + this.angle = THREE.Math.degToRad( data[ 3 ] ); + break; + + } + break; + + } + + }; + + function InstanceController() { + + this.url = ""; + this.skeleton = []; + this.instance_material = []; + + } + + InstanceController.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + this.skeleton = []; + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'skeleton': + + this.skeleton.push( child.textContent.replace(/^#/, '') ); + break; + + case 'bind_material': + + var instances = child.querySelectorAll('instance_material'); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[j]; + this.instance_material.push( (new InstanceMaterial()).parse(instance) ); + + } + + + break; + + case 'extra': + break; + + default: + break; + + } + } + + return this; + + }; + + function InstanceMaterial () { + + this.symbol = ""; + this.target = ""; + + } + + InstanceMaterial.prototype.parse = function ( element ) { + + this.symbol = element.getAttribute('symbol'); + this.target = element.getAttribute('target').replace(/^#/, ''); + return this; + + }; + + function InstanceGeometry() { + + this.url = ""; + this.instance_material = []; + + } + + InstanceGeometry.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + if ( child.nodeName === 'bind_material' ) { + + var instances = child.querySelectorAll('instance_material'); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[j]; + this.instance_material.push( (new InstanceMaterial()).parse(instance) ); + + } + + break; + + } + + } + + return this; + + }; + + function Geometry() { + + this.id = ""; + this.mesh = null; + + } + + Geometry.prototype.parse = function ( element ) { + + this.id = element.getAttribute('id'); + + extractDoubleSided( this, element ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + + switch ( child.nodeName ) { + + case 'mesh': + + this.mesh = (new Mesh(this)).parse(child); + break; + + case 'extra': + + // console.log( child ); + break; + + default: + break; + } + } + + return this; + + }; + + function Mesh( geometry ) { + + this.geometry = geometry.id; + this.primitives = []; + this.vertices = null; + this.geometry3js = null; + + } + + Mesh.prototype.parse = function ( element ) { + + this.primitives = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'source': + + _source( child ); + break; + + case 'vertices': + + this.vertices = ( new Vertices() ).parse( child ); + break; + + case 'linestrips': + + this.primitives.push( ( new LineStrips().parse( child ) ) ); + break; + + case 'triangles': + + this.primitives.push( ( new Triangles().parse( child ) ) ); + break; + + case 'polygons': + + this.primitives.push( ( new Polygons().parse( child ) ) ); + break; + + case 'polylist': + + this.primitives.push( ( new Polylist().parse( child ) ) ); + break; + + default: + break; + + } + + } + + this.geometry3js = new THREE.Geometry(); + + if ( this.vertices === null ) { + + // TODO (mrdoob): Study case when this is null (carrier.dae) + + return this; + + } + + var vertexData = sources[ this.vertices.input['POSITION'].source ].data; + + for ( var i = 0; i < vertexData.length; i += 3 ) { + + this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); + + } + + for ( var i = 0; i < this.primitives.length; i ++ ) { + + var primitive = this.primitives[ i ]; + primitive.setVertices( this.vertices ); + this.handlePrimitive( primitive, this.geometry3js ); + + } + + if ( this.geometry3js.calcNormals ) { + + this.geometry3js.computeVertexNormals(); + delete this.geometry3js.calcNormals; + + } + + return this; + + }; + + Mesh.prototype.handlePrimitive = function ( primitive, geom ) { + + if ( primitive instanceof LineStrips ) { + + // TODO: Handle indices. Maybe easier with BufferGeometry? + + geom.isLineStrip = true; + return; + + } + + var j, k, pList = primitive.p, inputs = primitive.inputs; + var input, index, idx32; + var source, numParams; + var vcIndex = 0, vcount = 3, maxOffset = 0; + var texture_sets = []; + + for ( j = 0; j < inputs.length; j ++ ) { + + input = inputs[ j ]; + + var offset = input.offset + 1; + maxOffset = (maxOffset < offset) ? offset : maxOffset; + + switch ( input.semantic ) { + + case 'TEXCOORD': + texture_sets.push( input.set ); + break; + + } + + } + + for ( var pCount = 0; pCount < pList.length; ++ pCount ) { + + var p = pList[ pCount ], i = 0; + + while ( i < p.length ) { + + var vs = []; + var ns = []; + var ts = null; + var cs = []; + + if ( primitive.vcount ) { + + vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; + + } else { + + vcount = p.length / maxOffset; + + } + + + for ( j = 0; j < vcount; j ++ ) { + + for ( k = 0; k < inputs.length; k ++ ) { + + input = inputs[ k ]; + source = sources[ input.source ]; + + index = p[ i + ( j * maxOffset ) + input.offset ]; + numParams = source.accessor.params.length; + idx32 = index * numParams; + + switch ( input.semantic ) { + + case 'VERTEX': + + vs.push( index ); + + break; + + case 'NORMAL': + + ns.push( getConvertedVec3( source.data, idx32 ) ); + + break; + + case 'TEXCOORD': + + ts = ts || { }; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); + + break; + + case 'COLOR': + + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + break; + + default: + + break; + + } + + } + + } + + if ( ns.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.NORMAL; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); + + } + + } else { + + geom.calcNormals = true; + + } + + } + + if ( !ts ) { + + ts = { }; + // check the vertices inputs + input = this.vertices.input.TEXCOORD; + + if ( input ) { + + texture_sets.push( input.set ); + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); + + } + + } + + } + + if ( cs.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.COLOR; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + } + + } + + } + + var face = null, faces = [], uv, uvArr; + + if ( vcount === 3 ) { + + faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); + + } else if ( vcount === 4 ) { + + faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); + + faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); + + } else if ( vcount > 4 && options.subdivideFaces ) { + + var clr = cs.length ? cs : new THREE.Color(), + vec1, vec2, vec3, v1, v2, norm; + + // subdivide into multiple Face3s + + for ( k = 1; k < vcount - 1; ) { + + faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) ); + + } + + } + + if ( faces.length ) { + + for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { + + face = faces[ndx]; + face.daeMaterial = primitive.material; + geom.faces.push( face ); + + for ( k = 0; k < texture_sets.length; k ++ ) { + + uv = ts[ texture_sets[k] ]; + + if ( vcount > 4 ) { + + // Grab the right UVs for the vertices in this face + uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; + + } else if ( vcount === 4 ) { + + if ( ndx === 0 ) { + + uvArr = [ uv[0], uv[1], uv[3] ]; + + } else { + + uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; + + } + + } else { + + uvArr = [ uv[0], uv[1], uv[2] ]; + + } + + if ( geom.faceVertexUvs[k] === undefined ) { + + geom.faceVertexUvs[k] = []; + + } + + geom.faceVertexUvs[k].push( uvArr ); + + } + + } + + } else { + + console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); + + } + + i += maxOffset * vcount; + + } + + } + + }; + + function Polygons () { + + this.material = ""; + this.count = 0; + this.inputs = []; + this.vcount = null; + this.p = []; + this.geometry = new THREE.Geometry(); + + } + + Polygons.prototype.setVertices = function ( vertices ) { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + if ( this.inputs[ i ].source === vertices.id ) { + + this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; + + } + + } + + }; + + Polygons.prototype.parse = function ( element ) { + + this.material = element.getAttribute( 'material' ); + this.count = _attr_as_int( element, 'count', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); + break; + + case 'vcount': + + this.vcount = _ints( child.textContent ); + break; + + case 'p': + + this.p.push( _ints( child.textContent ) ); + break; + + case 'ph': + + console.warn( 'polygon holes not yet supported!' ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Polylist () { + + Polygons.call( this ); + + this.vcount = []; + + } + + Polylist.prototype = Object.create( Polygons.prototype ); + Polylist.prototype.constructor = Polylist; + + function LineStrips() { + + Polygons.call( this ); + + this.vcount = 1; + + } + + LineStrips.prototype = Object.create( Polygons.prototype ); + LineStrips.prototype.constructor = LineStrips; + + function Triangles () { + + Polygons.call( this ); + + this.vcount = 3; + + } + + Triangles.prototype = Object.create( Polygons.prototype ); + Triangles.prototype.constructor = Triangles; + + function Accessor() { + + this.source = ""; + this.count = 0; + this.stride = 0; + this.params = []; + + } + + Accessor.prototype.parse = function ( element ) { + + this.params = []; + this.source = element.getAttribute( 'source' ); + this.count = _attr_as_int( element, 'count', 0 ); + this.stride = _attr_as_int( element, 'stride', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'param' ) { + + var param = {}; + param[ 'name' ] = child.getAttribute( 'name' ); + param[ 'type' ] = child.getAttribute( 'type' ); + this.params.push( param ); + + } + + } + + return this; + + }; + + function Vertices() { + + this.input = {}; + + } + + Vertices.prototype.parse = function ( element ) { + + this.id = element.getAttribute('id'); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[i].nodeName === 'input' ) { + + var input = ( new Input() ).parse( element.childNodes[ i ] ); + this.input[ input.semantic ] = input; + + } + + } + + return this; + + }; + + function Input () { + + this.semantic = ""; + this.offset = 0; + this.source = ""; + this.set = 0; + + } + + Input.prototype.parse = function ( element ) { + + this.semantic = element.getAttribute('semantic'); + this.source = element.getAttribute('source').replace(/^#/, ''); + this.set = _attr_as_int(element, 'set', -1); + this.offset = _attr_as_int(element, 'offset', 0); + + if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { + + this.set = 0; + + } + + return this; + + }; + + function Source ( id ) { + + this.id = id; + this.type = null; + + } + + Source.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + + switch ( child.nodeName ) { + + case 'bool_array': + + this.data = _bools( child.textContent ); + this.type = child.nodeName; + break; + + case 'float_array': + + this.data = _floats( child.textContent ); + this.type = child.nodeName; + break; + + case 'int_array': + + this.data = _ints( child.textContent ); + this.type = child.nodeName; + break; + + case 'IDREF_array': + case 'Name_array': + + this.data = _strings( child.textContent ); + this.type = child.nodeName; + break; + + case 'technique_common': + + for ( var j = 0; j < child.childNodes.length; j ++ ) { + + if ( child.childNodes[ j ].nodeName === 'accessor' ) { + + this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); + break; + + } + } + break; + + default: + // console.log(child.nodeName); + break; + + } + + } + + return this; + + }; + + Source.prototype.read = function () { + + var result = []; + + //for (var i = 0; i < this.accessor.params.length; i++) { + + var param = this.accessor.params[ 0 ]; + + //console.log(param.name + " " + param.type); + + switch ( param.type ) { + + case 'IDREF': + case 'Name': case 'name': + case 'float': + + return this.data; + + case 'float4x4': + + for ( var j = 0; j < this.data.length; j += 16 ) { + + var s = this.data.slice( j, j + 16 ); + var m = getConvertedMat4( s ); + result.push( m ); + } + + break; + + default: + + console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); + break; + + } + + //} + + return result; + + }; + + function Material () { + + this.id = ""; + this.name = ""; + this.instance_effect = null; + + } + + Material.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { + + this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); + break; + + } + + } + + return this; + + }; + + function ColorOrTexture () { + + this.color = new THREE.Color(); + this.color.setRGB( Math.random(), Math.random(), Math.random() ); + this.color.a = 1.0; + + this.texture = null; + this.texcoord = null; + this.texOpts = null; + + } + + ColorOrTexture.prototype.isColor = function () { + + return ( this.texture === null ); + + }; + + ColorOrTexture.prototype.isTexture = function () { + + return ( this.texture != null ); + + }; + + ColorOrTexture.prototype.parse = function ( element ) { + + if (element.nodeName === 'transparent') { + + this.opaque = element.getAttribute('opaque'); + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(); + this.color.setRGB( rgba[0], rgba[1], rgba[2] ); + this.color.a = rgba[3]; + break; + + case 'texture': + + this.texture = child.getAttribute('texture'); + this.texcoord = child.getAttribute('texcoord'); + // Defaults from: + // https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension + this.texOpts = { + offsetU: 0, + offsetV: 0, + repeatU: 1, + repeatV: 1, + wrapU: 1, + wrapV: 1 + }; + this.parseTexture( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + ColorOrTexture.prototype.parseTexture = function ( element ) { + + if ( ! element.childNodes ) return this; + + // This should be supported by Maya, 3dsMax, and MotionBuilder + + if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { + + element = element.childNodes[1]; + + if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { + + element = element.childNodes[1]; + + } + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'offsetU': + case 'offsetV': + case 'repeatU': + case 'repeatV': + + this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); + + break; + + case 'wrapU': + case 'wrapV': + + // some dae have a value of true which becomes NaN via parseInt + + if ( child.textContent.toUpperCase() === 'TRUE' ) { + + this.texOpts[ child.nodeName ] = 1; + + } else { + + this.texOpts[ child.nodeName ] = parseInt( child.textContent ); + + } + break; + + default: + + this.texOpts[ child.nodeName ] = child.textContent; + + break; + + } + + } + + return this; + + }; + + function Shader ( type, effect ) { + + this.type = type; + this.effect = effect; + this.material = null; + + } + + Shader.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'emission': + case 'diffuse': + case 'specular': + case 'transparent': + + this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); + break; + + case 'bump': + + // If 'bumptype' is 'heightfield', create a 'bump' property + // Else if 'bumptype' is 'normalmap', create a 'normal' property + // (Default to 'bump') + var bumpType = child.getAttribute( 'bumptype' ); + if ( bumpType ) { + if ( bumpType.toLowerCase() === "heightfield" ) { + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } else if ( bumpType.toLowerCase() === "normalmap" ) { + this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); + } else { + console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } + } else { + console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } + + break; + + case 'shininess': + case 'reflectivity': + case 'index_of_refraction': + case 'transparency': + + var f = child.querySelectorAll('float'); + + if ( f.length > 0 ) + this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); + + break; + + default: + break; + + } + + } + + this.create(); + return this; + + }; + + Shader.prototype.create = function() { + + var props = {}; + + var transparent = false; + + if (this['transparency'] !== undefined && this['transparent'] !== undefined) { + // convert transparent color RBG to average value + var transparentColor = this['transparent']; + var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; + + if (transparencyLevel > 0) { + transparent = true; + props[ 'transparent' ] = true; + props[ 'opacity' ] = 1 - transparencyLevel; + + } + + } + + var keys = { + 'diffuse':'map', + 'ambient':'lightMap', + 'specular':'specularMap', + 'emission':'emissionMap', + 'bump':'bumpMap', + 'normal':'normalMap' + }; + + for ( var prop in this ) { + + switch ( prop ) { + + case 'ambient': + case 'emission': + case 'diffuse': + case 'specular': + case 'bump': + case 'normal': + + var cot = this[ prop ]; + + if ( cot instanceof ColorOrTexture ) { + + if ( cot.isTexture() ) { + + var samplerId = cot.texture; + var sampler = this.effect.sampler[samplerId]; + + if ( sampler !== undefined && sampler.source !== undefined ) { + + var surface = this.effect.surface[sampler.source]; + + if ( surface !== undefined ) { + + var image = images[ surface.init_from ]; + + if ( image ) { + + var url = baseUrl + image.init_from; + + var texture; + var loader = THREE.Loader.Handlers.get( url ); + + if ( loader !== null ) { + + texture = loader.load( url ); + + } else { + + texture = new THREE.Texture(); + + loadTextureImage( texture, url ); + + } + + if ( sampler.wrap_s === "MIRROR" ) { + + texture.wrapS = THREE.MirroredRepeatWrapping; + + } else if ( sampler.wrap_s === "WRAP" || cot.texOpts.wrapU ) { + + texture.wrapS = THREE.RepeatWrapping; + + } else { + + texture.wrapS = THREE.ClampToEdgeWrapping; + + } + + if ( sampler.wrap_t === "MIRROR" ) { + + texture.wrapT = THREE.MirroredRepeatWrapping; + + } else if ( sampler.wrap_t === "WRAP" || cot.texOpts.wrapV ) { + + texture.wrapT = THREE.RepeatWrapping; + + } else { + + texture.wrapT = THREE.ClampToEdgeWrapping; + + } + + texture.offset.x = cot.texOpts.offsetU; + texture.offset.y = cot.texOpts.offsetV; + texture.repeat.x = cot.texOpts.repeatU; + texture.repeat.y = cot.texOpts.repeatV; + props[keys[prop]] = texture; + + // Texture with baked lighting? + if (prop === 'emission') props['emissive'] = 0xffffff; + + } + + } + + } + + } else if ( prop === 'diffuse' || !transparent ) { + + if ( prop === 'emission' ) { + + props[ 'emissive' ] = cot.color.getHex(); + + } else { + + props[ prop ] = cot.color.getHex(); + + } + + } + + } + + break; + + case 'shininess': + + props[ prop ] = this[ prop ]; + break; + + case 'reflectivity': + + props[ prop ] = this[ prop ]; + if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; + props['combine'] = THREE.MixOperation; //mix regular shading with reflective component + break; + + case 'index_of_refraction': + + props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable + if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; + break; + + case 'transparency': + // gets figured out up top + break; + + default: + break; + + } + + } + + props[ 'shading' ] = preferredShading; + props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; + + if ( props.diffuse !== undefined ) { + + props.color = props.diffuse; + delete props.diffuse; + + } + + switch ( this.type ) { + + case 'constant': + + if (props.emissive != undefined) props.color = props.emissive; + this.material = new THREE.MeshBasicMaterial( props ); + break; + + case 'phong': + case 'blinn': + + this.material = new THREE.MeshPhongMaterial( props ); + break; + + case 'lambert': + default: + + this.material = new THREE.MeshLambertMaterial( props ); + break; + + } + + return this.material; + + }; + + function Surface ( effect ) { + + this.effect = effect; + this.init_from = null; + this.format = null; + + } + + Surface.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'init_from': + + this.init_from = child.textContent; + break; + + case 'format': + + this.format = child.textContent; + break; + + default: + + console.log( "unhandled Surface prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Sampler2D ( effect ) { + + this.effect = effect; + this.source = null; + this.wrap_s = null; + this.wrap_t = null; + this.minfilter = null; + this.magfilter = null; + this.mipfilter = null; + + } + + Sampler2D.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + this.source = child.textContent; + break; + + case 'minfilter': + + this.minfilter = child.textContent; + break; + + case 'magfilter': + + this.magfilter = child.textContent; + break; + + case 'mipfilter': + + this.mipfilter = child.textContent; + break; + + case 'wrap_s': + + this.wrap_s = child.textContent; + break; + + case 'wrap_t': + + this.wrap_t = child.textContent; + break; + + default: + + console.log( "unhandled Sampler2D prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Effect () { + + this.id = ""; + this.name = ""; + this.shader = null; + this.surface = {}; + this.sampler = {}; + + } + + Effect.prototype.create = function () { + + if ( this.shader === null ) { + + return null; + + } + + }; + + Effect.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + extractDoubleSided( this, element ); + + this.shader = null; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseTechnique( this.parseProfileCOMMON( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Effect.prototype.parseNewparam = function ( element ) { + + var sid = element.getAttribute( 'sid' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'surface': + + this.surface[sid] = ( new Surface( this ) ).parse( child ); + break; + + case 'sampler2D': + + this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); + break; + + case 'extra': + + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + }; + + Effect.prototype.parseProfileCOMMON = function ( element ) { + + var technique; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseProfileCOMMON( child ); + break; + + case 'technique': + + technique = child; + break; + + case 'newparam': + + this.parseNewparam( child ); + break; + + case 'image': + + var _image = ( new _Image() ).parse( child ); + images[ _image.id ] = _image; + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + return technique; + + }; + + Effect.prototype.parseTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'constant': + case 'lambert': + case 'blinn': + case 'phong': + + this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); + break; + case 'extra': + this.parseExtra(child); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtra = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique': + this.parseExtraTechnique( child ); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtraTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bump': + this.shader.parse( element ); + break; + default: + break; + + } + + } + + }; + + function InstanceEffect () { + + this.url = ""; + + } + + InstanceEffect.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + return this; + + }; + + function Animation() { + + this.id = ""; + this.name = ""; + this.source = {}; + this.sampler = []; + this.channel = []; + + } + + Animation.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.source = {}; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'animation': + + var anim = ( new Animation() ).parse( child ); + + for ( var src in anim.source ) { + + this.source[ src ] = anim.source[ src ]; + + } + + for ( var j = 0; j < anim.channel.length; j ++ ) { + + this.channel.push( anim.channel[ j ] ); + this.sampler.push( anim.sampler[ j ] ); + + } + + break; + + case 'source': + + var src = ( new Source() ).parse( child ); + this.source[ src.id ] = src; + break; + + case 'sampler': + + this.sampler.push( ( new Sampler( this ) ).parse( child ) ); + break; + + case 'channel': + + this.channel.push( ( new Channel( this ) ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Channel( animation ) { + + this.animation = animation; + this.source = ""; + this.target = ""; + this.fullSid = null; + this.sid = null; + this.dotSyntax = null; + this.arrSyntax = null; + this.arrIndices = null; + this.member = null; + + } + + Channel.prototype.parse = function ( element ) { + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.target = element.getAttribute( 'target' ); + + var parts = this.target.split( '/' ); + + var id = parts.shift(); + var sid = parts.shift(); + + var dotSyntax = ( sid.indexOf(".") >= 0 ); + var arrSyntax = ( sid.indexOf("(") >= 0 ); + + if ( dotSyntax ) { + + parts = sid.split("."); + this.sid = parts.shift(); + this.member = parts.shift(); + + } else if ( arrSyntax ) { + + var arrIndices = sid.split("("); + this.sid = arrIndices.shift(); + + for (var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); + + } + + this.arrIndices = arrIndices; + + } else { + + this.sid = sid; + + } + + this.fullSid = sid; + this.dotSyntax = dotSyntax; + this.arrSyntax = arrSyntax; + + return this; + + }; + + function Sampler ( animation ) { + + this.id = ""; + this.animation = animation; + this.inputs = []; + this.input = null; + this.output = null; + this.strideOut = null; + this.interpolation = null; + this.startTime = null; + this.endTime = null; + this.duration = 0; + + } + + Sampler.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( (new Input()).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Sampler.prototype.create = function () { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + var input = this.inputs[ i ]; + var source = this.animation.source[ input.source ]; + + switch ( input.semantic ) { + + case 'INPUT': + + this.input = source.read(); + break; + + case 'OUTPUT': + + this.output = source.read(); + this.strideOut = source.accessor.stride; + break; + + case 'INTERPOLATION': + + this.interpolation = source.read(); + break; + + case 'IN_TANGENT': + + break; + + case 'OUT_TANGENT': + + break; + + default: + + console.log(input.semantic); + break; + + } + + } + + this.startTime = 0; + this.endTime = 0; + this.duration = 0; + + if ( this.input.length ) { + + this.startTime = 100000000; + this.endTime = -100000000; + + for ( var i = 0; i < this.input.length; i ++ ) { + + this.startTime = Math.min( this.startTime, this.input[ i ] ); + this.endTime = Math.max( this.endTime, this.input[ i ] ); + + } + + this.duration = this.endTime - this.startTime; + + } + + }; + + Sampler.prototype.getData = function ( type, ndx, member ) { + + var data; + + if ( type === 'matrix' && this.strideOut === 16 ) { + + data = this.output[ ndx ]; + + } else if ( this.strideOut > 1 ) { + + data = []; + ndx *= this.strideOut; + + for ( var i = 0; i < this.strideOut; ++ i ) { + + data[ i ] = this.output[ ndx + i ]; + + } + + if ( this.strideOut === 3 ) { + + switch ( type ) { + + case 'rotate': + case 'translate': + + fixCoords( data, -1 ); + break; + + case 'scale': + + fixCoords( data, 1 ); + break; + + } + + } else if ( this.strideOut === 4 && type === 'matrix' ) { + + fixCoords( data, -1 ); + + } + + } else { + + data = this.output[ ndx ]; + + if ( member && type === 'translate' ) { + data = getConvertedTranslation( member, data ); + } + + } + + return data; + + }; + + function Key ( time ) { + + this.targets = []; + this.time = time; + + } + + Key.prototype.addTarget = function ( fullSid, transform, member, data ) { + + this.targets.push( { + sid: fullSid, + member: member, + transform: transform, + data: data + } ); + + }; + + Key.prototype.apply = function ( opt_sid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + var target = this.targets[ i ]; + + if ( !opt_sid || target.sid === opt_sid ) { + + target.transform.update( target.data, target.member ); + + } + + } + + }; + + Key.prototype.getTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return this.targets[ i ]; + + } + + } + + return null; + + }; + + Key.prototype.hasTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return true; + + } + + } + + return false; + + }; + + // TODO: Currently only doing linear interpolation. Should support full COLLADA spec. + Key.prototype.interpolate = function ( nextKey, time ) { + + for ( var i = 0, l = this.targets.length; i < l; i ++ ) { + + var target = this.targets[ i ], + nextTarget = nextKey.getTarget( target.sid ), + data; + + if ( target.transform.type !== 'matrix' && nextTarget ) { + + var scale = ( time - this.time ) / ( nextKey.time - this.time ), + nextData = nextTarget.data, + prevData = target.data; + + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; + + if ( prevData.length ) { + + data = []; + + for ( var j = 0; j < prevData.length; ++ j ) { + + data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + } else { + + data = target.data; + + } + + target.transform.update( data, target.member ); + + } + + }; + + // Camera + function Camera() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + } + + Camera.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'optics': + + this.parseOptics( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Camera.prototype.parseOptics = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'technique_common' ) { + + var technique = element.childNodes[ i ]; + + for ( var j = 0; j < technique.childNodes.length; j ++ ) { + + this.technique = technique.childNodes[ j ].nodeName; + + if ( this.technique === 'perspective' ) { + + var perspective = technique.childNodes[ j ]; + + for ( var k = 0; k < perspective.childNodes.length; k ++ ) { + + var param = perspective.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'yfov': + this.yfov = param.textContent; + break; + case 'xfov': + this.xfov = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } else if ( this.technique === 'orthographic' ) { + + var orthographic = technique.childNodes[ j ]; + + for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { + + var param = orthographic.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'xmag': + this.xmag = param.textContent; + break; + case 'ymag': + this.ymag = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } + + } + + } + + } + + return this; + + }; + + function InstanceCamera() { + + this.url = ""; + + } + + InstanceCamera.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + + return this; + + }; + + // Light + + function Light() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + } + + Light.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon( child ); + break; + + case 'technique': + + this.parseTechnique( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Light.prototype.parseCommon = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + switch ( element.childNodes[ i ].nodeName ) { + + case 'directional': + case 'point': + case 'spot': + case 'ambient': + + this.technique = element.childNodes[ i ].nodeName; + + var light = element.childNodes[ i ]; + + for ( var j = 0; j < light.childNodes.length; j ++ ) { + + var child = light.childNodes[j]; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(0); + this.color.setRGB( rgba[0], rgba[1], rgba[2] ); + this.color.a = rgba[3]; + break; + + case 'falloff_angle': + + this.falloff_angle = parseFloat( child.textContent ); + break; + + case 'quadratic_attenuation': + var f = parseFloat( child.textContent ); + this.distance = f ? Math.sqrt( 1 / f ) : 0; + } + + } + + } + + } + + return this; + + }; + + Light.prototype.parseTechnique = function ( element ) { + + this.profile = element.getAttribute( 'profile' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'intensity': + + this.intensity = parseFloat(child.textContent); + break; + + } + + } + + return this; + + }; + + function InstanceLight() { + + this.url = ""; + + } + + InstanceLight.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + + return this; + + }; + + function KinematicsModel( ) { + + this.id = ''; + this.name = ''; + this.joints = []; + this.links = []; + + } + + KinematicsModel.prototype.parse = function( element ) { + + this.id = element.getAttribute('id'); + this.name = element.getAttribute('name'); + this.joints = []; + this.links = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon(child); + break; + + default: + break; + + } + + } + + return this; + + }; + + KinematicsModel.prototype.parseCommon = function( element ) { + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( element.childNodes[ i ].nodeName ) { + + case 'joint': + this.joints.push( (new Joint()).parse(child) ); + break; + + case 'link': + this.links.push( (new Link()).parse(child) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Joint( ) { + + this.sid = ''; + this.name = ''; + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + } + + Joint.prototype.parse = function( element ) { + + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + var axisElement = element.querySelector('axis'); + var _axis = _floats(axisElement.textContent); + this.axis = getConvertedVec3(_axis, 0); + + var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; + var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; + + this.limits = { + min: min, + max: max + }; + + var jointTypes = [ 'prismatic', 'revolute' ]; + for (var i = 0; i < jointTypes.length; i ++ ) { + + var type = jointTypes[ i ]; + + var jointElement = element.querySelector(type); + + if ( jointElement ) { + + this.type = type; + + } + + } + + // if the min is equal to or somehow greater than the max, consider the joint static + if ( this.limits.min >= this.limits.max ) { + + this.static = true; + + } + + this.middlePosition = (this.limits.min + this.limits.max) / 2.0; + return this; + + }; + + function Link( ) { + + this.sid = ''; + this.name = ''; + this.transforms = []; + this.attachments = []; + + } + + Link.prototype.parse = function( element ) { + + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.transforms = []; + this.attachments = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'attachment_full': + this.attachments.push( (new Attachment()).parse(child) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( (new Transform()).parse(child) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function Attachment( ) { + + this.joint = ''; + this.transforms = []; + this.links = []; + + } + + Attachment.prototype.parse = function( element ) { + + this.joint = element.getAttribute('joint').split('/').pop(); + this.links = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'link': + this.links.push( (new Link()).parse(child) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( (new Transform()).parse(child) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function _source( element ) { + + var id = element.getAttribute( 'id' ); + + if ( sources[ id ] != undefined ) { + + return sources[ id ]; + + } + + sources[ id ] = ( new Source(id )).parse( element ); + return sources[ id ]; + + } + + function _nsResolver( nsPrefix ) { + + if ( nsPrefix === "dae" ) { + + return "http://www.collada.org/2005/11/COLLADASchema"; + + } + + return null; + + } + + function _bools( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); + + } + + return data; + + } + + function _floats( str ) { + + var raw = _strings(str); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseFloat( raw[ i ] ) ); + + } + + return data; + + } + + function _ints( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseInt( raw[ i ], 10 ) ); + + } + + return data; + + } + + function _strings( str ) { + + return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; + + } + + function _trimString( str ) { + + return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); + + } + + function _attr_as_float( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseFloat( element.getAttribute( name ) ); + + } else { + + return defaultValue; + + } + + } + + function _attr_as_int( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseInt( element.getAttribute( name ), 10) ; + + } else { + + return defaultValue; + + } + + } + + function _attr_as_string( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return element.getAttribute( name ); + + } else { + + return defaultValue; + + } + + } + + function _format_float( f, num ) { + + if ( f === undefined ) { + + var s = '0.'; + + while ( s.length < num + 2 ) { + + s += '0'; + + } + + return s; + + } + + num = num || 2; + + var parts = f.toString().split( '.' ); + parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; + + while ( parts[ 1 ].length < num ) { + + parts[ 1 ] += '0'; + + } + + return parts.join( '.' ); + + } + + function loadTextureImage ( texture, url ) { + + var loader = new THREE.ImageLoader(); + + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + } ); + + } + + function extractDoubleSided( obj, element ) { + + obj.doubleSided = false; + + var node = element.querySelectorAll('extra double_sided')[0]; + + if ( node ) { + + if ( node && parseInt( node.textContent, 10 ) === 1 ) { + + obj.doubleSided = true; + + } + + } + + } + + // Up axis conversion + + function setUpConversion() { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + upConversion = null; + + } else { + + switch ( colladaUp ) { + + case 'X': + + upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; + break; + + case 'Y': + + upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; + break; + + case 'Z': + + upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; + break; + + } + + } + + } + + function fixCoords( data, sign ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return; + + } + + switch ( upConversion ) { + + case 'XtoY': + + var tmp = data[ 0 ]; + data[ 0 ] = sign * data[ 1 ]; + data[ 1 ] = tmp; + break; + + case 'XtoZ': + + var tmp = data[ 2 ]; + data[ 2 ] = data[ 1 ]; + data[ 1 ] = data[ 0 ]; + data[ 0 ] = tmp; + break; + + case 'YtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = sign * tmp; + break; + + case 'YtoZ': + + var tmp = data[ 1 ]; + data[ 1 ] = sign * data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoY': + + var tmp = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = sign * tmp; + break; + + } + + } + + function getConvertedTranslation( axis, data ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return data; + + } + + switch ( axis ) { + case 'X': + data = upConversion === 'XtoY' ? data * -1 : data; + break; + case 'Y': + data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; + break; + case 'Z': + data = upConversion === 'ZtoY' ? data * -1 : data ; + break; + default: + break; + } + + return data; + } + + function getConvertedVec3( data, offset ) { + + var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; + fixCoords( arr, -1 ); + return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); + + } + + function getConvertedMat4( data ) { + + if ( options.convertUpAxis ) { + + // First fix rotation and scale + + // Columns first + var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; + fixCoords( arr, -1 ); + data[ 0 ] = arr[ 0 ]; + data[ 4 ] = arr[ 1 ]; + data[ 8 ] = arr[ 2 ]; + arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; + fixCoords( arr, -1 ); + data[ 1 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 9 ] = arr[ 2 ]; + arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; + fixCoords( arr, -1 ); + data[ 2 ] = arr[ 0 ]; + data[ 6 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + // Rows second + arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; + fixCoords( arr, -1 ); + data[ 0 ] = arr[ 0 ]; + data[ 1 ] = arr[ 1 ]; + data[ 2 ] = arr[ 2 ]; + arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; + fixCoords( arr, -1 ); + data[ 4 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 6 ] = arr[ 2 ]; + arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; + fixCoords( arr, -1 ); + data[ 8 ] = arr[ 0 ]; + data[ 9 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + + // Now fix translation + arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; + fixCoords( arr, -1 ); + data[ 3 ] = arr[ 0 ]; + data[ 7 ] = arr[ 1 ]; + data[ 11 ] = arr[ 2 ]; + + } + + return new THREE.Matrix4().set( + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], + data[12], data[13], data[14], data[15] + ); + + } + + function getConvertedIndex( index ) { + + if ( index > -1 && index < 3 ) { + + var members = [ 'X', 'Y', 'Z' ], + indices = { X: 0, Y: 1, Z: 2 }; + + index = getConvertedMember( members[ index ] ); + index = indices[ index ]; + + } + + return index; + + } + + function getConvertedMember( member ) { + + if ( options.convertUpAxis ) { + + switch ( member ) { + + case 'X': + + switch ( upConversion ) { + + case 'XtoY': + case 'XtoZ': + case 'YtoX': + + member = 'Y'; + break; + + case 'ZtoX': + + member = 'Z'; + break; + + } + + break; + + case 'Y': + + switch ( upConversion ) { + + case 'XtoY': + case 'YtoX': + case 'ZtoX': + + member = 'X'; + break; + + case 'XtoZ': + case 'YtoZ': + case 'ZtoY': + + member = 'Z'; + break; + + } + + break; + + case 'Z': + + switch ( upConversion ) { + + case 'XtoZ': + + member = 'X'; + break; + + case 'YtoZ': + case 'ZtoX': + case 'ZtoY': + + member = 'Y'; + break; + + } + + break; + + } + + } + + return member; + + } + + return { + + load: load, + parse: parse, + setPreferredShading: setPreferredShading, + applySkin: applySkin, + geometries : geometries, + options: options + + }; + +}; +// File: utils/GLTF2Loader.js +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + * @author Don McCurdy / https://www.donmccurdy.com + */ + +THREE.GLTF2Loader = ( function () { + + function GLTF2Loader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + GLTF2Loader.prototype = { + + constructor: GLTF2Loader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); + + var loader = new THREE.FileLoader( scope.manager ); + + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( data ) { + + scope.parse( data, onLoad, path ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setPath: function ( value ) { + + this.path = value; + + }, + + parse: function ( data, callback, path ) { + + var content; + var extensions = {}; + + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { + + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + + } else { + + content = convertUint8ArrayToString( new Uint8Array( data ) ); + + } + + var json = JSON.parse( content ); + + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + + } + + console.time( 'GLTF2Loader' ); + + var parser = new GLTFParser( json, extensions, { + + path: path || this.path, + crossOrigin: this.crossOrigin + + } ); + + parser.parse( function ( scene, scenes, cameras, animations ) { + + console.timeEnd( 'GLTF2Loader' ); + + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + + callback( glTF ); + + } ); + + } + + }; + + /* GLTFREGISTRY */ + + function GLTFRegistry() { + + var objects = {}; + + return { + + get: function ( key ) { + + return objects[ key ]; + + }, + + add: function ( key, object ) { + + objects[ key ] = object; + + }, + + remove: function ( key ) { + + delete objects[ key ]; + + }, + + removeAll: function () { + + objects = {}; + + }, + + update: function ( scene, camera ) { + + for ( var name in objects ) { + + var object = objects[ name ]; + + if ( object.update ) { + + object.update( scene, camera ); + + } + + } + + } + + }; + + } + + /* GLTFSHADER */ + + function GLTFShader( targetNode, allNodes ) { + + var boundUniforms = {}; + + // bind each uniform to its source node + + var uniforms = targetNode.material.uniforms; + + for ( var uniformId in uniforms ) { + + var uniform = uniforms[ uniformId ]; + + if ( uniform.semantic ) { + + var sourceNodeRef = uniform.node; + + var sourceNode = targetNode; + + if ( sourceNodeRef ) { + + sourceNode = allNodes[ sourceNodeRef ]; + + } + + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + + } + + } + + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + + } + + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + + var boundUniforms = this.boundUniforms; + + for ( var name in boundUniforms ) { + + var boundUniform = boundUniforms[ name ]; + + switch ( boundUniform.semantic ) { + + case "MODELVIEW": + + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + + case "MODELVIEWINVERSETRANSPOSE": + + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + + case "PROJECTION": + + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + + case "JOINTMATRIX": + + var m4v = boundUniform.uniform.value; + + for ( var mi = 0; mi < m4v.length; mi ++ ) { + + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + + } + + break; + + default : + + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + + } + + } + + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' + }; + + /* MATERIALS COMMON EXTENSION */ + + function GLTFMaterialsCommonExtension( json ) { + + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + + this.lights = {}; + + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + + for ( var lightId in lights ) { + + var light = lights[ lightId ]; + var lightNode; + + var lightParams = light[ light.type ]; + var color = new THREE.Color().fromArray( lightParams.color ); + + switch ( light.type ) { + + case "directional": + lightNode = new THREE.DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "point": + lightNode = new THREE.PointLight( color ); + break; + + case "spot": + lightNode = new THREE.SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "ambient": + lightNode = new THREE.AmbientLight( color ); + break; + + } + + if ( lightNode ) { + + this.lights[ lightId ] = lightNode; + + } + + } + + } + + /* BINARY EXTENSION */ + + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; + var BINARY_EXTENSION_HEADER_LENGTH = 12; + var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; + + function GLTFBinaryExtension( data ) { + + this.name = EXTENSIONS.KHR_BINARY_GLTF; + this.content = null; + this.body = null; + + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + + this.header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ) + }; + + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { + + throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); + + } else if ( this.header.version < 2.0 ) { + + throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); + + } + + var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); + var chunkIndex = 0; + + while ( chunkIndex < chunkView.byteLength ) { + + var chunkLength = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + var chunkType = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { + + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); + this.content = convertUint8ArrayToString( contentArray ); + + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { + + var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; + this.body = data.slice( byteOffset, byteOffset + chunkLength ); + + } + + // Clients must ignore chunks with unknown types. + + chunkIndex += chunkLength; + + } + + if ( this.content === null ) { + + throw new Error( 'GLTF2Loader: JSON content not found.' ); + + } + + } + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + + /* CONSTANTS */ + + var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 + }; + + var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: THREE.Matrix3, + 35676: THREE.Matrix4, + 35664: THREE.Vector2, + 35665: THREE.Vector3, + 35666: THREE.Vector4, + 35678: THREE.Texture + }; + + var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + + var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipMapNearestFilter, + 9985: THREE.LinearMipMapNearestFilter, + 9986: THREE.NearestMipMapLinearFilter, + 9987: THREE.LinearMipMapLinearFilter + }; + + var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping + }; + + var WEBGL_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + + function _each( object, callback, thisObj ) { + + if ( !object ) { + return Promise.resolve(); + } + + var results; + var fns = []; + + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + + results = []; + + var length = object.length; + + for ( var idx = 0; idx < length; idx ++ ) { + + var value = callback.call( thisObj || this, object[ idx ], idx ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function( key, value ) { + + results[ key ] = value; + + }.bind( this, idx )); + + } else { + + results[ idx ] = value; + + } + + } + + } + + } else { + + results = {}; + + for ( var key in object ) { + + if ( object.hasOwnProperty( key ) ) { + + var value = callback.call( thisObj || this, object[ key ], key ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function( key, value ) { + + results[ key ] = value; + + }.bind( this, key )); + + } else { + + results[ key ] = value; + + } + + } + + } + + } + + } + + return Promise.all( fns ).then( function() { + + return results; + + }); + + } + + function resolveURL( url, path ) { + + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + + return url; + + } + + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + + return url; + + } + + // Blob URL + if ( /^blob:.*$/i.test( url ) ) { + + return url; + + } + + // Relative URL + return ( path || '' ) + url; + + } + + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + function convertUint8ArrayToString( array ) { + + var s = ''; + + for ( var i = 0; i < array.length; i ++ ) { + + s += String.fromCharCode( array[ i ] ); + + } + + return s; + + } + + // Three.js seems too dependent on attribute names so globally + // replace those in the shader code + function replaceTHREEShaderAttributes( shaderText, technique ) { + + // Expected technique attributes + var attributes = {}; + + for ( var attributeId in technique.attributes ) { + + var pname = technique.attributes[ attributeId ]; + + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + + } + + // Figure out which attributes to change in technique + + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + + for ( var attributeId in attributes ) { + + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + + params[ attributeId ] = shaderParam; + + } + + } + + for ( var pname in params ) { + + var param = params[ pname ]; + var semantic = param.semantic; + + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + + switch ( semantic ) { + + case 'POSITION': + + shaderText = shaderText.replace( regEx, 'position' ); + break; + + case 'NORMAL': + + shaderText = shaderText.replace( regEx, 'normal' ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + shaderText = shaderText.replace( regEx, 'uv' ); + break; + + case 'TEXCOORD_1': + + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + shaderText = shaderText.replace( regEx, 'color' ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + + } + + } + + return shaderText; + + } + + function createDefaultMaterial() { + + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + + } + + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + + this.isDeferredShaderMaterial = true; + + this.params = params; + + } + + DeferredShaderMaterial.prototype.create = function () { + + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + + for ( var uniformId in this.params.uniforms ) { + + var originalUniform = this.params.uniforms[ uniformId ]; + + if ( originalUniform.value instanceof THREE.Texture ) { + + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + + } + + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + + } + + this.params.uniforms = uniforms; + + return new THREE.RawShaderMaterial( this.params ); + + }; + + /* GLTF PARSER */ + + function GLTFParser( json, extensions, options ) { + + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + + // loader object cache + this.cache = new GLTFRegistry(); + + } + + GLTFParser.prototype._withDependencies = function ( dependencies ) { + + var _dependencies = {}; + + for ( var i = 0; i < dependencies.length; i ++ ) { + + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + + var cached = this.cache.get( dependency ); + + if ( cached !== undefined ) { + + _dependencies[ dependency ] = cached; + + } else if ( this[ fnName ] ) { + + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + + _dependencies[ dependency ] = fn; + + } + + } + + return _each( _dependencies, function ( dependency ) { + + return dependency; + + } ); + + }; + + GLTFParser.prototype.parse = function ( callback ) { + + var json = this.json; + + // Clear the loader cache + this.cache.removeAll(); + + // Fire the callback on complete + this._withDependencies( [ + + "scenes", + "cameras", + "animations" + + ] ).then( function ( dependencies ) { + + var scenes = []; + + for ( var name in dependencies.scenes ) { + + scenes.push( dependencies.scenes[ name ] ); + + } + + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + + var cameras = []; + + for ( var name in dependencies.cameras ) { + + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + + } + + var animations = []; + + for ( var name in dependencies.animations ) { + + animations.push( dependencies.animations[ name ] ); + + } + + callback( scene, scenes, cameras, animations ); + + } ); + + }; + + GLTFParser.prototype.loadShaders = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.shaders, function ( shader ) { + + if ( shader.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ shader.bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + + resolve( shaderText ); + + } ); + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadBuffers = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return _each( json.buffers, function ( buffer, name ) { + + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + + // If present, GLB container is required to be the first buffer. + if ( buffer.uri === undefined && name === 0 ) { + + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + + resolve( buffer ); + + } ); + + } ); + + } else { + + console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); + + } + + } ); + + }; + + GLTFParser.prototype.loadBufferViews = function () { + + var json = this.json; + + return this._withDependencies( [ + + "buffers" + + ] ).then( function ( dependencies ) { + + return _each( json.bufferViews, function ( bufferView ) { + + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAccessors = function () { + + var json = this.json; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.accessors, function ( accessor ) { + + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + + var array; + + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + + // Use the full buffer if it's interleaved. + array = new TypedArray( arraybuffer ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + + } else { + + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + + return new THREE.BufferAttribute( array, itemSize ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadTextures = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.textures, function ( texture ) { + + if ( texture.source !== undefined ) { + + return new Promise( function ( resolve ) { + + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + + var urlCreator; + + if ( source.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ source.bufferView ]; + var blob = new Blob( [ bufferView ], { type: source.mimeType } ); + urlCreator = window.URL || window.webkitURL; + sourceUri = urlCreator.createObjectURL( blob ); + + } + + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + + if ( textureLoader === null ) { + + textureLoader = new THREE.TextureLoader(); + + } + + textureLoader.setCrossOrigin( options.crossOrigin ); + + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + + if ( urlCreator !== undefined ) { + + urlCreator.revokeObjectURL( sourceUri ); + + } + + _texture.flipY = false; + + if ( texture.name !== undefined ) _texture.name = texture.name; + + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + + console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + + } + + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + + if ( texture.sampler !== undefined ) { + + var sampler = json.samplers[ texture.sampler ]; + + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + + } + + resolve( _texture ); + + }, undefined, function () { + + resolve(); + + } ); + + } ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMaterials = function () { + + var json = this.json; + + return this._withDependencies( [ + + "shaders", + "textures" + + ] ).then( function ( dependencies ) { + + return _each( json.materials, function ( material ) { + + var materialType; + var materialValues = {}; + var materialParams = {}; + + var khr_material; + + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + + } + + if ( khr_material ) { + + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + + switch ( khr_material.technique ) { + + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + + } + + keys.forEach( function( v ) { + + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + + } ); + + if ( khr_material.doubleSided || materialValues.doubleSided ) { + + materialParams.side = THREE.DoubleSide; + + } + + if ( khr_material.transparent || materialValues.transparent ) { + + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + + } + + } else if ( material.technique === undefined ) { + + if ( material.pbrMetallicRoughness !== undefined ) { + + // specification + // https://github.com/sbtron/glTF/blob/30de0b365d1566b1bbd8b9c140f9e995d3203226/specification/2.0/README.md#metallic-roughness-material + + materialType = THREE.MeshStandardMaterial; + + if ( material.pbrMetallicRoughness !== undefined ) { + + var metallicRoughness = material.pbrMetallicRoughness; + + materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + + var array = metallicRoughness.baseColorFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( metallicRoughness.baseColorTexture !== undefined ) { + + materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; + + } + + if ( materialParams.opacity < 1.0 || + ( materialParams.map !== undefined && + ( materialParams.map.format === THREE.AlphaFormat || + materialParams.map.format === THREE.RGBAFormat || + materialParams.map.format === THREE.LuminanceAlphaFormat ) ) ) { + + materialParams.transparent = true; + + } + + materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; + materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; + + if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { + + var textureIndex = metallicRoughness.metallicRoughnessTexture.index; + + // Note that currently metalnessMap would be entirely ignored because + // Three.js and glTF specification use different texture channels for metalness + // (Blue: Three.js, Red: glTF). + // But glTF specification team is discussing if they can change. + // Let's keep an eye on it so far. + // + // https://github.com/KhronosGroup/glTF/issues/857 + materialParams.metalnessMap = dependencies.textures[ textureIndex ]; + materialParams.roughnessMap = dependencies.textures[ textureIndex ]; + + } + + } + + } else { + + materialType = THREE.MeshPhongMaterial; + + } + + if ( material.normalTexture !== undefined ) { + + materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; + + } + + if ( material.occlusionTexture !== undefined ) { + + materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; + + } + + if ( material.emissiveTexture !== undefined ) { + + materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; + + } + + materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); + + if ( material.emissiveFactor !== undefined ) { + + materialParams.emissive.fromArray( material.emissiveFactor ); + + } + + Object.assign( materialValues, material.values ); + + } else { + + materialType = DeferredShaderMaterial; + + var technique = json.techniques[ material.technique ]; + + materialParams.uniforms = {}; + + var program = json.programs[ technique.program ]; + + if ( program ) { + + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + + if ( ! materialParams.fragmentShader ) { + + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = THREE.MeshPhongMaterial; + + } + + var vertexShader = dependencies.shaders[ program.vertexShader ]; + + if ( ! vertexShader ) { + + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = THREE.MeshPhongMaterial; + + } + + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + + var uniforms = technique.uniforms; + + for ( var uniformId in uniforms ) { + + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + + var ptype = shaderParam.type; + + if ( WEBGL_TYPE[ ptype ] ) { + + var pcount = shaderParam.count; + var value; + + if ( material.values !== undefined ) value = material.values[ pname ]; + + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + + switch ( ptype ) { + + case WEBGL_CONSTANTS.FLOAT: + + uvalue = shaderParam.value; + + if ( pname == "transparency" ) { + + materialParams.transparent = true; + + } + + if ( value !== undefined ) { + + uvalue = value; + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + + if ( shaderParam && shaderParam.value ) { + + uvalue.fromArray( shaderParam.value ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_MAT2: + + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + + case WEBGL_CONSTANTS.FLOAT_MAT4: + + if ( pcount ) { + + uvalue = new Array( pcount ); + + for ( var mi = 0; mi < pcount; mi ++ ) { + + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + + } + + if ( shaderParam && shaderParam.value ) { + + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } else { + + if ( shaderParam && shaderParam.value ) { + + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } + + break; + + case WEBGL_CONSTANTS.SAMPLER_2D: + + if ( value !== undefined ) { + + uvalue = dependencies.textures[ value ]; + + } else if ( shaderParam.value !== undefined ) { + + uvalue = dependencies.textures[ shaderParam.value ]; + + } else { + + uvalue = null; + + } + + break; + + } + + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + + } else { + + throw new Error( "Unknown shader uniform param type: " + ptype ); + + } + + } + + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + + for ( var i = 0, il = enables.length; i < il; i ++ ) { + + var enable = enables[ i ]; + + switch ( STATES_ENABLES[ enable ] ) { + + case 'CULL_FACE': + + enableCullFace = true; + + break; + + case 'DEPTH_TEST': + + enableDepthTest = true; + + break; + + case 'BLEND': + + enableBlend = true; + + break; + + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + + break; + + default: + + throw new Error( "Unknown technique.states.enable: " + enable ); + + } + + } + + if ( enableCullFace ) { + + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + + } else { + + materialParams.side = THREE.DoubleSide; + + } + + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + + var blendEquationSeparate = functions.blendEquationSeparate; + + if ( blendEquationSeparate !== undefined ) { + + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + + } else { + + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + + } + + var blendFuncSeparate = functions.blendFuncSeparate; + + if ( blendFuncSeparate !== undefined ) { + + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + + } else { + + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + + } + + } + + } + + if ( Array.isArray( materialValues.diffuse ) ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); + + } else if ( typeof( materialValues.diffuse ) === 'string' ) { + + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + + } + + delete materialParams.diffuse; + + if ( typeof( materialValues.reflective ) === 'string' ) { + + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + + } + + if ( typeof( materialValues.bump ) === 'string' ) { + + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + + } + + if ( Array.isArray( materialValues.emission ) ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + + } else { + + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + + } + + } else if ( typeof( materialValues.emission ) === 'string' ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.map = dependencies.textures[ materialValues.emission ]; + + } else { + + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + + } + + } + + if ( Array.isArray( materialValues.specular ) ) { + + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + + } else if ( typeof( materialValues.specular ) === 'string' ) { + + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + + } + + if ( materialValues.shininess !== undefined ) { + + materialParams.shininess = materialValues.shininess; + + } + + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + + return _material; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMeshes = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "materials" + + ] ).then( function ( dependencies ) { + + return _each( json.meshes, function ( mesh ) { + + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + + if ( mesh.extras ) group.userData = mesh.extras; + + var primitives = mesh.primitives || []; + + for ( var name in primitives ) { + + var primitive = primitives[ name ]; + + var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + + var geometry; + + var meshNode; + + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( attributeEntry === undefined ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'NORMAL': + + geometry.addAttribute( 'normal', bufferAttribute ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + geometry.addAttribute( 'uv', bufferAttribute ); + break; + + case 'TEXCOORD_1': + + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + geometry.addAttribute( 'color', bufferAttribute ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + } + + meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( ! attributeEntry ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + meshNode = new THREE.LineSegments( geometry, material ); + + } else { + + meshNode = new THREE.Line( geometry, material ); + + } + + } else { + + throw new Error( "Only triangular and line primitives are supported" ); + + } + + if ( geometry.attributes.color !== undefined ) { + + material.vertexColors = THREE.VertexColors; + material.needsUpdate = true; + + } + + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } + + return group; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadCameras = function () { + + var json = this.json; + + return _each( json.cameras, function ( camera ) { + + if ( camera.type == "perspective" && camera.perspective ) { + + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } else if ( camera.type == "orthographic" && camera.orthographic ) { + + var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } + + } ); + + }; + + GLTFParser.prototype.loadSkins = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors" + + ] ).then( function ( dependencies ) { + + return _each( json.skins, function ( skin ) { + + var bindShapeMatrix = new THREE.Matrix4(); + + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + + return _skin; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAnimations = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.animations, function ( animation, animationId ) { + + var tracks = []; + + for ( var channelId in animation.channels ) { + + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + + if ( sampler ) { + + var target = channel.target; + var name = target.node || target.id; // NOTE: target.id is deprecated. + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + + var node = dependencies.nodes[ name ]; + + if ( node ) { + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + + } + + } + + } + + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + + return new THREE.AnimationClip( name, undefined, tracks ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadNodes = function () { + + var json = this.json; + var extensions = this.extensions; + var scope = this; + + return _each( json.nodes, function ( node ) { + + var matrix = new THREE.Matrix4(); + + var _node; + + if ( node.jointName ) { + + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + + } else { + + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + + } + + if ( node.extras ) _node.userData = node.extras; + + if ( node.matrix !== undefined ) { + + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + + } else { + + if ( node.translation !== undefined ) { + + _node.position.fromArray( node.translation ); + + } + + if ( node.rotation !== undefined ) { + + _node.quaternion.fromArray( node.rotation ); + + } + + if ( node.scale !== undefined ) { + + _node.scale.fromArray( node.scale ); + + } + + } + + return _node; + + } ).then( function ( __nodes ) { + + return scope._withDependencies( [ + + "meshes", + "skins", + "cameras" + + ] ).then( function ( dependencies ) { + + return _each( __nodes, function ( _node, nodeId ) { + + var node = json.nodes[ nodeId ]; + + var meshes; + + if ( node.mesh !== undefined) { + + meshes = [ node.mesh ]; + + } else if ( node.meshes !== undefined ) { + + console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); + + meshes = node.meshes; + + } + + if ( meshes !== undefined ) { + + for ( var meshId in meshes ) { + + var mesh = meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + + if ( group === undefined ) { + + console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); + continue; + + } + + for ( var childrenId in group.children ) { + + var child = group.children[ childrenId ]; + + // clone Mesh to add to _node + + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + + var material; + + if ( originalMaterial.isDeferredShaderMaterial ) { + + originalMaterial = material = originalMaterial.create(); + + } else { + + material = originalMaterial; + + } + + switch ( child.type ) { + + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + + default: + child = new THREE.Mesh( originalGeometry, material ); + + } + + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var skinEntry; + + if ( node.skin !== undefined ) { + + skinEntry = dependencies.skins[ node.skin ]; + + } + + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + + var getJointNode = function ( jointId ) { + + var keys = Object.keys( __nodes ); + + for ( var i = 0, il = keys.length; i < il; i ++ ) { + + var n = __nodes[ keys[ i ] ]; + + if ( n.jointName === jointId ) return n; + + } + + return null; + + }; + + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var bones = []; + var boneInverses = []; + + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + + if ( jointNode ) { + + bones.push( jointNode ); + + var m = skinEntry.inverseBindMatrices.array; + var mat = new THREE.Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + + } else { + + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + + } + + } + + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + + var buildBoneGraph = function ( parentJson, parentObject, property ) { + + var children = parentJson[ property ]; + + if ( children === undefined ) return; + + for ( var i = 0, il = children.length; i < il; i ++ ) { + + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + + } + + } + + }; + + buildBoneGraph( node, child, 'skeletons' ); + + } + + _node.add( child ); + + } + + } + + } + + if ( node.camera !== undefined ) { + + var camera = dependencies.cameras[ node.camera ]; + + _node.add( camera ); + + } + + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + + _node.add( light ); + + } + + return _node; + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadScenes = function () { + + var json = this.json; + + // scene node hierachy builder + + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + + var node = json.nodes[ nodeId ]; + + if ( node.children ) { + + var children = node.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + + } + + } + + } + + return this._withDependencies( [ + + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.scenes, function ( scene ) { + + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + + if ( scene.extras ) _scene.userData = scene.extras; + + var nodes = scene.nodes || []; + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + + } + + _scene.traverse( function ( child ) { + + // Register raw material meshes with GLTF2Loader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + + } + + } ); + + return _scene; + + } ); + + } ); + + }; + + return GLTF2Loader; + +} )(); +// File: utils/GLTFLoader.js +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + */ + +THREE.GLTFLoader = ( function () { + function GLTFLoader( manager ) { + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + } + GLTFLoader.prototype = { + constructor: GLTFLoader, + load: function ( url, onLoad, onProgress, onError ) { + var scope = this; + var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); + var loader = new THREE.FileLoader( scope.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( data ) { + scope.parse( data, onLoad, path ); + }, onProgress, onError ); + }, + setCrossOrigin: function ( value ) { + this.crossOrigin = value; + }, + setPath: function ( value ) { + this.path = value; + }, + parse: function ( data, callback, path ) { + var content; + var extensions = {}; + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + } else { + content = convertUint8ArrayToString( new Uint8Array( data ) ); + } + var json = JSON.parse( content ); + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + } + console.time( 'GLTFLoader' ); + var parser = new GLTFParser( json, extensions, { + path: path || this.path, + crossOrigin: this.crossOrigin + } ); + parser.parse( function ( scene, scenes, cameras, animations ) { + console.timeEnd( 'GLTFLoader' ); + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + callback( glTF ); + } ); + } + }; + + /* GLTFREGISTRY */ + function GLTFRegistry() { + var objects = {}; + return { + get: function ( key ) { + return objects[ key ]; + }, + add: function ( key, object ) { + objects[ key ] = object; + }, + remove: function ( key ) { + delete objects[ key ]; + }, + removeAll: function () { + objects = {}; + }, + update: function ( scene, camera ) { + for ( var name in objects ) { + var object = objects[ name ]; + if ( object.update ) { + object.update( scene, camera ); + } + } + } + }; + } + + /* GLTFSHADERS */ + GLTFLoader.Shaders = { + update: function () { + console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); + } + }; + + /* GLTFSHADER */ + function GLTFShader( targetNode, allNodes ) { + var boundUniforms = {}; + // bind each uniform to its source node + var uniforms = targetNode.material.uniforms; + for ( var uniformId in uniforms ) { + var uniform = uniforms[ uniformId ]; + if ( uniform.semantic ) { + var sourceNodeRef = uniform.node; + var sourceNode = targetNode; + if ( sourceNodeRef ) { + sourceNode = allNodes[ sourceNodeRef ]; + } + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + } + } + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + } + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + var boundUniforms = this.boundUniforms; + for ( var name in boundUniforms ) { + var boundUniform = boundUniforms[ name ]; + switch ( boundUniform.semantic ) { + case "MODELVIEW": + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + case "MODELVIEWINVERSETRANSPOSE": + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + case "PROJECTION": + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + case "JOINTMATRIX": + var m4v = boundUniform.uniform.value; + for ( var mi = 0; mi < m4v.length; mi ++ ) { + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + } + break; + default : + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + } + } + }; + + /* ANIMATION */ + GLTFLoader.Animations = { + update: function () { + console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); + } + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' + }; + + /* MATERIALS COMMON EXTENSION */ + function GLTFMaterialsCommonExtension( json ) { + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + this.lights = {}; + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + for ( var lightId in lights ) { + var light = lights[ lightId ]; + var lightNode; + var lightParams = light[ light.type ]; + var color = new THREE.Color().fromArray( lightParams.color ); + switch ( light.type ) { + case "directional": + lightNode = new THREE.DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + case "point": + lightNode = new THREE.PointLight( color ); + break; + case "spot": + lightNode = new THREE.SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + case "ambient": + lightNode = new THREE.AmbientLight( color ); + break; + } + if ( lightNode ) { + this.lights[ lightId ] = lightNode; + } + } + } + + /* BINARY EXTENSION */ + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; + var BINARY_EXTENSION_HEADER_LENGTH = 20; + function GLTFBinaryExtension( data ) { + this.name = EXTENSIONS.KHR_BINARY_GLTF; + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + var header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ), + contentLength: headerView.getUint32( 12, true ), + contentFormat: headerView.getUint32( 16, true ) + }; + for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { + var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; + if ( header[ key ] !== value ) { + throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); + } + } + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); + this.header = header; + this.content = convertUint8ArrayToString( contentArray ); + this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); + } + GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { + var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + }; + GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) { + var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; + var bufferView = bufferViews[ metadata.bufferView ]; + var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) ); + return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData ); + }; + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + /* CONSTANTS */ + var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 + }; + var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: THREE.Matrix3, + 35676: THREE.Matrix4, + 35664: THREE.Vector2, + 35665: THREE.Vector3, + 35666: THREE.Vector4, + 35678: THREE.Texture + }; + var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipMapNearestFilter, + 9985: THREE.LinearMipMapNearestFilter, + 9986: THREE.NearestMipMapLinearFilter, + 9987: THREE.LinearMipMapLinearFilter + }; + var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping + }; + var WEBGL_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + function _each( object, callback, thisObj ) { + if ( !object ) { + return Promise.resolve(); + } + var results; + var fns = []; + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + results = []; + var length = object.length; + for ( var idx = 0; idx < length; idx ++ ) { + var value = callback.call( thisObj || this, object[ idx ], idx ); + if ( value ) { + fns.push( value ); + if ( value instanceof Promise ) { + value.then( function( key, value ) { + results[ key ] = value; + }.bind( this, idx )); + } else { + results[ idx ] = value; + } + } + } + } else { + results = {}; + for ( var key in object ) { + if ( object.hasOwnProperty( key ) ) { + var value = callback.call( thisObj || this, object[ key ], key ); + if ( value ) { + fns.push( value ); + if ( value instanceof Promise ) { + value.then( function( key, value ) { + results[ key ] = value; + }.bind( this, key )); + } else { + results[ key ] = value; + } + } + } + } + } + return Promise.all( fns ).then( function() { + return results; + }); + } + function resolveURL( url, path ) { + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + return url; + } + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + return url; + } + // Relative URL + return ( path || '' ) + url; + } + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + function convertUint8ArrayToString( array ) { + var s = ''; + for ( var i = 0; i < array.length; i ++ ) { + s += String.fromCharCode( array[ i ] ); + } + return s; + } + // Three.js seems too dependent on attribute names so globally + // replace those in the shader code + function replaceTHREEShaderAttributes( shaderText, technique ) { + // Expected technique attributes + var attributes = {}; + for ( var attributeId in technique.attributes ) { + var pname = technique.attributes[ attributeId ]; + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + } + // Figure out which attributes to change in technique + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + for ( var attributeId in attributes ) { + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + params[ attributeId ] = shaderParam; + } + } + for ( var pname in params ) { + var param = params[ pname ]; + var semantic = param.semantic; + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + switch ( semantic ) { + case "POSITION": + shaderText = shaderText.replace( regEx, 'position' ); + break; + case "NORMAL": + shaderText = shaderText.replace( regEx, 'normal' ); + break; + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + shaderText = shaderText.replace( regEx, 'uv' ); + break; + case 'TEXCOORD_1': + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + shaderText = shaderText.replace( regEx, 'color' ); + break; + case "WEIGHT": + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + case "JOINT": + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + } + } + return shaderText; + } + function createDefaultMaterial() { + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + } + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + this.isDeferredShaderMaterial = true; + this.params = params; + } + DeferredShaderMaterial.prototype.create = function () { + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + for ( var uniformId in this.params.uniforms ) { + var originalUniform = this.params.uniforms[ uniformId ]; + if ( originalUniform.value instanceof THREE.Texture ) { + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + } + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + } + this.params.uniforms = uniforms; + return new THREE.RawShaderMaterial( this.params ); + }; + + /* GLTF PARSER */ + function GLTFParser( json, extensions, options ) { + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + // loader object cache + this.cache = new GLTFRegistry(); + } + GLTFParser.prototype._withDependencies = function ( dependencies ) { + var _dependencies = {}; + for ( var i = 0; i < dependencies.length; i ++ ) { + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + var cached = this.cache.get( dependency ); + if ( cached !== undefined ) { + _dependencies[ dependency ] = cached; + } else if ( this[ fnName ] ) { + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + _dependencies[ dependency ] = fn; + } + } + return _each( _dependencies, function ( dependency ) { + return dependency; + } ); + }; + GLTFParser.prototype.parse = function ( callback ) { + var json = this.json; + // Clear the loader cache + this.cache.removeAll(); + // Fire the callback on complete + this._withDependencies( [ + "scenes", + "cameras", + "animations" + ] ).then( function ( dependencies ) { + var scenes = []; + for ( var name in dependencies.scenes ) { + scenes.push( dependencies.scenes[ name ] ); + } + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + var cameras = []; + for ( var name in dependencies.cameras ) { + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + } + var animations = []; + for ( var name in dependencies.animations ) { + animations.push( dependencies.animations[ name ] ); + } + callback( scene, scenes, cameras, animations ); + } ); + }; + GLTFParser.prototype.loadShaders = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.shaders, function ( shader ) { + if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); + } + return new Promise( function ( resolve ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + resolve( shaderText ); + } ); + } ); + } ); + } ); + }; + GLTFParser.prototype.loadBuffers = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return _each( json.buffers, function ( buffer, name ) { + if ( name === BINARY_EXTENSION_BUFFER_NAME ) { + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + } + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + return new Promise( function ( resolve ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + resolve( buffer ); + } ); + } ); + } else { + console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); + } + } ); + }; + GLTFParser.prototype.loadBufferViews = function () { + var json = this.json; + return this._withDependencies( [ + "buffers" + ] ).then( function ( dependencies ) { + return _each( json.bufferViews, function ( bufferView ) { + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + } ); + } ); + }; + GLTFParser.prototype.loadAccessors = function () { + var json = this.json; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.accessors, function ( accessor ) { + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + // Use the full buffer if it's interleaved. + var array = new TypedArray( arraybuffer ); + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + } else { + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + return new THREE.BufferAttribute( array, itemSize ); + } + } ); + } ); + }; + GLTFParser.prototype.loadTextures = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.textures, function ( texture ) { + if ( texture.source ) { + return new Promise( function ( resolve ) { + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); + } + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + if ( textureLoader === null ) { + textureLoader = new THREE.TextureLoader(); + } + textureLoader.setCrossOrigin( options.crossOrigin ); + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + _texture.flipY = false; + if ( texture.name !== undefined ) _texture.name = texture.name; + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + } + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + if ( texture.sampler ) { + var sampler = json.samplers[ texture.sampler ]; + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + } + resolve( _texture ); + }, undefined, function () { + resolve(); + } ); + } ); + } + } ); + } ); + }; + GLTFParser.prototype.loadMaterials = function () { + var json = this.json; + return this._withDependencies( [ + "shaders", + "textures" + ] ).then( function ( dependencies ) { + return _each( json.materials, function ( material ) { + var materialType; + var materialValues = {}; + var materialParams = {}; + var khr_material; + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + } + if ( khr_material ) { + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + switch ( khr_material.technique ) { + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + } + keys.forEach( function( v ) { + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + } ); + if ( khr_material.doubleSided || materialValues.doubleSided ) { + materialParams.side = THREE.DoubleSide; + } + if ( khr_material.transparent || materialValues.transparent ) { + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + } + } else if ( material.technique === undefined ) { + materialType = THREE.MeshPhongMaterial; + Object.assign( materialValues, material.values ); + } else { + materialType = DeferredShaderMaterial; + var technique = json.techniques[ material.technique ]; + materialParams.uniforms = {}; + var program = json.programs[ technique.program ]; + if ( program ) { + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + if ( ! materialParams.fragmentShader ) { + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = THREE.MeshPhongMaterial; + } + var vertexShader = dependencies.shaders[ program.vertexShader ]; + if ( ! vertexShader ) { + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = THREE.MeshPhongMaterial; + } + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + var uniforms = technique.uniforms; + for ( var uniformId in uniforms ) { + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + var ptype = shaderParam.type; + if ( WEBGL_TYPE[ ptype ] ) { + var pcount = shaderParam.count; + var value; + if ( material.values !== undefined ) value = material.values[ pname ]; + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + switch ( ptype ) { + case WEBGL_CONSTANTS.FLOAT: + uvalue = shaderParam.value; + if ( pname == "transparency" ) { + materialParams.transparent = true; + } + if ( value !== undefined ) { + uvalue = value; + } + break; + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + if ( shaderParam && shaderParam.value ) { + uvalue.fromArray( shaderParam.value ); + } + if ( value ) { + uvalue.fromArray( value ); + } + break; + case WEBGL_CONSTANTS.FLOAT_MAT2: + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + case WEBGL_CONSTANTS.FLOAT_MAT4: + if ( pcount ) { + uvalue = new Array( pcount ); + for ( var mi = 0; mi < pcount; mi ++ ) { + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + } + if ( shaderParam && shaderParam.value ) { + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + } + if ( value ) { + uvalue.fromArray( value ); + } + } else { + if ( shaderParam && shaderParam.value ) { + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + } + if ( value ) { + uvalue.fromArray( value ); + } + } + break; + case WEBGL_CONSTANTS.SAMPLER_2D: + if ( value !== undefined ) { + uvalue = dependencies.textures[ value ]; + } else if ( shaderParam.value !== undefined ) { + uvalue = dependencies.textures[ shaderParam.value ]; + } else { + uvalue = null; + } + break; + } + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + } else { + throw new Error( "Unknown shader uniform param type: " + ptype ); + } + } + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + for ( var i = 0, il = enables.length; i < il; i ++ ) { + var enable = enables[ i ]; + switch ( STATES_ENABLES[ enable ] ) { + case 'CULL_FACE': + enableCullFace = true; + break; + case 'DEPTH_TEST': + enableDepthTest = true; + break; + case 'BLEND': + enableBlend = true; + break; + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + break; + default: + throw new Error( "Unknown technique.states.enable: " + enable ); + } + } + if ( enableCullFace ) { + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + } else { + materialParams.side = THREE.DoubleSide; + } + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + var blendEquationSeparate = functions.blendEquationSeparate; + if ( blendEquationSeparate !== undefined ) { + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + } else { + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + } + var blendFuncSeparate = functions.blendFuncSeparate; + if ( blendFuncSeparate !== undefined ) { + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + } else { + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + } + } + } + if ( Array.isArray( materialValues.diffuse ) ) { + materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); + } else if ( typeof( materialValues.diffuse ) === 'string' ) { + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + } + delete materialParams.diffuse; + if ( typeof( materialValues.reflective ) === 'string' ) { + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + } + if ( typeof( materialValues.bump ) === 'string' ) { + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + } + if ( Array.isArray( materialValues.emission ) ) { + if ( materialType === THREE.MeshBasicMaterial ) { + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + } else { + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + } + } else if ( typeof( materialValues.emission ) === 'string' ) { + if ( materialType === THREE.MeshBasicMaterial ) { + materialParams.map = dependencies.textures[ materialValues.emission ]; + } else { + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + } + } + if ( Array.isArray( materialValues.specular ) ) { + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + } else if ( typeof( materialValues.specular ) === 'string' ) { + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + } + if ( materialValues.shininess !== undefined ) { + materialParams.shininess = materialValues.shininess; + } + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + return _material; + } ); + } ); + }; + GLTFParser.prototype.loadMeshes = function () { + var json = this.json; + return this._withDependencies( [ + "accessors", + "materials" + ] ).then( function ( dependencies ) { + return _each( json.meshes, function ( mesh ) { + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + if ( mesh.extras ) group.userData = mesh.extras; + var primitives = mesh.primitives || []; + for ( var name in primitives ) { + var primitive = primitives[ name ]; + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + var geometry = new THREE.BufferGeometry(); + var attributes = primitive.attributes; + for ( var attributeId in attributes ) { + var attributeEntry = attributes[ attributeId ]; + if ( ! attributeEntry ) return; + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + switch ( attributeId ) { + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + case 'NORMAL': + geometry.addAttribute( 'normal', bufferAttribute ); + break; + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + geometry.addAttribute( 'uv', bufferAttribute ); + break; + case 'TEXCOORD_1': + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + case 'WEIGHT': + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + case 'JOINT': + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + } + } + if ( primitive.indices ) { + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + } + var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + var meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + meshNode.name = ( name === "0" ? group.name : group.name + name ); + if ( primitive.extras ) meshNode.userData = primitive.extras; + group.add( meshNode ); + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + var geometry = new THREE.BufferGeometry(); + var attributes = primitive.attributes; + for ( var attributeId in attributes ) { + var attributeEntry = attributes[ attributeId ]; + if ( ! attributeEntry ) return; + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + switch ( attributeId ) { + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + } + } + var material = dependencies.materials[ primitive.material ]; + var meshNode; + if ( primitive.indices ) { + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + meshNode = new THREE.LineSegments( geometry, material ); + } else { + meshNode = new THREE.Line( geometry, material ); + } + meshNode.name = ( name === "0" ? group.name : group.name + name ); + if ( primitive.extras ) meshNode.userData = primitive.extras; + group.add( meshNode ); + } else { + console.warn( "Only triangular and line primitives are supported" ); + } + } + return group; + } ); + } ); + }; + GLTFParser.prototype.loadCameras = function () { + var json = this.json; + return _each( json.cameras, function ( camera ) { + if ( camera.type == "perspective" && camera.perspective ) { + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + if ( camera.extras ) _camera.userData = camera.extras; + return _camera; + } else if ( camera.type == "orthographic" && camera.orthographic ) { + var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + if ( camera.extras ) _camera.userData = camera.extras; + return _camera; + } + } ); + }; + GLTFParser.prototype.loadSkins = function () { + var json = this.json; + return this._withDependencies( [ + "accessors" + ] ).then( function ( dependencies ) { + return _each( json.skins, function ( skin ) { + var bindShapeMatrix = new THREE.Matrix4(); + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + return _skin; + } ); + } ); + }; + GLTFParser.prototype.loadAnimations = function () { + var json = this.json; + return this._withDependencies( [ + "accessors", + "nodes" + ] ).then( function ( dependencies ) { + return _each( json.animations, function ( animation, animationId ) { + var tracks = []; + for ( var channelId in animation.channels ) { + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + if ( sampler ) { + var target = channel.target; + var name = target.id; + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + var node = dependencies.nodes[ name ]; + if ( node ) { + node.updateMatrix(); + node.matrixAutoUpdate = true; + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + } + } + } + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + return new THREE.AnimationClip( name, undefined, tracks ); + } ); + } ); + }; + GLTFParser.prototype.loadNodes = function () { + var json = this.json; + var extensions = this.extensions; + var scope = this; + return _each( json.nodes, function ( node ) { + var matrix = new THREE.Matrix4(); + var _node; + if ( node.jointName ) { + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + } else { + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + } + if ( node.extras ) _node.userData = node.extras; + if ( node.matrix !== undefined ) { + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + } else { + if ( node.translation !== undefined ) { + _node.position.fromArray( node.translation ); + } + if ( node.rotation !== undefined ) { + _node.quaternion.fromArray( node.rotation ); + } + if ( node.scale !== undefined ) { + _node.scale.fromArray( node.scale ); + } + } + return _node; + } ).then( function ( __nodes ) { + return scope._withDependencies( [ + "meshes", + "skins", + "cameras" + ] ).then( function ( dependencies ) { + return _each( __nodes, function ( _node, nodeId ) { + var node = json.nodes[ nodeId ]; + if ( node.meshes !== undefined ) { + for ( var meshId in node.meshes ) { + var mesh = node.meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + if ( group === undefined ) { + console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); + continue; + } + for ( var childrenId in group.children ) { + var child = group.children[ childrenId ]; + // clone Mesh to add to _node + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + var material; + if ( originalMaterial.isDeferredShaderMaterial ) { + originalMaterial = material = originalMaterial.create(); + } else { + material = originalMaterial; + } + switch ( child.type ) { + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + default: + child = new THREE.Mesh( originalGeometry, material ); + } + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + var skinEntry; + if ( node.skin ) { + skinEntry = dependencies.skins[ node.skin ]; + } + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + var getJointNode = function ( jointId ) { + var keys = Object.keys( __nodes ); + for ( var i = 0, il = keys.length; i < il; i ++ ) { + var n = __nodes[ keys[ i ] ]; + if ( n.jointName === jointId ) return n; + } + return null; + }; + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + var bones = []; + var boneInverses = []; + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + if ( jointNode ) { + bones.push( jointNode ); + var m = skinEntry.inverseBindMatrices.array; + var mat = new THREE.Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + } else { + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + } + } + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + var buildBoneGraph = function ( parentJson, parentObject, property ) { + var children = parentJson[ property ]; + if ( children === undefined ) return; + for ( var i = 0, il = children.length; i < il; i ++ ) { + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + } + } + }; + buildBoneGraph( node, child, 'skeletons' ); + } + _node.add( child ); + } + } + } + if ( node.camera !== undefined ) { + var camera = dependencies.cameras[ node.camera ]; + _node.add( camera ); + } + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + _node.add( light ); + } + return _node; + } ); + } ); + } ); + }; + GLTFParser.prototype.loadScenes = function () { + var json = this.json; + // scene node hierachy builder + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + var node = json.nodes[ nodeId ]; + if ( node.children ) { + var children = node.children; + for ( var i = 0, l = children.length; i < l; i ++ ) { + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + } + } + } + return this._withDependencies( [ + "nodes" + ] ).then( function ( dependencies ) { + return _each( json.scenes, function ( scene ) { + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + if ( scene.extras ) _scene.userData = scene.extras; + var nodes = scene.nodes || []; + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + } + _scene.traverse( function ( child ) { + // Register raw material meshes with GLTFLoader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + } + } ); + return _scene; + } ); + } ); + }; + return GLTFLoader; +} )(); +// File: utils/JSONParser.js +"use strict"; + +var JSONParser = function() +{ +} + +JSONParser.prototype.constructor = JSONParser; + + /** + * Load X3D JSON into an element. + * jsobj - the JavaScript object to convert to DOM. + */ +JSONParser.prototype.parseJavaScript = function(jsobj) { + var child = this.CreateElement('scene'); + this.ConvertToX3DOM(jsobj, "", child); + console.log(jsobj, child); + return child; + }; + + // 'http://www.web3d.org/specifications/x3d-namespace' + + // Load X3D JavaScript object into XML or DOM + + /** + * Yet another way to set an attribute on an element. does not allow you to + * set JSON schema or encoding. + */ +JSONParser.prototype.elementSetAttribute = function(element, key, value) { + if (key === 'SON schema') { + // JSON Schema + } else if (key === 'ncoding') { + // encoding, UTF-8, UTF-16 or UTF-32 + } else { + if (typeof element.setAttribute === 'function') { + element.setAttribute(key, value); + } + } + }; + + /** + * converts children of object to DOM. + */ +JSONParser.prototype.ConvertChildren = function(parentkey, object, element) { + var key; + + for (key in object) { + if (typeof object[key] === 'object') { + if (isNaN(parseInt(key))) { + this.ConvertObject(key, object, element, parentkey.substr(1)); + } else { + this.ConvertToX3DOM(object[key], key, element, parentkey.substr(1)); + } + } + } + }; + + /** + * a method to create and element with tagnam key to DOM in a namespace. If + * containerField is set, then the containerField is set in the elemetn. + */ +JSONParser.prototype.CreateElement = function(key, containerField) { + var child = document.createElement(key); + if (typeof containerField !== 'undefined') { + this.elementSetAttribute(child, 'containerField', containerField); + } + return child; + }; + + /** + * a way to create a CDATA function or script in HTML, by using a DOM parser. + */ +JSONParser.prototype.CDATACreateFunction = function(document, element, str) { + var y = str.replace(/\\"/g, "\\\"") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/&/g, "&"); + do { + str = y; + y = str.replace(/'([^'\r\n]*)\n([^']*)'/g, "'$1\\n$2'"); + if (str !== y) { + console.log("CDATA Replacing",str,"with",y); + } + } while (y != str); + var domParser = new DOMParser(); + var cdataStr = ''; // has to be wrapped into an element + var scriptDoc = domParser .parseFromString (cdataStr, 'application/xml'); + var cdata = scriptDoc .children[0] .childNodes[1]; // space after script is childNode[0] + element .appendChild(cdata); + }; + + /** + * convert the object at object[key] to DOM. + */ +JSONParser.prototype.ConvertObject = function(key, object, element, containerField) { + var child; + if (object !== null && typeof object[key] === 'object') { + if (key.substr(0,1) === '@') { + this.ConvertToX3DOM(object[key], key, element); + } else if (key.substr(0,1) === '-') { + this.ConvertChildren(key, object[key], element); + } else if (key === '#comment') { + for (var c in object[key]) { + child = document.createComment(this.CommentStringToXML(object[key][c])); + element.appendChild(child); + } + } else if (key === '#text') { + child = document.createTextNode(object[key].join("")); + element.appendChild(child); + } else if (key === '#sourceText') { + this.CDATACreateFunction(document, element, object[key].join("\r\n")+"\r\n"); + } else { + if (key === 'connect' || key === 'fieldValue' || key === 'field' || key === 'meta' || key === 'component') { + for (var childkey in object[key]) { // for each field + if (typeof object[key][childkey] === 'object') { + child = this.CreateElement(key, containerField); + this.ConvertToX3DOM(object[key][childkey], childkey, child); + element.appendChild(child); + element.appendChild(document.createTextNode("\n")); + } + } + } else { + child = this.CreateElement(key, containerField); + this.ConvertToX3DOM(object[key], key, child); + element.appendChild(child); + element.appendChild(document.createTextNode("\n")); + } + } + } + }; + + /** + * convert a comment string in JavaScript to XML. Pass the string + */ +JSONParser.prototype.CommentStringToXML = function(str) { + var y = str; + str = str.replace(/\\\\/g, '\\'); + if (y !== str) { + console.log("X3DJSONLD replacing", y, "with", str); + } + return str; + }; + + /** + * convert an SFString to XML. + */ +JSONParser.prototype.SFStringToXML = function(str) { + var y = str; + /* + str = (""+str).replace(/\\\\/g, '\\\\'); + str = str.replace(/\\\\\\\\/g, '\\\\'); + str = str.replace(/(\\+)"/g, '\\"'); + */ + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/"/g, '\\\"'); + if (y !== str) { + console.log("X3DJSONLD [] replacing", y, "with", str); + } + return str; + }; + + /** + * convert a JSON String to XML. + */ +JSONParser.prototype.JSONStringToXML = function(str) { + var y = str; + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/\n/g, '\\n'); + if (y !== str) { + console.log("X3DJSONLD replacing", y, "with", str); + } + return str; + }; + + /** + * main routine for converting a JavaScript object to DOM. + * object is the object to convert. + * parentkey is the key of the object in the parent. + * element is the parent element. + * containerField is a possible containerField. + */ +JSONParser.prototype.ConvertToX3DOM = function(object, parentkey, element, containerField) { + var key; + var localArray = []; + var isArray = false; + var arrayOfStrings = false; + for (key in object) { + if (isNaN(parseInt(key))) { + isArray = false; + } else { + isArray = true; + } + if (isArray) { + if (typeof object[key] === 'number') { + localArray.push(object[key]); + } else if (typeof object[key] === 'string') { + localArray.push(object[key]); + arrayOfStrings = true; + } else if (typeof object[key] === 'boolean') { + localArray.push(object[key]); + } else if (typeof object[key] === 'object') { + /* + if (object[key] != null && typeof object[key].join === 'function') { + localArray.push(object[key].join(" ")); + } + */ + this.ConvertToX3DOM(object[key], key, element); + } else if (typeof object[key] === 'undefined') { + } else { + console.error("Unknown type found in array "+typeof object[key]); + } + } else if (typeof object[key] === 'object') { + // This is where the whole thing starts + if (key === 'scene') { + this.ConvertToX3DOM(object[key], key, element); + } else { + this.ConvertObject(key, object, element); + } + } else if (typeof object[key] === 'number') { + this.elementSetAttribute(element, key.substr(1),object[key]); + } else if (typeof object[key] === 'string') { + if (key === '#comment') { + var child = document.createComment(this.CommentStringToXML(object[key])); + element.appendChild(child); + } else if (key === '#text') { + var child = document.createTextNode(object[key]); + element.appendChild(child); + } else { + // ordinary string attributes + this.elementSetAttribute(element, key.substr(1), this.JSONStringToXML(object[key])); + } + } else if (typeof object[key] === 'boolean') { + this.elementSetAttribute(element, key.substr(1),object[key]); + } else if (typeof object[key] === 'undefined') { + } else { + console.error("Unknown type found in object "+typeof object[key]); + console.error(object); + } + } + if (isArray) { + if (parentkey.substr(0,1) === '@') { + if (arrayOfStrings) { + arrayOfStrings = false; + for (var str in localArray) { + localArray[str] = this.SFStringToXML(localArray[str]); + } + this.elementSetAttribute(element, parentkey.substr(1),'"'+localArray.join('" "')+'"'); + } else { + // if non string array + this.elementSetAttribute(element, parentkey.substr(1),localArray.join(" ")); + } + } + isArray = false; + } + return element; + }; +// File: utils/LoadManager.js +/* + * For use with XSeen JavaScript Library + * http://tools.realism.com/... + * + * Licensed under MIT or GNU in the same manner as XSeen + * + * (C)2017 Daly Realiusm, Los Angeles + * + */ + +/* + * Manages all download requests. + * Requests are queued up and processed to the maximum limit (.MaxRequests) + * Use this for processing text (X3D, XML, JSON, HTML) files. + * Not really setup for binary files (.jpg, png, etc.) + * + * Requires jQuery -- should work on removing that... + * + */ + +function LoadManager () { + this.urlQueue = []; + this.urlNext = -1; + this.MaxRequests = 3; + this.totalRequests = 0; + this.totalResponses = 0; + this.requestCount = 0; + var lmThat = this; + + this.load = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadText = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'text', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadHtml = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'html', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadXml = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'xml', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadJson = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'json', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadImage = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'image', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.success = function (response, string, xhr) { + if (typeof(xhr._loadManager.success) !== undefined) { + xhr._loadManager.success (response, xhr._loadManager.userdata, xhr); + } + } + + this.failure = function (xhr, errorCode, errorText) { + if (typeof(xhr._loadManager.failure) !== undefined) { + xhr._loadManager.failure (xhr, xhr._loadManager.userdata, errorCode, errorText); + } + } + + this.requestComplete = function (event, xhr, settings) { + lmThat.requestCount --; + lmThat.totalResponses++; + lmThat.loadNextUrl(); + } + + this.loadNextUrl = function () { + if (this.requestCount >= this.MaxRequests) {return; } + if (this.urlNext >= this.urlQueue.length || this.urlNext < 0) { + this.urlNext = -1; + for (var i=0; i 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.CODE_SLASH: + slashes[ slashesPointer++ ] = i; + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.CODE_LF: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); + slashesPointer = 0; + bufferPointer = 0; + break; + + case Consts.CODE_CR: + break; + + default: + word += String.fromCharCode( code ); + break; + } + } + }; + + /** + * Parse the provided text + * @memberOf Parser + * + * @param {string} text OBJ data as string + */ + Parser.prototype.parseText = function ( text ) { + var length = text.length; + var buffer = new Array( 32 ); + var bufferPointer = 0; + var slashes = new Array( 32 ); + var slashesPointer = 0; + var reachedFaces = false; + var char; + var word = ''; + for ( var i = 0; i < length; i++ ) { + + char = text[ i ]; + switch ( char ) { + case Consts.STRING_SPACE: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.STRING_SLASH: + slashes[ slashesPointer++ ] = i; + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.STRING_LF: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); + slashesPointer = 0; + bufferPointer = 0; + break; + + case Consts.STRING_CR: + break; + + default: + word += char; + } + } + }; + + Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) { + if ( bufferPointer < 1 ) return reachedFaces; + + var bufferLength = bufferPointer - 1; + switch ( buffer[ 0 ] ) { + case Consts.LINE_V: + + // object complete instance required if reached faces already (= reached next block of v) + if ( reachedFaces ) { + + this.processCompletedObject( null, this.rawObject.groupName ); + reachedFaces = false; + + } + this.rawObject.pushVertex( buffer ); + break; + + case Consts.LINE_VT: + this.rawObject.pushUv( buffer ); + break; + + case Consts.LINE_VN: + this.rawObject.pushNormal( buffer ); + break; + + case Consts.LINE_F: + reachedFaces = true; + /* + * 0: "f vertex/uv/normal ..." + * 1: "f vertex/uv ..." + * 2: "f vertex//normal ..." + * 3: "f vertex ..." + */ + var haveQuad = bufferLength % 4 === 0; + if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVn( buffer ); + } else { + this.rawObject.buildFaceVVn( buffer ); + } + + } else if ( bufferLength === slashesPointer * 2 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVt( buffer ); + } else { + this.rawObject.buildFaceVVt( buffer ); + } + + } else if ( bufferLength * 2 === slashesPointer * 3 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVtVn( buffer ); + } else { + this.rawObject.buildFaceVVtVn( buffer ); + } + + } else { + + if ( haveQuad ) { + this.rawObject.buildQuadV( buffer ); + } else { + this.rawObject.buildFaceV( buffer ); + } + + } + break; + + case Consts.LINE_L: + if ( bufferLength === slashesPointer * 2 ) { + + this.rawObject.buildLineVvt( buffer ); + + } else { + + this.rawObject.buildLineV( buffer ); + + } + break; + + case Consts.LINE_S: + this.rawObject.pushSmoothingGroup( buffer[ 1 ] ); + break; + + case Consts.LINE_G: + this.processCompletedGroup( buffer[ 1 ] ); + break; + + case Consts.LINE_O: + if ( this.rawObject.vertices.length > 0 ) { + + this.processCompletedObject( buffer[ 1 ], null ); + reachedFaces = false; + + } else { + + this.rawObject.pushObject( buffer[ 1 ] ); + + } + break; + + case Consts.LINE_MTLLIB: + this.rawObject.pushMtllib( buffer[ 1 ] ); + break; + + case Consts.LINE_USEMTL: + this.rawObject.pushUsemtl( buffer[ 1 ] ); + break; + + default: + break; + } + return reachedFaces; + }; + + Parser.prototype.processCompletedObject = function ( objectName, groupName ) { + this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + this.inputObjectCount++; + this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName ); + }; + + Parser.prototype.processCompletedGroup = function ( groupName ) { + var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + if ( notEmpty ) { + + this.inputObjectCount ++; + this.rawObject = this.rawObject.newInstanceFromGroup( groupName ); + + } else { + + // if a group was set that did not lead to object creation in finalize, then the group name has to be updated + this.rawObject.pushGroup( groupName ); + + } + }; + + Parser.prototype.finalize = function () { + this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + this.inputObjectCount++; + }; + + return Parser; + })(); + + /** + * {@link RawObject} is only used by {@link Parser}. + * The user of OBJLoader2 does not need to care about this class. + * It is defined publicly for inclusion in web worker based OBJ loader ({@link THREE.OBJLoader2.WWOBJLoader2}) + */ + var RawObject = (function () { + + function RawObject( objectName, groupName, mtllibName ) { + this.globalVertexOffset = 1; + this.globalUvOffset = 1; + this.globalNormalOffset = 1; + + this.vertices = []; + this.normals = []; + this.uvs = []; + + // faces are stored according combined index of group, material and smoothingGroup (0 or not) + this.mtllibName = Validator.verifyInput( mtllibName, 'none' ); + this.objectName = Validator.verifyInput( objectName, 'none' ); + this.groupName = Validator.verifyInput( groupName, 'none' ); + this.activeMtlName = 'none'; + this.activeSmoothingGroup = 1; + + this.mtlCount = 0; + this.smoothingGroupCount = 0; + + this.rawObjectDescriptions = []; + // this default index is required as it is possible to define faces without 'g' or 'usemtl' + var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; + } + + RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) { + return materialName + '|' + smoothingGroup; + }; + + RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) { + var newRawObject = new RawObject( objectName, groupName, this.mtllibName ); + + // move indices forward + newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; + newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; + newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; + + return newRawObject; + }; + + RawObject.prototype.newInstanceFromGroup = function ( groupName ) { + var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName ); + + // keep current buffers and indices forward + newRawObject.vertices = this.vertices; + newRawObject.uvs = this.uvs; + newRawObject.normals = this.normals; + newRawObject.globalVertexOffset = this.globalVertexOffset; + newRawObject.globalUvOffset = this.globalUvOffset; + newRawObject.globalNormalOffset = this.globalNormalOffset; + + return newRawObject; + }; + + RawObject.prototype.pushVertex = function ( buffer ) { + this.vertices.push( parseFloat( buffer[ 1 ] ) ); + this.vertices.push( parseFloat( buffer[ 2 ] ) ); + this.vertices.push( parseFloat( buffer[ 3 ] ) ); + }; + + RawObject.prototype.pushUv = function ( buffer ) { + this.uvs.push( parseFloat( buffer[ 1 ] ) ); + this.uvs.push( parseFloat( buffer[ 2 ] ) ); + }; + + RawObject.prototype.pushNormal = function ( buffer ) { + this.normals.push( parseFloat( buffer[ 1 ] ) ); + this.normals.push( parseFloat( buffer[ 2 ] ) ); + this.normals.push( parseFloat( buffer[ 3 ] ) ); + }; + + RawObject.prototype.pushObject = function ( objectName ) { + this.objectName = objectName; + }; + + RawObject.prototype.pushMtllib = function ( mtllibName ) { + this.mtllibName = mtllibName; + }; + + RawObject.prototype.pushGroup = function ( groupName ) { + this.groupName = groupName; + this.verifyIndex(); + }; + + RawObject.prototype.pushUsemtl = function ( mtlName ) { + if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; + this.activeMtlName = mtlName; + this.mtlCount++; + + this.verifyIndex(); + }; + + RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) { + var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup; + if ( this.activeSmoothingGroup === normalized ) return; + this.activeSmoothingGroup = normalized; + this.smoothingGroupCount++; + + this.verifyIndex(); + }; + + RawObject.prototype.verifyIndex = function () { + var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 ); + this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ]; + if ( ! Validator.isValid( this.rawObjectDescriptionInUse ) ) { + + this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; + + } + }; + + RawObject.prototype.buildQuadVVtVn = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] ); + this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] ); + this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] ); + } + }; + + RawObject.prototype.buildQuadVVt = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); + this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); + } + }; + + RawObject.prototype.buildQuadVVn = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); + this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); + } + }; + + RawObject.prototype.buildQuadV = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] ); + } + }; + + RawObject.prototype.buildFaceVVtVn = function ( indexArray ) { + for ( var i = 1; i < 10; i += 3 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVt( indexArray[ i + 1 ] ); + this.attachFaceVn( indexArray[ i + 2 ] ); + } + }; + + RawObject.prototype.buildFaceVVt = function ( indexArray ) { + for ( var i = 1; i < 7; i += 2 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVt( indexArray[ i + 1 ] ); + } + }; + + RawObject.prototype.buildFaceVVn = function ( indexArray ) { + for ( var i = 1; i < 7; i += 2 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVn( indexArray[ i + 1 ] ); + } + }; + + RawObject.prototype.buildFaceV = function ( indexArray ) { + for ( var i = 1; i < 4; i ++ ) { + this.attachFaceV_( indexArray[ i ] ); + } + }; + + RawObject.prototype.attachFaceV_ = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalVertexOffset ) * 3; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.vertices.push( this.vertices[ index++ ] ); + rodiu.vertices.push( this.vertices[ index++ ] ); + rodiu.vertices.push( this.vertices[ index ] ); + }; + + RawObject.prototype.attachFaceVt = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalUvOffset ) * 2; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.uvs.push( this.uvs[ index++ ] ); + rodiu.uvs.push( this.uvs[ index ] ); + }; + + RawObject.prototype.attachFaceVn = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalNormalOffset ) * 3; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.normals.push( this.normals[ index++ ] ); + rodiu.normals.push( this.normals[ index++ ] ); + rodiu.normals.push( this.normals[ index ] ); + }; + + /* + * Support for lines with or without texture. irst element in indexArray is the line identification + * 0: "f vertex/uv vertex/uv ..." + * 1: "f vertex vertex ..." + */ + RawObject.prototype.buildLineVvt = function ( lineArray ) { + var length = lineArray.length; + for ( var i = 1; i < length; i ++ ) { + this.vertices.push( parseInt( lineArray[ i ] ) ); + this.uvs.push( parseInt( lineArray[ i ] ) ); + } + }; + + RawObject.prototype.buildLineV = function ( lineArray ) { + var length = lineArray.length; + for ( var i = 1; i < length; i++ ) { + this.vertices.push( parseInt( lineArray[ i ] ) ); + } + }; + + /** + * Clear any empty rawObjectDescription and calculate absolute vertex, normal and uv counts + */ + RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) { + var temp = this.rawObjectDescriptions; + this.rawObjectDescriptions = []; + var rawObjectDescription; + var index = 0; + var absoluteVertexCount = 0; + var absoluteNormalCount = 0; + var absoluteUvCount = 0; + + for ( var name in temp ) { + + rawObjectDescription = temp[ name ]; + if ( rawObjectDescription.vertices.length > 0 ) { + + if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName; + this.rawObjectDescriptions[ index++ ] = rawObjectDescription; + absoluteVertexCount += rawObjectDescription.vertices.length; + absoluteUvCount += rawObjectDescription.uvs.length; + absoluteNormalCount += rawObjectDescription.normals.length; + + } + } + + // don not continue if no result + var notEmpty = false; + if ( index > 0 ) { + + if ( debug ) this.createReport( inputObjectCount, true ); + meshCreator.buildMesh( + this.rawObjectDescriptions, + inputObjectCount, + absoluteVertexCount, + absoluteNormalCount, + absoluteUvCount + ); + notEmpty = true; + + } + return notEmpty; + }; + + RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) { + var report = { + name: this.objectName ? this.objectName : 'groups', + mtllibName: this.mtllibName, + vertexCount: this.vertices.length / 3, + normalCount: this.normals.length / 3, + uvCount: this.uvs.length / 2, + smoothingGroupCount: this.smoothingGroupCount, + mtlCount: this.mtlCount, + rawObjectDescriptions: this.rawObjectDescriptions.length + }; + + if ( printDirectly ) { + console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name ); + console.log( 'Mtllib name: ' + report.mtllibName ); + console.log( 'Vertex count: ' + report.vertexCount ); + console.log( 'Normal count: ' + report.normalCount ); + console.log( 'UV count: ' + report.uvCount ); + console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount ); + console.log( 'Material count: ' + report.mtlCount ); + console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions ); + console.log( '' ); + } + + return report; + }; + + return RawObject; + })(); + + /** + * Descriptive information and data (vertices, normals, uvs) to passed on to mesh building function. + * @class + * + * @param {string} objectName Name of the mesh + * @param {string} groupName Name of the group + * @param {string} materialName Name of the material + * @param {number} smoothingGroup Normalized smoothingGroup (0: THREE.FlatShading, 1: THREE.SmoothShading) + */ + var RawObjectDescription = (function () { + + function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) { + this.objectName = objectName; + this.groupName = groupName; + this.materialName = materialName; + this.smoothingGroup = smoothingGroup; + this.vertices = []; + this.uvs = []; + this.normals = []; + } + + return RawObjectDescription; + })(); + + /** + * MeshCreator is used to transform RawObjectDescriptions to THREE.Mesh + * + * @class + */ + var MeshCreator = (function () { + + function MeshCreator() { + this.sceneGraphBaseNode = null; + this.materials = null; + this.debug = false; + this.globalObjectCount = 1; + + this.validated = false; + } + + MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) { + this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, this.sceneGraphBaseNode ); + this.sceneGraphBaseNode = Validator.verifyInput( this.sceneGraphBaseNode, new THREE.Group() ); + }; + + MeshCreator.prototype.setMaterials = function ( materials ) { + this.materials = Validator.verifyInput( materials, this.materials ); + this.materials = Validator.verifyInput( this.materials, { materials: [] } ); + }; + + MeshCreator.prototype.setDebug = function ( debug ) { + if ( debug === true || debug === false ) this.debug = debug; + }; + + MeshCreator.prototype.validate = function () { + if ( this.validated ) return; + + this.setSceneGraphBaseNode( null ); + this.setMaterials( null ); + this.setDebug( null ); + this.globalObjectCount = 1; + }; + + MeshCreator.prototype.finalize = function () { + this.sceneGraphBaseNode = null; + this.materials = null; + this.validated = false; + }; + + /** + * This is an internal function, but due to its importance to Parser it is documented. + * RawObjectDescriptions are transformed to THREE.Mesh. + * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check). + * This method shall be overridden by the web worker implementation + * + * @param {RawObjectDescription[]} rawObjectDescriptions Array of descriptive information and data (vertices, normals, uvs) about the parsed object(s) + * @param {number} inputObjectCount Number of objects already retrieved from OBJ + * @param {number} absoluteVertexCount Sum of all vertices of all rawObjectDescriptions + * @param {number} absoluteNormalCount Sum of all normals of all rawObjectDescriptions + * @param {number} absoluteUvCount Sum of all uvs of all rawObjectDescriptions + */ + MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) { + + if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount ); + + var bufferGeometry = new THREE.BufferGeometry(); + var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 ); + bufferGeometry.addAttribute( 'position', vertexBA ); + + var normalBA; + if ( absoluteNormalCount > 0 ) { + + normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 ); + bufferGeometry.addAttribute( 'normal', normalBA ); + + } + var uvBA; + if ( absoluteUvCount > 0 ) { + + uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 ); + bufferGeometry.addAttribute( 'uv', uvBA ); + + } + + if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount ); + + var rawObjectDescription; + var material; + var materialName; + var createMultiMaterial = rawObjectDescriptions.length > 1; + var materials = []; + var materialIndex = 0; + var materialIndexMapping = []; + var selectedMaterialIndex; + + var vertexBAOffset = 0; + var vertexGroupOffset = 0; + var vertexLength; + var normalOffset = 0; + var uvOffset = 0; + + for ( var oodIndex in rawObjectDescriptions ) { + rawObjectDescription = rawObjectDescriptions[ oodIndex ]; + + materialName = rawObjectDescription.materialName; + material = this.materials[ materialName ]; + if ( ! material ) { + + material = this.materials[ 'defaultMaterial' ]; + if ( ! material ) { + + material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} ); + material.name = 'defaultMaterial'; + this.materials[ 'defaultMaterial' ] = material; + + } + console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' ); + + } + // clone material in case flat shading is needed due to smoothingGroup 0 + if ( rawObjectDescription.smoothingGroup === 0 ) { + + materialName = material.name + '_flat'; + var materialClone = this.materials[ materialName ]; + if ( ! materialClone ) { + + materialClone = material.clone(); + materialClone.name = materialName; + materialClone.shading = THREE.FlatShading; + this.materials[ materialName ] = name; + + } + + } + + vertexLength = rawObjectDescription.vertices.length; + if ( createMultiMaterial ) { + + // re-use material if already used before. Reduces materials array size and eliminates duplicates + selectedMaterialIndex = materialIndexMapping[ materialName ]; + if ( ! selectedMaterialIndex ) { + + selectedMaterialIndex = materialIndex; + materialIndexMapping[ materialName ] = materialIndex; + materials.push( material ); + materialIndex++; + + } + + bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex ); + vertexGroupOffset += vertexLength / 3; + } + + vertexBA.set( rawObjectDescription.vertices, vertexBAOffset ); + vertexBAOffset += vertexLength; + + if ( normalBA ) { + + normalBA.set( rawObjectDescription.normals, normalOffset ); + normalOffset += rawObjectDescription.normals.length; + + } + if ( uvBA ) { + + uvBA.set( rawObjectDescription.uvs, uvOffset ); + uvOffset += rawObjectDescription.uvs.length; + + } + if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex ); + + } + if ( ! normalBA ) bufferGeometry.computeVertexNormals(); + + if ( createMultiMaterial ) material = materials; + var mesh = new THREE.Mesh( bufferGeometry, material ); + this.sceneGraphBaseNode.add( mesh ); + + this.globalObjectCount++; + }; + + MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) { + console.log( + ' Output Object no.: ' + this.globalObjectCount + + '\n objectName: ' + rawObjectDescription.objectName + + '\n groupName: ' + rawObjectDescription.groupName + + '\n materialName: ' + rawObjectDescription.materialName + + '\n materialIndex: ' + selectedMaterialIndex + + '\n smoothingGroup: ' + rawObjectDescription.smoothingGroup + + '\n #vertices: ' + rawObjectDescription.vertices.length / 3 + + '\n #uvs: ' + rawObjectDescription.uvs.length / 2 + + '\n #normals: ' + rawObjectDescription.normals.length / 3 + ); + }; + + return MeshCreator; + })(); + + OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) { + var workerCode = ''; + workerCode += funcBuildObject( 'Consts', Consts ); + workerCode += funcBuildObject( 'Validator', Validator ); + workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); + workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject ); + workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription ); + return workerCode; + }; + + return OBJLoader2; +})(); +// File: init/Definitions.js +/* + * XSeen JavaScript Library + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + * + * Based on code originally provided by + * Philip Taylor: http://philip.html5.org + */ + +/** + * The Namespace container for x3dom objects. + * @namespace x3dom + * + * Removed THREE loaders + loaders: { + 'file' : new THREE.FileLoader(), + 'image' : 0, + }, + + * */ +var xseen = { + canvases : [], + sceneInfo : [], + nodeDefinitions : {}, + parseTable : {}, + node : {}, + utils : {}, + eventManager : {}, + Events : {}, + Navigation : {}, + + loadMgr : {}, + loader : { + 'Null' : '', + 'ColladaLoader' : '', + 'GltfLegacy' : '', + 'GltfLoader' : '', + 'ObjLoader' : '', + 'ImageLoader' : '', + 'X3dLoader' : '', + }, + loadProgress : function (xhr) { + if (xhr.total != 0) { + console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); + } + }, + loadError : function (xhr, userdata, code, message) { + console.error('An error happened on '+userdata.e.id+'\n'+code+'\n'+message); + }, + loadMime : { + '' : {name: 'Null', loader: 'Null'}, + 'dae' : {name: 'Collada', loader: 'ColladaLoader'}, + 'glb' : {name: 'glTF Binary', loader: 'GltfLoader'}, + 'glbl' : {name: 'glTF Binary', loader: 'GltfLegacy'}, + 'gltf' : {name: 'glTF JSON', loader: 'GltfLoader'}, + 'obj' : {name: 'OBJ', loader: 'ObjLoader'}, + 'png' : {name: 'PNG', loader: 'ImageLoader'}, + 'jpg' : {name: 'JPEG', loader: 'ImageLoader'}, + 'jpeg' : {name: 'JPEG', loader: 'ImageLoader'}, + 'gif' : {name: 'GIF', loader: 'ImageLoader'}, + 'x3d' : {name: 'X3D XML', loader: 'X3dLoader'}, + }, +// helper + array_to_object : function (a) { + var o = {}; + for(var i=0;i ' + obj.type + ' (' + obj.name + ')'); + for (var i=0; i + var validParams = xseen.array_to_object([ + 'showLog', + 'showStat', + 'showProgress', + 'PrimitiveQuality', + 'components', + 'loadpath', + 'disableDoubleClick', + 'backend', + 'altImg', + 'runtimeEnabled', + 'keysEnabled', + 'showTouchpoints', + 'disableTouch', + 'maxActiveDownloads' + ]); + var components, prefix; + var showLoggingConsole = false; + + // for each XSeens element + for (var i=0; i < xseens.length; i++) { + + // default parameters + settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'false'); + settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'true'); + settings.setProperty("showStat", xseens[i].getAttribute("showStat") || 'false'); + settings.setProperty("showProgress", xseens[i].getAttribute("showProgress") || 'true'); + settings.setProperty("PrimitiveQuality", xseens[i].getAttribute("PrimitiveQuality") || 'High'); + + // for each param element inside the X3D element + // add settings to properties object + params = xseens[i].getElementsByTagName('PARAM'); + for (var j=0; j < params.length; j++) { + if (params[j].getAttribute('name') in validParams) { + settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value')); + } else { + //xseen.debug.logError("Unknown parameter: " + params[j].getAttribute('name')); + } + } + + // enable log + if (settings.getProperty('showLog') === 'true') { + showLoggingConsole = true; + } + + if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) { + // load components from params or default to x3d attribute + components = settings.getProperty('components', xseens[i].getAttribute("components")); + if (components) { + prefix = settings.getProperty('loadpath', xseens[i].getAttribute("loadpath")); + components = components.trim().split(','); + for (j=0; j < components.length; j++) { + xseen.loadJS(components[j] + ".js", prefix); + } + } + + // src=foo.x3d adding inline node, not a good idea, but... + if (xseens[i].getAttribute("src")) { + var _scene = document.createElement("scene"); + var _inl = document.createElement("Inline"); + _inl.setAttribute("url", xseens[i].getAttribute("src")); + _scene.appendChild(_inl); + xseens[i].appendChild(_scene); + } + } + } + // }}} + + if (showLoggingConsole == true) { + xseen.debug.activate(true); + } else { + xseen.debug.activate(false); + } + + // Convert the collection into a simple array (is this necessary?) +/* Don't think so -- commented out + xseens = Array.map(xseens, function (n) { + n.hasRuntime = true; + return n; + }); + */ + + if (xseen.versionInfo !== undefined) { + xseen.debug.logInfo("XSeen version " + xseen.versionInfo.version + ", " + + "Date " + xseen.versionInfo.date); + xseen.debug.logInfo(xseen.versionInfo.splashText); + } + + //xseen.debug.logInfo("Found " + xseen.length + " XSeen nodes"); + + + // Create a HTML canvas for every XSeen scene and wrap it with + // an X3D canvas and load the content + var x_element; + var x_canvas; + var altDiv, altP, aLnk, altImg; + var t0, t1; + + for (var i=0; i < xseens.length; i++) + { + x_element = xseens[i]; // The XSeen DOM element + + x_canvas = new THREE.Scene(); // May need addtl info if multiple: xseen.X3DCanvas(x_element, xseen.canvases.length); + xseen.canvases.push(x_canvas); // TODO: Need to handle failure to initialize? + t0 = new Date().getTime(); + +/* + * Handle opening tag attributes + * divHeight + * divWidth + * turntable Indicates if view automatically rotates (independent of navigation) + */ + + var divWidth = x_element.getAttribute('width'); + var divHeight = x_element.getAttribute('height'); + if (divHeight + divWidth < 100) { + divHeight = 450; + divWidth = 800; + } else if (divHeight < 50) { + divHeight = Math.floor(divWidth/2) + 50; + } else if (divWidth < 50) { + divWidth = divHeight * 2 - 100; + } + var turntable = (x_element.getAttribute('turntable') || '').toLowerCase(); + if (turntable == 'on' || turntable == 'yes' || turntable == 'y' || turntable == '1') { + turntable = true; + } else { + turntable = false; + } + turntable = false; +/* + * Removed because camera is stored in the Scene node (x_element._xseen.renderer.camera + * Leave variable definition so other code works... + var x_camera = new THREE.PerspectiveCamera( 75, divWidth / divHeight, 0.1, 1000 ); + x_camera.position.x = 0; + x_camera.position.z = 10; + */ + var x_camera = {}; + var x_renderer = new THREE.WebGLRenderer(); + x_renderer.setSize (divWidth, divHeight); + //x_element.appendChild (x_renderer.domElement); + + // Stereo camera effect + // from http://charliegerard.github.io/blog/Virtual-Reality-ThreeJs/ + var x_effect = new THREE.StereoEffect(x_renderer); + x_renderer.controls = {'update' : function() {return;}}; + +/* + * Add event handler to XSeen tag (x_element) + * These handle all mouse/cursor/button controls when the cursor is + * in the XSeen region of the page + */ + + x_element.addEventListener ('dblclick', xseen.Events.canvasHandler, true); + x_element.addEventListener ('click', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mousedown', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mousemove', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mouseup', xseen.Events.canvasHandler, true); + x_element.addEventListener ('xseen', xseen.Events.XSeenHandler); // Last chance for XSeen handling of event + x_element.addEventListener ('change', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event +/* + x_element.addEventListener ('mousedown', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + x_element.addEventListener ('mouseup', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + x_element.addEventListener ('mousemove', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + */ + xseen.sceneInfo.push ({ + 'size' : {'width':divWidth, 'height':divHeight}, + 'scene' : x_canvas, + 'renderer' : x_renderer, + 'effect' : x_effect, + 'camera' : [x_camera], + 'turntable' : turntable, + 'mixers' : [], + 'clock' : new THREE.Clock(), + 'element' : x_element, + 'selectable': [], + 'stacks' : [], + 'tmp' : {activeViewpoint:false}, + 'xseen' : xseen, + }); + x_element._xseen = {}; + x_element._xseen.children = []; + x_element._xseen.sceneInfo = xseen.sceneInfo[xseen.sceneInfo.length-1]; + + t1 = new Date().getTime() - t0; + xseen.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms."); + } + + var ready = (function(eventType) { + var evt = null; + + if (document.createEvent) { + evt = document.createEvent("Events"); + evt.initEvent(eventType, true, true); + document.dispatchEvent(evt); + } else if (document.createEventObject) { + evt = document.createEventObject(); + // http://stackoverflow.com/questions/1874866/how-to-fire-onload-event-on-document-in-ie + document.body.fireEvent('on' + eventType, evt); + } + })('load'); + + // for each X-Scene tag, parse and load the contents + var t=[]; + for (var i=0; i root element that was added after document load. */ + xseen.reload = function() { + onload(); + }; + + /* FIX PROBLEM IN CHROME - HACK - searching for better solution !!! */ + if (navigator.userAgent.indexOf("Chrome") != -1) { + document.__getElementsByTagName = document.getElementsByTagName; + + document.getElementsByTagName = function(tag) { + var obj = []; + var elems = this.__getElementsByTagName("*"); + + if(tag =="*"){ + obj = elems; + } else { + tag = tag.toUpperCase(); + for (var i = 0; i < elems.length; i++) { + var tagName = elems[i].tagName.toUpperCase(); + if (tagName === tag) { + obj.push(elems[i]); + } + } + } + + return obj; + }; + + document.__getElementById = document.getElementById; + document.getElementById = function(id) { + var obj = this.__getElementById(id); + + if (!obj) { + var elems = this.__getElementsByTagName("*"); + for (var i=0; i + * MODE_NAVIGATION events are 'captured' (not bubbled) and drive the camera position + * + * In MODE_SELECT + * mousedown sets redispatch to TRUE + * click Activates + * dblclick ?? + * mouseup terminates select + * mousemove sets redispatch to FALSE + * In all cases, recreate event as type='xseen' and dispatch from geometry when + * redispatch is TRUE. + */ + canvasHandler: function (ev) + { + //console.log ('Primary canvas event handler for event type: ' + ev.type); + var sceneInfo = ev.currentTarget._xseen.sceneInfo; + var localXseen = sceneInfo.xseen; + var lEvents = localXseen.Events; + var type = ev.type; + if (type == 'mousedown') { + lEvents.redispatch = true; + lEvents.mode = lEvents.MODE_SELECT; + lEvents.mouse.x = (ev.clientX / 800) * 2 -1; // TODO: Use real XSeen display sizes + lEvents.mouse.y = (ev.clientY / 450) * 2 -1; + // + lEvents.raycaster.setFromCamera(lEvents.mouse, sceneInfo.element._xseen.renderer.activeCamera); + var hitGeometryList = lEvents.raycaster.intersectObjects (sceneInfo.selectable, true); + if (hitGeometryList.length != 0) { + lEvents.object = hitGeometryList[0]; + } else { + lEvents.object = {}; + lEvents.redispatch = false; + lEvents.mode = lEvents.MODE_NAVIGATION; + } + } + if ((lEvents.redispatch || type == 'click' || type == 'dblclick') && typeof(lEvents.object.object) !== 'undefined') { + // Generate an XSeen (Custom)Event of the same type and dispatch it + var newEv = lEvents.createEvent (ev, lEvents.object); + lEvents.object.object.userData.dispatchEvent(newEv); + ev.stopPropagation(); // No propagation beyond this tag + } else { + //console.log ('Navigation mode...'); + } + if (type == 'mouseup') { + lEvents.redispatch = false; + lEvents.mode = lEvents.MODE_NAVIGATION; + } + }, + + createEvent: function (ev, selectedObject) + { + var properties = { + 'detail': { // This object contains all of the XSeen data + 'type': ev.type, + 'originalType': ev.type, + 'originator': selectedObject.object.userData, + 'position': { + 'x': selectedObject.point.x, + 'y': selectedObject.point.y, + 'z': selectedObject.point.z, + }, + 'normal': { + 'x': 0, + 'y': 0, + 'z': 0, + }, + 'uv': { + 'x': selectedObject.uv.x, + 'y': selectedObject.uv.y, + }, + 'screenX': ev.screenX, + 'screenY': ev.screenY, + 'clientX': ev.clientX, + 'clientY': ev.clientY, + 'ctrlKey': ev.ctrlKey, + 'shiftKey': ev.shiftKey, + 'altKey': ev.altKey, + 'metaKey': ev.metaKey, + 'button': ev.button, + 'buttons': ev.buttons, + }, + 'bubbles': ev.bubbles, + 'cancelable': ev.cancelable, + 'composed': ev.composed, + }; + + var newEvent = new CustomEvent('xseen', properties); + return newEvent; + }, + // Uses method described in https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + // to change 'this' for the handler method. Want 'this' to refer to the target node. + addHandler: function (route, source, eventName, destination, field) + { + var handler = {}; + handler.route = route; // Route element + handler.source = source; // Source element + handler.type = eventName; // Event type + handler.destination = destination; // Destination element + handler.field = field; // Destination field structure + handler.handler = destination._xseen.handlers[field.handlerName]; + this.routes.push (handler); + if (typeof(source._xseen) === 'undefined') { // DOM event + source.addEventListener (eventName, function(ev) { + handler.handler(ev) + }); + } else { // XSeen event + source.addEventListener ('xseen', function(ev) { + //console.log ('New event of original type: |'+ev.detail.originalType+'|; Desired type: |'+handler.type+'|'); + if (ev.detail.originalType == handler.type) { + handler.handler(ev) + } + }); + } + }, + + // Generic notification handler for XSeen's canvas + XSeenHandler: function (ev) + { + //console.log ('XSeen DEBUG Event Bubble handler ('+ev.type+'/'+ev.eventPhase+').'); + }, + XSeenDebugHandler : function (ev) + { + console.log ('XSeen DEBUG Event Capture handler ('+ev.type+'/'+ev.eventPhase+').'); + }, + }; + + this.debug = { + INFO: "INFO", + WARNING: "WARNING", + ERROR: "ERROR", + EXCEPTION: "EXCEPTION", + + // determines whether debugging/logging is active. If set to "false" + // no debugging messages will be logged. + isActive: false, + + // stores if firebug is available + isFirebugAvailable: false, + + // stores if the xseen.debug object is initialized already + isSetup: false, + + // stores if xseen.debug object is append already (Need for IE integration) + isAppend: false, + + // stores the number of lines logged + numLinesLogged: 0, + + // the maximum number of lines to log in order to prevent + // the browser to slow down + maxLinesToLog: 10000, + + // the container div for the logging messages + logContainer: null, + + /** @brief Setup the xseen.debug object. + + Checks for firebug and creates the container div for the logging + messages. + */ + setup: function() { + // If debugging is already setup simply return + if (xseen.debug.isSetup) { return; } + + // Check for firebug console + try { + if (window.console.firebug !== undefined) { + xseen.debug.isFirebugAvailable = true; + } + } + catch (err) { + xseen.debug.isFirebugAvailable = false; + } + + xseen.debug.setupLogContainer(); + + // setup should be setup only once, thus store if we done that already + xseen.debug.isSetup = true; + }, + + /** @brief Activates the log + */ + activate: function(visible) { + xseen.debug.isActive = true; + + //var aDiv = document.createElement("div"); + //aDiv.style.clear = "both"; + //aDiv.appendChild(document.createTextNode("\r\n")); + //aDiv.style.display = (visible) ? "block" : "none"; + xseen.debug.logContainer.style.display = (visible) ? "block" : "none"; + + //Need this HACK for IE/Flash integration. IE don't have a document.body at this time when starting Flash-Backend + if(!xseen.debug.isAppend) { + if(navigator.appName == "Microsoft Internet Explorer") { + //document.documentElement.appendChild(aDiv); + xseen.debug.logContainer.style.marginLeft = "8px"; + document.documentElement.appendChild(xseen.debug.logContainer); + }else{ + //document.body.appendChild(aDiv); + document.body.appendChild(xseen.debug.logContainer); + } + xseen.debug.isAppend = true; + } + }, + + /** @brief Inserts a container div for the logging messages into the HTML page + */ + setupLogContainer: function() { + xseen.debug.logContainer = document.createElement("div"); + xseen.debug.logContainer.id = "xseen_logdiv"; + xseen.debug.logContainer.setAttribute("class", "xseen-logContainer"); + xseen.debug.logContainer.style.clear = "both"; + //document.body.appendChild(xseen.debug.logContainer); + }, + + /** @brief Generic logging function which does all the work. + + @param msg the log message + @param logType the type of the log message. One of INFO, WARNING, ERROR + or EXCEPTION. + */ + doLog: function(msg, logType) { + + // If logging is deactivated do nothing and simply return + if (!xseen.debug.isActive) { return; } + + // If we have reached the maximum number of logged lines output + // a warning message + if (xseen.debug.numLinesLogged === xseen.debug.maxLinesToLog) { + msg = "Maximum number of log lines (=" + xseen.debug.maxLinesToLog + ") reached. Deactivating logging..."; + } + + // If the maximum number of log lines is exceeded do not log anything + // but simply return + if (xseen.debug.numLinesLogged > xseen.debug.maxLinesToLog) { return; } + + // Output a log line to the HTML page + var node = document.createElement("p"); + node.style.margin = 0; + switch (logType) { + case xseen.debug.INFO: + node.style.color = "#009900"; + break; + case xseen.debug.WARNING: + node.style.color = "#cd853f"; + break; + case xseen.debug.ERROR: + node.style.color = "#ff4500"; + break; + case xseen.debug.EXCEPTION: + node.style.color = "#ffff00"; + break; + default: + node.style.color = "#009900"; + break; + } + + // not sure if try/catch solves problem http://sourceforge.net/apps/trac/x3dom/ticket/52 + // but due to no avail of ATI gfxcard can't test + try { + node.innerHTML = logType + ": " + msg; + xseen.debug.logContainer.insertBefore(node, xseen.debug.logContainer.firstChild); + } catch (err) { + if (window.console.firebug !== undefined) { + window.console.warn(msg); + } + } + + // Use firebug's console if available + if (xseen.debug.isFirebugAvailable) { + switch (logType) { + case xseen.debug.INFO: + window.console.info(msg); + break; + case xseen.debug.WARNING: + window.console.warn(msg); + break; + case xseen.debug.ERROR: + window.console.error(msg); + break; + case xseen.debug.EXCEPTION: + window.console.debug(msg); + break; + default: + break; + } + } + + xseen.debug.numLinesLogged++; + }, + + /** Log an info message. */ + logInfo: function(msg) { + xseen.debug.doLog(msg, xseen.debug.INFO); + }, + + /** Log a warning message. */ + logWarning: function(msg) { + xseen.debug.doLog(msg, xseen.debug.WARNING); + }, + + /** Log an error message. */ + logError: function(msg) { + xseen.debug.doLog(msg, xseen.debug.ERROR); + }, + + /** Log an exception message. */ + logException: function(msg) { + xseen.debug.doLog(msg, xseen.debug.EXCEPTION); + }, + + /** Log an assertion. */ + assert: function(c, msg) { + if (!c) { + xseen.debug.doLog("Assertion failed in " + xseen.debug.assert.caller.name + ': ' + msg, xseen.debug.ERROR); + } + }, + + /** + Checks the type of a given object. + + @param obj the object to check. + @returns one of; "boolean", "number", "string", "object", + "function", or "null". + */ + typeOf: function (obj) { + var type = typeof obj; + return type === "object" && !obj ? "null" : type; + }, + + /** + Checks if a property of a specified object has the given type. + + @param obj the object to check. + @param name the property name. + @param type the property type (optional, default is "function"). + @returns true if the property exists and has the specified type, + otherwise false. + */ + exists: function (obj, name, type) { + type = type || "function"; + return (obj ? this.typeOf(obj[name]) : "null") === type; + }, + + /** + Dumps all members of the given object. + */ + dumpFields: function (node) { + var str = ""; + for (var fName in node) { + str += (fName + ", "); + } + str += '\n'; + xseen.debug.logInfo(str); + return str; + } + }; +// Call the setup function to... umm, well, setup xseen.debug + this.debug.setup(); + + }; +// File: ./Nav-Viewpoint.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.Navigation.(label); + * Computes the new viewing location for the specific mode. + * + * Each Navigation method takes the following parameters: + * speed Floating point value indicating motion speed. + * Units are distance per milli-second for linear motion or + * revolutions (2*pi) per milli-second for angular motion + * deltaT Time since last update in milli-seconds + * TODO: This is not true for the Turntable class of camera motion -- which isn't really Navigation anyway + * scene The 'sceneInfo' object for this HTML instance + * camera The current (active) camera (aka scene.element._xseen.renderer.activeCamera) + * + * Navigation is the user-controlled process of moving in the 3D world. + * + */ + +xseen.Navigation = { + 'TwoPi' : 2 * Math.PI, + 'none' : function () {}, // Does not allow user-controlled navigation + + 'turntable' : function (speed, deltaT, scene, camera) + { + var T, radians, radius, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + radius = vp.fields._radius0; + camera.position.x = radius * Math.sin(radians) + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.position.z = radius * Math.cos(radians); + camera.lookAt(scene.ORIGIN); + }, + + 'tilt' : function (speed, deltaT, scene, camera) + { + var T, radians, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.lookAt(scene.ORIGIN); + }, + + 'setup' : { + 'none' : function () {return null;}, + + 'orbit' : function (camera, renderer) + { + var controls; + controls = new THREE.OrbitControls( camera, renderer.domElement ); + //controls.addEventListener( 'change', render ); // remove when using animation loop + // enable animation loop when using damping or autorotation + //controls.enableDamping = true; + //controls.dampingFactor = 0.25; + controls.enableZoom = false; + controls.enableZoom = true; + return controls; + }, + + 'trackball' : function (camera, renderer) + { + var controls; + controls = new THREE.TrackballControls(camera, renderer.domElement); + + // These are from the example code at https://github.com/mrdoob/three.js/blob/master/examples/misc_controls_trackball.html + controls.rotateSpeed = 1.0; + controls.zoomSpeed = 1.2; + controls.panSpeed = 0.8; + controls.noZoom = false; + controls.noPan = false; + controls.staticMoving = true; + controls.dynamicDampingFactor = 0.3; + controls.keys = [ 65, 83, 68 ]; + + // Render function is 'xseen.renderFrame' + // remove when using animation loop + //controls.addEventListener( 'change', xseen.renderFrame ); + return controls; + }, + }, +}; +// File: ./Navigation.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.Navigation.(label); + * Computes the new viewing location for the specific mode. + * + * Each Navigation method takes the following parameters: + * speed Floating point value indicating motion speed. + * Units are distance per milli-second for linear motion or + * revolutions (2*pi) per milli-second for angular motion + * deltaT Time since last update in milli-seconds + * TODO: This is not true for the Turntable class of camera motion -- which isn't really Navigation anyway + * scene The 'sceneInfo' object for this HTML instance + * camera The current (active) camera (aka scene.element._xseen.renderer.activeCamera) + * + * Navigation is the user-controlled process of moving in the 3D world. + * + */ + +xseen.Navigation = { + 'TwoPi' : 2 * Math.PI, + 'none' : function () {}, // Does not allow user-controlled navigation + + 'turntable' : function (speed, deltaT, scene, camera) + { + var T, radians, radius, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + radius = vp.fields._radius0; + camera.position.x = radius * Math.sin(radians) + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.position.z = radius * Math.cos(radians); + camera.lookAt(scene.ORIGIN); + }, + + 'tilt' : function (speed, deltaT, scene, camera) + { + var T, radians, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.lookAt(scene.ORIGIN); + }, + + 'setup' : { + 'none' : function () {return null;}, + + 'orbit' : function (camera, renderer) + { + var controls; + controls = new THREE.OrbitControls( camera, renderer.domElement ); + //controls.addEventListener( 'change', render ); // remove when using animation loop + // enable animation loop when using damping or autorotation + //controls.enableDamping = true; + //controls.dampingFactor = 0.25; + controls.enableZoom = false; + controls.enableZoom = true; + return controls; + }, + }, +}; +// File: ./NodeDefinitions.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.nodes. is the definition of + * All internal variables are stored in ._internal. All functions start with '_' + * + * This is a bare-bones setup. There is no error checking - missing arguments or + * methods that do not exist (e.g., .init) + * + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump (_dumpTable) would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + * Fields are added with the .addField method. It takes its values from the argument list + * or an object passed as the first argument. The properties of the argument are: + * name - the name of the field. This is converted to lowercase before use + * datatype - the datatype of the field. There must be a method in xseen.types by this name + * defaultValue - the default value of the field to be used if the field is not present or incorrectly defined. + * If this argument is an array, then it is the set of allowed values. The first element is the default. + * enumerated - the list of allowed values when the datatype only allows specific values for this field (optional) + * animatable - Flag (T/F) indicating if the field is animatable. Generally speaking, enumerated fieles are not animatable + */ + +xseen.nodes = { + '_defineNode' : function(nodeName, nodeComponent, nodeMethod) { + //methodBase = 'xseen.node.'; + var methodBase = ''; + node = { + 'tag' : nodeName, + 'taglc' : nodeName.toLowerCase(), + 'component' : nodeComponent, + 'method' : methodBase + nodeMethod, + 'fields' : [], + 'fieldIndex': [], + 'addField' : function (fieldObj, datatype, defaultValue) { + var fieldName, namelc, enumerated, animatable; + if (typeof(fieldObj) === 'object') { + fieldName = fieldObj.name; + datatype = fieldObj.datatype; + defaultValue = fieldObj.defaultValue; + enumerated = (typeof(fieldObj.enumerated) === 'undefined') ? [] : fieldObj.enumerated; + animatable = (typeof(fieldObj.animatable) === 'undefined') ? false : fieldObj.animatable; + } else { + fieldName = fieldObj; + animatable = false; + if (typeof(defaultValue) == 'array') { + enumerated = defaultValue; + defaultValue = enumerated[0]; + } else { + enumerated = []; + } + } + namelc = fieldName.toLowerCase(); + this.fields.push ({ + 'field' : fieldName, + 'fieldlc' : namelc, + 'type' : datatype, + 'default' : defaultValue, + 'enumeration' : enumerated, + 'animatable' : animatable, + 'clone' : this.cloneField, + 'setFieldName' : this.setFieldName, + }); + this.fieldIndex[namelc] = this.fields.length-1; + return this; + }, + 'addNode' : function () { + xseen.parseTable[this.taglc] = this; + }, + 'cloneField' : function () { + var newFieldObject = { + 'field' : this.field, + 'fieldlc' : this.fieldlc, + 'type' : this.type, + 'default' : 0, + 'enumeration' : [], + 'animatable' : this.animatable, + 'clone' : this.clone, + 'setFieldName' : this.setFieldName, + }; + for (var i=0; i xseen.node[xseen.nodeDefinitions[nodeName].method].endParse (element, parent); + } + //xseen.debug.logInfo(" reached bottom, heading back up from |" + nodeName + "|"); +} +// File: ./Properties.js +/* + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + * + * Based on code originally provided by + * Philip Taylor: http://philip.html5.org + */ + + +xseen.Properties = function() { + this.properties = {}; +}; + +xseen.Properties.prototype.setProperty = function(name, value) { + xseen.debug.logInfo("Properties: Setting property '"+ name + "' to value '" + value + "'"); + this.properties[name] = value; +}; + +xseen.Properties.prototype.getProperty = function(name, def) { + if (this.properties[name]) { + return this.properties[name] + } else { + return def; + } +}; + +xseen.Properties.prototype.merge = function(other) { + for (var attrname in other.properties) { + this.properties[attrname] = other.properties[attrname]; + } +}; + +xseen.Properties.prototype.toString = function() { + var str = ""; + for (var name in this.properties) { + str += "Name: " + name + " Value: " + this.properties[name] + "\n"; + } + return str; +}; +// File: ./StackHandler.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.utlis.StackHandler(label); + * Creates a new stack that is managed by this class + * + * Note that the Push-Down stack (aka FILO) is implemented as a reverse list + * so that the Array methods .push and .pop can be used. The end of the array + * is the "top-most" element in the stack. + */ + +xseen.utils.StackHandler = function (label) { + this._internals = {}; // Internal class storage + this._internals.label = label; // Unique user-supplied name for this stack + this._internals.stack = []; // Maintains the stack. Last entry on stack is active + this._internals.active = -1; // Index of currently active list element + this._internals.activeNode = {}; // Entry of currently active list element + this._internals.defaultNode = {}; // The default entry to be active if nothing else is + + this._setActiveNode = function() { // Sets the entry specified by nodeId as active + this._internals.active = this._internals.stack.length-1; + if (this._internals.active >= 0) { + this._internals.activeNode = this._internals.stack[this._internals.active]; + } else { + this._internals.activeNode = this._internals.defaultNode; + } + } + + this.init = function() { // Clears existing stack + this._internals.stack = []; + } + + this.pushDown = function(node) { // Push new node onto stack and make active + this._internals.stack.push (node); + this._setActiveNode(); + } + + this.popOff = function() { // Pop node off stack and make next one active + this._internals.stack.pop(); + this._setActiveNode(); + } + + this.getActive = function() { + return this._internals.activeNode; + } + + this.setDefault = function(node) { + this._internals.defaultNode = node; + if (Object.keys(this._internals.activeNode).length === 0) { + this._internals.activeNode = this._internals.defaultNode; + } + } +} +// File: ./Types.js +/* + * xseen.types contains the datatype and conversion utilities. These convert one format to another. + * Any method ending in 'toX' where 'X' is some datatype is a conversion to that type + * Other methods convert from string with space-spearated values + */ +xseen.types = { + 'Deg2Rad' : Math.PI / 180, + + 'SFFloat' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return value; + }, + + 'SFInt' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return Math.round(value); + }, + + 'SFBool' : function (value, def) + { + if (value === null) {return def;} + if (value) {return true;} + if (!value) {return false;} + return def; + }, + + 'SFTime' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return value; + }, + + 'SFVec3f' : function (value, def) + { + if (value === null) {return def;} + var v3 = value.split(' '); + if (v3.length < 3 || Number.isNaN(v3[0]) || Number.isNaN(v3[1]) || Number.isNaN(v3[2])) { + return def; + } + return [v3[0]-0, v3[1]-0, v3[2]-0]; + }, + + 'SFVec2f' : function (value, def) + { + if (value === null) {return def;} + var v2 = value.split(' '); + if (v2.length != 2 || Number.isNaN(v2[0]) || Number.isNaN(v2[1])) { + return def; + } + return [v2[0]-0, v2[1]-0]; + }, + + 'SFRotation' : function (value, def) + { + if (value === null) {return def;} + var v4 = value.split(' '); + if (v4.length != 4 || Number.isNaN(v4[0]) || Number.isNaN(v4[1]) || Number.isNaN(v4[2]) || Number.isNaN(v4[3])) { + return def; + } + var result = { + 'vector' : [v4[0], v4[1], v4[2], v4[3]], + 'axis_angle' : [{'x': v4[0], 'y': v4[1], 'z': v4[2]}, v4[3]], + }; + return result; + }, + + 'SFColor' : function (value, defaultString) + { + var v3 = this.SFVec3f(value, defaultString); + v3[0] = Math.min(Math.max(v3[0], 0.0), 1.0); + v3[1] = Math.min(Math.max(v3[1], 0.0), 1.0); + v3[2] = Math.min(Math.max(v3[2], 0.0), 1.0); + return v3; + }, + + 'SFString' : function (value, def) + { + if (value === null) {value = def;} + return value; + }, + +// For MF* types, a default of '' means to return an empty array on parsing error + 'MFFloat' : function (value, def) + { + var defReturn = (def == '') ? [] : def; + if (value === null) {return defReturn;} + var mi = value.split(' '); + var rv = []; + for (var i=0; iDocumentation."]; +/* + * All X3D and A-Frame pre-defined solids, fixed camera, directional light, Material texture only, glTF model loader with animations, Assets and reuse, Viewpoint, Background, Lighting, Image Texture, [Indexed]TriangleSet, IndexedFaceSet, [Indexed]QuadSet
\nNext work
  • Event Model/Animation
  • Extrusion
  • Navigation
", + * + * All of the following are ALPHA releases for V0.4.x + * V0.4.0+13 Feature -- events (from HTML to XSeen) + * V0.4.1+14 Fix - minor text correction in xseen.node.geometry__TriangulateFix (nodes-x3d_Geometry.js) + * V0.4.1+15 Modified build.pl to increase compression by removing block comments + * V0.4.1+16 Feature -- XSeen events (from XSeen to HTML) + * V0.4.2+17 Feature -- XSeen internals events (from XSeen to XSeen) with changes to fix previous event handling + * V0.4.2+18 Feature -- Split screen VR display + * V0.4.3+19 Rebuild and fix loading caused by new Stereo library + * V0.4.3+20 Feature -- Navigation (orbit), including Stack update for Viewpoint and restructuring the rendering loop + * V0.4.3+21 Feature -- Changed handling of Viewpoint to include camera motion + * V0.4.4+22 Fix -- Internal event handling in passing on events of the proper type + * V0.4.5+23 Feature -- Navigation (trackball) + * V0.4.5+24 Fix -- when there is no navigation + * + * In progress + */ + var version = { + major : Major, + minor : Minor, + patch : Patch, + preRelease : PreRelease, + release : Release, + version : '', + date : RDate, + splashText : SplashText + }; +// Using the scheme at http://semver.org/ + version.version = version.major + '.' + version.minor + '.' + version.patch; + version.version += (version.preRelease != '') ? '-' + version.preRelease : ''; + version.version += (version.release != '') ? '+' + version.release : ''; + return version; +} +// File: nodes/nodes-af.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code for A-Frame nodes + + +xseen.node.core_NOOP = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.parsing = function (s, e) { + xseen.debug.logInfo ('Parsing init details stub for ' + s); +} + +xseen.node.af_Entity = { + 'init' : function (e,p) + { + xseen.node.parsing('A-Frame Entity'); + }, + 'fin' : function (e,p) {} +}; +xseen.node.af_Assets = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.af_AssetItem = { + 'init' : function (e,p) // Only field is SRC. + { + }, + 'fin' : function (e,p) {} +}; +xseen.node.af_Mixin = { + 'init' : function (e,p) // Lots of fields -- all nebelous until used + { + }, + 'fin' : function (e,p) {} +}; + + + +xseen.node.af_Appearance = function (e) { + var parameters = { + 'aoMap' : e._xseen.fields['ambient-occlusion-map'], + 'aoMapIntensity' : e._xseen.fields['ambient-occlusion-map-intensity'], + 'color' : e._xseen.fields['color'], + 'displacementMap' : e._xseen.fields['displacement-map'], + 'displacementScale' : e._xseen.fields['displacement-scale'], + 'displacementBias' : e._xseen.fields['displacement-bias'], + 'envMap' : e._xseen.fields['env-map'], + 'normalMap' : e._xseen.fields['normal-map'], + 'normalScale' : e._xseen.fields['normal-scale'], + 'wireframe' : e._xseen.fields['wireframe'], + 'wireframeLinewidth' : e._xseen.fields['wireframe-linewidth'], + }; + var material = new THREE.MeshPhongMaterial(parameters); + return material; +/* + * === All Entries === +.aoMap +.aoMapIntensity +.color + .combine +.displacementMap +.displacementScale +.displacementBias + .emissive + .emissiveMap + .emissiveIntensity +.envMap + .lightMap + .lightMapIntensity + .map + .morphNormals + .morphTargets +.normalMap +.normalScale + .reflectivity + .refractionRatio + .shininess + .skinning + .specular + .specularMap +.wireframe + .wireframeLinecap + .wireframeLinejoin +.wireframeLinewidth +/////////////////////////////////////////////////////////////////////////////// +e._xseen.fields['ambient-occlusion-map'] +e._xseen.fields['ambient-occlusion-map-intensity'] + e._xseen.fields['ambient-occlusion-texture-offset'] + e._xseen.fields['ambient-occlusion-texture-repeat'] +e._xseen.fields['color'] +e._xseen.fields['displacement-bias'] +e._xseen.fields['displacement-map'] +e._xseen.fields['displacement-scale'] + e._xseen.fields['displacement-texture-offset'] + e._xseen.fields['displacement-texture-repeat'] +e._xseen.fields['env-map'] + e._xseen.fields['fog'] + e._xseen.fields['metalness'] +e._xseen.fields['normal-map'] +e._xseen.fields['normal-scale'] + e._xseen.fields['normal-texture-offset'] + e._xseen.fields['normal-texture-repeat'] + e._xseen.fields['repeat'] + e._xseen.fields['roughness'] + e._xseen.fields['spherical-env-map'] + e._xseen.fields['src'] +e._xseen.fields['wireframe'] +e._xseen.fields['wireframe-linewidth'] + + * === Unused Entries === + .combine + .emissive + .emissiveMap + .emissiveIntensity + .lightMap + .lightMapIntensity + .map + .morphNormals + .morphTargets + .reflectivity + .refractionRatio + .shininess + .skinning + .specular + .specularMap + .wireframeLinecap + .wireframeLinejoin +/////////////////////////////////////////////////////////////////////////////// + e._xseen.fields['ambient-occlusion-texture-offset'] + e._xseen.fields['ambient-occlusion-texture-repeat'] + e._xseen.fields['displacement-texture-offset'] + e._xseen.fields['displacement-texture-repeat'] + e._xseen.fields['fog'] + e._xseen.fields['metalness'] + e._xseen.fields['normal-texture-offset'] + e._xseen.fields['normal-texture-repeat'] + e._xseen.fields['repeat'] + e._xseen.fields['roughness'] + e._xseen.fields['spherical-env-map'] + e._xseen.fields['src'] + */ +} + +xseen.node.af_Box = { + 'init' : function (e,p) + { + var geometry = new THREE.BoxGeometry( + e._xseen.fields.width, + e._xseen.fields.height, + e._xseen.fields.depth, + e._xseen.fields['segments-width'], + e._xseen.fields['segments-height'], + e._xseen.fields['segments-depth'] + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Cone = { + 'init' : function (e,p) + { + var geometry = new THREE.ConeGeometry( + e._xseen.fields.radius, + e._xseen.fields.height, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-height'], + e._xseen.fields['open-ended'], + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Cylinder = { + 'init' : function (e,p) + { + var geometry = new THREE.CylinderGeometry( + e._xseen.fields['radius-top'], + e._xseen.fields['radius-bottom'], + e._xseen.fields.height, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-height'], + e._xseen.fields['open-ended'], + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Dodecahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.DodecahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Icosahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.IcosahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Octahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.OctahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Sphere = { + 'init' : function (e,p) + { + var geometry = new THREE.SphereGeometry( + e._xseen.fields.radius, + e._xseen.fields['segments-width'], + e._xseen.fields['segments-height'], + e._xseen.fields['phi-start'] * xseen.types.Deg2Rad, + e._xseen.fields['phi-length'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Tetrahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.TetrahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Torus = { + 'init' : function (e,p) + { + var geometry = new THREE.TorusGeometry( + e._xseen.fields.radius, + e._xseen.fields.tube, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-tubular'], + e._xseen.fields.arc * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; +// File: nodes/nodes-Viewing.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Control Node definitions + + +xseen.node.unk_Viewpoint = { + 'init' : function (e,p) + { // This should really go in a separate push-down list for Viewpoints + e._xseen.fields._radius0 = Math.sqrt( e._xseen.fields.position[0]*e._xseen.fields.position[0] + + e._xseen.fields.position[1]*e._xseen.fields.position[1] + + e._xseen.fields.position[2]*e._xseen.fields.position[2]); + e._xseen.domNode = e; // Back-link to node if needed later on + e._xseen.position = new THREE.Vector3(e._xseen.fields.position[0], e._xseen.fields.position[1], e._xseen.fields.position[2]); + e._xseen.type = e._xseen.fields.cameratype; + e._xseen.motion = e._xseen.fields.motion; + e._xseen.motionspeed = e._xseen.fields.motionspeed * 1000; + if (e._xseen.motion == 'turntable' || e._xseen.motion == 'tilt') {e._xseen.motionspeed = 1.0/e._xseen.motionspeed;} + + if (!e._xseen.sceneInfo.tmp.activeViewpoint) { + e._xseen.sceneInfo.stacks.Viewpoints.pushDown(e._xseen); + e._xseen.sceneInfo.tmp.activeViewpoint = true; + } + + e._xseen.handlers = {}; + e._xseen.handlers.setactive = this.setactive; + }, + 'fin' : function (e,p) {}, + + 'setactive' : function (ev) + { + var xseenNode = this.destination._xseen; + xseenNode.sceneInfo.stacks.Viewpoints.pushDown(xseenNode); // TODO: This is probably not the right way to change VP in the stack + xseenNode.sceneInfo.element._xseen.renderer.activeCamera = + xseenNode.sceneInfo.element._xseen.renderer.cameras[xseenNode.fields.type]; + xseenNode.sceneInfo.element._xseen.renderer.activeRender = + xseenNode.sceneInfo.element._xseen.renderer.renderEffects[xseenNode.fields.type]; + if (xseenNode.fields.type != 'stereo') { + xseenNode.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, xseenNode.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); + } + }, +}; + +xseen.node.controls_Navigation = { + 'init' : function (e,p) + { // This should really go in a separate push-down list for Viewpoints + + e._xseen.domNode = e; // Back-link to node if needed later on + e._xseen.speed = e._xseen.fields.speed; + if (e._xseen.setup == 'examine') {e._xseen.setup == 'trackball';} + //e._xseen.type = e._xseen.fields.type; + e._xseen.type = 'none'; + e._xseen.setup = e._xseen.fields.type; + if (!(e._xseen.setup == 'orbit' || e._xseen.setup == 'trackball')) {e._xseen.setup = 'none';} + + if (!e._xseen.sceneInfo.tmp.activeNavigation) { + e._xseen.sceneInfo.stacks.Navigation.pushDown(e._xseen); + e._xseen.sceneInfo.tmp.activeNavigation = true; + } + + e._xseen.handlers = {}; + e._xseen.handlers.setactive = this.setactive; + }, + 'fin' : function (e,p) {}, + + 'setactive' : function (ev) + { +/* + this.destination._xseen.sceneInfo.stacks.Viewpoints.pushDown(this.destination); // TODO: This is probably not the right way to change VP in the stack + this.destination._xseen.sceneInfo.element._xseen.renderer.activeCamera = + this.destination._xseen.sceneInfo.element._xseen.renderer.cameras[this.destination._xseen.fields.type]; + this.destination._xseen.sceneInfo.element._xseen.renderer.activeRender = + this.destination._xseen.sceneInfo.element._xseen.renderer.renderEffects[this.destination._xseen.fields.type]; + if (this.destination._xseen.fields.type != 'stereo') { + this.destination._xseen.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, this.destination._xseen.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); + } + */ + }, +}; + +xseen.node.lighting_Light = { + 'init' : function (e,p) + { + var color = xseen.types.Color3toInt (e._xseen.fields.color); + var intensity = e._xseen.fields.intensity - 0; + var lamp, type=e._xseen.fields.type.toLowerCase(); +/* + if (typeof(p._xseen.children) == 'undefined') { + console.log('Parent of Light does not have children...'); + p._xseen.children = []; + } + */ + + if (type == 'point') { + // Ignored field -- e._xseen.fields.location + lamp = new THREE.PointLight (color, intensity); + lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); + lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); + + } else if (type == 'spot') { + lamp = new THREE.SpotLight (color, intensity); + lamp.position.set(0-e._xseen.fields.direction[0], 0-e._xseen.fields.direction[1], 0-e._xseen.fields.direction[2]); + lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); + lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); + lamp.angle = Math.max(0.0, Math.min(1.5707963267948966192313216916398, e._xseen.fields.cutoffangle)); + lamp.penumbra = 1 - Math.max(0.0, Math.min(lamp.angle, e._xseen.fields.beamwidth)) / lamp.angle; + + } else { // DirectionalLight (by default) + lamp = new THREE.DirectionalLight (color, intensity); + lamp.position.x = 0-e._xseen.fields.direction[0]; + lamp.position.y = 0-e._xseen.fields.direction[1]; + lamp.position.z = 0-e._xseen.fields.direction[2]; + } + p._xseen.children.push(lamp); + lamp = null; + } + , + 'fin' : function (e,p) + { + } +}; +// File: nodes/nodes-x3d_Appearance.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.appearance_Material = { + 'init' : function (e,p) + { + var transparency = e._xseen.fields.transparency - 0; + var shininess = e._xseen.fields.shininess - 0; + var colorDiffuse = xseen.types.Color3toInt (e._xseen.fields.diffusecolor); + var colorEmissive = xseen.types.Color3toInt (e._xseen.fields.emissivecolor); + var colorSpecular = xseen.types.Color3toInt (e._xseen.fields.specularcolor); + p._xseen.material = new THREE.MeshPhongMaterial( { +// p._xseen.material = new THREE.MeshBasicMaterial( { + 'color' : colorDiffuse, + 'emissive' : colorEmissive, + 'specular' : colorSpecular, + 'shininess' : shininess, + 'opacity' : 1.0-transparency, + 'transparent' : (transparency > 0.0) ? true : false + } ); + e._xseen.animate['diffusecolor'] = p._xseen.material.color; + e._xseen.animate['emissivecolor'] = p._xseen.material.emissive; + e._xseen.animate['specularcolor'] = p._xseen.material.specular; + e._xseen.animate['transparency'] = p._xseen.material.opacity; + e._xseen.animate['shininess'] = p._xseen.material.shininess; + }, + 'fin' : function (e,p) {} +}; + +xseen.node.appearance_ImageTexture = { + 'init' : function (e,p) + { + p._xseen.texture = xseen.loader.ImageLoader.load(e._xseen.fields.url); + p._xseen.texture.wrapS = (e._xseen.fields.repeats) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + p._xseen.texture.wrapT = (e._xseen.fields.repeatt) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + }, + 'fin' : function (e,p) {} +}; + +xseen.node.appearance_Appearance = { + 'init' : function (e,p) {}, + + 'fin' : function (e,p) + { + if (typeof(e._xseen.texture) !== 'undefined' && e._xseen.texture !== null) { + e._xseen.material.map = e._xseen.texture; + } + p._xseen.appearance = e._xseen.material; + } +}; +// File: nodes/nodes-x3d_Geometry.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.geometry_Coordinate = { + 'init' : function (e,p) + { + var vertices = []; + for (var i=0; i= 0 where each triple defines a triangle face. It is assumed that the + * indices define the face in counter-clockwise order when looking at the face. + * vertices - an array of THREE.Vector3 points + */ +xseen.node.geometry__Indexed3 = function (indices, vertices, normals=[], color=[]) { + var i, face, normal=[], faceCount=0, n; + var useNormals = (normals.length > 0) ? true : false; + var useColor = (color.length > 0) ? true : false; + var maxIndex = Math.max.apply(null, indices); + var minIndex = Math.min.apply(null, indices); + if (maxIndex >= vertices.length) { + console.log ('Maximum index ('+maxIndex+') exceeds vertex count ('+vertices.length+'). No geometry is created'); + return; + } + if (useNormals && maxIndex >= normals.length) { + console.log ('Maximum index ('+maxIndex+') exceeds normal count ('+normals.length+'). No geometry is created'); + return; + } + if (useColor && maxIndex >= color.length) { + console.log ('Maximum index ('+maxIndex+') exceeds color count ('+color.length+'). No geometry is created'); + return; + } + if (minIndex < 0) { + console.log ('Minimum index ('+minIndex+') less than zero. No geometry is created'); + return; + } + if (indices.length % 3 != 0) { + console.log ('Number of indices ('+indices.length+') not divisible by 3. No geometry is created'); + return; + } + + var geometry = new THREE.Geometry(); + var normal_pz = new THREE.Vector3 (0, 0, 1); + var normal_mz = new THREE.Vector3 (0, 0, -1); + for (var i=0; i 0) {clip.duration = e._xseen.fields.duration;} + e._xseen.mixer.clipAction( clip ).play(); + } ); + } else { // Play a specific animation + var clip = THREE.AnimationClip.findByName(response.animations, e._xseen.fields.playonload); + var action = e._xseen.mixer.clipAction (clip); + action.play(); + } + } + } + } +}; + + +xseen.node.x_Route = { + 'init' : function (e,p) + { + var dest = e._xseen.fields.destination; + var hand = e._xseen.fields.handler; + var externalHandler = false; + + // Make sure sufficient data is provided + if (e._xseen.fields.source == '' || + typeof(window[hand]) !== 'function' && + (dest == '' || e._xseen.fields.event == '' || e._xseen.fields.field == '')) { + xseen.debug.logError ('Route node missing field. No route setup. Source: '+e._xseen.fields.source+'.'+e._xseen.fields.event+'; Destination: '+dest+'.'+e._xseen.fields.field+'; Handler: '+hand); + return; + } else if (typeof(window[hand]) === 'function') { + externalHandler = true; + } + + // For toNode routing, check existence of source and destination elements + var eSource = document.getElementById (e._xseen.fields.source); + if (! externalHandler) { + var eDestination = document.getElementById (dest); + if (typeof(eSource) === 'undefined' || typeof(eDestination) === 'undefined') { + xseen.debug.logError ('Source or Destination node does not exist. No route setup'); + return; + } + // Get field information -- perhaps there is some use in the Animate node? + var fField = xseen.nodes._getFieldInfo (eDestination.nodeName, e._xseen.fields.field); + if (typeof(fField) === 'undefined' || !fField.good) { + xseen.debug.logError ('Destination field does not exist or incorrectly specified. No route setup'); + return; + } + // Set up listener on source node for specified event. The listener code is the 'set' method for the + // node. It is passed the DOM 'event' data structure. Since there may be more than one node of the type + // specified by 'destination', the event handler is attached to the node in e._xseen.handlers. This is done + // when the node is parsed + xseen.Events.addHandler (e, eSource, e._xseen.fields.event, eDestination, fField); + +/* + * External (to XSeen) event handler + * TODO: limit the events to those requested if e._xseen.fields.event != 'xseen' + * This probably requires an intermediatiary event handler + */ + } else { + var handler = window[hand]; + eSource.addEventListener ('xseen', handler); + } + }, + + 'fin' : function (e,p) + { + }, + 'evHandler' : function (u) + { + var de = u.e; + var df = u.f; + return de._xseen.handlers[df.handlerName]; + }, +}; +// File: nodes/nodes.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.core_NOOP = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.core_WorldInfo = { + 'init' : function (e,p) {parsing('WorldInfo', e)}, + 'fin' : function (e,p) {} +}; + +function parsing (s, e) { + xseen.debug.logInfo ('Parsing init details stub for ' + s); +} + + +xseen.node.unk_Shape = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) + { +// if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + if (typeof(e._xseen.materialProperty) !== 'undefined') { + e._xseen.appearance.vertexColors = THREE.VertexColors; + //e._xseen.appearance.vertexColors = THREE.FaceColors; + e._xseen.appearance._needsUpdate = true; + e._xseen.appearance.needsUpdate = true; + } + var mesh = new THREE.Mesh (e._xseen.geometry, e._xseen.appearance); + mesh.userData = e; + p._xseen.children.push(mesh); + p._xseen.sceneInfo.selectable.push(mesh); + mesh = null; + } +}; + +xseen.node.grouping_Transform = { + 'init' : function (e,p) + { + var group = new THREE.Group(); + if (e.nodeName == "TRANSFORM") { + var rotation = xseen.types.Rotation2Quat(e._xseen.fields.rotation); + group.name = 'Transform children [' + e.id + ']'; + group.position.x = e._xseen.fields.translation[0]; + group.position.y = e._xseen.fields.translation[1]; + group.position.z = e._xseen.fields.translation[2]; + group.scale.x = e._xseen.fields.scale[0]; + group.scale.y = e._xseen.fields.scale[1]; + group.scale.z = e._xseen.fields.scale[2]; + group.quaternion.x = rotation.x; + group.quaternion.y = rotation.y; + group.quaternion.z = rotation.z; + group.quaternion.w = rotation.w; + + e._xseen.animate['translation'] = group.position; + e._xseen.animate['rotation'] = group.quaternion; + e._xseen.animate['scale'] = group.scale; + } + e._xseen.sceneNode = group; + }, + 'fin' : function (e,p) + { + // Apply transform to all objects in e._xseen.children + e._xseen.children.forEach (function (child, ndx, wholeThing) + { + e._xseen.sceneNode.add(child); + }); + p._xseen.children.push(e._xseen.sceneNode); + } +}; + +xseen.node.networking_Inline = { + 'init' : function (e,p) + { + if (typeof(e._xseen.processedUrl) === 'undefined' || !e._xseen.requestedUrl) { + var uri = xseen.parseUrl (e._xseen.fields.url); + var type = uri.extension; + e._xseen.loadGroup = new THREE.Group(); + e._xseen.loadGroup.name = 'Inline content [' + e.id + ']'; + console.log ('Created Inline Group with UUID ' + e._xseen.loadGroup.uuid); + var userdata = {'requestType':'x3d', 'e':e, 'p':p} + if (type.toLowerCase() == 'json') { + userdata.requestType = 'json'; + xseen.loadMgr.loadJson (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); + } else { + xseen.loadMgr.loadXml (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); + } + e._xseen.requestedUrl = true; + } + //if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(e._xseen.loadGroup); + console.log ('Using Inline Group with UUID ' + e._xseen.loadGroup.uuid); + }, + 'fin' : function (e,p) + { + }, + + 'loadSuccess' : + function (response, userdata, xhr) { + userdata.e._xseen.processedUrl = true; + userdata.e._xseen.loadResponse = response; + console.log("download successful for "+userdata.e.id); + if (userdata.requestType == 'json') { + var tmp = {'scene': response}; + response = null; + response = (new JSONParser()).parseJavaScript(tmp); + } + var start = {'_xseen':0}; + var findSceneTag = function (fragment) { + if (typeof(fragment._xseen) === 'undefined') {fragment._xseen = {'childCount': -1};} + if (fragment.nodeName.toLowerCase() == 'scene') { + start = fragment; + return; + } else if (fragment.children.length > 0) { + for (fragment._xseen.childCount=0; fragment._xseen.childCount 0) { + userdata.e.appendChild(start.children[0]); + } + xseen.Parse(userdata.e, userdata.p, userdata.p._xseen.sceneInfo); + userdata.e._xseen.children.forEach (function (child, ndx, wholeThing) + { + userdata.e._xseen.loadGroup.add(child); +console.log ('...Adding ' + child.type + ' (' + child.name + ') to Inline Group? with UUID ' + userdata.e._xseen.loadGroup.uuid + ' (' + userdata.e._xseen.loadGroup.name + ')'); + }); + userdata.p._xseen.sceneInfo.scene.updateMatrixWorld(); + //xseen.debug.logInfo("Complete work on Inline..."); + } else { + console.log("Found illegal X3D file -- no 'scene' tag"); + } + // Parse (start, userdata.p)... + } +}; + +/* + * Most of this stuff is only done once per XSeen element. Loading of Inline contents should not + * repeat the definitions and canvas creation + */ +xseen.node.core_Scene = { + 'DEFAULT' : { + 'Viewpoint' : { + 'Position' : new THREE.Vector3 (0, 0, 10), + 'Orientation' : '0 1 0 0', // TODO: fix (and below) when handling orientation + 'Type' : 'perpsective', + 'Motion' : 'none', + 'MotionSpeed' : 1.0, + }, + 'Navigation' : { + 'Speed' : 1.0, // 16 spr (1 revolution per 16 seconds), in mseconds. + 'Type' : 'none', + 'Setup' : 'none', + } + }, + 'init' : function (e,p) + { + // Create default Viewpoint and Navigation + xseen.sceneInfo[0].stacks.Viewpoints.setDefault( + { + 'position' : this.DEFAULT.Viewpoint.Position, + 'type' : this.DEFAULT.Viewpoint.Type, + 'motion' : this.DEFAULT.Viewpoint.Motion, + 'motionspeed': this.DEFAULT.Viewpoint.MotionSpeed / 1000, + 'domNode' : e, + 'fields' : {}, + } + ); + xseen.sceneInfo[0].stacks.Navigation.setDefault( + { + 'speed' : this.DEFAULT.Navigation.Speed / 1000, + 'type' : this.DEFAULT.Navigation.Type, + 'setup' : this.DEFAULT.Navigation.Setup, + 'domNode' : e, + 'fields' : {}, + } + ); + + var width = e._xseen.sceneInfo.size.width; + var height = e._xseen.sceneInfo.size.height; + var x_renderer = new THREE.WebGLRenderer(); + x_renderer.setSize (width, height); + var perspectiveCamera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 ); + var orthoCamera = new THREE.OrthographicCamera( 75, width / height, 0.1, 1000 ); + //perspectiveCamera.translateX(this.DEFAULT.Viewpoint.Position.x).translateY(this.DEFAULT.Viewpoint.Position.y).translateZ(this.DEFAULT.Viewpoint.Position.z); // Default position + //orthoCamera.translateX(this.DEFAULT.Viewpoint.Position.x).translateY(this.DEFAULT.Viewpoint.Position.y).translateZ(this.DEFAULT.Viewpoint.Position.z); // Default position + perspectiveCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position + perspectiveCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position + perspectiveCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position + orthoCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position + orthoCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position + orthoCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position + + // Stereo viewing effect + // from http://charliegerard.github.io/blog/Virtual-Reality-ThreeJs/ + var x_effect = new THREE.StereoEffect(x_renderer); + + e.appendChild (x_renderer.domElement); + e._xseen.renderer = { + 'canvas' : e._xseen.sceneInfo.scene, + 'width' : width, + 'height' : height, + 'cameras' : { + 'perspective' : perspectiveCamera, + 'ortho' : orthoCamera, + 'stereo' : perspectiveCamera, + }, // Removed .sceneInfo camera because this node defines the camera + 'effects' : x_effect, + 'renderEffects' : { + 'normal' : x_renderer, + 'perspective' : x_renderer, + 'ortho' : x_renderer, + 'stereo' : x_effect, + }, + 'activeRender' : {}, + 'activeCamera' : {}, + 'controls' : {}, // Used for navigation + }; + e._xseen.renderer.activeRender = e._xseen.renderer.renderEffects.normal; + e._xseen.renderer.activeCamera = e._xseen.renderer.cameras.perspective; + }, + +/* + * This appears now to be working!!! + * + * Late loading content is not getting inserted into the scene graph for rendering. Need to read + * THREE docs about how to do that. + * Camera will need to be redone. Existing camera is treated as a special child. A separate camera + * should be established and Viewpoint nodes define "photostops" rather than a camera. The camera is + * in effect, parented to the "photostop". This probably needs to list of Viewpoints discussed in the + * X3D specification. + */ + 'fin' : function (e,p) + { + // Render all Children + //xseen.renderNewChildren (e._xseen.children, e._xseen.renderer.canvas); + e._xseen.children.forEach (function (child, ndx, wholeThing) + { + console.log('Adding child of type ' + child.type + ' (' + child.name + ')'); + e._xseen.renderer.canvas.add(child); + }); + xseen.dumpSceneGraph (); +// e._xseen.renderer.renderer.render( e._xseen.renderer.canvas, e._xseen.renderer.camera ); +// xseen.debug.logInfo("Rendered all elements -- Starting animation"); +/* + * TODO: Need to get current top-of-stack for all stack-bound nodes and set them as active. + * This only happens the initial time for each XSeen tag in the main HTML file + * + * At this time, only Viewpoint is stack-bound. Probably need to stack just the ._xseen object. + * Also, .fields.position is the initial specified location; not the navigated/animated one + */ + var vp = xseen.sceneInfo[0].stacks.Viewpoints.getActive(); + var nav = xseen.sceneInfo[0].stacks.Navigation.getActive(); + var currentCamera = e._xseen.renderer.activeCamera; + var currentRenderer = e._xseen.renderer.activeRender; + currentCamera.position.x = vp.position.x; + currentCamera.position.y = vp.position.y; + currentCamera.position.z = vp.position.z; + e._xseen.renderer.controls = xseen.Navigation.setup[nav.setup] (currentCamera, currentRenderer); + xseen.debug.logInfo("Ready to kick off rendering loop"); + xseen.renderFrame(); + }, + +}; + +xseen.node.env_Background = { + 'init' : function (e,p) + { + var color = new THREE.Color(e._xseen.fields.skycolor[0], e._xseen.fields.skycolor[1], e._xseen.fields.skycolor[2]); + var textureCube = new THREE.CubeTextureLoader() + .load ([e._xseen.fields.srcright, + e._xseen.fields.srcleft, + e._xseen.fields.srctop, + e._xseen.fields.srcbottom, + e._xseen.fields.srcfront, + e._xseen.fields.srcback], + this.loadSuccess({'e':e, 'p':p}) + ); + e._xseen.sceneInfo.scene.background = color; +/* + var material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } ); + var size = 1; + //var geometry = new THREE.BoxGeometry(200, 200, 2); + var geometry = new THREE.Geometry(); + geometry.vertices.push ( + new THREE.Vector3(-size, -size, size), + new THREE.Vector3( size, -size, size), + new THREE.Vector3( size, -size, -size), + new THREE.Vector3(-size, -size, -size), + new THREE.Vector3(-size, size, size), + new THREE.Vector3( size, size, size), + new THREE.Vector3( size, size, -size), + new THREE.Vector3(-size, size, -size) + ); + + geometry.faces.push ( // external facing geometry + new THREE.Face3(0, 1, 5), + new THREE.Face3(0, 5, 4), + new THREE.Face3(1, 2, 6), + new THREE.Face3(1, 6, 5), + new THREE.Face3(2, 3, 7), + new THREE.Face3(2, 7, 6), + new THREE.Face3(3, 0, 4), + new THREE.Face3(3, 4, 7), + new THREE.Face3(4, 5, 6), + new THREE.Face3(4, 6, 7), + new THREE.Face3(0, 2, 1), + new THREE.Face3(0, 3, 2), + ); + geometry.computeBoundingSphere(); + var mesh = new THREE.Mesh (geometry, material); + e._xseen.sceneInfo.element._xseen.renderer.canvas.add(mesh); +*/ + }, + + 'fin' : function (e,p) + { + p._xseen.appearance = e._xseen.material; + }, + + 'loadSuccess' : function (userdata) + { + var e = userdata.e; + var p = userdata.p; + return function (textureCube) + { + e._xseen.processedUrl = true; + e._xseen.loadTexture = textureCube; + e._xseen.sceneInfo.scene.background = textureCube; + } + }, + +}; +// File: nodes/nodes_Animate.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.x_Animate = { + 'init' : function (e,p) + { + var delay = e._xseen.fields.delay * 1000; // Convert to milliseconds + var duration = e._xseen.fields.duration * 1000; // Convert to milliseconds + var repeat = (e._xseen.fields.repeat < 0) ? Infinity : e._xseen.fields.repeat; + var interpolator = e._xseen.fields.interpolator; + var easing = e._xseen.fields.easing; + + var fields = xseen.parseTable[p.localName.toLowerCase()].fields; + var fieldIndex = xseen.parseTable[p.localName.toLowerCase()].fieldIndex; + var toField = e._xseen.fields.field; + var toFieldIndex = fieldIndex[toField]; + if (typeof(fields[toFieldIndex]) === 'undefined') { + xseen.debug.logInfo("Field '" + toField + "' not found in parent (" + p.localName.toLowerCase() + "). No animation performed."); + return; + } + var fieldObject = fields[toFieldIndex].clone().setFieldName('to'); // Parse table entry for 'toField' + var to = xseen.nodes._parseField(fieldObject, e); // Parsed data -- need to convert to THREE format + +// Convert 'to' to the datatype of 'field' and set interpolation type. + var interpolation; + if (fieldObject.type == 'SFVec3f') { + interpolation = TWEEN.Interpolation.Linear; + to = xseen.types.Vector3(to); + xseen.debug.logInfo("Interpolating field '" + toField + "' as 3-space."); + + } else if (fieldObject.type == 'SFColor') { + interpolation = this.Interpolator.color; + to = new THREE.Color (xseen.types.Color3toInt(to)); + xseen.debug.logInfo("Interpolation field '" + toField + "' as color."); + + } else if (fieldObject.type == 'SFRotation') { + interpolation = this.Interpolator.slerp; + to = xseen.types.Rotation2Quat(to); + xseen.debug.logInfo("Interpolation field '" + toField + "' as rotation."); + + } else { + xseen.debug.logInfo("Field '" + toField + "' not converted to THREE format. No animation performed."); + return; + } + var fieldTHREE = p._xseen.animate[toField]; // THREE field for animation + + var tween = new TWEEN.Tween(fieldTHREE) + .to(to, duration) + .delay(delay) + .repeat(repeat) + .interpolation(interpolation); + var easingType = e._xseen.fields.easingtype; + easingType = easingType.charAt(0).toUpperCase() + easingType.slice(1); + easing = (easingType != 'Linear' && easing == '') ? 'inout' : easing; + if (easing != '') { + easing = easing.replace('in', 'In').replace('out', 'Out'); + easingType = (easingType == 'Linear') ? 'Quadratic' : easingType; + e._xseen.fields.easing = easing; + e._xseen.fields.easingtype = easingType; + tween.easing(TWEEN.Easing[easingType][easing]); + } + +/* + * Put animation-specific data in node (e._xseen) so it can be accessed on events (through 'this') + * This includes initial value and field + * All handlers (goes into .handlers) + * TWEEN object + */ + e._xseen.initialValue = fieldTHREE.clone(); + e._xseen.animatingField = fieldTHREE; + e._xseen.handlers = {}; + e._xseen.handlers.setstart = this.setstart; + e._xseen.handlers.setstop = this.setstop; + e._xseen.handlers.setpause = this.setpause; + e._xseen.handlers.setresetstart = this.setresetstart; + e._xseen.animating = tween; + p._xseen.animation.push (tween); + tween.start(); + }, + 'fin' : function (e,p) {}, + 'setstart' : function (ev) + { + console.log ('Starting animation'); + this.destination._xseen.animating.start(); + }, + 'setstop' : function (ev) + { + console.log ('Stopping animation'); + this.destination._xseen.animating.stop(); + }, +/* + * TODO: Update TWEEN to support real pause & resume. + * Pause needs to hold current position + * Resume needs to restart the timer to current time so there is no "jump" + */ + 'setpause' : function (ev) + { + console.log ('Pausing (really stopping) animation'); + this.destination._xseen.animating.stop(); + }, + 'setresetstart' : function (ev) // TODO: Create seperate 'reset' method + { + console.log ('Reset and start animation'); + this.destination._xseen.animatingField = this.destination._xseen.initialValue; + this.destination._xseen.animating.start(); + }, + +/* + * Various interpolator functions for use with different data types + * All are designed to be used within TWEEN and take two arguments + * v A vector of way points (key values) that define the interpolated path + * k The interpolating factor that defines how far along the path for the current result + * + * Functions + * slerp - Linear in quaterian space (though not yet) + * color - Linear in color space (currently HSL as used by THREE) + * + */ + 'Interpolator' : { + 'slerp' : function (v,k) + { + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + + if (k < 0) { + return v[0].slerp(v[1], f); + } + + if (k > 1) { + return v[m].slerp(v[m-1], m-f); + } + + return v[i].slerp (v[i + 1 > m ? m : i + 1], f-i); + }, + 'color' : function (v,k) + { + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + var fn = this.slerpCompute; + + if (k < 0) { + return v[0].lerp(v[1], f); + } + if (k > 1) { + return v[m].lerp(v[m-1], m-f); + } + return v[i].lerp (v[i + 1 > m ? m : i + 1], f - i); + }, + }, +}; +// File: nodes/_Definitions-aframe.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen._addAframeAppearance = function (node) { + node + .addField('ambient-occlusion-map', 'SFString', '') + .addField('ambient-occlusion-map-intensity', 'SFFloat', 1) + .addField('ambient-occlusion-texture-offset', 'SFVec2f', '0 0') + .addField('ambient-occlusion-texture-repeat', 'SFVec2f', '1 1') + .addField('color', 'Color', '#FFF') + .addField('displacement-bias', 'SFFloat', 0.5) + .addField('displacement-map', 'SFString', '') + .addField('displacement-scale', 'SFFloat', 1) + .addField('displacement-texture-offset', 'SFVec2f', '0 0') + .addField('displacement-texture-repeat', 'SFVec2f', '1 1') + .addField('env-map', 'SFString', '') + .addField('fog', 'SFBool', true) + .addField('metalness', 'SFFloat', 0) + .addField('normal-map', 'SFString', '') + .addField('normal-scale', 'SFVec2f', '1 1') + .addField('normal-texture-offset', 'SFVec2f', '0 0') + .addField('normal-texture-repeat', 'SFVec2f', '1 1') + .addField('repeat', 'SFVec2f', '1 1') + .addField('roughness', 'SFFloat', 0.5) + .addField('spherical-env-map', 'SFString', '') + .addField('src', 'SFString', '') + .addField('wireframe', 'SFBool', false) + .addField('wireframe-linewidth', 'SFInt', 2) + .addNode(); +} + +xseen.nodes._defineNode('a-entity', 'A-Frame', 'af_Entity') + .addField('geometry', 'SFString', '') + .addField('material', 'SFString', '') + .addField('light', 'SFString', '') + .addNode(); + +var node; +node = xseen.nodes._defineNode('a-box', 'A-Frame', 'af_Box') + .addField('depth', 'SFFloat', 1) + .addField('height', 'SFFloat', 1) + .addField('width', 'SFFloat', 512) + .addField('segments-depth', 'SFInt', 1) + .addField('segments-height', 'SFInt', 1) + .addField('segments-width', 'SFInt', 1); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-cone', 'A-Frame', 'af_Cone') + .addField('height', 'SFFloat', 1) + .addField('radius', 'SFFloat', 1) + .addField('open-ended', 'SFBool', false) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 1) + .addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-cylinder', 'A-Frame', 'af_Cylinder') + .addField('height', 'SFFloat', 1) + .addField('open-ended', 'SFBool', false) + .addField('radius-bottom', 'SFFloat', 1) + .addField('radius-top', 'SFFloat', 1) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 1) + .addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-dodecahedron', 'A-Frame', 'af_Dodecahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-icosahedron', 'A-Frame', 'af_Icosahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-octahedron', 'A-Frame', 'af_Octahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-sphere', 'A-Frame', 'af_Sphere') + .addField('radius', 'SFFloat', 1) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 180) + .addField('phi-start', 'SFFloat', 0) + .addField('phi-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 18) + .addField('segments-width', 'SFInt', 36); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-tetrahedron', 'A-Frame', 'af_Tetrahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-torus', 'A-Frame', 'af_Torus') + .addField('radius', 'SFFloat', 2) + .addField('tube', 'SFFloat', 1) + .addField('arc', 'SFFloat', 360) + .addField('segments-radial', 'SFInt', 8) + .addField('segments-tubular', 'SFInt', 6); +xseen._addAframeAppearance (node); + +/* + * Asset management system nodes + */ +xseen.nodes._defineNode('a-assets', 'A-Frame', 'af_Assets') + .addNode(); +xseen.nodes._defineNode('a-asset-item', 'A-Frame', 'af_AssetItem') + .addField('src', 'SFString', '') + .addNode(); +xseen.nodes._defineNode('a-mixin', 'A-Frame', 'af_Mixin') + .addField('*', 'SFString', '') + .addNode(); +// File: nodes/_Definitions-x3d.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen.nodes._defineNode('Cone', 'Geometry3D', 'geometry3D_Cone') + .addField('bottomRadius', 'SFFloat', 1) + .addField('height', 'SFFloat', 2) + .addField('bottom', 'SFBool', true) + .addField('side', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode('Box', 'Geometry3D', 'geometry3D_Box') + .addField('size', 'SFVec3f', [1,1,1]) + .addNode(); + +xseen.nodes._defineNode('Sphere', 'Geometry3D', 'geometry3D_Sphere') + .addField('radius', 'SFFloat', '1') + .addNode(); + +xseen.nodes._defineNode('Cylinder', 'Geometry3D', 'geometry3D_Cylinder') + .addField('radius', 'SFFloat', 1) + .addField('height', 'SFFloat', 2) + .addField('bottom', 'SFBool', true) + .addField('side', 'SFBool', true) + .addField('top', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode ('Material', 'Appearance', 'appearance_Material') + .addField({name:'diffuseColor', datatype:'SFColor', defaultValue:[.8,.8,.8], animatable:true}) + .addField({name:'emissiveColor',datatype: 'SFColor', defaultValue:[0,0,0], animatable:true}) + .addField({name:'specularColor', datatype:'SFColor', defaultValue:[0,0,0], animatable:true}) + .addField({name:'transparency', datatype:'SFFloat', defaultValue:'0', animatable:true}) + .addField({name:'shininess', datatype:'SFFloat', defaultValue:'0', animatable:true}) + .addNode(); + +xseen.nodes._defineNode ('Transform', 'Grouping', 'grouping_Transform') + .addField({name:'translation', datatype:'SFVec3f', defaultValue:[0,0,0], animatable:true}) + .addField({name:'scale', datatype:'SFVec3f', defaultValue:[1,1,1], animatable:true}) + .addField({name:'rotation', datatype:'SFRotation', defaultValue:xseen.types.SFRotation('0 1 0 0',''), animatable:true}) + .addNode(); +xseen.nodes._defineNode ('Group', 'Grouping', 'grouping_Transform') + .addNode(); + +xseen.nodes._defineNode ('Light', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) // DirectionalLight + .addField('location', 'SFVec3f', [0,0,0]) // PointLight & SpotLight + .addField('radius', 'SFFloat', '100') // PointLight & SpotLight + .addField('attenuation', 'SFVec3f', [1,0,0]) // PointLight & SpotLight + .addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // SpotLight + .addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // SpotLight + .addField('color', 'SFColor', [1,1,1]) // General + .addField('intensity', 'SFFloat', '1') // General + .addField({name:'type', datatype:'EnumerateString', defaultValue:'Directional', enumerated:['Directional', 'Spot', 'Point'], animatable:true}) + .addNode(); +xseen.nodes._defineNode ('DirectionalLight', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Directional') + .addNode(); +xseen.nodes._defineNode ('PointLight', 'Lighting', 'lighting_Light') + .addField('location', 'SFVec3f', [0,0,0]) + .addField('radius', 'SFFloat', '100') + .addField('attenuation', 'SFVec3f', [1,0,0]) + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Point') + .addNode(); +xseen.nodes._defineNode ('SpotLight', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) + .addField('radius', 'SFFloat', '100') + .addField('attenuation', 'SFVec3f', [1,0,0]) + .addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // pi/4 + .addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // pi/2 + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Spot') + .addNode(); + +xseen.nodes._defineNode ('Viewpoint', 'Controls', 'unk_Viewpoint') + .addField('position', 'SFVec3f', '0 0 10') + .addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) + .addField('description', 'SFString', '') + .addField({name:'cameratype', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) + .addField({name:'type', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) + .addField({name:'motion', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'turntable', 'tilt'], animatable:false}) + .addField('motionspeed', 'SFFloat', 16) + .addField('active', 'SFBool', true) // incoming event + .addNode(); +xseen.nodes._defineNode ('NavigationMode', 'Controls', 'controls_Navigation') + .addField('speed', 'SFFloat', 1.) + .addField({name:'type', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'orbit', 'fly', 'examine', 'trackball'], animatable:false}) + .addNode(); +xseen.nodes._defineNode ('Camera', 'Controls', 'unk_Viewpoint') + .addField('position', 'SFVec3f', [0,0,10]) + .addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) + .addNode(); + +xseen.nodes._defineNode ('Inline', 'Networking', 'networking_Inline') + .addField('url', 'SFString', '') + .addNode(); + +xseen.nodes._defineNode ('scene', 'Core', 'core_Scene') + .addNode(); +xseen.nodes._defineNode ('canvas', 'Core', 'core_NOOP') + .addNode(); +xseen.nodes._defineNode ('WorldInfo', 'Core', 'core_WorldInfo') + .addNode(); +xseen.nodes._defineNode ('Appearance', 'Appearance', 'appearance_Appearance') + .addNode(); +xseen.nodes._defineNode ('ImageTexture', 'Appearance', 'appearance_ImageTexture') + .addField('url', 'SFString', '') + .addField('repeatS', 'SFBool', true) + .addField('repeatT', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode ('Shape', 'Shape', 'unk_Shape') + .addNode(); +xseen.nodes._defineNode('Background', 'Environmental', 'env_Background') + .addField('skyColor', 'SFColor', [0,0,0]) + .addField('srcFront', 'SFString', '') + .addField('srcBack', 'SFString', '') + .addField('srcTop', 'SFString', '') + .addField('srcBottom', 'SFString', '') + .addField('srcLeft', 'SFString', '') + .addField('srcRight', 'SFString', '') + .addField('backgroundIsCube', 'SFBool', 'true') + .addNode(); + +xseen.nodes._defineNode('TriangleSet', 'Geometry', 'geometry_TriangleSet') + .addField('ccw', 'SFBool', 'true') + .addField('colorPerVertex', 'SFBool', 'true') + .addField('solid', 'SFBool', 'true') + .addNode(); +xseen.nodes._defineNode('IndexedTriangleSet', 'Geometry', 'geometry_IndexedTriangleSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('index', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('Coordinate', 'Geometry', 'geometry_Coordinate') + .addField('point', 'MFVec3f', []) + .addNode(); +xseen.nodes._defineNode('Normal', 'Geometry', 'geometry_Normal') + .addField('vector', 'MFVec3f', []) + .addNode(); +xseen.nodes._defineNode('Color', 'Geometry', 'geometry_Color') + .addField('color', 'MFColor', []) + .addNode(); +xseen.nodes._defineNode('IndexedFaceSet', 'Geometry', 'geometry_IndexedFaceSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('coordIndex', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('IndexedQuadSet', 'Geometry', 'geometry_IndexedQuadSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('index', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('QuadSet', 'Geometry', 'geometry_QuadSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addNode(); + +//xseen.nodes._dumpTable(); +// File: nodes/_Definitions-xseen.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen.nodes._defineNode('model', 'XSeen', 'x_Model') + .addField('src', 'SFString', '') + .addField('playonload', 'SFString', '') + .addField('duration', 'SFFloat', '-1') + .addNode(); + +xseen.nodes._defineNode('animate', 'XSeen', 'x_Animate') + .addField('field', 'SFString', '') + .addField('to', 'MFFloat', '') // Needs to be 'field' datatype. That is not known until node-parse. For now insist on numeric array + .addField('delay', 'SFTime', 0) + .addField('duration', 'SFTime', 0) + .addField('repeat', 'SFInt', 0) + .addField({name:'interpolator', datatype:'EnumerateString', defaultValue:'position', enumerated:['position', 'rotation', 'color'], animatable:false}) + .addField({name:'Easing', datatype:'EnumerateString', defaultValue:'', enumerated:['', 'in', 'out', 'inout'], animatable:false}) + .addField({name:'EasingType', datatype:'EnumerateString', defaultValue:'linear', enumerated:['linear', 'quadratic', 'sinusoidal', 'exponential', 'elastic', 'bounce'], animatable:false}) + .addField('start', 'SFBool', true) // incoming event, need to set timer trigger + .addField('stop', 'SFBool', true) // incoming event, need to set timer trigger + .addField('resetstart', 'SFBool', true) // incoming event, need to set timer trigger + .addField('pause', 'SFBool', true) // incoming event, need to set timer trigger + .addNode(); + +xseen.nodes._defineNode('route', 'XSeen', 'x_Route') + .addField('source', 'SFString', '') + .addField('event', 'SFString', '') + .addField('destination', 'SFString', '') + .addField('field', 'SFString', '') + .addField('handler', 'SFString', '') + .addNode(); + + +// Dump parse table +//xseen.nodes._dumpTable(); \ No newline at end of file diff --git a/Release/XSeen.0.4.5+23_0e9dbd1.min.js b/Release/XSeen.0.4.5+23_0e9dbd1.min.js new file mode 100644 index 0000000..a41c58d --- /dev/null +++ b/Release/XSeen.0.4.5+23_0e9dbd1.min.js @@ -0,0 +1,9290 @@ +/* + * XSeen V0.4.5+23_0e9dbd1 + * Built Tue Jul 11 08:08:45 2017 + * + +Dual licensed under the MIT and GPL licenses. + +==[MIT]==================================================================== +Copyright (c) 2017, Daly Realism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +==[GPL]==================================================================== + +XSeen - Declarative 3D for HTML + +Copyright (C) 2017, Daly Realism + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +=== COPYRIGHT +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (C) 2017, Daly Realism for XSeen +Copyright, Fraunhofer for X3DOM +Copyright, Mozilla for A-Frame +Copyright, THREE and Khronos for various parts of THREE.js +Copyright (C) 2017, John Carlson for JSON->XML converter (JSONParser.js) + +=== +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + */ +THREE.ColladaLoader = function () { +var COLLADA = null; +var scene = null; +var visualScene; +var kinematicsModel; +var readyCallbackFunc = null; +var sources = {}; +var images = {}; +var animations = {}; +var controllers = {}; +var geometries = {}; +var materials = {}; +var effects = {}; +var cameras = {}; +var lights = {}; +var animData; +var kinematics; +var visualScenes; +var kinematicsModels; +var baseUrl; +var morphs; +var skins; +var flip_uv = true; +var preferredShading = THREE.SmoothShading; +var options = { +centerGeometry: false, +convertUpAxis: false, +subdivideFaces: true, +upAxis: 'Y', +defaultEnvMap: null +}; +var colladaUnit = 1.0; +var colladaUp = 'Y'; +var upConversion = null; +function load ( url, readyCallback, progressCallback, failCallback ) { +var length = 0; +if ( document.implementation && document.implementation.createDocument ) { +var request = new XMLHttpRequest(); +request.onreadystatechange = function() { +if ( request.readyState === 4 ) { +if ( request.status === 0 || request.status === 200 ) { +if ( request.response ) { +readyCallbackFunc = readyCallback; +parse( request.response, undefined, url ); +} else { +if ( failCallback ) { +failCallback( { type: 'error', url: url } ); +} else { +console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); +} +} +}else{ +if( failCallback ){ +failCallback( { type: 'error', url: url } ); +}else{ +console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' ); +} +} +} else if ( request.readyState === 3 ) { +if ( progressCallback ) { +if ( length === 0 ) { +length = request.getResponseHeader( "Content-Length" ); +} +progressCallback( { total: length, loaded: request.responseText.length } ); +} +} +}; +request.open( "GET", url, true ); +request.send( null ); +} else { +alert( "Don't know how to parse XML!" ); +} +} +function parse( text, callBack, url ) { +COLLADA = new DOMParser().parseFromString( text, 'text/xml' ); +callBack = callBack || readyCallbackFunc; +if ( url !== undefined ) { +var parts = url.split( '/' ); +parts.pop(); +baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; +} +parseAsset(); +setUpConversion(); +images = parseLib( "library_images image", _Image, "image" ); +materials = parseLib( "library_materials material", Material, "material" ); +effects = parseLib( "library_effects effect", Effect, "effect" ); +geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); +cameras = parseLib( "library_cameras camera", Camera, "camera" ); +lights = parseLib( "library_lights light", Light, "light" ); +controllers = parseLib( "library_controllers controller", Controller, "controller" ); +animations = parseLib( "library_animations animation", Animation, "animation" ); +visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); +kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); +morphs = []; +skins = []; +visualScene = parseScene(); +scene = new THREE.Group(); +for ( var i = 0; i < visualScene.nodes.length; i ++ ) { +scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); +} +scene.scale.multiplyScalar( colladaUnit ); +createAnimations(); +kinematicsModel = parseKinematicsModel(); +createKinematics(); +var result = { +scene: scene, +morphs: morphs, +skins: skins, +animations: animData, +kinematics: kinematics, +dae: { +images: images, +materials: materials, +cameras: cameras, +lights: lights, +effects: effects, +geometries: geometries, +controllers: controllers, +animations: animations, +visualScenes: visualScenes, +visualScene: visualScene, +scene: visualScene, +kinematicsModels: kinematicsModels, +kinematicsModel: kinematicsModel +} +}; +if ( callBack ) { +callBack( result ); +} +return result; +} +function setPreferredShading ( shading ) { +preferredShading = shading; +} +function parseAsset () { +var elements = COLLADA.querySelectorAll('asset'); +var element = elements[0]; +if ( element && element.childNodes ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'unit': +var meter = child.getAttribute( 'meter' ); +if ( meter ) { +colladaUnit = parseFloat( meter ); +} +break; +case 'up_axis': +colladaUp = child.textContent.charAt(0); +break; +} +} +} +} +function parseLib ( q, classSpec, prefix ) { +var elements = COLLADA.querySelectorAll(q); +var lib = {}; +var i = 0; +var elementsLength = elements.length; +for ( var j = 0; j < elementsLength; j ++ ) { +var element = elements[j]; +var daeElement = ( new classSpec() ).parse( element ); +if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); +lib[ daeElement.id ] = daeElement; +} +return lib; +} +function parseScene() { +var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; +if ( sceneElement ) { +var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); +return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; +} else { +return null; +} +} +function parseKinematicsModel() { +var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; +if ( kinematicsModelElement ) { +var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); +return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; +} else { +return null; +} +} +function createAnimations() { +animData = []; +recurseHierarchy( scene ); +} +function recurseHierarchy( node ) { +var n = visualScene.getChildById( node.colladaId, true ), +newData = null; +if ( n && n.keys ) { +newData = { +fps: 60, +hierarchy: [ { +node: n, +keys: n.keys, +sids: n.sids +} ], +node: node, +name: 'animation_' + node.name, +length: 0 +}; +animData.push(newData); +for ( var i = 0, il = n.keys.length; i < il; i ++ ) { +newData.length = Math.max( newData.length, n.keys[i].time ); +} +} else { +newData = { +hierarchy: [ { +keys: [], +sids: [] +} ] +} +} +for ( var i = 0, il = node.children.length; i < il; i ++ ) { +var d = recurseHierarchy( node.children[i] ); +for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { +newData.hierarchy.push( { +keys: [], +sids: [] +} ); +} +} +return newData; +} +function calcAnimationBounds () { +var start = 1000000; +var end = -start; +var frames = 0; +var ID; +for ( var id in animations ) { +var animation = animations[ id ]; +ID = ID || animation.id; +for ( var i = 0; i < animation.sampler.length; i ++ ) { +var sampler = animation.sampler[ i ]; +sampler.create(); +start = Math.min( start, sampler.startTime ); +end = Math.max( end, sampler.endTime ); +frames = Math.max( frames, sampler.input.length ); +} +} +return { start:start, end:end, frames:frames,ID:ID }; +} +function createMorph ( geometry, ctrl ) { +var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; +if ( !morphCtrl || !morphCtrl.morph ) { +console.log("could not find morph controller!"); +return; +} +var morph = morphCtrl.morph; +for ( var i = 0; i < morph.targets.length; i ++ ) { +var target_id = morph.targets[ i ]; +var daeGeometry = geometries[ target_id ]; +if ( !daeGeometry.mesh || +!daeGeometry.mesh.primitives || +!daeGeometry.mesh.primitives.length ) { +continue; +} +var target = daeGeometry.mesh.primitives[ 0 ].geometry; +if ( target.vertices.length === geometry.vertices.length ) { +geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); +} +} +geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); +} +function createSkin ( geometry, ctrl, applyBindShape ) { +var skinCtrl = controllers[ ctrl.url ]; +if ( !skinCtrl || !skinCtrl.skin ) { +console.log( "could not find skin controller!" ); +return; +} +if ( !ctrl.skeleton || !ctrl.skeleton.length ) { +console.log( "could not find the skeleton for the skin!" ); +return; +} +var skin = skinCtrl.skin; +var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); +var hierarchy = []; +applyBindShape = applyBindShape !== undefined ? applyBindShape : true; +var bones = []; +geometry.skinWeights = []; +geometry.skinIndices = []; +if ( applyBindShape ) { +for ( var i = 0; i < geometry.vertices.length; i ++ ) { +geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); +} +} +} +function setupSkeleton ( node, bones, frame, parent ) { +node.world = node.world || new THREE.Matrix4(); +node.localworld = node.localworld || new THREE.Matrix4(); +node.world.copy( node.matrix ); +node.localworld.copy( node.matrix ); +if ( node.channels && node.channels.length ) { +var channel = node.channels[ 0 ]; +var m = channel.sampler.output[ frame ]; +if ( m instanceof THREE.Matrix4 ) { +node.world.copy( m ); +node.localworld.copy(m); +if (frame === 0) +node.matrix.copy(m); +} +} +if ( parent ) { +node.world.multiplyMatrices( parent, node.world ); +} +bones.push( node ); +for ( var i = 0; i < node.nodes.length; i ++ ) { +setupSkeleton( node.nodes[ i ], bones, frame, node.world ); +} +} +function setupSkinningMatrices ( bones, skin ) { +for ( var i = 0; i < bones.length; i ++ ) { +var bone = bones[ i ]; +var found = -1; +if ( bone.type != 'JOINT' ) continue; +for ( var j = 0; j < skin.joints.length; j ++ ) { +if ( bone.sid === skin.joints[ j ] ) { +found = j; +break; +} +} +if ( found >= 0 ) { +var inv = skin.invBindMatrices[ found ]; +bone.invBindMatrix = inv; +bone.skinningMatrix = new THREE.Matrix4(); +bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) +bone.animatrix = new THREE.Matrix4(); +bone.animatrix.copy(bone.localworld); +bone.weights = []; +for ( var j = 0; j < skin.weights.length; j ++ ) { +for (var k = 0; k < skin.weights[ j ].length; k ++ ) { +var w = skin.weights[ j ][ k ]; +if ( w.joint === found ) { +bone.weights.push( w ); +} +} +} +} else { +console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); +bone.skinningMatrix = new THREE.Matrix4(); +bone.weights = []; +} +} +} +function flattenSkeleton(skeleton) { +var list = []; +var walk = function(parentid, node, list) { +var bone = {}; +bone.name = node.sid; +bone.parent = parentid; +bone.matrix = node.matrix; +var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; +bone.matrix.decompose(data[0], data[1], data[2]); +bone.pos = [ data[0].x,data[0].y,data[0].z ]; +bone.scl = [ data[2].x,data[2].y,data[2].z ]; +bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; +list.push(bone); +for (var i in node.nodes) { +walk(node.sid, node.nodes[i], list); +} +}; +walk(-1, skeleton, list); +return list; +} +function skinToBindPose(geometry,skeleton,skinController) { +var bones = []; +setupSkeleton( skeleton, bones, -1 ); +setupSkinningMatrices( bones, skinController.skin ); +var v = new THREE.Vector3(); +var skinned = []; +for (var i = 0; i < geometry.vertices.length; i ++) { +skinned.push(new THREE.Vector3()); +} +for ( i = 0; i < bones.length; i ++ ) { +if ( bones[ i ].type != 'JOINT' ) continue; +for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { +var w = bones[ i ].weights[ j ]; +var vidx = w.index; +var weight = w.weight; +var o = geometry.vertices[vidx]; +var s = skinned[vidx]; +v.x = o.x; +v.y = o.y; +v.z = o.z; +v.applyMatrix4( bones[i].skinningMatrix ); +s.x += (v.x * weight); +s.y += (v.y * weight); +s.z += (v.z * weight); +} +} +for (var i = 0; i < geometry.vertices.length; i ++) { +geometry.vertices[i] = skinned[i]; +} +} +function applySkin ( geometry, instanceCtrl, frame ) { +var skinController = controllers[ instanceCtrl.url ]; +frame = frame !== undefined ? frame : 40; +if ( !skinController || !skinController.skin ) { +console.log( 'ColladaLoader: Could not find skin controller.' ); +return; +} +if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { +console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); +return; +} +var animationBounds = calcAnimationBounds(); +var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); +var bonelist = flattenSkeleton(skeleton); +var joints = skinController.skin.joints; +var sortedbones = []; +for (var i = 0; i < joints.length; i ++) { +for (var j = 0; j < bonelist.length; j ++) { +if (bonelist[j].name === joints[i]) { +sortedbones[i] = bonelist[j]; +} +} +} +for (var i = 0; i < sortedbones.length; i ++) { +for (var j = 0; j < sortedbones.length; j ++) { +if (sortedbones[i].parent === sortedbones[j].name) { +sortedbones[i].parent = j; +} +} +} +var i, j, w, vidx, weight; +var v = new THREE.Vector3(), o, s; +for ( i = 0; i < geometry.vertices.length; i ++ ) { +geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); +} +var skinIndices = []; +var skinWeights = []; +var weights = skinController.skin.weights; +for ( var i =0; i < weights.length; i ++ ) { +var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); +var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); +skinIndices.push(indicies); +skinWeights.push(weight); +} +geometry.skinIndices = skinIndices; +geometry.skinWeights = skinWeights; +geometry.bones = sortedbones; +var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; +for (var j = 0; j < sortedbones.length; j ++) { +animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); +} +console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); +skinToBindPose(geometry, skeleton, skinController); +for ( frame = 0; frame < animationBounds.frames; frame ++ ) { +var bones = []; +var skinned = []; +setupSkeleton( skeleton, bones, frame ); +setupSkinningMatrices( bones, skinController.skin ); +for (var i = 0; i < bones.length; i ++) { +for (var j = 0; j < animationdata.hierarchy.length; j ++) { +if (animationdata.hierarchy[j].name === bones[i].sid) { +var key = {}; +key.time = (frame / 30); +key.matrix = bones[i].animatrix; +if (frame === 0) +bones[i].matrix = key.matrix; +var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; +key.matrix.decompose(data[0], data[1], data[2]); +key.pos = [ data[0].x,data[0].y,data[0].z ]; +key.scl = [ data[2].x,data[2].y,data[2].z ]; +key.rot = data[1]; +animationdata.hierarchy[j].keys.push(key); +} +} +} +geometry.animation = animationdata; +} +} +function createKinematics() { +if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { +kinematics = undefined; +return; +} +var jointMap = {}; +var _addToMap = function( jointIndex, parentVisualElement ) { +var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); +var colladaNode = visualScene.getChildById( parentVisualElementId, true ); +var joint = kinematicsModel.joints[ jointIndex ]; +scene.traverse(function( node ) { +if ( node.colladaId == parentVisualElementId ) { +jointMap[ jointIndex ] = { +node: node, +transforms: colladaNode.transforms, +joint: joint, +position: joint.zeroPosition +}; +} +}); +}; +kinematics = { +joints: kinematicsModel && kinematicsModel.joints, +getJointValue: function( jointIndex ) { +var jointData = jointMap[ jointIndex ]; +if ( jointData ) { +return jointData.position; +} else { +console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); +} +}, +setJointValue: function( jointIndex, value ) { +var jointData = jointMap[ jointIndex ]; +if ( jointData ) { +var joint = jointData.joint; +if ( value > joint.limits.max || value < joint.limits.min ) { +console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); +} else if ( joint.static ) { +console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); +} else { +var threejsNode = jointData.node; +var axis = joint.axis; +var transforms = jointData.transforms; +var matrix = new THREE.Matrix4(); +var m1 = new THREE.Matrix4(); +for (i = 0; i < transforms.length; i ++ ) { +var transform = transforms[ i ]; +if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { +switch ( joint.type ) { +case 'revolute': +matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); +break; +case 'prismatic': +matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); +break; +default: +console.warn( 'setJointValue: unknown joint type: ' + joint.type ); +break; +} +} else { +switch ( transform.type ) { +case 'matrix': +matrix.multiply( transform.obj ); +break; +case 'translate': +matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); +break; +case 'rotate': +matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); +break; +} +} +} +var elementsFloat32Arr = matrix.elements; +var elements = Array.prototype.slice.call( elementsFloat32Arr ); +var elementsRowMajor = [ +elements[ 0 ], +elements[ 4 ], +elements[ 8 ], +elements[ 12 ], +elements[ 1 ], +elements[ 5 ], +elements[ 9 ], +elements[ 13 ], +elements[ 2 ], +elements[ 6 ], +elements[ 10 ], +elements[ 14 ], +elements[ 3 ], +elements[ 7 ], +elements[ 11 ], +elements[ 15 ] +]; +threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); +threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); +jointMap[ jointIndex ].position = value; +} +} else { +console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); +} +} +}; +var element = COLLADA.querySelector('scene instance_kinematics_scene'); +if ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bind_joint_axis': +var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); +var axis = child.querySelector('axis param').textContent; +var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); +var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); +if ( visualTargetElement ) { +var parentVisualElement = visualTargetElement.parentElement; +_addToMap(jointIndex, parentVisualElement); +} +break; +default: +break; +} +} +} +} +function createSceneGraph ( node, parent ) { +var obj = new THREE.Object3D(); +var skinned = false; +var skinController; +var morphController; +var i, j; +for ( i = 0; i < node.controllers.length; i ++ ) { +var controller = controllers[ node.controllers[ i ].url ]; +switch ( controller.type ) { +case 'skin': +if ( geometries[ controller.skin.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = controller.skin.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +skinned = true; +skinController = node.controllers[ i ]; +} else if ( controllers[ controller.skin.source ] ) { +var second = controllers[ controller.skin.source ]; +morphController = second; +if ( second.morph && geometries[ second.morph.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = second.morph.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +} +} +break; +case 'morph': +if ( geometries[ controller.morph.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = controller.morph.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +morphController = node.controllers[ i ]; +} +console.log( 'ColladaLoader: Morph-controller partially supported.' ); +default: +break; +} +} +var double_sided_materials = {}; +for ( i = 0; i < node.geometries.length; i ++ ) { +var instance_geometry = node.geometries[i]; +var instance_materials = instance_geometry.instance_material; +var geometry = geometries[ instance_geometry.url ]; +var used_materials = {}; +var used_materials_array = []; +var num_materials = 0; +var first_material; +if ( geometry ) { +if ( !geometry.mesh || !geometry.mesh.primitives ) +continue; +if ( obj.name.length === 0 ) { +obj.name = geometry.id; +} +if ( instance_materials ) { +for ( j = 0; j < instance_materials.length; j ++ ) { +var instance_material = instance_materials[ j ]; +var mat = materials[ instance_material.target ]; +var effect_id = mat.instance_effect.url; +var shader = effects[ effect_id ].shader; +var material3js = shader.material; +if ( geometry.doubleSided ) { +if ( !( instance_material.symbol in double_sided_materials ) ) { +var _copied_material = material3js.clone(); +_copied_material.side = THREE.DoubleSide; +double_sided_materials[ instance_material.symbol ] = _copied_material; +} +material3js = double_sided_materials[ instance_material.symbol ]; +} +material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; +used_materials[ instance_material.symbol ] = num_materials; +used_materials_array.push( material3js ); +first_material = material3js; +first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; +num_materials ++; +} +} +var mesh; +var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); +var geom = geometry.mesh.geometry3js; +if ( num_materials > 1 ) { +material = new THREE.MultiMaterial( used_materials_array ); +for ( j = 0; j < geom.faces.length; j ++ ) { +var face = geom.faces[ j ]; +face.materialIndex = used_materials[ face.daeMaterial ] +} +} +if ( skinController !== undefined ) { +applySkin( geom, skinController ); +if ( geom.morphTargets.length > 0 ) { +material.morphTargets = true; +material.skinning = false; +} else { +material.morphTargets = false; +material.skinning = true; +} +mesh = new THREE.SkinnedMesh( geom, material, false ); +mesh.name = 'skin_' + skins.length; +skins.push( mesh ); +} else if ( morphController !== undefined ) { +createMorph( geom, morphController ); +material.morphTargets = true; +mesh = new THREE.Mesh( geom, material ); +mesh.name = 'morph_' + morphs.length; +morphs.push( mesh ); +} else { +if ( geom.isLineStrip === true ) { +mesh = new THREE.Line( geom ); +} else { +mesh = new THREE.Mesh( geom, material ); +} +} +obj.add(mesh); +} +} +for ( i = 0; i < node.cameras.length; i ++ ) { +var instance_camera = node.cameras[i]; +var cparams = cameras[instance_camera.url]; +var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), +parseFloat(cparams.znear), parseFloat(cparams.zfar)); +obj.add(cam); +} +for ( i = 0; i < node.lights.length; i ++ ) { +var light = null; +var instance_light = node.lights[i]; +var lparams = lights[instance_light.url]; +if ( lparams && lparams.technique ) { +var color = lparams.color.getHex(); +var intensity = lparams.intensity; +var distance = lparams.distance; +var angle = lparams.falloff_angle; +switch ( lparams.technique ) { +case 'directional': +light = new THREE.DirectionalLight( color, intensity, distance ); +light.position.set(0, 0, 1); +break; +case 'point': +light = new THREE.PointLight( color, intensity, distance ); +break; +case 'spot': +light = new THREE.SpotLight( color, intensity, distance, angle ); +light.position.set(0, 0, 1); +break; +case 'ambient': +light = new THREE.AmbientLight( color ); +break; +} +} +if (light) { +obj.add(light); +} +} +obj.name = node.name || node.id || ""; +obj.colladaId = node.id || ""; +obj.layer = node.layer || ""; +obj.matrix = node.matrix; +obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); +if ( options.centerGeometry && obj.geometry ) { +var delta = obj.geometry.center(); +delta.multiply( obj.scale ); +delta.applyQuaternion( obj.quaternion ); +obj.position.sub( delta ); +} +for ( i = 0; i < node.nodes.length; i ++ ) { +obj.add( createSceneGraph( node.nodes[i], node ) ); +} +return obj; +} +function getJointId( skin, id ) { +for ( var i = 0; i < skin.joints.length; i ++ ) { +if ( skin.joints[ i ] === id ) { +return i; +} +} +} +function getLibraryNode( id ) { +var nodes = COLLADA.querySelectorAll('library_nodes node'); +for ( var i = 0; i < nodes.length; i++ ) { +var attObj = nodes[i].attributes.getNamedItem('id'); +if ( attObj && attObj.value === id ) { +return nodes[i]; +} +} +return undefined; +} +function getChannelsForNode ( node ) { +var channels = []; +var startTime = 1000000; +var endTime = -1000000; +for ( var id in animations ) { +var animation = animations[id]; +for ( var i = 0; i < animation.channel.length; i ++ ) { +var channel = animation.channel[i]; +var sampler = animation.sampler[i]; +var id = channel.target.split('/')[0]; +if ( id == node.id ) { +sampler.create(); +channel.sampler = sampler; +startTime = Math.min(startTime, sampler.startTime); +endTime = Math.max(endTime, sampler.endTime); +channels.push(channel); +} +} +} +if ( channels.length ) { +node.startTime = startTime; +node.endTime = endTime; +} +return channels; +} +function calcFrameDuration( node ) { +var minT = 10000000; +for ( var i = 0; i < node.channels.length; i ++ ) { +var sampler = node.channels[i].sampler; +for ( var j = 0; j < sampler.input.length - 1; j ++ ) { +var t0 = sampler.input[ j ]; +var t1 = sampler.input[ j + 1 ]; +minT = Math.min( minT, t1 - t0 ); +} +} +return minT; +} +function calcMatrixAt( node, t ) { +var animated = {}; +var i, j; +for ( i = 0; i < node.channels.length; i ++ ) { +var channel = node.channels[ i ]; +animated[ channel.sid ] = channel; +} +var matrix = new THREE.Matrix4(); +for ( i = 0; i < node.transforms.length; i ++ ) { +var transform = node.transforms[ i ]; +var channel = animated[ transform.sid ]; +if ( channel !== undefined ) { +var sampler = channel.sampler; +var value; +for ( j = 0; j < sampler.input.length - 1; j ++ ) { +if ( sampler.input[ j + 1 ] > t ) { +value = sampler.output[ j ]; +break; +} +} +if ( value !== undefined ) { +if ( value instanceof THREE.Matrix4 ) { +matrix.multiplyMatrices( matrix, value ); +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} +return matrix; +} +function bakeAnimations ( node ) { +if ( node.channels && node.channels.length ) { +var keys = [], +sids = []; +for ( var i = 0, il = node.channels.length; i < il; i ++ ) { +var channel = node.channels[i], +fullSid = channel.fullSid, +sampler = channel.sampler, +input = sampler.input, +transform = node.getTransformBySid( channel.sid ), +member; +if ( channel.arrIndices ) { +member = []; +for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { +member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); +} +} else { +member = getConvertedMember( channel.member ); +} +if ( transform ) { +if ( sids.indexOf( fullSid ) === -1 ) { +sids.push( fullSid ); +} +for ( var j = 0, jl = input.length; j < jl; j ++ ) { +var time = input[j], +data = sampler.getData( transform.type, j, member ), +key = findKey( keys, time ); +if ( !key ) { +key = new Key( time ); +var timeNdx = findTimeNdx( keys, time ); +keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); +} +key.addTarget( fullSid, transform, member, data ); +} +} else { +console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); +} +} +for ( var i = 0; i < sids.length; i ++ ) { +var sid = sids[ i ]; +for ( var j = 0; j < keys.length; j ++ ) { +var key = keys[ j ]; +if ( !key.hasTarget( sid ) ) { +interpolateKeys( keys, key, j, sid ); +} +} +} +node.keys = keys; +node.sids = sids; +} +} +function findKey ( keys, time) { +var retVal = null; +for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { +var key = keys[i]; +if ( key.time === time ) { +retVal = key; +} else if ( key.time > time ) { +break; +} +} +return retVal; +} +function findTimeNdx ( keys, time) { +var ndx = -1; +for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { +var key = keys[i]; +if ( key.time >= time ) { +ndx = i; +} +} +return ndx; +} +function interpolateKeys ( keys, key, ndx, fullSid ) { +var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), +nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); +if ( prevKey && nextKey ) { +var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), +prevTarget = prevKey.getTarget( fullSid ), +nextData = nextKey.getTarget( fullSid ).data, +prevData = prevTarget.data, +data; +if ( prevTarget.type === 'matrix' ) { +data = prevData; +} else if ( prevData.length ) { +data = []; +for ( var i = 0; i < prevData.length; ++ i ) { +data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; +} +} else { +data = prevData + ( nextData - prevData ) * scale; +} +key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); +} +} +function getNextKeyWith( keys, fullSid, ndx ) { +for ( ; ndx < keys.length; ndx ++ ) { +var key = keys[ ndx ]; +if ( key.hasTarget( fullSid ) ) { +return key; +} +} +return null; +} +function getPrevKeyWith( keys, fullSid, ndx ) { +ndx = ndx >= 0 ? ndx : ndx + keys.length; +for ( ; ndx >= 0; ndx -- ) { +var key = keys[ ndx ]; +if ( key.hasTarget( fullSid ) ) { +return key; +} +} +return null; +} +function _Image() { +this.id = ""; +this.init_from = ""; +} +_Image.prototype.parse = function(element) { +this.id = element.getAttribute('id'); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeName === 'init_from' ) { +this.init_from = child.textContent; +} +} +return this; +}; +function Controller() { +this.id = ""; +this.name = ""; +this.type = ""; +this.skin = null; +this.morph = null; +} +Controller.prototype.parse = function( element ) { +this.id = element.getAttribute('id'); +this.name = element.getAttribute('name'); +this.type = "none"; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'skin': +this.skin = (new Skin()).parse(child); +this.type = child.nodeName; +break; +case 'morph': +this.morph = (new Morph()).parse(child); +this.type = child.nodeName; +break; +default: +break; +} +} +return this; +}; +function Morph() { +this.method = null; +this.source = null; +this.targets = null; +this.weights = null; +} +Morph.prototype.parse = function( element ) { +var sources = {}; +var inputs = []; +var i; +this.method = element.getAttribute( 'method' ); +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +for ( i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'source': +var source = ( new Source() ).parse( child ); +sources[ source.id ] = source; +break; +case 'targets': +inputs = this.parseInputs( child ); +break; +default: +console.log( child.nodeName ); +break; +} +} +for ( i = 0; i < inputs.length; i ++ ) { +var input = inputs[ i ]; +var source = sources[ input.source ]; +switch ( input.semantic ) { +case 'MORPH_TARGET': +this.targets = source.read(); +break; +case 'MORPH_WEIGHT': +this.weights = source.read(); +break; +default: +break; +} +} +return this; +}; +Morph.prototype.parseInputs = function(element) { +var inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1) continue; +switch ( child.nodeName ) { +case 'input': +inputs.push( (new Input()).parse(child) ); +break; +default: +break; +} +} +return inputs; +}; +function Skin() { +this.source = ""; +this.bindShapeMatrix = null; +this.invBindMatrices = []; +this.joints = []; +this.weights = []; +} +Skin.prototype.parse = function( element ) { +var sources = {}; +var joints, weights; +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +this.invBindMatrices = []; +this.joints = []; +this.weights = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bind_shape_matrix': +var f = _floats(child.textContent); +this.bindShapeMatrix = getConvertedMat4( f ); +break; +case 'source': +var src = new Source().parse(child); +sources[ src.id ] = src; +break; +case 'joints': +joints = child; +break; +case 'vertex_weights': +weights = child; +break; +default: +console.log( child.nodeName ); +break; +} +} +this.parseJoints( joints, sources ); +this.parseWeights( weights, sources ); +return this; +}; +Skin.prototype.parseJoints = function ( element, sources ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +var input = ( new Input() ).parse( child ); +var source = sources[ input.source ]; +if ( input.semantic === 'JOINT' ) { +this.joints = source.read(); +} else if ( input.semantic === 'INV_BIND_MATRIX' ) { +this.invBindMatrices = source.read(); +} +break; +default: +break; +} +} +}; +Skin.prototype.parseWeights = function ( element, sources ) { +var v, vcount, inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +inputs.push( ( new Input() ).parse( child ) ); +break; +case 'v': +v = _ints( child.textContent ); +break; +case 'vcount': +vcount = _ints( child.textContent ); +break; +default: +break; +} +} +var index = 0; +for ( var i = 0; i < vcount.length; i ++ ) { +var numBones = vcount[i]; +var vertex_weights = []; +for ( var j = 0; j < numBones; j ++ ) { +var influence = {}; +for ( var k = 0; k < inputs.length; k ++ ) { +var input = inputs[ k ]; +var value = v[ index + input.offset ]; +switch ( input.semantic ) { +case 'JOINT': +influence.joint = value;//this.joints[value]; +break; +case 'WEIGHT': +influence.weight = sources[ input.source ].data[ value ]; +break; +default: +break; +} +} +vertex_weights.push( influence ); +index += inputs.length; +} +for ( var j = 0; j < vertex_weights.length; j ++ ) { +vertex_weights[ j ].index = i; +} +this.weights.push( vertex_weights ); +} +}; +function VisualScene () { +this.id = ""; +this.name = ""; +this.nodes = []; +this.scene = new THREE.Group(); +} +VisualScene.prototype.getChildById = function( id, recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var node = this.nodes[ i ].getChildById( id, recursive ); +if ( node ) { +return node; +} +} +return null; +}; +VisualScene.prototype.getChildBySid = function( sid, recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var node = this.nodes[ i ].getChildBySid( sid, recursive ); +if ( node ) { +return node; +} +} +return null; +}; +VisualScene.prototype.parse = function( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +this.nodes = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'node': +this.nodes.push( ( new Node() ).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +function Node() { +this.id = ""; +this.name = ""; +this.sid = ""; +this.nodes = []; +this.controllers = []; +this.transforms = []; +this.geometries = []; +this.channels = []; +this.matrix = new THREE.Matrix4(); +} +Node.prototype.getChannelForTransform = function( transformSid ) { +for ( var i = 0; i < this.channels.length; i ++ ) { +var channel = this.channels[i]; +var parts = channel.target.split('/'); +var id = parts.shift(); +var sid = parts.shift(); +var dotSyntax = (sid.indexOf(".") >= 0); +var arrSyntax = (sid.indexOf("(") >= 0); +var arrIndices; +var member; +if ( dotSyntax ) { +parts = sid.split("."); +sid = parts.shift(); +member = parts.shift(); +} else if ( arrSyntax ) { +arrIndices = sid.split("("); +sid = arrIndices.shift(); +for ( var j = 0; j < arrIndices.length; j ++ ) { +arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); +} +} +if ( sid === transformSid ) { +channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; +return channel; +} +} +return null; +}; +Node.prototype.getChildById = function ( id, recursive ) { +if ( this.id === id ) { +return this; +} +if ( recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var n = this.nodes[ i ].getChildById( id, recursive ); +if ( n ) { +return n; +} +} +} +return null; +}; +Node.prototype.getChildBySid = function ( sid, recursive ) { +if ( this.sid === sid ) { +return this; +} +if ( recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var n = this.nodes[ i ].getChildBySid( sid, recursive ); +if ( n ) { +return n; +} +} +} +return null; +}; +Node.prototype.getTransformBySid = function ( sid ) { +for ( var i = 0; i < this.transforms.length; i ++ ) { +if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; +} +return null; +}; +Node.prototype.parse = function( element ) { +var url; +this.id = element.getAttribute('id'); +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.type = element.getAttribute('type'); +this.layer = element.getAttribute('layer'); +this.type = this.type === 'JOINT' ? this.type : 'NODE'; +this.nodes = []; +this.transforms = []; +this.geometries = []; +this.cameras = []; +this.lights = []; +this.controllers = []; +this.matrix = new THREE.Matrix4(); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'node': +this.nodes.push( ( new Node() ).parse( child ) ); +break; +case 'instance_camera': +this.cameras.push( ( new InstanceCamera() ).parse( child ) ); +break; +case 'instance_controller': +this.controllers.push( ( new InstanceController() ).parse( child ) ); +break; +case 'instance_geometry': +this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); +break; +case 'instance_light': +this.lights.push( ( new InstanceLight() ).parse( child ) ); +break; +case 'instance_node': +url = child.getAttribute( 'url' ).replace( /^#/, '' ); +var iNode = getLibraryNode( url ); +if ( iNode ) { +this.nodes.push( ( new Node() ).parse( iNode )) ; +} +break; +case 'rotate': +case 'translate': +case 'scale': +case 'matrix': +case 'lookat': +case 'skew': +this.transforms.push( ( new Transform() ).parse( child ) ); +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +this.channels = getChannelsForNode( this ); +bakeAnimations( this ); +this.updateMatrix(); +return this; +}; +Node.prototype.updateMatrix = function () { +this.matrix.identity(); +for ( var i = 0; i < this.transforms.length; i ++ ) { +this.transforms[ i ].apply( this.matrix ); +} +}; +function Transform () { +this.sid = ""; +this.type = ""; +this.data = []; +this.obj = null; +} +Transform.prototype.parse = function ( element ) { +this.sid = element.getAttribute( 'sid' ); +this.type = element.nodeName; +this.data = _floats( element.textContent ); +this.convert(); +return this; +}; +Transform.prototype.convert = function () { +switch ( this.type ) { +case 'matrix': +this.obj = getConvertedMat4( this.data ); +break; +case 'rotate': +this.angle = THREE.Math.degToRad( this.data[3] ); +case 'translate': +fixCoords( this.data, -1 ); +this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); +break; +case 'scale': +fixCoords( this.data, 1 ); +this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); +break; +default: +console.log( 'Can not convert Transform of type ' + this.type ); +break; +} +}; +Transform.prototype.apply = function () { +var m1 = new THREE.Matrix4(); +return function ( matrix ) { +switch ( this.type ) { +case 'matrix': +matrix.multiply( this.obj ); +break; +case 'translate': +matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); +break; +case 'rotate': +matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); +break; +case 'scale': +matrix.scale( this.obj ); +break; +} +}; +}(); +Transform.prototype.update = function ( data, member ) { +var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; +switch ( this.type ) { +case 'matrix': +if ( ! member ) { +this.obj.copy( data ); +} else if ( member.length === 1 ) { +switch ( member[ 0 ] ) { +case 0: +this.obj.n11 = data[ 0 ]; +this.obj.n21 = data[ 1 ]; +this.obj.n31 = data[ 2 ]; +this.obj.n41 = data[ 3 ]; +break; +case 1: +this.obj.n12 = data[ 0 ]; +this.obj.n22 = data[ 1 ]; +this.obj.n32 = data[ 2 ]; +this.obj.n42 = data[ 3 ]; +break; +case 2: +this.obj.n13 = data[ 0 ]; +this.obj.n23 = data[ 1 ]; +this.obj.n33 = data[ 2 ]; +this.obj.n43 = data[ 3 ]; +break; +case 3: +this.obj.n14 = data[ 0 ]; +this.obj.n24 = data[ 1 ]; +this.obj.n34 = data[ 2 ]; +this.obj.n44 = data[ 3 ]; +break; +} +} else if ( member.length === 2 ) { +var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); +this.obj[ propName ] = data; +} else { +console.log('Incorrect addressing of matrix in transform.'); +} +break; +case 'translate': +case 'scale': +if ( Object.prototype.toString.call( member ) === '[object Array]' ) { +member = members[ member[ 0 ] ]; +} +switch ( member ) { +case 'X': +this.obj.x = data; +break; +case 'Y': +this.obj.y = data; +break; +case 'Z': +this.obj.z = data; +break; +default: +this.obj.x = data[ 0 ]; +this.obj.y = data[ 1 ]; +this.obj.z = data[ 2 ]; +break; +} +break; +case 'rotate': +if ( Object.prototype.toString.call( member ) === '[object Array]' ) { +member = members[ member[ 0 ] ]; +} +switch ( member ) { +case 'X': +this.obj.x = data; +break; +case 'Y': +this.obj.y = data; +break; +case 'Z': +this.obj.z = data; +break; +case 'ANGLE': +this.angle = THREE.Math.degToRad( data ); +break; +default: +this.obj.x = data[ 0 ]; +this.obj.y = data[ 1 ]; +this.obj.z = data[ 2 ]; +this.angle = THREE.Math.degToRad( data[ 3 ] ); +break; +} +break; +} +}; +function InstanceController() { +this.url = ""; +this.skeleton = []; +this.instance_material = []; +} +InstanceController.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +this.skeleton = []; +this.instance_material = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType !== 1 ) continue; +switch ( child.nodeName ) { +case 'skeleton': +this.skeleton.push( child.textContent.replace(/^#/, '') ); +break; +case 'bind_material': +var instances = child.querySelectorAll('instance_material'); +for ( var j = 0; j < instances.length; j ++ ) { +var instance = instances[j]; +this.instance_material.push( (new InstanceMaterial()).parse(instance) ); +} +break; +case 'extra': +break; +default: +break; +} +} +return this; +}; +function InstanceMaterial () { +this.symbol = ""; +this.target = ""; +} +InstanceMaterial.prototype.parse = function ( element ) { +this.symbol = element.getAttribute('symbol'); +this.target = element.getAttribute('target').replace(/^#/, ''); +return this; +}; +function InstanceGeometry() { +this.url = ""; +this.instance_material = []; +} +InstanceGeometry.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +this.instance_material = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +if ( child.nodeName === 'bind_material' ) { +var instances = child.querySelectorAll('instance_material'); +for ( var j = 0; j < instances.length; j ++ ) { +var instance = instances[j]; +this.instance_material.push( (new InstanceMaterial()).parse(instance) ); +} +break; +} +} +return this; +}; +function Geometry() { +this.id = ""; +this.mesh = null; +} +Geometry.prototype.parse = function ( element ) { +this.id = element.getAttribute('id'); +extractDoubleSided( this, element ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +switch ( child.nodeName ) { +case 'mesh': +this.mesh = (new Mesh(this)).parse(child); +break; +case 'extra': +break; +default: +break; +} +} +return this; +}; +function Mesh( geometry ) { +this.geometry = geometry.id; +this.primitives = []; +this.vertices = null; +this.geometry3js = null; +} +Mesh.prototype.parse = function ( element ) { +this.primitives = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'source': +_source( child ); +break; +case 'vertices': +this.vertices = ( new Vertices() ).parse( child ); +break; +case 'linestrips': +this.primitives.push( ( new LineStrips().parse( child ) ) ); +break; +case 'triangles': +this.primitives.push( ( new Triangles().parse( child ) ) ); +break; +case 'polygons': +this.primitives.push( ( new Polygons().parse( child ) ) ); +break; +case 'polylist': +this.primitives.push( ( new Polylist().parse( child ) ) ); +break; +default: +break; +} +} +this.geometry3js = new THREE.Geometry(); +if ( this.vertices === null ) { +return this; +} +var vertexData = sources[ this.vertices.input['POSITION'].source ].data; +for ( var i = 0; i < vertexData.length; i += 3 ) { +this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); +} +for ( var i = 0; i < this.primitives.length; i ++ ) { +var primitive = this.primitives[ i ]; +primitive.setVertices( this.vertices ); +this.handlePrimitive( primitive, this.geometry3js ); +} +if ( this.geometry3js.calcNormals ) { +this.geometry3js.computeVertexNormals(); +delete this.geometry3js.calcNormals; +} +return this; +}; +Mesh.prototype.handlePrimitive = function ( primitive, geom ) { +if ( primitive instanceof LineStrips ) { +geom.isLineStrip = true; +return; +} +var j, k, pList = primitive.p, inputs = primitive.inputs; +var input, index, idx32; +var source, numParams; +var vcIndex = 0, vcount = 3, maxOffset = 0; +var texture_sets = []; +for ( j = 0; j < inputs.length; j ++ ) { +input = inputs[ j ]; +var offset = input.offset + 1; +maxOffset = (maxOffset < offset) ? offset : maxOffset; +switch ( input.semantic ) { +case 'TEXCOORD': +texture_sets.push( input.set ); +break; +} +} +for ( var pCount = 0; pCount < pList.length; ++ pCount ) { +var p = pList[ pCount ], i = 0; +while ( i < p.length ) { +var vs = []; +var ns = []; +var ts = null; +var cs = []; +if ( primitive.vcount ) { +vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; +} else { +vcount = p.length / maxOffset; +} +for ( j = 0; j < vcount; j ++ ) { +for ( k = 0; k < inputs.length; k ++ ) { +input = inputs[ k ]; +source = sources[ input.source ]; +index = p[ i + ( j * maxOffset ) + input.offset ]; +numParams = source.accessor.params.length; +idx32 = index * numParams; +switch ( input.semantic ) { +case 'VERTEX': +vs.push( index ); +break; +case 'NORMAL': +ns.push( getConvertedVec3( source.data, idx32 ) ); +break; +case 'TEXCOORD': +ts = ts || { }; +if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; +ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); +break; +case 'COLOR': +cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); +break; +default: +break; +} +} +} +if ( ns.length === 0 ) { +input = this.vertices.input.NORMAL; +if ( input ) { +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); +} +} else { +geom.calcNormals = true; +} +} +if ( !ts ) { +ts = { }; +input = this.vertices.input.TEXCOORD; +if ( input ) { +texture_sets.push( input.set ); +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +idx32 = vs[ ndx ] * numParams; +if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; +ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); +} +} +} +if ( cs.length === 0 ) { +input = this.vertices.input.COLOR; +if ( input ) { +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +idx32 = vs[ ndx ] * numParams; +cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); +} +} +} +var face = null, faces = [], uv, uvArr; +if ( vcount === 3 ) { +faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); +} else if ( vcount === 4 ) { +faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); +faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); +} else if ( vcount > 4 && options.subdivideFaces ) { +var clr = cs.length ? cs : new THREE.Color(), +vec1, vec2, vec3, v1, v2, norm; +for ( k = 1; k < vcount - 1; ) { +faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) ); +} +} +if ( faces.length ) { +for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { +face = faces[ndx]; +face.daeMaterial = primitive.material; +geom.faces.push( face ); +for ( k = 0; k < texture_sets.length; k ++ ) { +uv = ts[ texture_sets[k] ]; +if ( vcount > 4 ) { +uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; +} else if ( vcount === 4 ) { +if ( ndx === 0 ) { +uvArr = [ uv[0], uv[1], uv[3] ]; +} else { +uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; +} +} else { +uvArr = [ uv[0], uv[1], uv[2] ]; +} +if ( geom.faceVertexUvs[k] === undefined ) { +geom.faceVertexUvs[k] = []; +} +geom.faceVertexUvs[k].push( uvArr ); +} +} +} else { +console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); +} +i += maxOffset * vcount; +} +} +}; +function Polygons () { +this.material = ""; +this.count = 0; +this.inputs = []; +this.vcount = null; +this.p = []; +this.geometry = new THREE.Geometry(); +} +Polygons.prototype.setVertices = function ( vertices ) { +for ( var i = 0; i < this.inputs.length; i ++ ) { +if ( this.inputs[ i ].source === vertices.id ) { +this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; +} +} +}; +Polygons.prototype.parse = function ( element ) { +this.material = element.getAttribute( 'material' ); +this.count = _attr_as_int( element, 'count', 0 ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'input': +this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); +break; +case 'vcount': +this.vcount = _ints( child.textContent ); +break; +case 'p': +this.p.push( _ints( child.textContent ) ); +break; +case 'ph': +console.warn( 'polygon holes not yet supported!' ); +break; +default: +break; +} +} +return this; +}; +function Polylist () { +Polygons.call( this ); +this.vcount = []; +} +Polylist.prototype = Object.create( Polygons.prototype ); +Polylist.prototype.constructor = Polylist; +function LineStrips() { +Polygons.call( this ); +this.vcount = 1; +} +LineStrips.prototype = Object.create( Polygons.prototype ); +LineStrips.prototype.constructor = LineStrips; +function Triangles () { +Polygons.call( this ); +this.vcount = 3; +} +Triangles.prototype = Object.create( Polygons.prototype ); +Triangles.prototype.constructor = Triangles; +function Accessor() { +this.source = ""; +this.count = 0; +this.stride = 0; +this.params = []; +} +Accessor.prototype.parse = function ( element ) { +this.params = []; +this.source = element.getAttribute( 'source' ); +this.count = _attr_as_int( element, 'count', 0 ); +this.stride = _attr_as_int( element, 'stride', 0 ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeName === 'param' ) { +var param = {}; +param[ 'name' ] = child.getAttribute( 'name' ); +param[ 'type' ] = child.getAttribute( 'type' ); +this.params.push( param ); +} +} +return this; +}; +function Vertices() { +this.input = {}; +} +Vertices.prototype.parse = function ( element ) { +this.id = element.getAttribute('id'); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[i].nodeName === 'input' ) { +var input = ( new Input() ).parse( element.childNodes[ i ] ); +this.input[ input.semantic ] = input; +} +} +return this; +}; +function Input () { +this.semantic = ""; +this.offset = 0; +this.source = ""; +this.set = 0; +} +Input.prototype.parse = function ( element ) { +this.semantic = element.getAttribute('semantic'); +this.source = element.getAttribute('source').replace(/^#/, ''); +this.set = _attr_as_int(element, 'set', -1); +this.offset = _attr_as_int(element, 'offset', 0); +if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { +this.set = 0; +} +return this; +}; +function Source ( id ) { +this.id = id; +this.type = null; +} +Source.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +switch ( child.nodeName ) { +case 'bool_array': +this.data = _bools( child.textContent ); +this.type = child.nodeName; +break; +case 'float_array': +this.data = _floats( child.textContent ); +this.type = child.nodeName; +break; +case 'int_array': +this.data = _ints( child.textContent ); +this.type = child.nodeName; +break; +case 'IDREF_array': +case 'Name_array': +this.data = _strings( child.textContent ); +this.type = child.nodeName; +break; +case 'technique_common': +for ( var j = 0; j < child.childNodes.length; j ++ ) { +if ( child.childNodes[ j ].nodeName === 'accessor' ) { +this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); +break; +} +} +break; +default: +break; +} +} +return this; +}; +Source.prototype.read = function () { +var result = []; +var param = this.accessor.params[ 0 ]; +switch ( param.type ) { +case 'IDREF': +case 'Name': case 'name': +case 'float': +return this.data; +case 'float4x4': +for ( var j = 0; j < this.data.length; j += 16 ) { +var s = this.data.slice( j, j + 16 ); +var m = getConvertedMat4( s ); +result.push( m ); +} +break; +default: +console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); +break; +} +return result; +}; +function Material () { +this.id = ""; +this.name = ""; +this.instance_effect = null; +} +Material.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { +this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); +break; +} +} +return this; +}; +function ColorOrTexture () { +this.color = new THREE.Color(); +this.color.setRGB( Math.random(), Math.random(), Math.random() ); +this.color.a = 1.0; +this.texture = null; +this.texcoord = null; +this.texOpts = null; +} +ColorOrTexture.prototype.isColor = function () { +return ( this.texture === null ); +}; +ColorOrTexture.prototype.isTexture = function () { +return ( this.texture != null ); +}; +ColorOrTexture.prototype.parse = function ( element ) { +if (element.nodeName === 'transparent') { +this.opaque = element.getAttribute('opaque'); +} +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'color': +var rgba = _floats( child.textContent ); +this.color = new THREE.Color(); +this.color.setRGB( rgba[0], rgba[1], rgba[2] ); +this.color.a = rgba[3]; +break; +case 'texture': +this.texture = child.getAttribute('texture'); +this.texcoord = child.getAttribute('texcoord'); +this.texOpts = { +offsetU: 0, +offsetV: 0, +repeatU: 1, +repeatV: 1, +wrapU: 1, +wrapV: 1 +}; +this.parseTexture( child ); +break; +default: +break; +} +} +return this; +}; +ColorOrTexture.prototype.parseTexture = function ( element ) { +if ( ! element.childNodes ) return this; +if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { +element = element.childNodes[1]; +if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { +element = element.childNodes[1]; +} +} +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'offsetU': +case 'offsetV': +case 'repeatU': +case 'repeatV': +this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); +break; +case 'wrapU': +case 'wrapV': +if ( child.textContent.toUpperCase() === 'TRUE' ) { +this.texOpts[ child.nodeName ] = 1; +} else { +this.texOpts[ child.nodeName ] = parseInt( child.textContent ); +} +break; +default: +this.texOpts[ child.nodeName ] = child.textContent; +break; +} +} +return this; +}; +function Shader ( type, effect ) { +this.type = type; +this.effect = effect; +this.material = null; +} +Shader.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'emission': +case 'diffuse': +case 'specular': +case 'transparent': +this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); +break; +case 'bump': +var bumpType = child.getAttribute( 'bumptype' ); +if ( bumpType ) { +if ( bumpType.toLowerCase() === "heightfield" ) { +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} else if ( bumpType.toLowerCase() === "normalmap" ) { +this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); +} else { +console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} +} else { +console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} +break; +case 'shininess': +case 'reflectivity': +case 'index_of_refraction': +case 'transparency': +var f = child.querySelectorAll('float'); +if ( f.length > 0 ) +this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); +break; +default: +break; +} +} +this.create(); +return this; +}; +Shader.prototype.create = function() { +var props = {}; +var transparent = false; +if (this['transparency'] !== undefined && this['transparent'] !== undefined) { +var transparentColor = this['transparent']; +var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; +if (transparencyLevel > 0) { +transparent = true; +props[ 'transparent' ] = true; +props[ 'opacity' ] = 1 - transparencyLevel; +} +} +var keys = { +'diffuse':'map', +'ambient':'lightMap', +'specular':'specularMap', +'emission':'emissionMap', +'bump':'bumpMap', +'normal':'normalMap' +}; +for ( var prop in this ) { +switch ( prop ) { +case 'ambient': +case 'emission': +case 'diffuse': +case 'specular': +case 'bump': +case 'normal': +var cot = this[ prop ]; +if ( cot instanceof ColorOrTexture ) { +if ( cot.isTexture() ) { +var samplerId = cot.texture; +var sampler = this.effect.sampler[samplerId]; +if ( sampler !== undefined && sampler.source !== undefined ) { +var surface = this.effect.surface[sampler.source]; +if ( surface !== undefined ) { +var image = images[ surface.init_from ]; +if ( image ) { +var url = baseUrl + image.init_from; +var texture; +var loader = THREE.Loader.Handlers.get( url ); +if ( loader !== null ) { +texture = loader.load( url ); +} else { +texture = new THREE.Texture(); +loadTextureImage( texture, url ); +} +if ( sampler.wrap_s === "MIRROR" ) { +texture.wrapS = THREE.MirroredRepeatWrapping; +} else if ( sampler.wrap_s === "WRAP" || cot.texOpts.wrapU ) { +texture.wrapS = THREE.RepeatWrapping; +} else { +texture.wrapS = THREE.ClampToEdgeWrapping; +} +if ( sampler.wrap_t === "MIRROR" ) { +texture.wrapT = THREE.MirroredRepeatWrapping; +} else if ( sampler.wrap_t === "WRAP" || cot.texOpts.wrapV ) { +texture.wrapT = THREE.RepeatWrapping; +} else { +texture.wrapT = THREE.ClampToEdgeWrapping; +} +texture.offset.x = cot.texOpts.offsetU; +texture.offset.y = cot.texOpts.offsetV; +texture.repeat.x = cot.texOpts.repeatU; +texture.repeat.y = cot.texOpts.repeatV; +props[keys[prop]] = texture; +if (prop === 'emission') props['emissive'] = 0xffffff; +} +} +} +} else if ( prop === 'diffuse' || !transparent ) { +if ( prop === 'emission' ) { +props[ 'emissive' ] = cot.color.getHex(); +} else { +props[ prop ] = cot.color.getHex(); +} +} +} +break; +case 'shininess': +props[ prop ] = this[ prop ]; +break; +case 'reflectivity': +props[ prop ] = this[ prop ]; +if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; +props['combine'] = THREE.MixOperation; //mix regular shading with reflective component +break; +case 'index_of_refraction': +props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable +if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; +break; +case 'transparency': +break; +default: +break; +} +} +props[ 'shading' ] = preferredShading; +props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; +if ( props.diffuse !== undefined ) { +props.color = props.diffuse; +delete props.diffuse; +} +switch ( this.type ) { +case 'constant': +if (props.emissive != undefined) props.color = props.emissive; +this.material = new THREE.MeshBasicMaterial( props ); +break; +case 'phong': +case 'blinn': +this.material = new THREE.MeshPhongMaterial( props ); +break; +case 'lambert': +default: +this.material = new THREE.MeshLambertMaterial( props ); +break; +} +return this.material; +}; +function Surface ( effect ) { +this.effect = effect; +this.init_from = null; +this.format = null; +} +Surface.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'init_from': +this.init_from = child.textContent; +break; +case 'format': +this.format = child.textContent; +break; +default: +console.log( "unhandled Surface prop: " + child.nodeName ); +break; +} +} +return this; +}; +function Sampler2D ( effect ) { +this.effect = effect; +this.source = null; +this.wrap_s = null; +this.wrap_t = null; +this.minfilter = null; +this.magfilter = null; +this.mipfilter = null; +} +Sampler2D.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'source': +this.source = child.textContent; +break; +case 'minfilter': +this.minfilter = child.textContent; +break; +case 'magfilter': +this.magfilter = child.textContent; +break; +case 'mipfilter': +this.mipfilter = child.textContent; +break; +case 'wrap_s': +this.wrap_s = child.textContent; +break; +case 'wrap_t': +this.wrap_t = child.textContent; +break; +default: +console.log( "unhandled Sampler2D prop: " + child.nodeName ); +break; +} +} +return this; +}; +function Effect () { +this.id = ""; +this.name = ""; +this.shader = null; +this.surface = {}; +this.sampler = {}; +} +Effect.prototype.create = function () { +if ( this.shader === null ) { +return null; +} +}; +Effect.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +extractDoubleSided( this, element ); +this.shader = null; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'profile_COMMON': +this.parseTechnique( this.parseProfileCOMMON( child ) ); +break; +default: +break; +} +} +return this; +}; +Effect.prototype.parseNewparam = function ( element ) { +var sid = element.getAttribute( 'sid' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'surface': +this.surface[sid] = ( new Surface( this ) ).parse( child ); +break; +case 'sampler2D': +this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +}; +Effect.prototype.parseProfileCOMMON = function ( element ) { +var technique; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'profile_COMMON': +this.parseProfileCOMMON( child ); +break; +case 'technique': +technique = child; +break; +case 'newparam': +this.parseNewparam( child ); +break; +case 'image': +var _image = ( new _Image() ).parse( child ); +images[ _image.id ] = _image; +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +return technique; +}; +Effect.prototype.parseTechnique = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'constant': +case 'lambert': +case 'blinn': +case 'phong': +this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); +break; +case 'extra': +this.parseExtra(child); +break; +default: +break; +} +} +}; +Effect.prototype.parseExtra = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique': +this.parseExtraTechnique( child ); +break; +default: +break; +} +} +}; +Effect.prototype.parseExtraTechnique = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bump': +this.shader.parse( element ); +break; +default: +break; +} +} +}; +function InstanceEffect () { +this.url = ""; +} +InstanceEffect.prototype.parse = function ( element ) { +this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); +return this; +}; +function Animation() { +this.id = ""; +this.name = ""; +this.source = {}; +this.sampler = []; +this.channel = []; +} +Animation.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +this.source = {}; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'animation': +var anim = ( new Animation() ).parse( child ); +for ( var src in anim.source ) { +this.source[ src ] = anim.source[ src ]; +} +for ( var j = 0; j < anim.channel.length; j ++ ) { +this.channel.push( anim.channel[ j ] ); +this.sampler.push( anim.sampler[ j ] ); +} +break; +case 'source': +var src = ( new Source() ).parse( child ); +this.source[ src.id ] = src; +break; +case 'sampler': +this.sampler.push( ( new Sampler( this ) ).parse( child ) ); +break; +case 'channel': +this.channel.push( ( new Channel( this ) ).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +function Channel( animation ) { +this.animation = animation; +this.source = ""; +this.target = ""; +this.fullSid = null; +this.sid = null; +this.dotSyntax = null; +this.arrSyntax = null; +this.arrIndices = null; +this.member = null; +} +Channel.prototype.parse = function ( element ) { +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +this.target = element.getAttribute( 'target' ); +var parts = this.target.split( '/' ); +var id = parts.shift(); +var sid = parts.shift(); +var dotSyntax = ( sid.indexOf(".") >= 0 ); +var arrSyntax = ( sid.indexOf("(") >= 0 ); +if ( dotSyntax ) { +parts = sid.split("."); +this.sid = parts.shift(); +this.member = parts.shift(); +} else if ( arrSyntax ) { +var arrIndices = sid.split("("); +this.sid = arrIndices.shift(); +for (var j = 0; j < arrIndices.length; j ++ ) { +arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); +} +this.arrIndices = arrIndices; +} else { +this.sid = sid; +} +this.fullSid = sid; +this.dotSyntax = dotSyntax; +this.arrSyntax = arrSyntax; +return this; +}; +function Sampler ( animation ) { +this.id = ""; +this.animation = animation; +this.inputs = []; +this.input = null; +this.output = null; +this.strideOut = null; +this.interpolation = null; +this.startTime = null; +this.endTime = null; +this.duration = 0; +} +Sampler.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +this.inputs.push( (new Input()).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +Sampler.prototype.create = function () { +for ( var i = 0; i < this.inputs.length; i ++ ) { +var input = this.inputs[ i ]; +var source = this.animation.source[ input.source ]; +switch ( input.semantic ) { +case 'INPUT': +this.input = source.read(); +break; +case 'OUTPUT': +this.output = source.read(); +this.strideOut = source.accessor.stride; +break; +case 'INTERPOLATION': +this.interpolation = source.read(); +break; +case 'IN_TANGENT': +break; +case 'OUT_TANGENT': +break; +default: +console.log(input.semantic); +break; +} +} +this.startTime = 0; +this.endTime = 0; +this.duration = 0; +if ( this.input.length ) { +this.startTime = 100000000; +this.endTime = -100000000; +for ( var i = 0; i < this.input.length; i ++ ) { +this.startTime = Math.min( this.startTime, this.input[ i ] ); +this.endTime = Math.max( this.endTime, this.input[ i ] ); +} +this.duration = this.endTime - this.startTime; +} +}; +Sampler.prototype.getData = function ( type, ndx, member ) { +var data; +if ( type === 'matrix' && this.strideOut === 16 ) { +data = this.output[ ndx ]; +} else if ( this.strideOut > 1 ) { +data = []; +ndx *= this.strideOut; +for ( var i = 0; i < this.strideOut; ++ i ) { +data[ i ] = this.output[ ndx + i ]; +} +if ( this.strideOut === 3 ) { +switch ( type ) { +case 'rotate': +case 'translate': +fixCoords( data, -1 ); +break; +case 'scale': +fixCoords( data, 1 ); +break; +} +} else if ( this.strideOut === 4 && type === 'matrix' ) { +fixCoords( data, -1 ); +} +} else { +data = this.output[ ndx ]; +if ( member && type === 'translate' ) { +data = getConvertedTranslation( member, data ); +} +} +return data; +}; +function Key ( time ) { +this.targets = []; +this.time = time; +} +Key.prototype.addTarget = function ( fullSid, transform, member, data ) { +this.targets.push( { +sid: fullSid, +member: member, +transform: transform, +data: data +} ); +}; +Key.prototype.apply = function ( opt_sid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +var target = this.targets[ i ]; +if ( !opt_sid || target.sid === opt_sid ) { +target.transform.update( target.data, target.member ); +} +} +}; +Key.prototype.getTarget = function ( fullSid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +if ( this.targets[ i ].sid === fullSid ) { +return this.targets[ i ]; +} +} +return null; +}; +Key.prototype.hasTarget = function ( fullSid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +if ( this.targets[ i ].sid === fullSid ) { +return true; +} +} +return false; +}; +Key.prototype.interpolate = function ( nextKey, time ) { +for ( var i = 0, l = this.targets.length; i < l; i ++ ) { +var target = this.targets[ i ], +nextTarget = nextKey.getTarget( target.sid ), +data; +if ( target.transform.type !== 'matrix' && nextTarget ) { +var scale = ( time - this.time ) / ( nextKey.time - this.time ), +nextData = nextTarget.data, +prevData = target.data; +if ( scale < 0 ) scale = 0; +if ( scale > 1 ) scale = 1; +if ( prevData.length ) { +data = []; +for ( var j = 0; j < prevData.length; ++ j ) { +data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; +} +} else { +data = prevData + ( nextData - prevData ) * scale; +} +} else { +data = target.data; +} +target.transform.update( data, target.member ); +} +}; +function Camera() { +this.id = ""; +this.name = ""; +this.technique = ""; +} +Camera.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'optics': +this.parseOptics( child ); +break; +default: +break; +} +} +return this; +}; +Camera.prototype.parseOptics = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[ i ].nodeName === 'technique_common' ) { +var technique = element.childNodes[ i ]; +for ( var j = 0; j < technique.childNodes.length; j ++ ) { +this.technique = technique.childNodes[ j ].nodeName; +if ( this.technique === 'perspective' ) { +var perspective = technique.childNodes[ j ]; +for ( var k = 0; k < perspective.childNodes.length; k ++ ) { +var param = perspective.childNodes[ k ]; +switch ( param.nodeName ) { +case 'yfov': +this.yfov = param.textContent; +break; +case 'xfov': +this.xfov = param.textContent; +break; +case 'znear': +this.znear = param.textContent; +break; +case 'zfar': +this.zfar = param.textContent; +break; +case 'aspect_ratio': +this.aspect_ratio = param.textContent; +break; +} +} +} else if ( this.technique === 'orthographic' ) { +var orthographic = technique.childNodes[ j ]; +for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { +var param = orthographic.childNodes[ k ]; +switch ( param.nodeName ) { +case 'xmag': +this.xmag = param.textContent; +break; +case 'ymag': +this.ymag = param.textContent; +break; +case 'znear': +this.znear = param.textContent; +break; +case 'zfar': +this.zfar = param.textContent; +break; +case 'aspect_ratio': +this.aspect_ratio = param.textContent; +break; +} +} +} +} +} +} +return this; +}; +function InstanceCamera() { +this.url = ""; +} +InstanceCamera.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +return this; +}; +function Light() { +this.id = ""; +this.name = ""; +this.technique = ""; +} +Light.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique_common': +this.parseCommon( child ); +break; +case 'technique': +this.parseTechnique( child ); +break; +default: +break; +} +} +return this; +}; +Light.prototype.parseCommon = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +switch ( element.childNodes[ i ].nodeName ) { +case 'directional': +case 'point': +case 'spot': +case 'ambient': +this.technique = element.childNodes[ i ].nodeName; +var light = element.childNodes[ i ]; +for ( var j = 0; j < light.childNodes.length; j ++ ) { +var child = light.childNodes[j]; +switch ( child.nodeName ) { +case 'color': +var rgba = _floats( child.textContent ); +this.color = new THREE.Color(0); +this.color.setRGB( rgba[0], rgba[1], rgba[2] ); +this.color.a = rgba[3]; +break; +case 'falloff_angle': +this.falloff_angle = parseFloat( child.textContent ); +break; +case 'quadratic_attenuation': +var f = parseFloat( child.textContent ); +this.distance = f ? Math.sqrt( 1 / f ) : 0; +} +} +} +} +return this; +}; +Light.prototype.parseTechnique = function ( element ) { +this.profile = element.getAttribute( 'profile' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'intensity': +this.intensity = parseFloat(child.textContent); +break; +} +} +return this; +}; +function InstanceLight() { +this.url = ""; +} +InstanceLight.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +return this; +}; +function KinematicsModel( ) { +this.id = ''; +this.name = ''; +this.joints = []; +this.links = []; +} +KinematicsModel.prototype.parse = function( element ) { +this.id = element.getAttribute('id'); +this.name = element.getAttribute('name'); +this.joints = []; +this.links = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique_common': +this.parseCommon(child); +break; +default: +break; +} +} +return this; +}; +KinematicsModel.prototype.parseCommon = function( element ) { +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( element.childNodes[ i ].nodeName ) { +case 'joint': +this.joints.push( (new Joint()).parse(child) ); +break; +case 'link': +this.links.push( (new Link()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function Joint( ) { +this.sid = ''; +this.name = ''; +this.axis = new THREE.Vector3(); +this.limits = { +min: 0, +max: 0 +}; +this.type = ''; +this.static = false; +this.zeroPosition = 0.0; +this.middlePosition = 0.0; +} +Joint.prototype.parse = function( element ) { +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.axis = new THREE.Vector3(); +this.limits = { +min: 0, +max: 0 +}; +this.type = ''; +this.static = false; +this.zeroPosition = 0.0; +this.middlePosition = 0.0; +var axisElement = element.querySelector('axis'); +var _axis = _floats(axisElement.textContent); +this.axis = getConvertedVec3(_axis, 0); +var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; +var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; +this.limits = { +min: min, +max: max +}; +var jointTypes = [ 'prismatic', 'revolute' ]; +for (var i = 0; i < jointTypes.length; i ++ ) { +var type = jointTypes[ i ]; +var jointElement = element.querySelector(type); +if ( jointElement ) { +this.type = type; +} +} +if ( this.limits.min >= this.limits.max ) { +this.static = true; +} +this.middlePosition = (this.limits.min + this.limits.max) / 2.0; +return this; +}; +function Link( ) { +this.sid = ''; +this.name = ''; +this.transforms = []; +this.attachments = []; +} +Link.prototype.parse = function( element ) { +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.transforms = []; +this.attachments = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'attachment_full': +this.attachments.push( (new Attachment()).parse(child) ); +break; +case 'rotate': +case 'translate': +case 'matrix': +this.transforms.push( (new Transform()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function Attachment( ) { +this.joint = ''; +this.transforms = []; +this.links = []; +} +Attachment.prototype.parse = function( element ) { +this.joint = element.getAttribute('joint').split('/').pop(); +this.links = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'link': +this.links.push( (new Link()).parse(child) ); +break; +case 'rotate': +case 'translate': +case 'matrix': +this.transforms.push( (new Transform()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function _source( element ) { +var id = element.getAttribute( 'id' ); +if ( sources[ id ] != undefined ) { +return sources[ id ]; +} +sources[ id ] = ( new Source(id )).parse( element ); +return sources[ id ]; +} +function _nsResolver( nsPrefix ) { +if ( nsPrefix === "dae" ) { +return "http://www.collada.org/2005/11/COLLADASchema"; +} +return null; +} +function _bools( str ) { +var raw = _strings( str ); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); +} +return data; +} +function _floats( str ) { +var raw = _strings(str); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( parseFloat( raw[ i ] ) ); +} +return data; +} +function _ints( str ) { +var raw = _strings( str ); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( parseInt( raw[ i ], 10 ) ); +} +return data; +} +function _strings( str ) { +return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; +} +function _trimString( str ) { +return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); +} +function _attr_as_float( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return parseFloat( element.getAttribute( name ) ); +} else { +return defaultValue; +} +} +function _attr_as_int( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return parseInt( element.getAttribute( name ), 10) ; +} else { +return defaultValue; +} +} +function _attr_as_string( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return element.getAttribute( name ); +} else { +return defaultValue; +} +} +function _format_float( f, num ) { +if ( f === undefined ) { +var s = '0.'; +while ( s.length < num + 2 ) { +s += '0'; +} +return s; +} +num = num || 2; +var parts = f.toString().split( '.' ); +parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; +while ( parts[ 1 ].length < num ) { +parts[ 1 ] += '0'; +} +return parts.join( '.' ); +} +function loadTextureImage ( texture, url ) { +var loader = new THREE.ImageLoader(); +loader.load( url, function ( image ) { +texture.image = image; +texture.needsUpdate = true; +} ); +} +function extractDoubleSided( obj, element ) { +obj.doubleSided = false; +var node = element.querySelectorAll('extra double_sided')[0]; +if ( node ) { +if ( node && parseInt( node.textContent, 10 ) === 1 ) { +obj.doubleSided = true; +} +} +} +function setUpConversion() { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +upConversion = null; +} else { +switch ( colladaUp ) { +case 'X': +upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; +break; +case 'Y': +upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; +break; +case 'Z': +upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; +break; +} +} +} +function fixCoords( data, sign ) { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +return; +} +switch ( upConversion ) { +case 'XtoY': +var tmp = data[ 0 ]; +data[ 0 ] = sign * data[ 1 ]; +data[ 1 ] = tmp; +break; +case 'XtoZ': +var tmp = data[ 2 ]; +data[ 2 ] = data[ 1 ]; +data[ 1 ] = data[ 0 ]; +data[ 0 ] = tmp; +break; +case 'YtoX': +var tmp = data[ 0 ]; +data[ 0 ] = data[ 1 ]; +data[ 1 ] = sign * tmp; +break; +case 'YtoZ': +var tmp = data[ 1 ]; +data[ 1 ] = sign * data[ 2 ]; +data[ 2 ] = tmp; +break; +case 'ZtoX': +var tmp = data[ 0 ]; +data[ 0 ] = data[ 1 ]; +data[ 1 ] = data[ 2 ]; +data[ 2 ] = tmp; +break; +case 'ZtoY': +var tmp = data[ 1 ]; +data[ 1 ] = data[ 2 ]; +data[ 2 ] = sign * tmp; +break; +} +} +function getConvertedTranslation( axis, data ) { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +return data; +} +switch ( axis ) { +case 'X': +data = upConversion === 'XtoY' ? data * -1 : data; +break; +case 'Y': +data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; +break; +case 'Z': +data = upConversion === 'ZtoY' ? data * -1 : data ; +break; +default: +break; +} +return data; +} +function getConvertedVec3( data, offset ) { +var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; +fixCoords( arr, -1 ); +return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); +} +function getConvertedMat4( data ) { +if ( options.convertUpAxis ) { +var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; +fixCoords( arr, -1 ); +data[ 0 ] = arr[ 0 ]; +data[ 4 ] = arr[ 1 ]; +data[ 8 ] = arr[ 2 ]; +arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; +fixCoords( arr, -1 ); +data[ 1 ] = arr[ 0 ]; +data[ 5 ] = arr[ 1 ]; +data[ 9 ] = arr[ 2 ]; +arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; +fixCoords( arr, -1 ); +data[ 2 ] = arr[ 0 ]; +data[ 6 ] = arr[ 1 ]; +data[ 10 ] = arr[ 2 ]; +arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; +fixCoords( arr, -1 ); +data[ 0 ] = arr[ 0 ]; +data[ 1 ] = arr[ 1 ]; +data[ 2 ] = arr[ 2 ]; +arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; +fixCoords( arr, -1 ); +data[ 4 ] = arr[ 0 ]; +data[ 5 ] = arr[ 1 ]; +data[ 6 ] = arr[ 2 ]; +arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; +fixCoords( arr, -1 ); +data[ 8 ] = arr[ 0 ]; +data[ 9 ] = arr[ 1 ]; +data[ 10 ] = arr[ 2 ]; +arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; +fixCoords( arr, -1 ); +data[ 3 ] = arr[ 0 ]; +data[ 7 ] = arr[ 1 ]; +data[ 11 ] = arr[ 2 ]; +} +return new THREE.Matrix4().set( +data[0], data[1], data[2], data[3], +data[4], data[5], data[6], data[7], +data[8], data[9], data[10], data[11], +data[12], data[13], data[14], data[15] +); +} +function getConvertedIndex( index ) { +if ( index > -1 && index < 3 ) { +var members = [ 'X', 'Y', 'Z' ], +indices = { X: 0, Y: 1, Z: 2 }; +index = getConvertedMember( members[ index ] ); +index = indices[ index ]; +} +return index; +} +function getConvertedMember( member ) { +if ( options.convertUpAxis ) { +switch ( member ) { +case 'X': +switch ( upConversion ) { +case 'XtoY': +case 'XtoZ': +case 'YtoX': +member = 'Y'; +break; +case 'ZtoX': +member = 'Z'; +break; +} +break; +case 'Y': +switch ( upConversion ) { +case 'XtoY': +case 'YtoX': +case 'ZtoX': +member = 'X'; +break; +case 'XtoZ': +case 'YtoZ': +case 'ZtoY': +member = 'Z'; +break; +} +break; +case 'Z': +switch ( upConversion ) { +case 'XtoZ': +member = 'X'; +break; +case 'YtoZ': +case 'ZtoX': +case 'ZtoY': +member = 'Y'; +break; +} +break; +} +} +return member; +} +return { +load: load, +parse: parse, +setPreferredShading: setPreferredShading, +applySkin: applySkin, +geometries : geometries, +options: options +}; +}; +THREE.GLTF2Loader = ( function () { +function GLTF2Loader( manager ) { +this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +} +GLTF2Loader.prototype = { +constructor: GLTF2Loader, +load: function ( url, onLoad, onProgress, onError ) { +var scope = this; +var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); +var loader = new THREE.FileLoader( scope.manager ); +loader.setResponseType( 'arraybuffer' ); +loader.load( url, function ( data ) { +scope.parse( data, onLoad, path ); +}, onProgress, onError ); +}, +setCrossOrigin: function ( value ) { +this.crossOrigin = value; +}, +setPath: function ( value ) { +this.path = value; +}, +parse: function ( data, callback, path ) { +var content; +var extensions = {}; +var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); +if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { +extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); +content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; +} else { +content = convertUint8ArrayToString( new Uint8Array( data ) ); +} +var json = JSON.parse( content ); +if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { +extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); +} +console.time( 'GLTF2Loader' ); +var parser = new GLTFParser( json, extensions, { +path: path || this.path, +crossOrigin: this.crossOrigin +} ); +parser.parse( function ( scene, scenes, cameras, animations ) { +console.timeEnd( 'GLTF2Loader' ); +var glTF = { +"scene": scene, +"scenes": scenes, +"cameras": cameras, +"animations": animations +}; +callback( glTF ); +} ); +} +}; +function GLTFRegistry() { +var objects = {}; +return { +get: function ( key ) { +return objects[ key ]; +}, +add: function ( key, object ) { +objects[ key ] = object; +}, +remove: function ( key ) { +delete objects[ key ]; +}, +removeAll: function () { +objects = {}; +}, +update: function ( scene, camera ) { +for ( var name in objects ) { +var object = objects[ name ]; +if ( object.update ) { +object.update( scene, camera ); +} +} +} +}; +} +function GLTFShader( targetNode, allNodes ) { +var boundUniforms = {}; +var uniforms = targetNode.material.uniforms; +for ( var uniformId in uniforms ) { +var uniform = uniforms[ uniformId ]; +if ( uniform.semantic ) { +var sourceNodeRef = uniform.node; +var sourceNode = targetNode; +if ( sourceNodeRef ) { +sourceNode = allNodes[ sourceNodeRef ]; +} +boundUniforms[ uniformId ] = { +semantic: uniform.semantic, +sourceNode: sourceNode, +targetNode: targetNode, +uniform: uniform +}; +} +} +this.boundUniforms = boundUniforms; +this._m4 = new THREE.Matrix4(); +} +GLTFShader.prototype.update = function ( scene, camera ) { +var boundUniforms = this.boundUniforms; +for ( var name in boundUniforms ) { +var boundUniform = boundUniforms[ name ]; +switch ( boundUniform.semantic ) { +case "MODELVIEW": +var m4 = boundUniform.uniform.value; +m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +break; +case "MODELVIEWINVERSETRANSPOSE": +var m3 = boundUniform.uniform.value; +this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +m3.getNormalMatrix( this._m4 ); +break; +case "PROJECTION": +var m4 = boundUniform.uniform.value; +m4.copy( camera.projectionMatrix ); +break; +case "JOINTMATRIX": +var m4v = boundUniform.uniform.value; +for ( var mi = 0; mi < m4v.length; mi ++ ) { +m4v[ mi ] +.getInverse( boundUniform.sourceNode.matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) +.multiply( boundUniform.targetNode.bindMatrix ); +} +break; +default : +console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); +break; +} +} +}; +var EXTENSIONS = { +KHR_BINARY_GLTF: 'KHR_binary_glTF', +KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; +function GLTFMaterialsCommonExtension( json ) { +this.name = EXTENSIONS.KHR_MATERIALS_COMMON; +this.lights = {}; +var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; +var lights = extension.lights || {}; +for ( var lightId in lights ) { +var light = lights[ lightId ]; +var lightNode; +var lightParams = light[ light.type ]; +var color = new THREE.Color().fromArray( lightParams.color ); +switch ( light.type ) { +case "directional": +lightNode = new THREE.DirectionalLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "point": +lightNode = new THREE.PointLight( color ); +break; +case "spot": +lightNode = new THREE.SpotLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "ambient": +lightNode = new THREE.AmbientLight( color ); +break; +} +if ( lightNode ) { +this.lights[ lightId ] = lightNode; +} +} +} +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; +var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; +var BINARY_EXTENSION_HEADER_LENGTH = 12; +var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; +function GLTFBinaryExtension( data ) { +this.name = EXTENSIONS.KHR_BINARY_GLTF; +this.content = null; +this.body = null; +var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); +this.header = { +magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), +version: headerView.getUint32( 4, true ), +length: headerView.getUint32( 8, true ) +}; +if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { +throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); +} else if ( this.header.version < 2.0 ) { +throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); +} +var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); +var chunkIndex = 0; +while ( chunkIndex < chunkView.byteLength ) { +var chunkLength = chunkView.getUint32( chunkIndex, true ); +chunkIndex += 4; +var chunkType = chunkView.getUint32( chunkIndex, true ); +chunkIndex += 4; +if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { +var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); +this.content = convertUint8ArrayToString( contentArray ); +} else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { +var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; +this.body = data.slice( byteOffset, byteOffset + chunkLength ); +} +chunkIndex += chunkLength; +} +if ( this.content === null ) { +throw new Error( 'GLTF2Loader: JSON content not found.' ); +} +} +var WEBGL_CONSTANTS = { +FLOAT: 5126, +FLOAT_MAT3: 35675, +FLOAT_MAT4: 35676, +FLOAT_VEC2: 35664, +FLOAT_VEC3: 35665, +FLOAT_VEC4: 35666, +LINEAR: 9729, +REPEAT: 10497, +SAMPLER_2D: 35678, +TRIANGLES: 4, +LINES: 1, +UNSIGNED_BYTE: 5121, +UNSIGNED_SHORT: 5123, +VERTEX_SHADER: 35633, +FRAGMENT_SHADER: 35632 +}; +var WEBGL_TYPE = { +5126: Number, +35675: THREE.Matrix3, +35676: THREE.Matrix4, +35664: THREE.Vector2, +35665: THREE.Vector3, +35666: THREE.Vector4, +35678: THREE.Texture +}; +var WEBGL_COMPONENT_TYPES = { +5120: Int8Array, +5121: Uint8Array, +5122: Int16Array, +5123: Uint16Array, +5125: Uint32Array, +5126: Float32Array +}; +var WEBGL_FILTERS = { +9728: THREE.NearestFilter, +9729: THREE.LinearFilter, +9984: THREE.NearestMipMapNearestFilter, +9985: THREE.LinearMipMapNearestFilter, +9986: THREE.NearestMipMapLinearFilter, +9987: THREE.LinearMipMapLinearFilter +}; +var WEBGL_WRAPPINGS = { +33071: THREE.ClampToEdgeWrapping, +33648: THREE.MirroredRepeatWrapping, +10497: THREE.RepeatWrapping +}; +var WEBGL_TEXTURE_FORMATS = { +6406: THREE.AlphaFormat, +6407: THREE.RGBFormat, +6408: THREE.RGBAFormat, +6409: THREE.LuminanceFormat, +6410: THREE.LuminanceAlphaFormat +}; +var WEBGL_TEXTURE_DATATYPES = { +5121: THREE.UnsignedByteType, +32819: THREE.UnsignedShort4444Type, +32820: THREE.UnsignedShort5551Type, +33635: THREE.UnsignedShort565Type +}; +var WEBGL_SIDES = { +1028: THREE.BackSide, // Culling front +1029: THREE.FrontSide // Culling back +}; +var WEBGL_DEPTH_FUNCS = { +512: THREE.NeverDepth, +513: THREE.LessDepth, +514: THREE.EqualDepth, +515: THREE.LessEqualDepth, +516: THREE.GreaterEqualDepth, +517: THREE.NotEqualDepth, +518: THREE.GreaterEqualDepth, +519: THREE.AlwaysDepth +}; +var WEBGL_BLEND_EQUATIONS = { +32774: THREE.AddEquation, +32778: THREE.SubtractEquation, +32779: THREE.ReverseSubtractEquation +}; +var WEBGL_BLEND_FUNCS = { +0: THREE.ZeroFactor, +1: THREE.OneFactor, +768: THREE.SrcColorFactor, +769: THREE.OneMinusSrcColorFactor, +770: THREE.SrcAlphaFactor, +771: THREE.OneMinusSrcAlphaFactor, +772: THREE.DstAlphaFactor, +773: THREE.OneMinusDstAlphaFactor, +774: THREE.DstColorFactor, +775: THREE.OneMinusDstColorFactor, +776: THREE.SrcAlphaSaturateFactor +}; +var WEBGL_TYPE_SIZES = { +'SCALAR': 1, +'VEC2': 2, +'VEC3': 3, +'VEC4': 4, +'MAT2': 4, +'MAT3': 9, +'MAT4': 16 +}; +var PATH_PROPERTIES = { +scale: 'scale', +translation: 'position', +rotation: 'quaternion' +}; +var INTERPOLATION = { +LINEAR: THREE.InterpolateLinear, +STEP: THREE.InterpolateDiscrete +}; +var STATES_ENABLES = { +2884: 'CULL_FACE', +2929: 'DEPTH_TEST', +3042: 'BLEND', +3089: 'SCISSOR_TEST', +32823: 'POLYGON_OFFSET_FILL', +32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; +function _each( object, callback, thisObj ) { +if ( !object ) { +return Promise.resolve(); +} +var results; +var fns = []; +if ( Object.prototype.toString.call( object ) === '[object Array]' ) { +results = []; +var length = object.length; +for ( var idx = 0; idx < length; idx ++ ) { +var value = callback.call( thisObj || this, object[ idx ], idx ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, idx )); +} else { +results[ idx ] = value; +} +} +} +} else { +results = {}; +for ( var key in object ) { +if ( object.hasOwnProperty( key ) ) { +var value = callback.call( thisObj || this, object[ key ], key ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, key )); +} else { +results[ key ] = value; +} +} +} +} +} +return Promise.all( fns ).then( function() { +return results; +}); +} +function resolveURL( url, path ) { +if ( typeof url !== 'string' || url === '' ) +return ''; +if ( /^(https?:)?\/\//i.test( url ) ) { +return url; +} +if ( /^data:.*,.*$/i.test( url ) ) { +return url; +} +if ( /^blob:.*$/i.test( url ) ) { +return url; +} +return ( path || '' ) + url; +} +function convertUint8ArrayToString( array ) { +var s = ''; +for ( var i = 0; i < array.length; i ++ ) { +s += String.fromCharCode( array[ i ] ); +} +return s; +} +function replaceTHREEShaderAttributes( shaderText, technique ) { +var attributes = {}; +for ( var attributeId in technique.attributes ) { +var pname = technique.attributes[ attributeId ]; +var param = technique.parameters[ pname ]; +var atype = param.type; +var semantic = param.semantic; +attributes[ attributeId ] = { +type: atype, +semantic: semantic +}; +} +var shaderParams = technique.parameters; +var shaderAttributes = technique.attributes; +var params = {}; +for ( var attributeId in attributes ) { +var pname = shaderAttributes[ attributeId ]; +var shaderParam = shaderParams[ pname ]; +var semantic = shaderParam.semantic; +if ( semantic ) { +params[ attributeId ] = shaderParam; +} +} +for ( var pname in params ) { +var param = params[ pname ]; +var semantic = param.semantic; +var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); +switch ( semantic ) { +case 'POSITION': +shaderText = shaderText.replace( regEx, 'position' ); +break; +case 'NORMAL': +shaderText = shaderText.replace( regEx, 'normal' ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +shaderText = shaderText.replace( regEx, 'uv' ); +break; +case 'TEXCOORD_1': +shaderText = shaderText.replace( regEx, 'uv2' ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +shaderText = shaderText.replace( regEx, 'color' ); +break; +case 'WEIGHTS_0': +case 'WEIGHT': // WEIGHT semantic deprecated. +shaderText = shaderText.replace( regEx, 'skinWeight' ); +break; +case 'JOINTS_0': +case 'JOINT': // JOINT semantic deprecated. +shaderText = shaderText.replace( regEx, 'skinIndex' ); +break; +} +} +return shaderText; +} +function createDefaultMaterial() { +return new THREE.MeshPhongMaterial( { +color: 0x00000, +emissive: 0x888888, +specular: 0x000000, +shininess: 0, +transparent: false, +depthTest: true, +side: THREE.FrontSide +} ); +} +function DeferredShaderMaterial( params ) { +this.isDeferredShaderMaterial = true; +this.params = params; +} +DeferredShaderMaterial.prototype.create = function () { +var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); +for ( var uniformId in this.params.uniforms ) { +var originalUniform = this.params.uniforms[ uniformId ]; +if ( originalUniform.value instanceof THREE.Texture ) { +uniforms[ uniformId ].value = originalUniform.value; +uniforms[ uniformId ].value.needsUpdate = true; +} +uniforms[ uniformId ].semantic = originalUniform.semantic; +uniforms[ uniformId ].node = originalUniform.node; +} +this.params.uniforms = uniforms; +return new THREE.RawShaderMaterial( this.params ); +}; +function GLTFParser( json, extensions, options ) { +this.json = json || {}; +this.extensions = extensions || {}; +this.options = options || {}; +this.cache = new GLTFRegistry(); +} +GLTFParser.prototype._withDependencies = function ( dependencies ) { +var _dependencies = {}; +for ( var i = 0; i < dependencies.length; i ++ ) { +var dependency = dependencies[ i ]; +var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); +var cached = this.cache.get( dependency ); +if ( cached !== undefined ) { +_dependencies[ dependency ] = cached; +} else if ( this[ fnName ] ) { +var fn = this[ fnName ](); +this.cache.add( dependency, fn ); +_dependencies[ dependency ] = fn; +} +} +return _each( _dependencies, function ( dependency ) { +return dependency; +} ); +}; +GLTFParser.prototype.parse = function ( callback ) { +var json = this.json; +this.cache.removeAll(); +this._withDependencies( [ +"scenes", +"cameras", +"animations" +] ).then( function ( dependencies ) { +var scenes = []; +for ( var name in dependencies.scenes ) { +scenes.push( dependencies.scenes[ name ] ); +} +var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; +var cameras = []; +for ( var name in dependencies.cameras ) { +var camera = dependencies.cameras[ name ]; +cameras.push( camera ); +} +var animations = []; +for ( var name in dependencies.animations ) { +animations.push( dependencies.animations[ name ] ); +} +callback( scene, scenes, cameras, animations ); +} ); +}; +GLTFParser.prototype.loadShaders = function () { +var json = this.json; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.shaders, function ( shader ) { +if ( shader.bufferView !== undefined ) { +var bufferView = dependencies.bufferViews[ shader.bufferView ]; +var array = new Uint8Array( bufferView ); +return convertUint8ArrayToString( array ); +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'text' ); +loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { +resolve( shaderText ); +} ); +} ); +} ); +} ); +}; +GLTFParser.prototype.loadBuffers = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return _each( json.buffers, function ( buffer, name ) { +if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { +if ( buffer.uri === undefined && name === 0 ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'arraybuffer' ); +loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { +resolve( buffer ); +} ); +} ); +} else { +console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); +} +} ); +}; +GLTFParser.prototype.loadBufferViews = function () { +var json = this.json; +return this._withDependencies( [ +"buffers" +] ).then( function ( dependencies ) { +return _each( json.bufferViews, function ( bufferView ) { +var arraybuffer = dependencies.buffers[ bufferView.buffer ]; +var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; +return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); +} ); +} ); +}; +GLTFParser.prototype.loadAccessors = function () { +var json = this.json; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.accessors, function ( accessor ) { +var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; +var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; +var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; +var elementBytes = TypedArray.BYTES_PER_ELEMENT; +var itemBytes = elementBytes * itemSize; +var array; +if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { +array = new TypedArray( arraybuffer ); +var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); +return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); +} else { +array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); +return new THREE.BufferAttribute( array, itemSize ); +} +} ); +} ); +}; +GLTFParser.prototype.loadTextures = function () { +var json = this.json; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.textures, function ( texture ) { +if ( texture.source !== undefined ) { +return new Promise( function ( resolve ) { +var source = json.images[ texture.source ]; +var sourceUri = source.uri; +var urlCreator; +if ( source.bufferView !== undefined ) { +var bufferView = dependencies.bufferViews[ source.bufferView ]; +var blob = new Blob( [ bufferView ], { type: source.mimeType } ); +urlCreator = window.URL || window.webkitURL; +sourceUri = urlCreator.createObjectURL( blob ); +} +var textureLoader = THREE.Loader.Handlers.get( sourceUri ); +if ( textureLoader === null ) { +textureLoader = new THREE.TextureLoader(); +} +textureLoader.setCrossOrigin( options.crossOrigin ); +textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { +if ( urlCreator !== undefined ) { +urlCreator.revokeObjectURL( sourceUri ); +} +_texture.flipY = false; +if ( texture.name !== undefined ) _texture.name = texture.name; +_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; +if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { +console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + +'internalFormat will be forced to be the same value as format.' ); +} +_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; +if ( texture.sampler !== undefined ) { +var sampler = json.samplers[ texture.sampler ]; +_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; +_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; +_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; +_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; +} +resolve( _texture ); +}, undefined, function () { +resolve(); +} ); +} ); +} +} ); +} ); +}; +GLTFParser.prototype.loadMaterials = function () { +var json = this.json; +return this._withDependencies( [ +"shaders", +"textures" +] ).then( function ( dependencies ) { +return _each( json.materials, function ( material ) { +var materialType; +var materialValues = {}; +var materialParams = {}; +var khr_material; +if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { +khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; +} +if ( khr_material ) { +var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; +switch ( khr_material.technique ) { +case 'BLINN' : +case 'PHONG' : +materialType = THREE.MeshPhongMaterial; +keys.push( 'diffuse', 'specular', 'shininess' ); +break; +case 'LAMBERT' : +materialType = THREE.MeshLambertMaterial; +keys.push( 'diffuse' ); +break; +case 'CONSTANT' : +default : +materialType = THREE.MeshBasicMaterial; +break; +} +keys.forEach( function( v ) { +if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; +} ); +if ( khr_material.doubleSided || materialValues.doubleSided ) { +materialParams.side = THREE.DoubleSide; +} +if ( khr_material.transparent || materialValues.transparent ) { +materialParams.transparent = true; +materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; +} +} else if ( material.technique === undefined ) { +if ( material.pbrMetallicRoughness !== undefined ) { +materialType = THREE.MeshStandardMaterial; +if ( material.pbrMetallicRoughness !== undefined ) { +var metallicRoughness = material.pbrMetallicRoughness; +materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); +materialParams.opacity = 1.0; +if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { +var array = metallicRoughness.baseColorFactor; +materialParams.color.fromArray( array ); +materialParams.opacity = array[ 3 ]; +} +if ( metallicRoughness.baseColorTexture !== undefined ) { +materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; +} +if ( materialParams.opacity < 1.0 || +( materialParams.map !== undefined && +( materialParams.map.format === THREE.AlphaFormat || +materialParams.map.format === THREE.RGBAFormat || +materialParams.map.format === THREE.LuminanceAlphaFormat ) ) ) { +materialParams.transparent = true; +} +materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; +materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; +if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { +var textureIndex = metallicRoughness.metallicRoughnessTexture.index; +materialParams.metalnessMap = dependencies.textures[ textureIndex ]; +materialParams.roughnessMap = dependencies.textures[ textureIndex ]; +} +} +} else { +materialType = THREE.MeshPhongMaterial; +} +if ( material.normalTexture !== undefined ) { +materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; +} +if ( material.occlusionTexture !== undefined ) { +materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; +} +if ( material.emissiveTexture !== undefined ) { +materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; +} +materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); +if ( material.emissiveFactor !== undefined ) { +materialParams.emissive.fromArray( material.emissiveFactor ); +} +Object.assign( materialValues, material.values ); +} else { +materialType = DeferredShaderMaterial; +var technique = json.techniques[ material.technique ]; +materialParams.uniforms = {}; +var program = json.programs[ technique.program ]; +if ( program ) { +materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; +if ( ! materialParams.fragmentShader ) { +console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); +materialType = THREE.MeshPhongMaterial; +} +var vertexShader = dependencies.shaders[ program.vertexShader ]; +if ( ! vertexShader ) { +console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); +materialType = THREE.MeshPhongMaterial; +} +materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); +var uniforms = technique.uniforms; +for ( var uniformId in uniforms ) { +var pname = uniforms[ uniformId ]; +var shaderParam = technique.parameters[ pname ]; +var ptype = shaderParam.type; +if ( WEBGL_TYPE[ ptype ] ) { +var pcount = shaderParam.count; +var value; +if ( material.values !== undefined ) value = material.values[ pname ]; +var uvalue = new WEBGL_TYPE[ ptype ](); +var usemantic = shaderParam.semantic; +var unode = shaderParam.node; +switch ( ptype ) { +case WEBGL_CONSTANTS.FLOAT: +uvalue = shaderParam.value; +if ( pname == "transparency" ) { +materialParams.transparent = true; +} +if ( value !== undefined ) { +uvalue = value; +} +break; +case WEBGL_CONSTANTS.FLOAT_VEC2: +case WEBGL_CONSTANTS.FLOAT_VEC3: +case WEBGL_CONSTANTS.FLOAT_VEC4: +case WEBGL_CONSTANTS.FLOAT_MAT3: +if ( shaderParam && shaderParam.value ) { +uvalue.fromArray( shaderParam.value ); +} +if ( value ) { +uvalue.fromArray( value ); +} +break; +case WEBGL_CONSTANTS.FLOAT_MAT2: +console.warn( "FLOAT_MAT2 is not a supported uniform type" ); +break; +case WEBGL_CONSTANTS.FLOAT_MAT4: +if ( pcount ) { +uvalue = new Array( pcount ); +for ( var mi = 0; mi < pcount; mi ++ ) { +uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); +} +if ( shaderParam && shaderParam.value ) { +var m4v = shaderParam.value; +uvalue.fromArray( m4v ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} else { +if ( shaderParam && shaderParam.value ) { +var m4 = shaderParam.value; +uvalue.fromArray( m4 ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} +break; +case WEBGL_CONSTANTS.SAMPLER_2D: +if ( value !== undefined ) { +uvalue = dependencies.textures[ value ]; +} else if ( shaderParam.value !== undefined ) { +uvalue = dependencies.textures[ shaderParam.value ]; +} else { +uvalue = null; +} +break; +} +materialParams.uniforms[ uniformId ] = { +value: uvalue, +semantic: usemantic, +node: unode +}; +} else { +throw new Error( "Unknown shader uniform param type: " + ptype ); +} +} +var states = technique.states || {}; +var enables = states.enable || []; +var functions = states.functions || {}; +var enableCullFace = false; +var enableDepthTest = false; +var enableBlend = false; +for ( var i = 0, il = enables.length; i < il; i ++ ) { +var enable = enables[ i ]; +switch ( STATES_ENABLES[ enable ] ) { +case 'CULL_FACE': +enableCullFace = true; +break; +case 'DEPTH_TEST': +enableDepthTest = true; +break; +case 'BLEND': +enableBlend = true; +break; +case 'SCISSOR_TEST': +case 'POLYGON_OFFSET_FILL': +case 'SAMPLE_ALPHA_TO_COVERAGE': +break; +default: +throw new Error( "Unknown technique.states.enable: " + enable ); +} +} +if ( enableCullFace ) { +materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; +} else { +materialParams.side = THREE.DoubleSide; +} +materialParams.depthTest = enableDepthTest; +materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; +materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; +materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; +materialParams.transparent = enableBlend; +var blendEquationSeparate = functions.blendEquationSeparate; +if ( blendEquationSeparate !== undefined ) { +materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; +materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; +} else { +materialParams.blendEquation = THREE.AddEquation; +materialParams.blendEquationAlpha = THREE.AddEquation; +} +var blendFuncSeparate = functions.blendFuncSeparate; +if ( blendFuncSeparate !== undefined ) { +materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; +materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; +materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; +materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; +} else { +materialParams.blendSrc = THREE.OneFactor; +materialParams.blendDst = THREE.ZeroFactor; +materialParams.blendSrcAlpha = THREE.OneFactor; +materialParams.blendDstAlpha = THREE.ZeroFactor; +} +} +} +if ( Array.isArray( materialValues.diffuse ) ) { +materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); +} else if ( typeof( materialValues.diffuse ) === 'string' ) { +materialParams.map = dependencies.textures[ materialValues.diffuse ]; +} +delete materialParams.diffuse; +if ( typeof( materialValues.reflective ) === 'string' ) { +materialParams.envMap = dependencies.textures[ materialValues.reflective ]; +} +if ( typeof( materialValues.bump ) === 'string' ) { +materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; +} +if ( Array.isArray( materialValues.emission ) ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.color = new THREE.Color().fromArray( materialValues.emission ); +} else { +materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); +} +} else if ( typeof( materialValues.emission ) === 'string' ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.map = dependencies.textures[ materialValues.emission ]; +} else { +materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; +} +} +if ( Array.isArray( materialValues.specular ) ) { +materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); +} else if ( typeof( materialValues.specular ) === 'string' ) { +materialParams.specularMap = dependencies.textures[ materialValues.specular ]; +} +if ( materialValues.shininess !== undefined ) { +materialParams.shininess = materialValues.shininess; +} +var _material = new materialType( materialParams ); +if ( material.name !== undefined ) _material.name = material.name; +return _material; +} ); +} ); +}; +GLTFParser.prototype.loadMeshes = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"materials" +] ).then( function ( dependencies ) { +return _each( json.meshes, function ( mesh ) { +var group = new THREE.Group(); +if ( mesh.name !== undefined ) group.name = mesh.name; +if ( mesh.extras ) group.userData = mesh.extras; +var primitives = mesh.primitives || []; +for ( var name in primitives ) { +var primitive = primitives[ name ]; +var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); +var geometry; +var meshNode; +if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { +geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( attributeEntry === undefined ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'NORMAL': +geometry.addAttribute( 'normal', bufferAttribute ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +geometry.addAttribute( 'uv', bufferAttribute ); +break; +case 'TEXCOORD_1': +geometry.addAttribute( 'uv2', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +case 'WEIGHTS_0': +case 'WEIGHT': // WEIGHT semantic deprecated. +geometry.addAttribute( 'skinWeight', bufferAttribute ); +break; +case 'JOINTS_0': +case 'JOINT': // JOINT semantic deprecated. +geometry.addAttribute( 'skinIndex', bufferAttribute ); +break; +} +} +if ( primitive.indices !== undefined ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +} +meshNode = new THREE.Mesh( geometry, material ); +meshNode.castShadow = true; +} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { +geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +} +} +if ( primitive.indices !== undefined ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +meshNode = new THREE.LineSegments( geometry, material ); +} else { +meshNode = new THREE.Line( geometry, material ); +} +} else { +throw new Error( "Only triangular and line primitives are supported" ); +} +if ( geometry.attributes.color !== undefined ) { +material.vertexColors = THREE.VertexColors; +material.needsUpdate = true; +} +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} +return group; +} ); +} ); +}; +GLTFParser.prototype.loadCameras = function () { +var json = this.json; +return _each( json.cameras, function ( camera ) { +if ( camera.type == "perspective" && camera.perspective ) { +var yfov = camera.perspective.yfov; +var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; +var xfov = yfov * aspectRatio; +var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} else if ( camera.type == "orthographic" && camera.orthographic ) { +var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} +} ); +}; +GLTFParser.prototype.loadSkins = function () { +var json = this.json; +return this._withDependencies( [ +"accessors" +] ).then( function ( dependencies ) { +return _each( json.skins, function ( skin ) { +var bindShapeMatrix = new THREE.Matrix4(); +if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); +var _skin = { +bindShapeMatrix: bindShapeMatrix, +jointNames: skin.jointNames, +inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] +}; +return _skin; +} ); +} ); +}; +GLTFParser.prototype.loadAnimations = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"nodes" +] ).then( function ( dependencies ) { +return _each( json.animations, function ( animation, animationId ) { +var tracks = []; +for ( var channelId in animation.channels ) { +var channel = animation.channels[ channelId ]; +var sampler = animation.samplers[ channel.sampler ]; +if ( sampler ) { +var target = channel.target; +var name = target.node || target.id; // NOTE: target.id is deprecated. +var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; +var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; +var inputAccessor = dependencies.accessors[ input ]; +var outputAccessor = dependencies.accessors[ output ]; +var node = dependencies.nodes[ name ]; +if ( node ) { +node.updateMatrix(); +node.matrixAutoUpdate = true; +var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation +? THREE.QuaternionKeyframeTrack +: THREE.VectorKeyframeTrack; +var targetName = node.name ? node.name : node.uuid; +var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; +tracks.push( new TypedKeyframeTrack( +targetName + '.' + PATH_PROPERTIES[ target.path ], +THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), +THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), +interpolation +) ); +} +} +} +var name = animation.name !== undefined ? animation.name : "animation_" + animationId; +return new THREE.AnimationClip( name, undefined, tracks ); +} ); +} ); +}; +GLTFParser.prototype.loadNodes = function () { +var json = this.json; +var extensions = this.extensions; +var scope = this; +return _each( json.nodes, function ( node ) { +var matrix = new THREE.Matrix4(); +var _node; +if ( node.jointName ) { +_node = new THREE.Bone(); +_node.name = node.name !== undefined ? node.name : node.jointName; +_node.jointName = node.jointName; +} else { +_node = new THREE.Object3D(); +if ( node.name !== undefined ) _node.name = node.name; +} +if ( node.extras ) _node.userData = node.extras; +if ( node.matrix !== undefined ) { +matrix.fromArray( node.matrix ); +_node.applyMatrix( matrix ); +} else { +if ( node.translation !== undefined ) { +_node.position.fromArray( node.translation ); +} +if ( node.rotation !== undefined ) { +_node.quaternion.fromArray( node.rotation ); +} +if ( node.scale !== undefined ) { +_node.scale.fromArray( node.scale ); +} +} +return _node; +} ).then( function ( __nodes ) { +return scope._withDependencies( [ +"meshes", +"skins", +"cameras" +] ).then( function ( dependencies ) { +return _each( __nodes, function ( _node, nodeId ) { +var node = json.nodes[ nodeId ]; +var meshes; +if ( node.mesh !== undefined) { +meshes = [ node.mesh ]; +} else if ( node.meshes !== undefined ) { +console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); +meshes = node.meshes; +} +if ( meshes !== undefined ) { +for ( var meshId in meshes ) { +var mesh = meshes[ meshId ]; +var group = dependencies.meshes[ mesh ]; +if ( group === undefined ) { +console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); +continue; +} +for ( var childrenId in group.children ) { +var child = group.children[ childrenId ]; +var originalMaterial = child.material; +var originalGeometry = child.geometry; +var originalUserData = child.userData; +var originalName = child.name; +var material; +if ( originalMaterial.isDeferredShaderMaterial ) { +originalMaterial = material = originalMaterial.create(); +} else { +material = originalMaterial; +} +switch ( child.type ) { +case 'LineSegments': +child = new THREE.LineSegments( originalGeometry, material ); +break; +case 'LineLoop': +child = new THREE.LineLoop( originalGeometry, material ); +break; +case 'Line': +child = new THREE.Line( originalGeometry, material ); +break; +default: +child = new THREE.Mesh( originalGeometry, material ); +} +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var skinEntry; +if ( node.skin !== undefined ) { +skinEntry = dependencies.skins[ node.skin ]; +} +if ( skinEntry ) { +var getJointNode = function ( jointId ) { +var keys = Object.keys( __nodes ); +for ( var i = 0, il = keys.length; i < il; i ++ ) { +var n = __nodes[ keys[ i ] ]; +if ( n.jointName === jointId ) return n; +} +return null; +}; +var geometry = originalGeometry; +var material = originalMaterial; +material.skinning = true; +child = new THREE.SkinnedMesh( geometry, material, false ); +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var bones = []; +var boneInverses = []; +for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { +var jointId = skinEntry.jointNames[ i ]; +var jointNode = getJointNode( jointId ); +if ( jointNode ) { +bones.push( jointNode ); +var m = skinEntry.inverseBindMatrices.array; +var mat = new THREE.Matrix4().fromArray( m, i * 16 ); +boneInverses.push( mat ); +} else { +console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); +} +} +child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); +var buildBoneGraph = function ( parentJson, parentObject, property ) { +var children = parentJson[ property ]; +if ( children === undefined ) return; +for ( var i = 0, il = children.length; i < il; i ++ ) { +var nodeId = children[ i ]; +var bone = __nodes[ nodeId ]; +var boneJson = json.nodes[ nodeId ]; +if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { +parentObject.add( bone ); +buildBoneGraph( boneJson, bone, 'children' ); +} +} +}; +buildBoneGraph( node, child, 'skeletons' ); +} +_node.add( child ); +} +} +} +if ( node.camera !== undefined ) { +var camera = dependencies.cameras[ node.camera ]; +_node.add( camera ); +} +if ( node.extensions +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { +var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; +var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; +_node.add( light ); +} +return _node; +} ); +} ); +} ); +}; +GLTFParser.prototype.loadScenes = function () { +var json = this.json; +function buildNodeHierachy( nodeId, parentObject, allNodes ) { +var _node = allNodes[ nodeId ]; +parentObject.add( _node ); +var node = json.nodes[ nodeId ]; +if ( node.children ) { +var children = node.children; +for ( var i = 0, l = children.length; i < l; i ++ ) { +var child = children[ i ]; +buildNodeHierachy( child, _node, allNodes ); +} +} +} +return this._withDependencies( [ +"nodes" +] ).then( function ( dependencies ) { +return _each( json.scenes, function ( scene ) { +var _scene = new THREE.Scene(); +if ( scene.name !== undefined ) _scene.name = scene.name; +if ( scene.extras ) _scene.userData = scene.extras; +var nodes = scene.nodes || []; +for ( var i = 0, l = nodes.length; i < l; i ++ ) { +var nodeId = nodes[ i ]; +buildNodeHierachy( nodeId, _scene, dependencies.nodes ); +} +_scene.traverse( function ( child ) { +if ( child.material && child.material.isRawShaderMaterial ) { +child.gltfShader = new GLTFShader( child, dependencies.nodes ); +child.onBeforeRender = function(renderer, scene, camera){ +this.gltfShader.update(scene, camera); +}; +} +} ); +return _scene; +} ); +} ); +}; +return GLTF2Loader; +} )(); +THREE.GLTFLoader = ( function () { +function GLTFLoader( manager ) { +this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +} +GLTFLoader.prototype = { +constructor: GLTFLoader, +load: function ( url, onLoad, onProgress, onError ) { +var scope = this; +var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); +var loader = new THREE.FileLoader( scope.manager ); +loader.setResponseType( 'arraybuffer' ); +loader.load( url, function ( data ) { +scope.parse( data, onLoad, path ); +}, onProgress, onError ); +}, +setCrossOrigin: function ( value ) { +this.crossOrigin = value; +}, +setPath: function ( value ) { +this.path = value; +}, +parse: function ( data, callback, path ) { +var content; +var extensions = {}; +var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); +if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { +extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); +content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; +} else { +content = convertUint8ArrayToString( new Uint8Array( data ) ); +} +var json = JSON.parse( content ); +if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { +extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); +} +console.time( 'GLTFLoader' ); +var parser = new GLTFParser( json, extensions, { +path: path || this.path, +crossOrigin: this.crossOrigin +} ); +parser.parse( function ( scene, scenes, cameras, animations ) { +console.timeEnd( 'GLTFLoader' ); +var glTF = { +"scene": scene, +"scenes": scenes, +"cameras": cameras, +"animations": animations +}; +callback( glTF ); +} ); +} +}; +function GLTFRegistry() { +var objects = {}; +return { +get: function ( key ) { +return objects[ key ]; +}, +add: function ( key, object ) { +objects[ key ] = object; +}, +remove: function ( key ) { +delete objects[ key ]; +}, +removeAll: function () { +objects = {}; +}, +update: function ( scene, camera ) { +for ( var name in objects ) { +var object = objects[ name ]; +if ( object.update ) { +object.update( scene, camera ); +} +} +} +}; +} +GLTFLoader.Shaders = { +update: function () { +console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); +} +}; +function GLTFShader( targetNode, allNodes ) { +var boundUniforms = {}; +var uniforms = targetNode.material.uniforms; +for ( var uniformId in uniforms ) { +var uniform = uniforms[ uniformId ]; +if ( uniform.semantic ) { +var sourceNodeRef = uniform.node; +var sourceNode = targetNode; +if ( sourceNodeRef ) { +sourceNode = allNodes[ sourceNodeRef ]; +} +boundUniforms[ uniformId ] = { +semantic: uniform.semantic, +sourceNode: sourceNode, +targetNode: targetNode, +uniform: uniform +}; +} +} +this.boundUniforms = boundUniforms; +this._m4 = new THREE.Matrix4(); +} +GLTFShader.prototype.update = function ( scene, camera ) { +var boundUniforms = this.boundUniforms; +for ( var name in boundUniforms ) { +var boundUniform = boundUniforms[ name ]; +switch ( boundUniform.semantic ) { +case "MODELVIEW": +var m4 = boundUniform.uniform.value; +m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +break; +case "MODELVIEWINVERSETRANSPOSE": +var m3 = boundUniform.uniform.value; +this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +m3.getNormalMatrix( this._m4 ); +break; +case "PROJECTION": +var m4 = boundUniform.uniform.value; +m4.copy( camera.projectionMatrix ); +break; +case "JOINTMATRIX": +var m4v = boundUniform.uniform.value; +for ( var mi = 0; mi < m4v.length; mi ++ ) { +m4v[ mi ] +.getInverse( boundUniform.sourceNode.matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) +.multiply( boundUniform.targetNode.bindMatrix ); +} +break; +default : +console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); +break; +} +} +}; +GLTFLoader.Animations = { +update: function () { +console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); +} +}; +var EXTENSIONS = { +KHR_BINARY_GLTF: 'KHR_binary_glTF', +KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; +function GLTFMaterialsCommonExtension( json ) { +this.name = EXTENSIONS.KHR_MATERIALS_COMMON; +this.lights = {}; +var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; +var lights = extension.lights || {}; +for ( var lightId in lights ) { +var light = lights[ lightId ]; +var lightNode; +var lightParams = light[ light.type ]; +var color = new THREE.Color().fromArray( lightParams.color ); +switch ( light.type ) { +case "directional": +lightNode = new THREE.DirectionalLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "point": +lightNode = new THREE.PointLight( color ); +break; +case "spot": +lightNode = new THREE.SpotLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "ambient": +lightNode = new THREE.AmbientLight( color ); +break; +} +if ( lightNode ) { +this.lights[ lightId ] = lightNode; +} +} +} +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; +var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; +var BINARY_EXTENSION_HEADER_LENGTH = 20; +function GLTFBinaryExtension( data ) { +this.name = EXTENSIONS.KHR_BINARY_GLTF; +var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); +var header = { +magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), +version: headerView.getUint32( 4, true ), +length: headerView.getUint32( 8, true ), +contentLength: headerView.getUint32( 12, true ), +contentFormat: headerView.getUint32( 16, true ) +}; +for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { +var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; +if ( header[ key ] !== value ) { +throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); +} +} +var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); +this.header = header; +this.content = convertUint8ArrayToString( contentArray ); +this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); +} +GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { +var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; +var array = new Uint8Array( bufferView ); +return convertUint8ArrayToString( array ); +}; +GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) { +var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; +var bufferView = bufferViews[ metadata.bufferView ]; +var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) ); +return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData ); +}; +var WEBGL_CONSTANTS = { +FLOAT: 5126, +FLOAT_MAT3: 35675, +FLOAT_MAT4: 35676, +FLOAT_VEC2: 35664, +FLOAT_VEC3: 35665, +FLOAT_VEC4: 35666, +LINEAR: 9729, +REPEAT: 10497, +SAMPLER_2D: 35678, +TRIANGLES: 4, +LINES: 1, +UNSIGNED_BYTE: 5121, +UNSIGNED_SHORT: 5123, +VERTEX_SHADER: 35633, +FRAGMENT_SHADER: 35632 +}; +var WEBGL_TYPE = { +5126: Number, +35675: THREE.Matrix3, +35676: THREE.Matrix4, +35664: THREE.Vector2, +35665: THREE.Vector3, +35666: THREE.Vector4, +35678: THREE.Texture +}; +var WEBGL_COMPONENT_TYPES = { +5120: Int8Array, +5121: Uint8Array, +5122: Int16Array, +5123: Uint16Array, +5125: Uint32Array, +5126: Float32Array +}; +var WEBGL_FILTERS = { +9728: THREE.NearestFilter, +9729: THREE.LinearFilter, +9984: THREE.NearestMipMapNearestFilter, +9985: THREE.LinearMipMapNearestFilter, +9986: THREE.NearestMipMapLinearFilter, +9987: THREE.LinearMipMapLinearFilter +}; +var WEBGL_WRAPPINGS = { +33071: THREE.ClampToEdgeWrapping, +33648: THREE.MirroredRepeatWrapping, +10497: THREE.RepeatWrapping +}; +var WEBGL_TEXTURE_FORMATS = { +6406: THREE.AlphaFormat, +6407: THREE.RGBFormat, +6408: THREE.RGBAFormat, +6409: THREE.LuminanceFormat, +6410: THREE.LuminanceAlphaFormat +}; +var WEBGL_TEXTURE_DATATYPES = { +5121: THREE.UnsignedByteType, +32819: THREE.UnsignedShort4444Type, +32820: THREE.UnsignedShort5551Type, +33635: THREE.UnsignedShort565Type +}; +var WEBGL_SIDES = { +1028: THREE.BackSide, // Culling front +1029: THREE.FrontSide // Culling back +}; +var WEBGL_DEPTH_FUNCS = { +512: THREE.NeverDepth, +513: THREE.LessDepth, +514: THREE.EqualDepth, +515: THREE.LessEqualDepth, +516: THREE.GreaterEqualDepth, +517: THREE.NotEqualDepth, +518: THREE.GreaterEqualDepth, +519: THREE.AlwaysDepth +}; +var WEBGL_BLEND_EQUATIONS = { +32774: THREE.AddEquation, +32778: THREE.SubtractEquation, +32779: THREE.ReverseSubtractEquation +}; +var WEBGL_BLEND_FUNCS = { +0: THREE.ZeroFactor, +1: THREE.OneFactor, +768: THREE.SrcColorFactor, +769: THREE.OneMinusSrcColorFactor, +770: THREE.SrcAlphaFactor, +771: THREE.OneMinusSrcAlphaFactor, +772: THREE.DstAlphaFactor, +773: THREE.OneMinusDstAlphaFactor, +774: THREE.DstColorFactor, +775: THREE.OneMinusDstColorFactor, +776: THREE.SrcAlphaSaturateFactor +}; +var WEBGL_TYPE_SIZES = { +'SCALAR': 1, +'VEC2': 2, +'VEC3': 3, +'VEC4': 4, +'MAT2': 4, +'MAT3': 9, +'MAT4': 16 +}; +var PATH_PROPERTIES = { +scale: 'scale', +translation: 'position', +rotation: 'quaternion' +}; +var INTERPOLATION = { +LINEAR: THREE.InterpolateLinear, +STEP: THREE.InterpolateDiscrete +}; +var STATES_ENABLES = { +2884: 'CULL_FACE', +2929: 'DEPTH_TEST', +3042: 'BLEND', +3089: 'SCISSOR_TEST', +32823: 'POLYGON_OFFSET_FILL', +32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; +function _each( object, callback, thisObj ) { +if ( !object ) { +return Promise.resolve(); +} +var results; +var fns = []; +if ( Object.prototype.toString.call( object ) === '[object Array]' ) { +results = []; +var length = object.length; +for ( var idx = 0; idx < length; idx ++ ) { +var value = callback.call( thisObj || this, object[ idx ], idx ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, idx )); +} else { +results[ idx ] = value; +} +} +} +} else { +results = {}; +for ( var key in object ) { +if ( object.hasOwnProperty( key ) ) { +var value = callback.call( thisObj || this, object[ key ], key ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, key )); +} else { +results[ key ] = value; +} +} +} +} +} +return Promise.all( fns ).then( function() { +return results; +}); +} +function resolveURL( url, path ) { +if ( typeof url !== 'string' || url === '' ) +return ''; +if ( /^(https?:)?\/\//i.test( url ) ) { +return url; +} +if ( /^data:.*,.*$/i.test( url ) ) { +return url; +} +return ( path || '' ) + url; +} +function convertUint8ArrayToString( array ) { +var s = ''; +for ( var i = 0; i < array.length; i ++ ) { +s += String.fromCharCode( array[ i ] ); +} +return s; +} +function replaceTHREEShaderAttributes( shaderText, technique ) { +var attributes = {}; +for ( var attributeId in technique.attributes ) { +var pname = technique.attributes[ attributeId ]; +var param = technique.parameters[ pname ]; +var atype = param.type; +var semantic = param.semantic; +attributes[ attributeId ] = { +type: atype, +semantic: semantic +}; +} +var shaderParams = technique.parameters; +var shaderAttributes = technique.attributes; +var params = {}; +for ( var attributeId in attributes ) { +var pname = shaderAttributes[ attributeId ]; +var shaderParam = shaderParams[ pname ]; +var semantic = shaderParam.semantic; +if ( semantic ) { +params[ attributeId ] = shaderParam; +} +} +for ( var pname in params ) { +var param = params[ pname ]; +var semantic = param.semantic; +var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); +switch ( semantic ) { +case "POSITION": +shaderText = shaderText.replace( regEx, 'position' ); +break; +case "NORMAL": +shaderText = shaderText.replace( regEx, 'normal' ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +shaderText = shaderText.replace( regEx, 'uv' ); +break; +case 'TEXCOORD_1': +shaderText = shaderText.replace( regEx, 'uv2' ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +shaderText = shaderText.replace( regEx, 'color' ); +break; +case "WEIGHT": +shaderText = shaderText.replace( regEx, 'skinWeight' ); +break; +case "JOINT": +shaderText = shaderText.replace( regEx, 'skinIndex' ); +break; +} +} +return shaderText; +} +function createDefaultMaterial() { +return new THREE.MeshPhongMaterial( { +color: 0x00000, +emissive: 0x888888, +specular: 0x000000, +shininess: 0, +transparent: false, +depthTest: true, +side: THREE.FrontSide +} ); +} +function DeferredShaderMaterial( params ) { +this.isDeferredShaderMaterial = true; +this.params = params; +} +DeferredShaderMaterial.prototype.create = function () { +var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); +for ( var uniformId in this.params.uniforms ) { +var originalUniform = this.params.uniforms[ uniformId ]; +if ( originalUniform.value instanceof THREE.Texture ) { +uniforms[ uniformId ].value = originalUniform.value; +uniforms[ uniformId ].value.needsUpdate = true; +} +uniforms[ uniformId ].semantic = originalUniform.semantic; +uniforms[ uniformId ].node = originalUniform.node; +} +this.params.uniforms = uniforms; +return new THREE.RawShaderMaterial( this.params ); +}; +function GLTFParser( json, extensions, options ) { +this.json = json || {}; +this.extensions = extensions || {}; +this.options = options || {}; +this.cache = new GLTFRegistry(); +} +GLTFParser.prototype._withDependencies = function ( dependencies ) { +var _dependencies = {}; +for ( var i = 0; i < dependencies.length; i ++ ) { +var dependency = dependencies[ i ]; +var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); +var cached = this.cache.get( dependency ); +if ( cached !== undefined ) { +_dependencies[ dependency ] = cached; +} else if ( this[ fnName ] ) { +var fn = this[ fnName ](); +this.cache.add( dependency, fn ); +_dependencies[ dependency ] = fn; +} +} +return _each( _dependencies, function ( dependency ) { +return dependency; +} ); +}; +GLTFParser.prototype.parse = function ( callback ) { +var json = this.json; +this.cache.removeAll(); +this._withDependencies( [ +"scenes", +"cameras", +"animations" +] ).then( function ( dependencies ) { +var scenes = []; +for ( var name in dependencies.scenes ) { +scenes.push( dependencies.scenes[ name ] ); +} +var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; +var cameras = []; +for ( var name in dependencies.cameras ) { +var camera = dependencies.cameras[ name ]; +cameras.push( camera ); +} +var animations = []; +for ( var name in dependencies.animations ) { +animations.push( dependencies.animations[ name ] ); +} +callback( scene, scenes, cameras, animations ); +} ); +}; +GLTFParser.prototype.loadShaders = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.shaders, function ( shader ) { +if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'text' ); +loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { +resolve( shaderText ); +} ); +} ); +} ); +} ); +}; +GLTFParser.prototype.loadBuffers = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return _each( json.buffers, function ( buffer, name ) { +if ( name === BINARY_EXTENSION_BUFFER_NAME ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; +} +if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'arraybuffer' ); +loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { +resolve( buffer ); +} ); +} ); +} else { +console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); +} +} ); +}; +GLTFParser.prototype.loadBufferViews = function () { +var json = this.json; +return this._withDependencies( [ +"buffers" +] ).then( function ( dependencies ) { +return _each( json.bufferViews, function ( bufferView ) { +var arraybuffer = dependencies.buffers[ bufferView.buffer ]; +var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; +return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); +} ); +} ); +}; +GLTFParser.prototype.loadAccessors = function () { +var json = this.json; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.accessors, function ( accessor ) { +var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; +var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; +var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; +var elementBytes = TypedArray.BYTES_PER_ELEMENT; +var itemBytes = elementBytes * itemSize; +if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { +var array = new TypedArray( arraybuffer ); +var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); +return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); +} else { +array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); +return new THREE.BufferAttribute( array, itemSize ); +} +} ); +} ); +}; +GLTFParser.prototype.loadTextures = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.textures, function ( texture ) { +if ( texture.source ) { +return new Promise( function ( resolve ) { +var source = json.images[ texture.source ]; +var sourceUri = source.uri; +if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { +sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); +} +var textureLoader = THREE.Loader.Handlers.get( sourceUri ); +if ( textureLoader === null ) { +textureLoader = new THREE.TextureLoader(); +} +textureLoader.setCrossOrigin( options.crossOrigin ); +textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { +_texture.flipY = false; +if ( texture.name !== undefined ) _texture.name = texture.name; +_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; +if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { +console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + +'internalFormat will be forced to be the same value as format.' ); +} +_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; +if ( texture.sampler ) { +var sampler = json.samplers[ texture.sampler ]; +_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; +_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; +_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; +_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; +} +resolve( _texture ); +}, undefined, function () { +resolve(); +} ); +} ); +} +} ); +} ); +}; +GLTFParser.prototype.loadMaterials = function () { +var json = this.json; +return this._withDependencies( [ +"shaders", +"textures" +] ).then( function ( dependencies ) { +return _each( json.materials, function ( material ) { +var materialType; +var materialValues = {}; +var materialParams = {}; +var khr_material; +if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { +khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; +} +if ( khr_material ) { +var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; +switch ( khr_material.technique ) { +case 'BLINN' : +case 'PHONG' : +materialType = THREE.MeshPhongMaterial; +keys.push( 'diffuse', 'specular', 'shininess' ); +break; +case 'LAMBERT' : +materialType = THREE.MeshLambertMaterial; +keys.push( 'diffuse' ); +break; +case 'CONSTANT' : +default : +materialType = THREE.MeshBasicMaterial; +break; +} +keys.forEach( function( v ) { +if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; +} ); +if ( khr_material.doubleSided || materialValues.doubleSided ) { +materialParams.side = THREE.DoubleSide; +} +if ( khr_material.transparent || materialValues.transparent ) { +materialParams.transparent = true; +materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; +} +} else if ( material.technique === undefined ) { +materialType = THREE.MeshPhongMaterial; +Object.assign( materialValues, material.values ); +} else { +materialType = DeferredShaderMaterial; +var technique = json.techniques[ material.technique ]; +materialParams.uniforms = {}; +var program = json.programs[ technique.program ]; +if ( program ) { +materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; +if ( ! materialParams.fragmentShader ) { +console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); +materialType = THREE.MeshPhongMaterial; +} +var vertexShader = dependencies.shaders[ program.vertexShader ]; +if ( ! vertexShader ) { +console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); +materialType = THREE.MeshPhongMaterial; +} +materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); +var uniforms = technique.uniforms; +for ( var uniformId in uniforms ) { +var pname = uniforms[ uniformId ]; +var shaderParam = technique.parameters[ pname ]; +var ptype = shaderParam.type; +if ( WEBGL_TYPE[ ptype ] ) { +var pcount = shaderParam.count; +var value; +if ( material.values !== undefined ) value = material.values[ pname ]; +var uvalue = new WEBGL_TYPE[ ptype ](); +var usemantic = shaderParam.semantic; +var unode = shaderParam.node; +switch ( ptype ) { +case WEBGL_CONSTANTS.FLOAT: +uvalue = shaderParam.value; +if ( pname == "transparency" ) { +materialParams.transparent = true; +} +if ( value !== undefined ) { +uvalue = value; +} +break; +case WEBGL_CONSTANTS.FLOAT_VEC2: +case WEBGL_CONSTANTS.FLOAT_VEC3: +case WEBGL_CONSTANTS.FLOAT_VEC4: +case WEBGL_CONSTANTS.FLOAT_MAT3: +if ( shaderParam && shaderParam.value ) { +uvalue.fromArray( shaderParam.value ); +} +if ( value ) { +uvalue.fromArray( value ); +} +break; +case WEBGL_CONSTANTS.FLOAT_MAT2: +console.warn( "FLOAT_MAT2 is not a supported uniform type" ); +break; +case WEBGL_CONSTANTS.FLOAT_MAT4: +if ( pcount ) { +uvalue = new Array( pcount ); +for ( var mi = 0; mi < pcount; mi ++ ) { +uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); +} +if ( shaderParam && shaderParam.value ) { +var m4v = shaderParam.value; +uvalue.fromArray( m4v ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} else { +if ( shaderParam && shaderParam.value ) { +var m4 = shaderParam.value; +uvalue.fromArray( m4 ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} +break; +case WEBGL_CONSTANTS.SAMPLER_2D: +if ( value !== undefined ) { +uvalue = dependencies.textures[ value ]; +} else if ( shaderParam.value !== undefined ) { +uvalue = dependencies.textures[ shaderParam.value ]; +} else { +uvalue = null; +} +break; +} +materialParams.uniforms[ uniformId ] = { +value: uvalue, +semantic: usemantic, +node: unode +}; +} else { +throw new Error( "Unknown shader uniform param type: " + ptype ); +} +} +var states = technique.states || {}; +var enables = states.enable || []; +var functions = states.functions || {}; +var enableCullFace = false; +var enableDepthTest = false; +var enableBlend = false; +for ( var i = 0, il = enables.length; i < il; i ++ ) { +var enable = enables[ i ]; +switch ( STATES_ENABLES[ enable ] ) { +case 'CULL_FACE': +enableCullFace = true; +break; +case 'DEPTH_TEST': +enableDepthTest = true; +break; +case 'BLEND': +enableBlend = true; +break; +case 'SCISSOR_TEST': +case 'POLYGON_OFFSET_FILL': +case 'SAMPLE_ALPHA_TO_COVERAGE': +break; +default: +throw new Error( "Unknown technique.states.enable: " + enable ); +} +} +if ( enableCullFace ) { +materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; +} else { +materialParams.side = THREE.DoubleSide; +} +materialParams.depthTest = enableDepthTest; +materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; +materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; +materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; +materialParams.transparent = enableBlend; +var blendEquationSeparate = functions.blendEquationSeparate; +if ( blendEquationSeparate !== undefined ) { +materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; +materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; +} else { +materialParams.blendEquation = THREE.AddEquation; +materialParams.blendEquationAlpha = THREE.AddEquation; +} +var blendFuncSeparate = functions.blendFuncSeparate; +if ( blendFuncSeparate !== undefined ) { +materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; +materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; +materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; +materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; +} else { +materialParams.blendSrc = THREE.OneFactor; +materialParams.blendDst = THREE.ZeroFactor; +materialParams.blendSrcAlpha = THREE.OneFactor; +materialParams.blendDstAlpha = THREE.ZeroFactor; +} +} +} +if ( Array.isArray( materialValues.diffuse ) ) { +materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); +} else if ( typeof( materialValues.diffuse ) === 'string' ) { +materialParams.map = dependencies.textures[ materialValues.diffuse ]; +} +delete materialParams.diffuse; +if ( typeof( materialValues.reflective ) === 'string' ) { +materialParams.envMap = dependencies.textures[ materialValues.reflective ]; +} +if ( typeof( materialValues.bump ) === 'string' ) { +materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; +} +if ( Array.isArray( materialValues.emission ) ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.color = new THREE.Color().fromArray( materialValues.emission ); +} else { +materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); +} +} else if ( typeof( materialValues.emission ) === 'string' ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.map = dependencies.textures[ materialValues.emission ]; +} else { +materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; +} +} +if ( Array.isArray( materialValues.specular ) ) { +materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); +} else if ( typeof( materialValues.specular ) === 'string' ) { +materialParams.specularMap = dependencies.textures[ materialValues.specular ]; +} +if ( materialValues.shininess !== undefined ) { +materialParams.shininess = materialValues.shininess; +} +var _material = new materialType( materialParams ); +if ( material.name !== undefined ) _material.name = material.name; +return _material; +} ); +} ); +}; +GLTFParser.prototype.loadMeshes = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"materials" +] ).then( function ( dependencies ) { +return _each( json.meshes, function ( mesh ) { +var group = new THREE.Group(); +if ( mesh.name !== undefined ) group.name = mesh.name; +if ( mesh.extras ) group.userData = mesh.extras; +var primitives = mesh.primitives || []; +for ( var name in primitives ) { +var primitive = primitives[ name ]; +if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { +var geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'NORMAL': +geometry.addAttribute( 'normal', bufferAttribute ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +geometry.addAttribute( 'uv', bufferAttribute ); +break; +case 'TEXCOORD_1': +geometry.addAttribute( 'uv2', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +case 'WEIGHT': +geometry.addAttribute( 'skinWeight', bufferAttribute ); +break; +case 'JOINT': +geometry.addAttribute( 'skinIndex', bufferAttribute ); +break; +} +} +if ( primitive.indices ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +} +var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); +var meshNode = new THREE.Mesh( geometry, material ); +meshNode.castShadow = true; +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { +var geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +} +} +var material = dependencies.materials[ primitive.material ]; +var meshNode; +if ( primitive.indices ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +meshNode = new THREE.LineSegments( geometry, material ); +} else { +meshNode = new THREE.Line( geometry, material ); +} +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} else { +console.warn( "Only triangular and line primitives are supported" ); +} +} +return group; +} ); +} ); +}; +GLTFParser.prototype.loadCameras = function () { +var json = this.json; +return _each( json.cameras, function ( camera ) { +if ( camera.type == "perspective" && camera.perspective ) { +var yfov = camera.perspective.yfov; +var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; +var xfov = yfov * aspectRatio; +var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} else if ( camera.type == "orthographic" && camera.orthographic ) { +var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} +} ); +}; +GLTFParser.prototype.loadSkins = function () { +var json = this.json; +return this._withDependencies( [ +"accessors" +] ).then( function ( dependencies ) { +return _each( json.skins, function ( skin ) { +var bindShapeMatrix = new THREE.Matrix4(); +if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); +var _skin = { +bindShapeMatrix: bindShapeMatrix, +jointNames: skin.jointNames, +inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] +}; +return _skin; +} ); +} ); +}; +GLTFParser.prototype.loadAnimations = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"nodes" +] ).then( function ( dependencies ) { +return _each( json.animations, function ( animation, animationId ) { +var tracks = []; +for ( var channelId in animation.channels ) { +var channel = animation.channels[ channelId ]; +var sampler = animation.samplers[ channel.sampler ]; +if ( sampler ) { +var target = channel.target; +var name = target.id; +var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; +var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; +var inputAccessor = dependencies.accessors[ input ]; +var outputAccessor = dependencies.accessors[ output ]; +var node = dependencies.nodes[ name ]; +if ( node ) { +node.updateMatrix(); +node.matrixAutoUpdate = true; +var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation +? THREE.QuaternionKeyframeTrack +: THREE.VectorKeyframeTrack; +var targetName = node.name ? node.name : node.uuid; +var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; +tracks.push( new TypedKeyframeTrack( +targetName + '.' + PATH_PROPERTIES[ target.path ], +THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), +THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), +interpolation +) ); +} +} +} +var name = animation.name !== undefined ? animation.name : "animation_" + animationId; +return new THREE.AnimationClip( name, undefined, tracks ); +} ); +} ); +}; +GLTFParser.prototype.loadNodes = function () { +var json = this.json; +var extensions = this.extensions; +var scope = this; +return _each( json.nodes, function ( node ) { +var matrix = new THREE.Matrix4(); +var _node; +if ( node.jointName ) { +_node = new THREE.Bone(); +_node.name = node.name !== undefined ? node.name : node.jointName; +_node.jointName = node.jointName; +} else { +_node = new THREE.Object3D(); +if ( node.name !== undefined ) _node.name = node.name; +} +if ( node.extras ) _node.userData = node.extras; +if ( node.matrix !== undefined ) { +matrix.fromArray( node.matrix ); +_node.applyMatrix( matrix ); +} else { +if ( node.translation !== undefined ) { +_node.position.fromArray( node.translation ); +} +if ( node.rotation !== undefined ) { +_node.quaternion.fromArray( node.rotation ); +} +if ( node.scale !== undefined ) { +_node.scale.fromArray( node.scale ); +} +} +return _node; +} ).then( function ( __nodes ) { +return scope._withDependencies( [ +"meshes", +"skins", +"cameras" +] ).then( function ( dependencies ) { +return _each( __nodes, function ( _node, nodeId ) { +var node = json.nodes[ nodeId ]; +if ( node.meshes !== undefined ) { +for ( var meshId in node.meshes ) { +var mesh = node.meshes[ meshId ]; +var group = dependencies.meshes[ mesh ]; +if ( group === undefined ) { +console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); +continue; +} +for ( var childrenId in group.children ) { +var child = group.children[ childrenId ]; +var originalMaterial = child.material; +var originalGeometry = child.geometry; +var originalUserData = child.userData; +var originalName = child.name; +var material; +if ( originalMaterial.isDeferredShaderMaterial ) { +originalMaterial = material = originalMaterial.create(); +} else { +material = originalMaterial; +} +switch ( child.type ) { +case 'LineSegments': +child = new THREE.LineSegments( originalGeometry, material ); +break; +case 'LineLoop': +child = new THREE.LineLoop( originalGeometry, material ); +break; +case 'Line': +child = new THREE.Line( originalGeometry, material ); +break; +default: +child = new THREE.Mesh( originalGeometry, material ); +} +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var skinEntry; +if ( node.skin ) { +skinEntry = dependencies.skins[ node.skin ]; +} +if ( skinEntry ) { +var getJointNode = function ( jointId ) { +var keys = Object.keys( __nodes ); +for ( var i = 0, il = keys.length; i < il; i ++ ) { +var n = __nodes[ keys[ i ] ]; +if ( n.jointName === jointId ) return n; +} +return null; +}; +var geometry = originalGeometry; +var material = originalMaterial; +material.skinning = true; +child = new THREE.SkinnedMesh( geometry, material, false ); +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var bones = []; +var boneInverses = []; +for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { +var jointId = skinEntry.jointNames[ i ]; +var jointNode = getJointNode( jointId ); +if ( jointNode ) { +bones.push( jointNode ); +var m = skinEntry.inverseBindMatrices.array; +var mat = new THREE.Matrix4().fromArray( m, i * 16 ); +boneInverses.push( mat ); +} else { +console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); +} +} +child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); +var buildBoneGraph = function ( parentJson, parentObject, property ) { +var children = parentJson[ property ]; +if ( children === undefined ) return; +for ( var i = 0, il = children.length; i < il; i ++ ) { +var nodeId = children[ i ]; +var bone = __nodes[ nodeId ]; +var boneJson = json.nodes[ nodeId ]; +if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { +parentObject.add( bone ); +buildBoneGraph( boneJson, bone, 'children' ); +} +} +}; +buildBoneGraph( node, child, 'skeletons' ); +} +_node.add( child ); +} +} +} +if ( node.camera !== undefined ) { +var camera = dependencies.cameras[ node.camera ]; +_node.add( camera ); +} +if ( node.extensions +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { +var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; +var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; +_node.add( light ); +} +return _node; +} ); +} ); +} ); +}; +GLTFParser.prototype.loadScenes = function () { +var json = this.json; +function buildNodeHierachy( nodeId, parentObject, allNodes ) { +var _node = allNodes[ nodeId ]; +parentObject.add( _node ); +var node = json.nodes[ nodeId ]; +if ( node.children ) { +var children = node.children; +for ( var i = 0, l = children.length; i < l; i ++ ) { +var child = children[ i ]; +buildNodeHierachy( child, _node, allNodes ); +} +} +} +return this._withDependencies( [ +"nodes" +] ).then( function ( dependencies ) { +return _each( json.scenes, function ( scene ) { +var _scene = new THREE.Scene(); +if ( scene.name !== undefined ) _scene.name = scene.name; +if ( scene.extras ) _scene.userData = scene.extras; +var nodes = scene.nodes || []; +for ( var i = 0, l = nodes.length; i < l; i ++ ) { +var nodeId = nodes[ i ]; +buildNodeHierachy( nodeId, _scene, dependencies.nodes ); +} +_scene.traverse( function ( child ) { +if ( child.material && child.material.isRawShaderMaterial ) { +child.gltfShader = new GLTFShader( child, dependencies.nodes ); +child.onBeforeRender = function(renderer, scene, camera){ +this.gltfShader.update(scene, camera); +}; +} +} ); +return _scene; +} ); +} ); +}; +return GLTFLoader; +} )(); +"use strict"; +var JSONParser = function() +{ +} +JSONParser.prototype.constructor = JSONParser; +JSONParser.prototype.parseJavaScript = function(jsobj) { +var child = this.CreateElement('scene'); +this.ConvertToX3DOM(jsobj, "", child); +console.log(jsobj, child); +return child; +}; +JSONParser.prototype.elementSetAttribute = function(element, key, value) { +if (key === 'SON schema') { +} else if (key === 'ncoding') { +} else { +if (typeof element.setAttribute === 'function') { +element.setAttribute(key, value); +} +} +}; +JSONParser.prototype.ConvertChildren = function(parentkey, object, element) { +var key; +for (key in object) { +if (typeof object[key] === 'object') { +if (isNaN(parseInt(key))) { +this.ConvertObject(key, object, element, parentkey.substr(1)); +} else { +this.ConvertToX3DOM(object[key], key, element, parentkey.substr(1)); +} +} +} +}; +JSONParser.prototype.CreateElement = function(key, containerField) { +var child = document.createElement(key); +if (typeof containerField !== 'undefined') { +this.elementSetAttribute(child, 'containerField', containerField); +} +return child; +}; +JSONParser.prototype.CDATACreateFunction = function(document, element, str) { +var y = str.replace(/\\"/g, "\\\"") +.replace(/</g, "<") +.replace(/>/g, ">") +.replace(/&/g, "&"); +do { +str = y; +y = str.replace(/'([^'\r\n]*)\n([^']*)'/g, "'$1\\n$2'"); +if (str !== y) { +console.log("CDATA Replacing",str,"with",y); +} +} while (y != str); +var domParser = new DOMParser(); +var cdataStr = ''; // has to be wrapped into an element +var scriptDoc = domParser .parseFromString (cdataStr, 'application/xml'); +var cdata = scriptDoc .children[0] .childNodes[1]; // space after script is childNode[0] +element .appendChild(cdata); +}; +JSONParser.prototype.ConvertObject = function(key, object, element, containerField) { +var child; +if (object !== null && typeof object[key] === 'object') { +if (key.substr(0,1) === '@') { +this.ConvertToX3DOM(object[key], key, element); +} else if (key.substr(0,1) === '-') { +this.ConvertChildren(key, object[key], element); +} else if (key === '#comment') { +for (var c in object[key]) { +child = document.createComment(this.CommentStringToXML(object[key][c])); +element.appendChild(child); +} +} else if (key === '#text') { +child = document.createTextNode(object[key].join("")); +element.appendChild(child); +} else if (key === '#sourceText') { +this.CDATACreateFunction(document, element, object[key].join("\r\n")+"\r\n"); +} else { +if (key === 'connect' || key === 'fieldValue' || key === 'field' || key === 'meta' || key === 'component') { +for (var childkey in object[key]) { // for each field +if (typeof object[key][childkey] === 'object') { +child = this.CreateElement(key, containerField); +this.ConvertToX3DOM(object[key][childkey], childkey, child); +element.appendChild(child); +element.appendChild(document.createTextNode("\n")); +} +} +} else { +child = this.CreateElement(key, containerField); +this.ConvertToX3DOM(object[key], key, child); +element.appendChild(child); +element.appendChild(document.createTextNode("\n")); +} +} +} +}; +JSONParser.prototype.CommentStringToXML = function(str) { +var y = str; +str = str.replace(/\\\\/g, '\\'); +if (y !== str) { +console.log("X3DJSONLD replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.SFStringToXML = function(str) { +var y = str; +str = str.replace(/\\/g, '\\\\'); +str = str.replace(/"/g, '\\\"'); +if (y !== str) { +console.log("X3DJSONLD [] replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.JSONStringToXML = function(str) { +var y = str; +str = str.replace(/\\/g, '\\\\'); +str = str.replace(/\n/g, '\\n'); +if (y !== str) { +console.log("X3DJSONLD replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.ConvertToX3DOM = function(object, parentkey, element, containerField) { +var key; +var localArray = []; +var isArray = false; +var arrayOfStrings = false; +for (key in object) { +if (isNaN(parseInt(key))) { +isArray = false; +} else { +isArray = true; +} +if (isArray) { +if (typeof object[key] === 'number') { +localArray.push(object[key]); +} else if (typeof object[key] === 'string') { +localArray.push(object[key]); +arrayOfStrings = true; +} else if (typeof object[key] === 'boolean') { +localArray.push(object[key]); +} else if (typeof object[key] === 'object') { +this.ConvertToX3DOM(object[key], key, element); +} else if (typeof object[key] === 'undefined') { +} else { +console.error("Unknown type found in array "+typeof object[key]); +} +} else if (typeof object[key] === 'object') { +if (key === 'scene') { +this.ConvertToX3DOM(object[key], key, element); +} else { +this.ConvertObject(key, object, element); +} +} else if (typeof object[key] === 'number') { +this.elementSetAttribute(element, key.substr(1),object[key]); +} else if (typeof object[key] === 'string') { +if (key === '#comment') { +var child = document.createComment(this.CommentStringToXML(object[key])); +element.appendChild(child); +} else if (key === '#text') { +var child = document.createTextNode(object[key]); +element.appendChild(child); +} else { +this.elementSetAttribute(element, key.substr(1), this.JSONStringToXML(object[key])); +} +} else if (typeof object[key] === 'boolean') { +this.elementSetAttribute(element, key.substr(1),object[key]); +} else if (typeof object[key] === 'undefined') { +} else { +console.error("Unknown type found in object "+typeof object[key]); +console.error(object); +} +} +if (isArray) { +if (parentkey.substr(0,1) === '@') { +if (arrayOfStrings) { +arrayOfStrings = false; +for (var str in localArray) { +localArray[str] = this.SFStringToXML(localArray[str]); +} +this.elementSetAttribute(element, parentkey.substr(1),'"'+localArray.join('" "')+'"'); +} else { +this.elementSetAttribute(element, parentkey.substr(1),localArray.join(" ")); +} +} +isArray = false; +} +return element; +}; +function LoadManager () { +this.urlQueue = []; +this.urlNext = -1; +this.MaxRequests = 3; +this.totalRequests = 0; +this.totalResponses = 0; +this.requestCount = 0; +var lmThat = this; +this.load = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadText = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'text', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadHtml = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'html', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadXml = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'xml', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadJson = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'json', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadImage = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'image', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.success = function (response, string, xhr) { +if (typeof(xhr._loadManager.success) !== undefined) { +xhr._loadManager.success (response, xhr._loadManager.userdata, xhr); +} +} +this.failure = function (xhr, errorCode, errorText) { +if (typeof(xhr._loadManager.failure) !== undefined) { +xhr._loadManager.failure (xhr, xhr._loadManager.userdata, errorCode, errorText); +} +} +this.requestComplete = function (event, xhr, settings) { +lmThat.requestCount --; +lmThat.totalResponses++; +lmThat.loadNextUrl(); +} +this.loadNextUrl = function () { +if (this.requestCount >= this.MaxRequests) {return; } +if (this.urlNext >= this.urlQueue.length || this.urlNext < 0) { +this.urlNext = -1; +for (var i=0; i 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.CODE_SLASH: +slashes[ slashesPointer++ ] = i; +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.CODE_LF: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); +slashesPointer = 0; +bufferPointer = 0; +break; +case Consts.CODE_CR: +break; +default: +word += String.fromCharCode( code ); +break; +} +} +}; +Parser.prototype.parseText = function ( text ) { +var length = text.length; +var buffer = new Array( 32 ); +var bufferPointer = 0; +var slashes = new Array( 32 ); +var slashesPointer = 0; +var reachedFaces = false; +var char; +var word = ''; +for ( var i = 0; i < length; i++ ) { +char = text[ i ]; +switch ( char ) { +case Consts.STRING_SPACE: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.STRING_SLASH: +slashes[ slashesPointer++ ] = i; +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.STRING_LF: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); +slashesPointer = 0; +bufferPointer = 0; +break; +case Consts.STRING_CR: +break; +default: +word += char; +} +} +}; +Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) { +if ( bufferPointer < 1 ) return reachedFaces; +var bufferLength = bufferPointer - 1; +switch ( buffer[ 0 ] ) { +case Consts.LINE_V: +if ( reachedFaces ) { +this.processCompletedObject( null, this.rawObject.groupName ); +reachedFaces = false; +} +this.rawObject.pushVertex( buffer ); +break; +case Consts.LINE_VT: +this.rawObject.pushUv( buffer ); +break; +case Consts.LINE_VN: +this.rawObject.pushNormal( buffer ); +break; +case Consts.LINE_F: +reachedFaces = true; +var haveQuad = bufferLength % 4 === 0; +if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVn( buffer ); +} else { +this.rawObject.buildFaceVVn( buffer ); +} +} else if ( bufferLength === slashesPointer * 2 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVt( buffer ); +} else { +this.rawObject.buildFaceVVt( buffer ); +} +} else if ( bufferLength * 2 === slashesPointer * 3 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVtVn( buffer ); +} else { +this.rawObject.buildFaceVVtVn( buffer ); +} +} else { +if ( haveQuad ) { +this.rawObject.buildQuadV( buffer ); +} else { +this.rawObject.buildFaceV( buffer ); +} +} +break; +case Consts.LINE_L: +if ( bufferLength === slashesPointer * 2 ) { +this.rawObject.buildLineVvt( buffer ); +} else { +this.rawObject.buildLineV( buffer ); +} +break; +case Consts.LINE_S: +this.rawObject.pushSmoothingGroup( buffer[ 1 ] ); +break; +case Consts.LINE_G: +this.processCompletedGroup( buffer[ 1 ] ); +break; +case Consts.LINE_O: +if ( this.rawObject.vertices.length > 0 ) { +this.processCompletedObject( buffer[ 1 ], null ); +reachedFaces = false; +} else { +this.rawObject.pushObject( buffer[ 1 ] ); +} +break; +case Consts.LINE_MTLLIB: +this.rawObject.pushMtllib( buffer[ 1 ] ); +break; +case Consts.LINE_USEMTL: +this.rawObject.pushUsemtl( buffer[ 1 ] ); +break; +default: +break; +} +return reachedFaces; +}; +Parser.prototype.processCompletedObject = function ( objectName, groupName ) { +this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +this.inputObjectCount++; +this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName ); +}; +Parser.prototype.processCompletedGroup = function ( groupName ) { +var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +if ( notEmpty ) { +this.inputObjectCount ++; +this.rawObject = this.rawObject.newInstanceFromGroup( groupName ); +} else { +this.rawObject.pushGroup( groupName ); +} +}; +Parser.prototype.finalize = function () { +this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +this.inputObjectCount++; +}; +return Parser; +})(); +var RawObject = (function () { +function RawObject( objectName, groupName, mtllibName ) { +this.globalVertexOffset = 1; +this.globalUvOffset = 1; +this.globalNormalOffset = 1; +this.vertices = []; +this.normals = []; +this.uvs = []; +this.mtllibName = Validator.verifyInput( mtllibName, 'none' ); +this.objectName = Validator.verifyInput( objectName, 'none' ); +this.groupName = Validator.verifyInput( groupName, 'none' ); +this.activeMtlName = 'none'; +this.activeSmoothingGroup = 1; +this.mtlCount = 0; +this.smoothingGroupCount = 0; +this.rawObjectDescriptions = []; +var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; +} +RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) { +return materialName + '|' + smoothingGroup; +}; +RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) { +var newRawObject = new RawObject( objectName, groupName, this.mtllibName ); +newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; +newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; +newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; +return newRawObject; +}; +RawObject.prototype.newInstanceFromGroup = function ( groupName ) { +var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName ); +newRawObject.vertices = this.vertices; +newRawObject.uvs = this.uvs; +newRawObject.normals = this.normals; +newRawObject.globalVertexOffset = this.globalVertexOffset; +newRawObject.globalUvOffset = this.globalUvOffset; +newRawObject.globalNormalOffset = this.globalNormalOffset; +return newRawObject; +}; +RawObject.prototype.pushVertex = function ( buffer ) { +this.vertices.push( parseFloat( buffer[ 1 ] ) ); +this.vertices.push( parseFloat( buffer[ 2 ] ) ); +this.vertices.push( parseFloat( buffer[ 3 ] ) ); +}; +RawObject.prototype.pushUv = function ( buffer ) { +this.uvs.push( parseFloat( buffer[ 1 ] ) ); +this.uvs.push( parseFloat( buffer[ 2 ] ) ); +}; +RawObject.prototype.pushNormal = function ( buffer ) { +this.normals.push( parseFloat( buffer[ 1 ] ) ); +this.normals.push( parseFloat( buffer[ 2 ] ) ); +this.normals.push( parseFloat( buffer[ 3 ] ) ); +}; +RawObject.prototype.pushObject = function ( objectName ) { +this.objectName = objectName; +}; +RawObject.prototype.pushMtllib = function ( mtllibName ) { +this.mtllibName = mtllibName; +}; +RawObject.prototype.pushGroup = function ( groupName ) { +this.groupName = groupName; +this.verifyIndex(); +}; +RawObject.prototype.pushUsemtl = function ( mtlName ) { +if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; +this.activeMtlName = mtlName; +this.mtlCount++; +this.verifyIndex(); +}; +RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) { +var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup; +if ( this.activeSmoothingGroup === normalized ) return; +this.activeSmoothingGroup = normalized; +this.smoothingGroupCount++; +this.verifyIndex(); +}; +RawObject.prototype.verifyIndex = function () { +var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 ); +this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ]; +if ( ! Validator.isValid( this.rawObjectDescriptionInUse ) ) { +this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; +} +}; +RawObject.prototype.buildQuadVVtVn = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] ); +this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] ); +this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] ); +} +}; +RawObject.prototype.buildQuadVVt = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); +this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); +} +}; +RawObject.prototype.buildQuadVVn = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); +this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); +} +}; +RawObject.prototype.buildQuadV = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] ); +} +}; +RawObject.prototype.buildFaceVVtVn = function ( indexArray ) { +for ( var i = 1; i < 10; i += 3 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVt( indexArray[ i + 1 ] ); +this.attachFaceVn( indexArray[ i + 2 ] ); +} +}; +RawObject.prototype.buildFaceVVt = function ( indexArray ) { +for ( var i = 1; i < 7; i += 2 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVt( indexArray[ i + 1 ] ); +} +}; +RawObject.prototype.buildFaceVVn = function ( indexArray ) { +for ( var i = 1; i < 7; i += 2 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVn( indexArray[ i + 1 ] ); +} +}; +RawObject.prototype.buildFaceV = function ( indexArray ) { +for ( var i = 1; i < 4; i ++ ) { +this.attachFaceV_( indexArray[ i ] ); +} +}; +RawObject.prototype.attachFaceV_ = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalVertexOffset ) * 3; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.vertices.push( this.vertices[ index++ ] ); +rodiu.vertices.push( this.vertices[ index++ ] ); +rodiu.vertices.push( this.vertices[ index ] ); +}; +RawObject.prototype.attachFaceVt = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalUvOffset ) * 2; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.uvs.push( this.uvs[ index++ ] ); +rodiu.uvs.push( this.uvs[ index ] ); +}; +RawObject.prototype.attachFaceVn = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalNormalOffset ) * 3; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.normals.push( this.normals[ index++ ] ); +rodiu.normals.push( this.normals[ index++ ] ); +rodiu.normals.push( this.normals[ index ] ); +}; +RawObject.prototype.buildLineVvt = function ( lineArray ) { +var length = lineArray.length; +for ( var i = 1; i < length; i ++ ) { +this.vertices.push( parseInt( lineArray[ i ] ) ); +this.uvs.push( parseInt( lineArray[ i ] ) ); +} +}; +RawObject.prototype.buildLineV = function ( lineArray ) { +var length = lineArray.length; +for ( var i = 1; i < length; i++ ) { +this.vertices.push( parseInt( lineArray[ i ] ) ); +} +}; +RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) { +var temp = this.rawObjectDescriptions; +this.rawObjectDescriptions = []; +var rawObjectDescription; +var index = 0; +var absoluteVertexCount = 0; +var absoluteNormalCount = 0; +var absoluteUvCount = 0; +for ( var name in temp ) { +rawObjectDescription = temp[ name ]; +if ( rawObjectDescription.vertices.length > 0 ) { +if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName; +this.rawObjectDescriptions[ index++ ] = rawObjectDescription; +absoluteVertexCount += rawObjectDescription.vertices.length; +absoluteUvCount += rawObjectDescription.uvs.length; +absoluteNormalCount += rawObjectDescription.normals.length; +} +} +var notEmpty = false; +if ( index > 0 ) { +if ( debug ) this.createReport( inputObjectCount, true ); +meshCreator.buildMesh( +this.rawObjectDescriptions, +inputObjectCount, +absoluteVertexCount, +absoluteNormalCount, +absoluteUvCount +); +notEmpty = true; +} +return notEmpty; +}; +RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) { +var report = { +name: this.objectName ? this.objectName : 'groups', +mtllibName: this.mtllibName, +vertexCount: this.vertices.length / 3, +normalCount: this.normals.length / 3, +uvCount: this.uvs.length / 2, +smoothingGroupCount: this.smoothingGroupCount, +mtlCount: this.mtlCount, +rawObjectDescriptions: this.rawObjectDescriptions.length +}; +if ( printDirectly ) { +console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name ); +console.log( 'Mtllib name: ' + report.mtllibName ); +console.log( 'Vertex count: ' + report.vertexCount ); +console.log( 'Normal count: ' + report.normalCount ); +console.log( 'UV count: ' + report.uvCount ); +console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount ); +console.log( 'Material count: ' + report.mtlCount ); +console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions ); +console.log( '' ); +} +return report; +}; +return RawObject; +})(); +var RawObjectDescription = (function () { +function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) { +this.objectName = objectName; +this.groupName = groupName; +this.materialName = materialName; +this.smoothingGroup = smoothingGroup; +this.vertices = []; +this.uvs = []; +this.normals = []; +} +return RawObjectDescription; +})(); +var MeshCreator = (function () { +function MeshCreator() { +this.sceneGraphBaseNode = null; +this.materials = null; +this.debug = false; +this.globalObjectCount = 1; +this.validated = false; +} +MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) { +this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, this.sceneGraphBaseNode ); +this.sceneGraphBaseNode = Validator.verifyInput( this.sceneGraphBaseNode, new THREE.Group() ); +}; +MeshCreator.prototype.setMaterials = function ( materials ) { +this.materials = Validator.verifyInput( materials, this.materials ); +this.materials = Validator.verifyInput( this.materials, { materials: [] } ); +}; +MeshCreator.prototype.setDebug = function ( debug ) { +if ( debug === true || debug === false ) this.debug = debug; +}; +MeshCreator.prototype.validate = function () { +if ( this.validated ) return; +this.setSceneGraphBaseNode( null ); +this.setMaterials( null ); +this.setDebug( null ); +this.globalObjectCount = 1; +}; +MeshCreator.prototype.finalize = function () { +this.sceneGraphBaseNode = null; +this.materials = null; +this.validated = false; +}; +MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) { +if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount ); +var bufferGeometry = new THREE.BufferGeometry(); +var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 ); +bufferGeometry.addAttribute( 'position', vertexBA ); +var normalBA; +if ( absoluteNormalCount > 0 ) { +normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 ); +bufferGeometry.addAttribute( 'normal', normalBA ); +} +var uvBA; +if ( absoluteUvCount > 0 ) { +uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 ); +bufferGeometry.addAttribute( 'uv', uvBA ); +} +if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount ); +var rawObjectDescription; +var material; +var materialName; +var createMultiMaterial = rawObjectDescriptions.length > 1; +var materials = []; +var materialIndex = 0; +var materialIndexMapping = []; +var selectedMaterialIndex; +var vertexBAOffset = 0; +var vertexGroupOffset = 0; +var vertexLength; +var normalOffset = 0; +var uvOffset = 0; +for ( var oodIndex in rawObjectDescriptions ) { +rawObjectDescription = rawObjectDescriptions[ oodIndex ]; +materialName = rawObjectDescription.materialName; +material = this.materials[ materialName ]; +if ( ! material ) { +material = this.materials[ 'defaultMaterial' ]; +if ( ! material ) { +material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} ); +material.name = 'defaultMaterial'; +this.materials[ 'defaultMaterial' ] = material; +} +console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' ); +} +if ( rawObjectDescription.smoothingGroup === 0 ) { +materialName = material.name + '_flat'; +var materialClone = this.materials[ materialName ]; +if ( ! materialClone ) { +materialClone = material.clone(); +materialClone.name = materialName; +materialClone.shading = THREE.FlatShading; +this.materials[ materialName ] = name; +} +} +vertexLength = rawObjectDescription.vertices.length; +if ( createMultiMaterial ) { +selectedMaterialIndex = materialIndexMapping[ materialName ]; +if ( ! selectedMaterialIndex ) { +selectedMaterialIndex = materialIndex; +materialIndexMapping[ materialName ] = materialIndex; +materials.push( material ); +materialIndex++; +} +bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex ); +vertexGroupOffset += vertexLength / 3; +} +vertexBA.set( rawObjectDescription.vertices, vertexBAOffset ); +vertexBAOffset += vertexLength; +if ( normalBA ) { +normalBA.set( rawObjectDescription.normals, normalOffset ); +normalOffset += rawObjectDescription.normals.length; +} +if ( uvBA ) { +uvBA.set( rawObjectDescription.uvs, uvOffset ); +uvOffset += rawObjectDescription.uvs.length; +} +if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex ); +} +if ( ! normalBA ) bufferGeometry.computeVertexNormals(); +if ( createMultiMaterial ) material = materials; +var mesh = new THREE.Mesh( bufferGeometry, material ); +this.sceneGraphBaseNode.add( mesh ); +this.globalObjectCount++; +}; +MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) { +console.log( +' Output Object no.: ' + this.globalObjectCount + +'\n objectName: ' + rawObjectDescription.objectName + +'\n groupName: ' + rawObjectDescription.groupName + +'\n materialName: ' + rawObjectDescription.materialName + +'\n materialIndex: ' + selectedMaterialIndex + +'\n smoothingGroup: ' + rawObjectDescription.smoothingGroup + +'\n #vertices: ' + rawObjectDescription.vertices.length / 3 + +'\n #uvs: ' + rawObjectDescription.uvs.length / 2 + +'\n #normals: ' + rawObjectDescription.normals.length / 3 +); +}; +return MeshCreator; +})(); +OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) { +var workerCode = ''; +workerCode += funcBuildObject( 'Consts', Consts ); +workerCode += funcBuildObject( 'Validator', Validator ); +workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); +workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject ); +workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription ); +return workerCode; +}; +return OBJLoader2; +})(); +var xseen = { +canvases : [], +sceneInfo : [], +nodeDefinitions : {}, +parseTable : {}, +node : {}, +utils : {}, +eventManager : {}, +Events : {}, +Navigation : {}, +loadMgr : {}, +loader : { +'Null' : '', +'ColladaLoader' : '', +'GltfLegacy' : '', +'GltfLoader' : '', +'ObjLoader' : '', +'ImageLoader' : '', +'X3dLoader' : '', +}, +loadProgress : function (xhr) { +if (xhr.total != 0) { +console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); +} +}, +loadError : function (xhr, userdata, code, message) { +console.error('An error happened on '+userdata.e.id+'\n'+code+'\n'+message); +}, +loadMime : { +'' : {name: 'Null', loader: 'Null'}, +'dae' : {name: 'Collada', loader: 'ColladaLoader'}, +'glb' : {name: 'glTF Binary', loader: 'GltfLoader'}, +'glbl' : {name: 'glTF Binary', loader: 'GltfLegacy'}, +'gltf' : {name: 'glTF JSON', loader: 'GltfLoader'}, +'obj' : {name: 'OBJ', loader: 'ObjLoader'}, +'png' : {name: 'PNG', loader: 'ImageLoader'}, +'jpg' : {name: 'JPEG', loader: 'ImageLoader'}, +'jpeg' : {name: 'JPEG', loader: 'ImageLoader'}, +'gif' : {name: 'GIF', loader: 'ImageLoader'}, +'x3d' : {name: 'X3D XML', loader: 'X3dLoader'}, +}, +array_to_object : function (a) { +var o = {}; +for(var i=0;i ' + obj.type + ' (' + obj.name + ')'); +for (var i=0; i +var validParams = xseen.array_to_object([ +'showLog', +'showStat', +'showProgress', +'PrimitiveQuality', +'components', +'loadpath', +'disableDoubleClick', +'backend', +'altImg', +'runtimeEnabled', +'keysEnabled', +'showTouchpoints', +'disableTouch', +'maxActiveDownloads' +]); +var components, prefix; +var showLoggingConsole = false; +for (var i=0; i < xseens.length; i++) { +settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'false'); +settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'true'); +settings.setProperty("showStat", xseens[i].getAttribute("showStat") || 'false'); +settings.setProperty("showProgress", xseens[i].getAttribute("showProgress") || 'true'); +settings.setProperty("PrimitiveQuality", xseens[i].getAttribute("PrimitiveQuality") || 'High'); +params = xseens[i].getElementsByTagName('PARAM'); +for (var j=0; j < params.length; j++) { +if (params[j].getAttribute('name') in validParams) { +settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value')); +} else { +} +} +if (settings.getProperty('showLog') === 'true') { +showLoggingConsole = true; +} +if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) { +components = settings.getProperty('components', xseens[i].getAttribute("components")); +if (components) { +prefix = settings.getProperty('loadpath', xseens[i].getAttribute("loadpath")); +components = components.trim().split(','); +for (j=0; j < components.length; j++) { +xseen.loadJS(components[j] + ".js", prefix); +} +} +if (xseens[i].getAttribute("src")) { +var _scene = document.createElement("scene"); +var _inl = document.createElement("Inline"); +_inl.setAttribute("url", xseens[i].getAttribute("src")); +_scene.appendChild(_inl); +xseens[i].appendChild(_scene); +} +} +} +if (showLoggingConsole == true) { +xseen.debug.activate(true); +} else { +xseen.debug.activate(false); +} +if (xseen.versionInfo !== undefined) { +xseen.debug.logInfo("XSeen version " + xseen.versionInfo.version + ", " + +"Date " + xseen.versionInfo.date); +xseen.debug.logInfo(xseen.versionInfo.splashText); +} +var x_element; +var x_canvas; +var altDiv, altP, aLnk, altImg; +var t0, t1; +for (var i=0; i < xseens.length; i++) +{ +x_element = xseens[i]; // The XSeen DOM element +x_canvas = new THREE.Scene(); // May need addtl info if multiple: xseen.X3DCanvas(x_element, xseen.canvases.length); +xseen.canvases.push(x_canvas); // TODO: Need to handle failure to initialize? +t0 = new Date().getTime(); +var divWidth = x_element.getAttribute('width'); +var divHeight = x_element.getAttribute('height'); +if (divHeight + divWidth < 100) { +divHeight = 450; +divWidth = 800; +} else if (divHeight < 50) { +divHeight = Math.floor(divWidth/2) + 50; +} else if (divWidth < 50) { +divWidth = divHeight * 2 - 100; +} +var turntable = (x_element.getAttribute('turntable') || '').toLowerCase(); +if (turntable == 'on' || turntable == 'yes' || turntable == 'y' || turntable == '1') { +turntable = true; +} else { +turntable = false; +} +turntable = false; +var x_camera = {}; +var x_renderer = new THREE.WebGLRenderer(); +x_renderer.setSize (divWidth, divHeight); +var x_effect = new THREE.StereoEffect(x_renderer); +x_renderer.controls = {'update' : function() {return;}}; +x_element.addEventListener ('dblclick', xseen.Events.canvasHandler, true); +x_element.addEventListener ('click', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mousedown', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mousemove', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mouseup', xseen.Events.canvasHandler, true); +x_element.addEventListener ('xseen', xseen.Events.XSeenHandler); // Last chance for XSeen handling of event +x_element.addEventListener ('change', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event +xseen.sceneInfo.push ({ +'size' : {'width':divWidth, 'height':divHeight}, +'scene' : x_canvas, +'renderer' : x_renderer, +'effect' : x_effect, +'camera' : [x_camera], +'turntable' : turntable, +'mixers' : [], +'clock' : new THREE.Clock(), +'element' : x_element, +'selectable': [], +'stacks' : [], +'tmp' : {activeViewpoint:false}, +'xseen' : xseen, +}); +x_element._xseen = {}; +x_element._xseen.children = []; +x_element._xseen.sceneInfo = xseen.sceneInfo[xseen.sceneInfo.length-1]; +t1 = new Date().getTime() - t0; +xseen.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms."); +} +var ready = (function(eventType) { +var evt = null; +if (document.createEvent) { +evt = document.createEvent("Events"); +evt.initEvent(eventType, true, true); +document.dispatchEvent(evt); +} else if (document.createEventObject) { +evt = document.createEventObject(); +document.body.fireEvent('on' + eventType, evt); +} +})('load'); +var t=[]; +for (var i=0; i xseen.debug.maxLinesToLog) { return; } +var node = document.createElement("p"); +node.style.margin = 0; +switch (logType) { +case xseen.debug.INFO: +node.style.color = "#009900"; +break; +case xseen.debug.WARNING: +node.style.color = "#cd853f"; +break; +case xseen.debug.ERROR: +node.style.color = "#ff4500"; +break; +case xseen.debug.EXCEPTION: +node.style.color = "#ffff00"; +break; +default: +node.style.color = "#009900"; +break; +} +try { +node.innerHTML = logType + ": " + msg; +xseen.debug.logContainer.insertBefore(node, xseen.debug.logContainer.firstChild); +} catch (err) { +if (window.console.firebug !== undefined) { +window.console.warn(msg); +} +} +if (xseen.debug.isFirebugAvailable) { +switch (logType) { +case xseen.debug.INFO: +window.console.info(msg); +break; +case xseen.debug.WARNING: +window.console.warn(msg); +break; +case xseen.debug.ERROR: +window.console.error(msg); +break; +case xseen.debug.EXCEPTION: +window.console.debug(msg); +break; +default: +break; +} +} +xseen.debug.numLinesLogged++; +}, +logInfo: function(msg) { +xseen.debug.doLog(msg, xseen.debug.INFO); +}, +logWarning: function(msg) { +xseen.debug.doLog(msg, xseen.debug.WARNING); +}, +logError: function(msg) { +xseen.debug.doLog(msg, xseen.debug.ERROR); +}, +logException: function(msg) { +xseen.debug.doLog(msg, xseen.debug.EXCEPTION); +}, +assert: function(c, msg) { +if (!c) { +xseen.debug.doLog("Assertion failed in " + xseen.debug.assert.caller.name + ': ' + msg, xseen.debug.ERROR); +} +}, +typeOf: function (obj) { +var type = typeof obj; +return type === "object" && !obj ? "null" : type; +}, +exists: function (obj, name, type) { +type = type || "function"; +return (obj ? this.typeOf(obj[name]) : "null") === type; +}, +dumpFields: function (node) { +var str = ""; +for (var fName in node) { +str += (fName + ", "); +} +str += '\n'; +xseen.debug.logInfo(str); +return str; +} +}; +this.debug.setup(); +}; +xseen.Navigation = { +'TwoPi' : 2 * Math.PI, +'none' : function () {}, // Does not allow user-controlled navigation +'turntable' : function (speed, deltaT, scene, camera) +{ +var T, radians, radius, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +radius = vp.fields._radius0; +camera.position.x = radius * Math.sin(radians) +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.position.z = radius * Math.cos(radians); +camera.lookAt(scene.ORIGIN); +}, +'tilt' : function (speed, deltaT, scene, camera) +{ +var T, radians, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.lookAt(scene.ORIGIN); +}, +'setup' : { +'none' : function () {return null;}, +'orbit' : function (camera, renderer) +{ +var controls; +controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.enableZoom = false; +controls.enableZoom = true; +return controls; +}, +'trackball' : function (camera, renderer) +{ +var controls; +controls = new THREE.TrackballControls(camera, renderer.domElement); +controls.rotateSpeed = 1.0; +controls.zoomSpeed = 1.2; +controls.panSpeed = 0.8; +controls.noZoom = false; +controls.noPan = false; +controls.staticMoving = true; +controls.dynamicDampingFactor = 0.3; +controls.keys = [ 65, 83, 68 ]; +return controls; +}, +}, +}; +xseen.Navigation = { +'TwoPi' : 2 * Math.PI, +'none' : function () {}, // Does not allow user-controlled navigation +'turntable' : function (speed, deltaT, scene, camera) +{ +var T, radians, radius, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +radius = vp.fields._radius0; +camera.position.x = radius * Math.sin(radians) +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.position.z = radius * Math.cos(radians); +camera.lookAt(scene.ORIGIN); +}, +'tilt' : function (speed, deltaT, scene, camera) +{ +var T, radians, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.lookAt(scene.ORIGIN); +}, +'setup' : { +'none' : function () {return null;}, +'orbit' : function (camera, renderer) +{ +var controls; +controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.enableZoom = false; +controls.enableZoom = true; +return controls; +}, +}, +}; +xseen.nodes = { +'_defineNode' : function(nodeName, nodeComponent, nodeMethod) { +var methodBase = ''; +node = { +'tag' : nodeName, +'taglc' : nodeName.toLowerCase(), +'component' : nodeComponent, +'method' : methodBase + nodeMethod, +'fields' : [], +'fieldIndex': [], +'addField' : function (fieldObj, datatype, defaultValue) { +var fieldName, namelc, enumerated, animatable; +if (typeof(fieldObj) === 'object') { +fieldName = fieldObj.name; +datatype = fieldObj.datatype; +defaultValue = fieldObj.defaultValue; +enumerated = (typeof(fieldObj.enumerated) === 'undefined') ? [] : fieldObj.enumerated; +animatable = (typeof(fieldObj.animatable) === 'undefined') ? false : fieldObj.animatable; +} else { +fieldName = fieldObj; +animatable = false; +if (typeof(defaultValue) == 'array') { +enumerated = defaultValue; +defaultValue = enumerated[0]; +} else { +enumerated = []; +} +} +namelc = fieldName.toLowerCase(); +this.fields.push ({ +'field' : fieldName, +'fieldlc' : namelc, +'type' : datatype, +'default' : defaultValue, +'enumeration' : enumerated, +'animatable' : animatable, +'clone' : this.cloneField, +'setFieldName' : this.setFieldName, +}); +this.fieldIndex[namelc] = this.fields.length-1; +return this; +}, +'addNode' : function () { +xseen.parseTable[this.taglc] = this; +}, +'cloneField' : function () { +var newFieldObject = { +'field' : this.field, +'fieldlc' : this.fieldlc, +'type' : this.type, +'default' : 0, +'enumeration' : [], +'animatable' : this.animatable, +'clone' : this.clone, +'setFieldName' : this.setFieldName, +}; +for (var i=0; i= 0) { +this._internals.activeNode = this._internals.stack[this._internals.active]; +} else { +this._internals.activeNode = this._internals.defaultNode; +} +} +this.init = function() { // Clears existing stack +this._internals.stack = []; +} +this.pushDown = function(node) { // Push new node onto stack and make active +this._internals.stack.push (node); +this._setActiveNode(); +} +this.popOff = function() { // Pop node off stack and make next one active +this._internals.stack.pop(); +this._setActiveNode(); +} +this.getActive = function() { +return this._internals.activeNode; +} +this.setDefault = function(node) { +this._internals.defaultNode = node; +if (Object.keys(this._internals.activeNode).length === 0) { +this._internals.activeNode = this._internals.defaultNode; +} +} +} +xseen.types = { +'Deg2Rad' : Math.PI / 180, +'SFFloat' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return value; +}, +'SFInt' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return Math.round(value); +}, +'SFBool' : function (value, def) +{ +if (value === null) {return def;} +if (value) {return true;} +if (!value) {return false;} +return def; +}, +'SFTime' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return value; +}, +'SFVec3f' : function (value, def) +{ +if (value === null) {return def;} +var v3 = value.split(' '); +if (v3.length < 3 || Number.isNaN(v3[0]) || Number.isNaN(v3[1]) || Number.isNaN(v3[2])) { +return def; +} +return [v3[0]-0, v3[1]-0, v3[2]-0]; +}, +'SFVec2f' : function (value, def) +{ +if (value === null) {return def;} +var v2 = value.split(' '); +if (v2.length != 2 || Number.isNaN(v2[0]) || Number.isNaN(v2[1])) { +return def; +} +return [v2[0]-0, v2[1]-0]; +}, +'SFRotation' : function (value, def) +{ +if (value === null) {return def;} +var v4 = value.split(' '); +if (v4.length != 4 || Number.isNaN(v4[0]) || Number.isNaN(v4[1]) || Number.isNaN(v4[2]) || Number.isNaN(v4[3])) { +return def; +} +var result = { +'vector' : [v4[0], v4[1], v4[2], v4[3]], +'axis_angle' : [{'x': v4[0], 'y': v4[1], 'z': v4[2]}, v4[3]], +}; +return result; +}, +'SFColor' : function (value, defaultString) +{ +var v3 = this.SFVec3f(value, defaultString); +v3[0] = Math.min(Math.max(v3[0], 0.0), 1.0); +v3[1] = Math.min(Math.max(v3[1], 0.0), 1.0); +v3[2] = Math.min(Math.max(v3[2], 0.0), 1.0); +return v3; +}, +'SFString' : function (value, def) +{ +if (value === null) {value = def;} +return value; +}, +'MFFloat' : function (value, def) +{ +var defReturn = (def == '') ? [] : def; +if (value === null) {return defReturn;} +var mi = value.split(' '); +var rv = []; +for (var i=0; iDocumentation."]; +var version = { +major : Major, +minor : Minor, +patch : Patch, +preRelease : PreRelease, +release : Release, +version : '', +date : RDate, +splashText : SplashText +}; +version.version = version.major + '.' + version.minor + '.' + version.patch; +version.version += (version.preRelease != '') ? '-' + version.preRelease : ''; +version.version += (version.release != '') ? '+' + version.release : ''; +return version; +} +xseen.node.core_NOOP = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.parsing = function (s, e) { +xseen.debug.logInfo ('Parsing init details stub for ' + s); +} +xseen.node.af_Entity = { +'init' : function (e,p) +{ +xseen.node.parsing('A-Frame Entity'); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Assets = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.af_AssetItem = { +'init' : function (e,p) // Only field is SRC. +{ +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Mixin = { +'init' : function (e,p) // Lots of fields -- all nebelous until used +{ +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Appearance = function (e) { +var parameters = { +'aoMap' : e._xseen.fields['ambient-occlusion-map'], +'aoMapIntensity' : e._xseen.fields['ambient-occlusion-map-intensity'], +'color' : e._xseen.fields['color'], +'displacementMap' : e._xseen.fields['displacement-map'], +'displacementScale' : e._xseen.fields['displacement-scale'], +'displacementBias' : e._xseen.fields['displacement-bias'], +'envMap' : e._xseen.fields['env-map'], +'normalMap' : e._xseen.fields['normal-map'], +'normalScale' : e._xseen.fields['normal-scale'], +'wireframe' : e._xseen.fields['wireframe'], +'wireframeLinewidth' : e._xseen.fields['wireframe-linewidth'], +}; +var material = new THREE.MeshPhongMaterial(parameters); +return material; +} +xseen.node.af_Box = { +'init' : function (e,p) +{ +var geometry = new THREE.BoxGeometry( +e._xseen.fields.width, +e._xseen.fields.height, +e._xseen.fields.depth, +e._xseen.fields['segments-width'], +e._xseen.fields['segments-height'], +e._xseen.fields['segments-depth'] +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Cone = { +'init' : function (e,p) +{ +var geometry = new THREE.ConeGeometry( +e._xseen.fields.radius, +e._xseen.fields.height, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-height'], +e._xseen.fields['open-ended'], +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Cylinder = { +'init' : function (e,p) +{ +var geometry = new THREE.CylinderGeometry( +e._xseen.fields['radius-top'], +e._xseen.fields['radius-bottom'], +e._xseen.fields.height, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-height'], +e._xseen.fields['open-ended'], +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Dodecahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.DodecahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Icosahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.IcosahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Octahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.OctahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Sphere = { +'init' : function (e,p) +{ +var geometry = new THREE.SphereGeometry( +e._xseen.fields.radius, +e._xseen.fields['segments-width'], +e._xseen.fields['segments-height'], +e._xseen.fields['phi-start'] * xseen.types.Deg2Rad, +e._xseen.fields['phi-length'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Tetrahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.TetrahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Torus = { +'init' : function (e,p) +{ +var geometry = new THREE.TorusGeometry( +e._xseen.fields.radius, +e._xseen.fields.tube, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-tubular'], +e._xseen.fields.arc * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.unk_Viewpoint = { +'init' : function (e,p) +{ // This should really go in a separate push-down list for Viewpoints +e._xseen.fields._radius0 = Math.sqrt( e._xseen.fields.position[0]*e._xseen.fields.position[0] + +e._xseen.fields.position[1]*e._xseen.fields.position[1] + +e._xseen.fields.position[2]*e._xseen.fields.position[2]); +e._xseen.domNode = e; // Back-link to node if needed later on +e._xseen.position = new THREE.Vector3(e._xseen.fields.position[0], e._xseen.fields.position[1], e._xseen.fields.position[2]); +e._xseen.type = e._xseen.fields.cameratype; +e._xseen.motion = e._xseen.fields.motion; +e._xseen.motionspeed = e._xseen.fields.motionspeed * 1000; +if (e._xseen.motion == 'turntable' || e._xseen.motion == 'tilt') {e._xseen.motionspeed = 1.0/e._xseen.motionspeed;} +if (!e._xseen.sceneInfo.tmp.activeViewpoint) { +e._xseen.sceneInfo.stacks.Viewpoints.pushDown(e._xseen); +e._xseen.sceneInfo.tmp.activeViewpoint = true; +} +e._xseen.handlers = {}; +e._xseen.handlers.setactive = this.setactive; +}, +'fin' : function (e,p) {}, +'setactive' : function (ev) +{ +var xseenNode = this.destination._xseen; +xseenNode.sceneInfo.stacks.Viewpoints.pushDown(xseenNode); // TODO: This is probably not the right way to change VP in the stack +xseenNode.sceneInfo.element._xseen.renderer.activeCamera = +xseenNode.sceneInfo.element._xseen.renderer.cameras[xseenNode.fields.type]; +xseenNode.sceneInfo.element._xseen.renderer.activeRender = +xseenNode.sceneInfo.element._xseen.renderer.renderEffects[xseenNode.fields.type]; +if (xseenNode.fields.type != 'stereo') { +xseenNode.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, xseenNode.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); +} +}, +}; +xseen.node.controls_Navigation = { +'init' : function (e,p) +{ // This should really go in a separate push-down list for Viewpoints +e._xseen.domNode = e; // Back-link to node if needed later on +e._xseen.speed = e._xseen.fields.speed; +if (e._xseen.setup == 'examine') {e._xseen.setup == 'trackball';} +e._xseen.type = 'none'; +e._xseen.setup = e._xseen.fields.type; +if (!(e._xseen.setup == 'orbit' || e._xseen.setup == 'trackball')) {e._xseen.setup = 'none';} +if (!e._xseen.sceneInfo.tmp.activeNavigation) { +e._xseen.sceneInfo.stacks.Navigation.pushDown(e._xseen); +e._xseen.sceneInfo.tmp.activeNavigation = true; +} +e._xseen.handlers = {}; +e._xseen.handlers.setactive = this.setactive; +}, +'fin' : function (e,p) {}, +'setactive' : function (ev) +{ +}, +}; +xseen.node.lighting_Light = { +'init' : function (e,p) +{ +var color = xseen.types.Color3toInt (e._xseen.fields.color); +var intensity = e._xseen.fields.intensity - 0; +var lamp, type=e._xseen.fields.type.toLowerCase(); +if (type == 'point') { +lamp = new THREE.PointLight (color, intensity); +lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); +lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); +} else if (type == 'spot') { +lamp = new THREE.SpotLight (color, intensity); +lamp.position.set(0-e._xseen.fields.direction[0], 0-e._xseen.fields.direction[1], 0-e._xseen.fields.direction[2]); +lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); +lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); +lamp.angle = Math.max(0.0, Math.min(1.5707963267948966192313216916398, e._xseen.fields.cutoffangle)); +lamp.penumbra = 1 - Math.max(0.0, Math.min(lamp.angle, e._xseen.fields.beamwidth)) / lamp.angle; +} else { // DirectionalLight (by default) +lamp = new THREE.DirectionalLight (color, intensity); +lamp.position.x = 0-e._xseen.fields.direction[0]; +lamp.position.y = 0-e._xseen.fields.direction[1]; +lamp.position.z = 0-e._xseen.fields.direction[2]; +} +p._xseen.children.push(lamp); +lamp = null; +} +, +'fin' : function (e,p) +{ +} +}; +xseen.node.appearance_Material = { +'init' : function (e,p) +{ +var transparency = e._xseen.fields.transparency - 0; +var shininess = e._xseen.fields.shininess - 0; +var colorDiffuse = xseen.types.Color3toInt (e._xseen.fields.diffusecolor); +var colorEmissive = xseen.types.Color3toInt (e._xseen.fields.emissivecolor); +var colorSpecular = xseen.types.Color3toInt (e._xseen.fields.specularcolor); +p._xseen.material = new THREE.MeshPhongMaterial( { +'color' : colorDiffuse, +'emissive' : colorEmissive, +'specular' : colorSpecular, +'shininess' : shininess, +'opacity' : 1.0-transparency, +'transparent' : (transparency > 0.0) ? true : false +} ); +e._xseen.animate['diffusecolor'] = p._xseen.material.color; +e._xseen.animate['emissivecolor'] = p._xseen.material.emissive; +e._xseen.animate['specularcolor'] = p._xseen.material.specular; +e._xseen.animate['transparency'] = p._xseen.material.opacity; +e._xseen.animate['shininess'] = p._xseen.material.shininess; +}, +'fin' : function (e,p) {} +}; +xseen.node.appearance_ImageTexture = { +'init' : function (e,p) +{ +p._xseen.texture = xseen.loader.ImageLoader.load(e._xseen.fields.url); +p._xseen.texture.wrapS = (e._xseen.fields.repeats) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; +p._xseen.texture.wrapT = (e._xseen.fields.repeatt) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; +}, +'fin' : function (e,p) {} +}; +xseen.node.appearance_Appearance = { +'init' : function (e,p) {}, +'fin' : function (e,p) +{ +if (typeof(e._xseen.texture) !== 'undefined' && e._xseen.texture !== null) { +e._xseen.material.map = e._xseen.texture; +} +p._xseen.appearance = e._xseen.material; +} +}; +xseen.node.geometry_Coordinate = { +'init' : function (e,p) +{ +var vertices = []; +for (var i=0; i 0) ? true : false; +var useColor = (color.length > 0) ? true : false; +var maxIndex = Math.max.apply(null, indices); +var minIndex = Math.min.apply(null, indices); +if (maxIndex >= vertices.length) { +console.log ('Maximum index ('+maxIndex+') exceeds vertex count ('+vertices.length+'). No geometry is created'); +return; +} +if (useNormals && maxIndex >= normals.length) { +console.log ('Maximum index ('+maxIndex+') exceeds normal count ('+normals.length+'). No geometry is created'); +return; +} +if (useColor && maxIndex >= color.length) { +console.log ('Maximum index ('+maxIndex+') exceeds color count ('+color.length+'). No geometry is created'); +return; +} +if (minIndex < 0) { +console.log ('Minimum index ('+minIndex+') less than zero. No geometry is created'); +return; +} +if (indices.length % 3 != 0) { +console.log ('Number of indices ('+indices.length+') not divisible by 3. No geometry is created'); +return; +} +var geometry = new THREE.Geometry(); +var normal_pz = new THREE.Vector3 (0, 0, 1); +var normal_mz = new THREE.Vector3 (0, 0, -1); +for (var i=0; i 0) {clip.duration = e._xseen.fields.duration;} +e._xseen.mixer.clipAction( clip ).play(); +} ); +} else { // Play a specific animation +var clip = THREE.AnimationClip.findByName(response.animations, e._xseen.fields.playonload); +var action = e._xseen.mixer.clipAction (clip); +action.play(); +} +} +} +} +}; +xseen.node.x_Route = { +'init' : function (e,p) +{ +var dest = e._xseen.fields.destination; +var hand = e._xseen.fields.handler; +var externalHandler = false; +if (e._xseen.fields.source == '' || +typeof(window[hand]) !== 'function' && +(dest == '' || e._xseen.fields.event == '' || e._xseen.fields.field == '')) { +xseen.debug.logError ('Route node missing field. No route setup. Source: '+e._xseen.fields.source+'.'+e._xseen.fields.event+'; Destination: '+dest+'.'+e._xseen.fields.field+'; Handler: '+hand); +return; +} else if (typeof(window[hand]) === 'function') { +externalHandler = true; +} +var eSource = document.getElementById (e._xseen.fields.source); +if (! externalHandler) { +var eDestination = document.getElementById (dest); +if (typeof(eSource) === 'undefined' || typeof(eDestination) === 'undefined') { +xseen.debug.logError ('Source or Destination node does not exist. No route setup'); +return; +} +var fField = xseen.nodes._getFieldInfo (eDestination.nodeName, e._xseen.fields.field); +if (typeof(fField) === 'undefined' || !fField.good) { +xseen.debug.logError ('Destination field does not exist or incorrectly specified. No route setup'); +return; +} +xseen.Events.addHandler (e, eSource, e._xseen.fields.event, eDestination, fField); +} else { +var handler = window[hand]; +eSource.addEventListener ('xseen', handler); +} +}, +'fin' : function (e,p) +{ +}, +'evHandler' : function (u) +{ +var de = u.e; +var df = u.f; +return de._xseen.handlers[df.handlerName]; +}, +}; +xseen.node.core_NOOP = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.core_WorldInfo = { +'init' : function (e,p) {parsing('WorldInfo', e)}, +'fin' : function (e,p) {} +}; +function parsing (s, e) { +xseen.debug.logInfo ('Parsing init details stub for ' + s); +} +xseen.node.unk_Shape = { +'init' : function (e,p) {}, +'fin' : function (e,p) +{ +if (typeof(e._xseen.materialProperty) !== 'undefined') { +e._xseen.appearance.vertexColors = THREE.VertexColors; +e._xseen.appearance._needsUpdate = true; +e._xseen.appearance.needsUpdate = true; +} +var mesh = new THREE.Mesh (e._xseen.geometry, e._xseen.appearance); +mesh.userData = e; +p._xseen.children.push(mesh); +p._xseen.sceneInfo.selectable.push(mesh); +mesh = null; +} +}; +xseen.node.grouping_Transform = { +'init' : function (e,p) +{ +var group = new THREE.Group(); +if (e.nodeName == "TRANSFORM") { +var rotation = xseen.types.Rotation2Quat(e._xseen.fields.rotation); +group.name = 'Transform children [' + e.id + ']'; +group.position.x = e._xseen.fields.translation[0]; +group.position.y = e._xseen.fields.translation[1]; +group.position.z = e._xseen.fields.translation[2]; +group.scale.x = e._xseen.fields.scale[0]; +group.scale.y = e._xseen.fields.scale[1]; +group.scale.z = e._xseen.fields.scale[2]; +group.quaternion.x = rotation.x; +group.quaternion.y = rotation.y; +group.quaternion.z = rotation.z; +group.quaternion.w = rotation.w; +e._xseen.animate['translation'] = group.position; +e._xseen.animate['rotation'] = group.quaternion; +e._xseen.animate['scale'] = group.scale; +} +e._xseen.sceneNode = group; +}, +'fin' : function (e,p) +{ +e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +e._xseen.sceneNode.add(child); +}); +p._xseen.children.push(e._xseen.sceneNode); +} +}; +xseen.node.networking_Inline = { +'init' : function (e,p) +{ +if (typeof(e._xseen.processedUrl) === 'undefined' || !e._xseen.requestedUrl) { +var uri = xseen.parseUrl (e._xseen.fields.url); +var type = uri.extension; +e._xseen.loadGroup = new THREE.Group(); +e._xseen.loadGroup.name = 'Inline content [' + e.id + ']'; +console.log ('Created Inline Group with UUID ' + e._xseen.loadGroup.uuid); +var userdata = {'requestType':'x3d', 'e':e, 'p':p} +if (type.toLowerCase() == 'json') { +userdata.requestType = 'json'; +xseen.loadMgr.loadJson (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); +} else { +xseen.loadMgr.loadXml (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); +} +e._xseen.requestedUrl = true; +} +p._xseen.children.push(e._xseen.loadGroup); +console.log ('Using Inline Group with UUID ' + e._xseen.loadGroup.uuid); +}, +'fin' : function (e,p) +{ +}, +'loadSuccess' : +function (response, userdata, xhr) { +userdata.e._xseen.processedUrl = true; +userdata.e._xseen.loadResponse = response; +console.log("download successful for "+userdata.e.id); +if (userdata.requestType == 'json') { +var tmp = {'scene': response}; +response = null; +response = (new JSONParser()).parseJavaScript(tmp); +} +var start = {'_xseen':0}; +var findSceneTag = function (fragment) { +if (typeof(fragment._xseen) === 'undefined') {fragment._xseen = {'childCount': -1};} +if (fragment.nodeName.toLowerCase() == 'scene') { +start = fragment; +return; +} else if (fragment.children.length > 0) { +for (fragment._xseen.childCount=0; fragment._xseen.childCount 0) { +userdata.e.appendChild(start.children[0]); +} +xseen.Parse(userdata.e, userdata.p, userdata.p._xseen.sceneInfo); +userdata.e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +userdata.e._xseen.loadGroup.add(child); +console.log ('...Adding ' + child.type + ' (' + child.name + ') to Inline Group? with UUID ' + userdata.e._xseen.loadGroup.uuid + ' (' + userdata.e._xseen.loadGroup.name + ')'); +}); +userdata.p._xseen.sceneInfo.scene.updateMatrixWorld(); +} else { +console.log("Found illegal X3D file -- no 'scene' tag"); +} +} +}; +xseen.node.core_Scene = { +'DEFAULT' : { +'Viewpoint' : { +'Position' : new THREE.Vector3 (0, 0, 10), +'Orientation' : '0 1 0 0', // TODO: fix (and below) when handling orientation +'Type' : 'perpsective', +'Motion' : 'none', +'MotionSpeed' : 1.0, +}, +'Navigation' : { +'Speed' : 1.0, // 16 spr (1 revolution per 16 seconds), in mseconds. +'Type' : 'none', +'Setup' : 'none', +} +}, +'init' : function (e,p) +{ +xseen.sceneInfo[0].stacks.Viewpoints.setDefault( +{ +'position' : this.DEFAULT.Viewpoint.Position, +'type' : this.DEFAULT.Viewpoint.Type, +'motion' : this.DEFAULT.Viewpoint.Motion, +'motionspeed': this.DEFAULT.Viewpoint.MotionSpeed / 1000, +'domNode' : e, +'fields' : {}, +} +); +xseen.sceneInfo[0].stacks.Navigation.setDefault( +{ +'speed' : this.DEFAULT.Navigation.Speed / 1000, +'type' : this.DEFAULT.Navigation.Type, +'setup' : this.DEFAULT.Navigation.Setup, +'domNode' : e, +'fields' : {}, +} +); +var width = e._xseen.sceneInfo.size.width; +var height = e._xseen.sceneInfo.size.height; +var x_renderer = new THREE.WebGLRenderer(); +x_renderer.setSize (width, height); +var perspectiveCamera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 ); +var orthoCamera = new THREE.OrthographicCamera( 75, width / height, 0.1, 1000 ); +perspectiveCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position +perspectiveCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position +perspectiveCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position +orthoCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position +orthoCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position +orthoCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position +var x_effect = new THREE.StereoEffect(x_renderer); +e.appendChild (x_renderer.domElement); +e._xseen.renderer = { +'canvas' : e._xseen.sceneInfo.scene, +'width' : width, +'height' : height, +'cameras' : { +'perspective' : perspectiveCamera, +'ortho' : orthoCamera, +'stereo' : perspectiveCamera, +}, // Removed .sceneInfo camera because this node defines the camera +'effects' : x_effect, +'renderEffects' : { +'normal' : x_renderer, +'perspective' : x_renderer, +'ortho' : x_renderer, +'stereo' : x_effect, +}, +'activeRender' : {}, +'activeCamera' : {}, +'controls' : {}, // Used for navigation +}; +e._xseen.renderer.activeRender = e._xseen.renderer.renderEffects.normal; +e._xseen.renderer.activeCamera = e._xseen.renderer.cameras.perspective; +}, +'fin' : function (e,p) +{ +e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +console.log('Adding child of type ' + child.type + ' (' + child.name + ')'); +e._xseen.renderer.canvas.add(child); +}); +xseen.dumpSceneGraph (); +var vp = xseen.sceneInfo[0].stacks.Viewpoints.getActive(); +var nav = xseen.sceneInfo[0].stacks.Navigation.getActive(); +var currentCamera = e._xseen.renderer.activeCamera; +var currentRenderer = e._xseen.renderer.activeRender; +currentCamera.position.x = vp.position.x; +currentCamera.position.y = vp.position.y; +currentCamera.position.z = vp.position.z; +e._xseen.renderer.controls = xseen.Navigation.setup[nav.setup] (currentCamera, currentRenderer); +xseen.debug.logInfo("Ready to kick off rendering loop"); +xseen.renderFrame(); +}, +}; +xseen.node.env_Background = { +'init' : function (e,p) +{ +var color = new THREE.Color(e._xseen.fields.skycolor[0], e._xseen.fields.skycolor[1], e._xseen.fields.skycolor[2]); +var textureCube = new THREE.CubeTextureLoader() +.load ([e._xseen.fields.srcright, +e._xseen.fields.srcleft, +e._xseen.fields.srctop, +e._xseen.fields.srcbottom, +e._xseen.fields.srcfront, +e._xseen.fields.srcback], +this.loadSuccess({'e':e, 'p':p}) +); +e._xseen.sceneInfo.scene.background = color; +}, +'fin' : function (e,p) +{ +p._xseen.appearance = e._xseen.material; +}, +'loadSuccess' : function (userdata) +{ +var e = userdata.e; +var p = userdata.p; +return function (textureCube) +{ +e._xseen.processedUrl = true; +e._xseen.loadTexture = textureCube; +e._xseen.sceneInfo.scene.background = textureCube; +} +}, +}; +xseen.node.x_Animate = { +'init' : function (e,p) +{ +var delay = e._xseen.fields.delay * 1000; // Convert to milliseconds +var duration = e._xseen.fields.duration * 1000; // Convert to milliseconds +var repeat = (e._xseen.fields.repeat < 0) ? Infinity : e._xseen.fields.repeat; +var interpolator = e._xseen.fields.interpolator; +var easing = e._xseen.fields.easing; +var fields = xseen.parseTable[p.localName.toLowerCase()].fields; +var fieldIndex = xseen.parseTable[p.localName.toLowerCase()].fieldIndex; +var toField = e._xseen.fields.field; +var toFieldIndex = fieldIndex[toField]; +if (typeof(fields[toFieldIndex]) === 'undefined') { +xseen.debug.logInfo("Field '" + toField + "' not found in parent (" + p.localName.toLowerCase() + "). No animation performed."); +return; +} +var fieldObject = fields[toFieldIndex].clone().setFieldName('to'); // Parse table entry for 'toField' +var to = xseen.nodes._parseField(fieldObject, e); // Parsed data -- need to convert to THREE format +var interpolation; +if (fieldObject.type == 'SFVec3f') { +interpolation = TWEEN.Interpolation.Linear; +to = xseen.types.Vector3(to); +xseen.debug.logInfo("Interpolating field '" + toField + "' as 3-space."); +} else if (fieldObject.type == 'SFColor') { +interpolation = this.Interpolator.color; +to = new THREE.Color (xseen.types.Color3toInt(to)); +xseen.debug.logInfo("Interpolation field '" + toField + "' as color."); +} else if (fieldObject.type == 'SFRotation') { +interpolation = this.Interpolator.slerp; +to = xseen.types.Rotation2Quat(to); +xseen.debug.logInfo("Interpolation field '" + toField + "' as rotation."); +} else { +xseen.debug.logInfo("Field '" + toField + "' not converted to THREE format. No animation performed."); +return; +} +var fieldTHREE = p._xseen.animate[toField]; // THREE field for animation +var tween = new TWEEN.Tween(fieldTHREE) +.to(to, duration) +.delay(delay) +.repeat(repeat) +.interpolation(interpolation); +var easingType = e._xseen.fields.easingtype; +easingType = easingType.charAt(0).toUpperCase() + easingType.slice(1); +easing = (easingType != 'Linear' && easing == '') ? 'inout' : easing; +if (easing != '') { +easing = easing.replace('in', 'In').replace('out', 'Out'); +easingType = (easingType == 'Linear') ? 'Quadratic' : easingType; +e._xseen.fields.easing = easing; +e._xseen.fields.easingtype = easingType; +tween.easing(TWEEN.Easing[easingType][easing]); +} +e._xseen.initialValue = fieldTHREE.clone(); +e._xseen.animatingField = fieldTHREE; +e._xseen.handlers = {}; +e._xseen.handlers.setstart = this.setstart; +e._xseen.handlers.setstop = this.setstop; +e._xseen.handlers.setpause = this.setpause; +e._xseen.handlers.setresetstart = this.setresetstart; +e._xseen.animating = tween; +p._xseen.animation.push (tween); +tween.start(); +}, +'fin' : function (e,p) {}, +'setstart' : function (ev) +{ +console.log ('Starting animation'); +this.destination._xseen.animating.start(); +}, +'setstop' : function (ev) +{ +console.log ('Stopping animation'); +this.destination._xseen.animating.stop(); +}, +'setpause' : function (ev) +{ +console.log ('Pausing (really stopping) animation'); +this.destination._xseen.animating.stop(); +}, +'setresetstart' : function (ev) // TODO: Create seperate 'reset' method +{ +console.log ('Reset and start animation'); +this.destination._xseen.animatingField = this.destination._xseen.initialValue; +this.destination._xseen.animating.start(); +}, +'Interpolator' : { +'slerp' : function (v,k) +{ +var m = v.length - 1; +var f = m * k; +var i = Math.floor(f); +if (k < 0) { +return v[0].slerp(v[1], f); +} +if (k > 1) { +return v[m].slerp(v[m-1], m-f); +} +return v[i].slerp (v[i + 1 > m ? m : i + 1], f-i); +}, +'color' : function (v,k) +{ +var m = v.length - 1; +var f = m * k; +var i = Math.floor(f); +var fn = this.slerpCompute; +if (k < 0) { +return v[0].lerp(v[1], f); +} +if (k > 1) { +return v[m].lerp(v[m-1], m-f); +} +return v[i].lerp (v[i + 1 > m ? m : i + 1], f - i); +}, +}, +}; +xseen._addAframeAppearance = function (node) { +node +.addField('ambient-occlusion-map', 'SFString', '') +.addField('ambient-occlusion-map-intensity', 'SFFloat', 1) +.addField('ambient-occlusion-texture-offset', 'SFVec2f', '0 0') +.addField('ambient-occlusion-texture-repeat', 'SFVec2f', '1 1') +.addField('color', 'Color', '#FFF') +.addField('displacement-bias', 'SFFloat', 0.5) +.addField('displacement-map', 'SFString', '') +.addField('displacement-scale', 'SFFloat', 1) +.addField('displacement-texture-offset', 'SFVec2f', '0 0') +.addField('displacement-texture-repeat', 'SFVec2f', '1 1') +.addField('env-map', 'SFString', '') +.addField('fog', 'SFBool', true) +.addField('metalness', 'SFFloat', 0) +.addField('normal-map', 'SFString', '') +.addField('normal-scale', 'SFVec2f', '1 1') +.addField('normal-texture-offset', 'SFVec2f', '0 0') +.addField('normal-texture-repeat', 'SFVec2f', '1 1') +.addField('repeat', 'SFVec2f', '1 1') +.addField('roughness', 'SFFloat', 0.5) +.addField('spherical-env-map', 'SFString', '') +.addField('src', 'SFString', '') +.addField('wireframe', 'SFBool', false) +.addField('wireframe-linewidth', 'SFInt', 2) +.addNode(); +} +xseen.nodes._defineNode('a-entity', 'A-Frame', 'af_Entity') +.addField('geometry', 'SFString', '') +.addField('material', 'SFString', '') +.addField('light', 'SFString', '') +.addNode(); +var node; +node = xseen.nodes._defineNode('a-box', 'A-Frame', 'af_Box') +.addField('depth', 'SFFloat', 1) +.addField('height', 'SFFloat', 1) +.addField('width', 'SFFloat', 512) +.addField('segments-depth', 'SFInt', 1) +.addField('segments-height', 'SFInt', 1) +.addField('segments-width', 'SFInt', 1); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-cone', 'A-Frame', 'af_Cone') +.addField('height', 'SFFloat', 1) +.addField('radius', 'SFFloat', 1) +.addField('open-ended', 'SFBool', false) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 1) +.addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-cylinder', 'A-Frame', 'af_Cylinder') +.addField('height', 'SFFloat', 1) +.addField('open-ended', 'SFBool', false) +.addField('radius-bottom', 'SFFloat', 1) +.addField('radius-top', 'SFFloat', 1) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 1) +.addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-dodecahedron', 'A-Frame', 'af_Dodecahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-icosahedron', 'A-Frame', 'af_Icosahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-octahedron', 'A-Frame', 'af_Octahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-sphere', 'A-Frame', 'af_Sphere') +.addField('radius', 'SFFloat', 1) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 180) +.addField('phi-start', 'SFFloat', 0) +.addField('phi-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 18) +.addField('segments-width', 'SFInt', 36); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-tetrahedron', 'A-Frame', 'af_Tetrahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-torus', 'A-Frame', 'af_Torus') +.addField('radius', 'SFFloat', 2) +.addField('tube', 'SFFloat', 1) +.addField('arc', 'SFFloat', 360) +.addField('segments-radial', 'SFInt', 8) +.addField('segments-tubular', 'SFInt', 6); +xseen._addAframeAppearance (node); +xseen.nodes._defineNode('a-assets', 'A-Frame', 'af_Assets') +.addNode(); +xseen.nodes._defineNode('a-asset-item', 'A-Frame', 'af_AssetItem') +.addField('src', 'SFString', '') +.addNode(); +xseen.nodes._defineNode('a-mixin', 'A-Frame', 'af_Mixin') +.addField('*', 'SFString', '') +.addNode(); +xseen.nodes._defineNode('Cone', 'Geometry3D', 'geometry3D_Cone') +.addField('bottomRadius', 'SFFloat', 1) +.addField('height', 'SFFloat', 2) +.addField('bottom', 'SFBool', true) +.addField('side', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode('Box', 'Geometry3D', 'geometry3D_Box') +.addField('size', 'SFVec3f', [1,1,1]) +.addNode(); +xseen.nodes._defineNode('Sphere', 'Geometry3D', 'geometry3D_Sphere') +.addField('radius', 'SFFloat', '1') +.addNode(); +xseen.nodes._defineNode('Cylinder', 'Geometry3D', 'geometry3D_Cylinder') +.addField('radius', 'SFFloat', 1) +.addField('height', 'SFFloat', 2) +.addField('bottom', 'SFBool', true) +.addField('side', 'SFBool', true) +.addField('top', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode ('Material', 'Appearance', 'appearance_Material') +.addField({name:'diffuseColor', datatype:'SFColor', defaultValue:[.8,.8,.8], animatable:true}) +.addField({name:'emissiveColor',datatype: 'SFColor', defaultValue:[0,0,0], animatable:true}) +.addField({name:'specularColor', datatype:'SFColor', defaultValue:[0,0,0], animatable:true}) +.addField({name:'transparency', datatype:'SFFloat', defaultValue:'0', animatable:true}) +.addField({name:'shininess', datatype:'SFFloat', defaultValue:'0', animatable:true}) +.addNode(); +xseen.nodes._defineNode ('Transform', 'Grouping', 'grouping_Transform') +.addField({name:'translation', datatype:'SFVec3f', defaultValue:[0,0,0], animatable:true}) +.addField({name:'scale', datatype:'SFVec3f', defaultValue:[1,1,1], animatable:true}) +.addField({name:'rotation', datatype:'SFRotation', defaultValue:xseen.types.SFRotation('0 1 0 0',''), animatable:true}) +.addNode(); +xseen.nodes._defineNode ('Group', 'Grouping', 'grouping_Transform') +.addNode(); +xseen.nodes._defineNode ('Light', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) // DirectionalLight +.addField('location', 'SFVec3f', [0,0,0]) // PointLight & SpotLight +.addField('radius', 'SFFloat', '100') // PointLight & SpotLight +.addField('attenuation', 'SFVec3f', [1,0,0]) // PointLight & SpotLight +.addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // SpotLight +.addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // SpotLight +.addField('color', 'SFColor', [1,1,1]) // General +.addField('intensity', 'SFFloat', '1') // General +.addField({name:'type', datatype:'EnumerateString', defaultValue:'Directional', enumerated:['Directional', 'Spot', 'Point'], animatable:true}) +.addNode(); +xseen.nodes._defineNode ('DirectionalLight', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Directional') +.addNode(); +xseen.nodes._defineNode ('PointLight', 'Lighting', 'lighting_Light') +.addField('location', 'SFVec3f', [0,0,0]) +.addField('radius', 'SFFloat', '100') +.addField('attenuation', 'SFVec3f', [1,0,0]) +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Point') +.addNode(); +xseen.nodes._defineNode ('SpotLight', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) +.addField('radius', 'SFFloat', '100') +.addField('attenuation', 'SFVec3f', [1,0,0]) +.addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // pi/4 +.addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // pi/2 +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Spot') +.addNode(); +xseen.nodes._defineNode ('Viewpoint', 'Controls', 'unk_Viewpoint') +.addField('position', 'SFVec3f', '0 0 10') +.addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) +.addField('description', 'SFString', '') +.addField({name:'cameratype', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) +.addField({name:'type', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) +.addField({name:'motion', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'turntable', 'tilt'], animatable:false}) +.addField('motionspeed', 'SFFloat', 16) +.addField('active', 'SFBool', true) // incoming event +.addNode(); +xseen.nodes._defineNode ('NavigationMode', 'Controls', 'controls_Navigation') +.addField('speed', 'SFFloat', 1.) +.addField({name:'type', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'orbit', 'fly', 'examine', 'trackball'], animatable:false}) +.addNode(); +xseen.nodes._defineNode ('Camera', 'Controls', 'unk_Viewpoint') +.addField('position', 'SFVec3f', [0,0,10]) +.addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) +.addNode(); +xseen.nodes._defineNode ('Inline', 'Networking', 'networking_Inline') +.addField('url', 'SFString', '') +.addNode(); +xseen.nodes._defineNode ('scene', 'Core', 'core_Scene') +.addNode(); +xseen.nodes._defineNode ('canvas', 'Core', 'core_NOOP') +.addNode(); +xseen.nodes._defineNode ('WorldInfo', 'Core', 'core_WorldInfo') +.addNode(); +xseen.nodes._defineNode ('Appearance', 'Appearance', 'appearance_Appearance') +.addNode(); +xseen.nodes._defineNode ('ImageTexture', 'Appearance', 'appearance_ImageTexture') +.addField('url', 'SFString', '') +.addField('repeatS', 'SFBool', true) +.addField('repeatT', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode ('Shape', 'Shape', 'unk_Shape') +.addNode(); +xseen.nodes._defineNode('Background', 'Environmental', 'env_Background') +.addField('skyColor', 'SFColor', [0,0,0]) +.addField('srcFront', 'SFString', '') +.addField('srcBack', 'SFString', '') +.addField('srcTop', 'SFString', '') +.addField('srcBottom', 'SFString', '') +.addField('srcLeft', 'SFString', '') +.addField('srcRight', 'SFString', '') +.addField('backgroundIsCube', 'SFBool', 'true') +.addNode(); +xseen.nodes._defineNode('TriangleSet', 'Geometry', 'geometry_TriangleSet') +.addField('ccw', 'SFBool', 'true') +.addField('colorPerVertex', 'SFBool', 'true') +.addField('solid', 'SFBool', 'true') +.addNode(); +xseen.nodes._defineNode('IndexedTriangleSet', 'Geometry', 'geometry_IndexedTriangleSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('index', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('Coordinate', 'Geometry', 'geometry_Coordinate') +.addField('point', 'MFVec3f', []) +.addNode(); +xseen.nodes._defineNode('Normal', 'Geometry', 'geometry_Normal') +.addField('vector', 'MFVec3f', []) +.addNode(); +xseen.nodes._defineNode('Color', 'Geometry', 'geometry_Color') +.addField('color', 'MFColor', []) +.addNode(); +xseen.nodes._defineNode('IndexedFaceSet', 'Geometry', 'geometry_IndexedFaceSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('coordIndex', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('IndexedQuadSet', 'Geometry', 'geometry_IndexedQuadSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('index', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('QuadSet', 'Geometry', 'geometry_QuadSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode('model', 'XSeen', 'x_Model') +.addField('src', 'SFString', '') +.addField('playonload', 'SFString', '') +.addField('duration', 'SFFloat', '-1') +.addNode(); +xseen.nodes._defineNode('animate', 'XSeen', 'x_Animate') +.addField('field', 'SFString', '') +.addField('to', 'MFFloat', '') // Needs to be 'field' datatype. That is not known until node-parse. For now insist on numeric array +.addField('delay', 'SFTime', 0) +.addField('duration', 'SFTime', 0) +.addField('repeat', 'SFInt', 0) +.addField({name:'interpolator', datatype:'EnumerateString', defaultValue:'position', enumerated:['position', 'rotation', 'color'], animatable:false}) +.addField({name:'Easing', datatype:'EnumerateString', defaultValue:'', enumerated:['', 'in', 'out', 'inout'], animatable:false}) +.addField({name:'EasingType', datatype:'EnumerateString', defaultValue:'linear', enumerated:['linear', 'quadratic', 'sinusoidal', 'exponential', 'elastic', 'bounce'], animatable:false}) +.addField('start', 'SFBool', true) // incoming event, need to set timer trigger +.addField('stop', 'SFBool', true) // incoming event, need to set timer trigger +.addField('resetstart', 'SFBool', true) // incoming event, need to set timer trigger +.addField('pause', 'SFBool', true) // incoming event, need to set timer trigger +.addNode(); +xseen.nodes._defineNode('route', 'XSeen', 'x_Route') +.addField('source', 'SFString', '') +.addField('event', 'SFString', '') +.addField('destination', 'SFString', '') +.addField('field', 'SFString', '') +.addField('handler', 'SFString', '') +.addNode(); \ No newline at end of file diff --git a/Release/XSeen.0.4.6+25_0e9dbd1.js b/Release/XSeen.0.4.6+25_0e9dbd1.js new file mode 100644 index 0000000..7769d26 --- /dev/null +++ b/Release/XSeen.0.4.6+25_0e9dbd1.js @@ -0,0 +1,14404 @@ +/* + * XSeen V0.4.6+25_0e9dbd1 + * Built Tue Jul 11 08:10:44 2017 + * + +Dual licensed under the MIT and GPL licenses. + +==[MIT]==================================================================== +Copyright (c) 2017, Daly Realism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +==[GPL]==================================================================== + +XSeen - Declarative 3D for HTML + +Copyright (C) 2017, Daly Realism + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +=== COPYRIGHT +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (C) 2017, Daly Realism for XSeen +Copyright, Fraunhofer for X3DOM +Copyright, Mozilla for A-Frame +Copyright, THREE and Khronos for various parts of THREE.js +Copyright (C) 2017, John Carlson for JSON->XML converter (JSONParser.js) + +=== +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + */ +// File: utils/ColladaLoader.js +/** +* @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com +* @author Tony Parisi / http://www.tonyparisi.com/ +*/ + +THREE.ColladaLoader = function () { + + var COLLADA = null; + var scene = null; + var visualScene; + var kinematicsModel; + + var readyCallbackFunc = null; + + var sources = {}; + var images = {}; + var animations = {}; + var controllers = {}; + var geometries = {}; + var materials = {}; + var effects = {}; + var cameras = {}; + var lights = {}; + + var animData; + var kinematics; + var visualScenes; + var kinematicsModels; + var baseUrl; + var morphs; + var skins; + + var flip_uv = true; + var preferredShading = THREE.SmoothShading; + + var options = { + // Force Geometry to always be centered at the local origin of the + // containing Mesh. + centerGeometry: false, + + // Axis conversion is done for geometries, animations, and controllers. + // If we ever pull cameras or lights out of the COLLADA file, they'll + // need extra work. + convertUpAxis: false, + + subdivideFaces: true, + + upAxis: 'Y', + + // For reflective or refractive materials we'll use this cubemap + defaultEnvMap: null + + }; + + var colladaUnit = 1.0; + var colladaUp = 'Y'; + var upConversion = null; + + function load ( url, readyCallback, progressCallback, failCallback ) { + + var length = 0; + + if ( document.implementation && document.implementation.createDocument ) { + + var request = new XMLHttpRequest(); + + request.onreadystatechange = function() { + + if ( request.readyState === 4 ) { + + if ( request.status === 0 || request.status === 200 ) { + + if ( request.response ) { + + readyCallbackFunc = readyCallback; + parse( request.response, undefined, url ); + + } else { + + if ( failCallback ) { + + failCallback( { type: 'error', url: url } ); + + } else { + + console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); + + } + + } + + }else{ + + if( failCallback ){ + + failCallback( { type: 'error', url: url } ); + + }else{ + + console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' ); + + } + + } + + } else if ( request.readyState === 3 ) { + + if ( progressCallback ) { + + if ( length === 0 ) { + + length = request.getResponseHeader( "Content-Length" ); + + } + + progressCallback( { total: length, loaded: request.responseText.length } ); + + } + + } + + }; + + request.open( "GET", url, true ); + request.send( null ); + + } else { + + alert( "Don't know how to parse XML!" ); + + } + + } + + function parse( text, callBack, url ) { + + COLLADA = new DOMParser().parseFromString( text, 'text/xml' ); + callBack = callBack || readyCallbackFunc; + + if ( url !== undefined ) { + + var parts = url.split( '/' ); + parts.pop(); + baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; + + } + + parseAsset(); + setUpConversion(); + images = parseLib( "library_images image", _Image, "image" ); + materials = parseLib( "library_materials material", Material, "material" ); + effects = parseLib( "library_effects effect", Effect, "effect" ); + geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); + cameras = parseLib( "library_cameras camera", Camera, "camera" ); + lights = parseLib( "library_lights light", Light, "light" ); + controllers = parseLib( "library_controllers controller", Controller, "controller" ); + animations = parseLib( "library_animations animation", Animation, "animation" ); + visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); + kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); + + morphs = []; + skins = []; + + visualScene = parseScene(); + scene = new THREE.Group(); + + for ( var i = 0; i < visualScene.nodes.length; i ++ ) { + + scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); + + } + + // unit conversion + scene.scale.multiplyScalar( colladaUnit ); + + createAnimations(); + + kinematicsModel = parseKinematicsModel(); + createKinematics(); + + var result = { + + scene: scene, + morphs: morphs, + skins: skins, + animations: animData, + kinematics: kinematics, + dae: { + images: images, + materials: materials, + cameras: cameras, + lights: lights, + effects: effects, + geometries: geometries, + controllers: controllers, + animations: animations, + visualScenes: visualScenes, + visualScene: visualScene, + scene: visualScene, + kinematicsModels: kinematicsModels, + kinematicsModel: kinematicsModel + } + + }; + + if ( callBack ) { + + callBack( result ); + + } + + return result; + + } + + function setPreferredShading ( shading ) { + + preferredShading = shading; + + } + + function parseAsset () { + + var elements = COLLADA.querySelectorAll('asset'); + + var element = elements[0]; + + if ( element && element.childNodes ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'unit': + + var meter = child.getAttribute( 'meter' ); + + if ( meter ) { + + colladaUnit = parseFloat( meter ); + + } + + break; + + case 'up_axis': + + colladaUp = child.textContent.charAt(0); + break; + + } + + } + + } + + } + + function parseLib ( q, classSpec, prefix ) { + + var elements = COLLADA.querySelectorAll(q); + + var lib = {}; + + var i = 0; + + var elementsLength = elements.length; + + for ( var j = 0; j < elementsLength; j ++ ) { + + var element = elements[j]; + var daeElement = ( new classSpec() ).parse( element ); + + if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); + lib[ daeElement.id ] = daeElement; + + } + + return lib; + + } + + function parseScene() { + + var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; + + if ( sceneElement ) { + + var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); + return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; + + } else { + + return null; + + } + + } + + function parseKinematicsModel() { + + var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; + + if ( kinematicsModelElement ) { + + var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); + return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; + + } else { + + return null; + + } + + } + + function createAnimations() { + + animData = []; + + // fill in the keys + recurseHierarchy( scene ); + + } + + function recurseHierarchy( node ) { + + var n = visualScene.getChildById( node.colladaId, true ), + newData = null; + + if ( n && n.keys ) { + + newData = { + fps: 60, + hierarchy: [ { + node: n, + keys: n.keys, + sids: n.sids + } ], + node: node, + name: 'animation_' + node.name, + length: 0 + }; + + animData.push(newData); + + for ( var i = 0, il = n.keys.length; i < il; i ++ ) { + + newData.length = Math.max( newData.length, n.keys[i].time ); + + } + + } else { + + newData = { + hierarchy: [ { + keys: [], + sids: [] + } ] + } + + } + + for ( var i = 0, il = node.children.length; i < il; i ++ ) { + + var d = recurseHierarchy( node.children[i] ); + + for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { + + newData.hierarchy.push( { + keys: [], + sids: [] + } ); + + } + + } + + return newData; + + } + + function calcAnimationBounds () { + + var start = 1000000; + var end = -start; + var frames = 0; + var ID; + for ( var id in animations ) { + + var animation = animations[ id ]; + ID = ID || animation.id; + for ( var i = 0; i < animation.sampler.length; i ++ ) { + + var sampler = animation.sampler[ i ]; + + sampler.create(); + + start = Math.min( start, sampler.startTime ); + end = Math.max( end, sampler.endTime ); + frames = Math.max( frames, sampler.input.length ); + + } + + } + + return { start:start, end:end, frames:frames,ID:ID }; + + } + + function createMorph ( geometry, ctrl ) { + + var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; + + if ( !morphCtrl || !morphCtrl.morph ) { + + console.log("could not find morph controller!"); + return; + + } + + var morph = morphCtrl.morph; + + for ( var i = 0; i < morph.targets.length; i ++ ) { + + var target_id = morph.targets[ i ]; + var daeGeometry = geometries[ target_id ]; + + if ( !daeGeometry.mesh || + !daeGeometry.mesh.primitives || + !daeGeometry.mesh.primitives.length ) { + continue; + } + + var target = daeGeometry.mesh.primitives[ 0 ].geometry; + + if ( target.vertices.length === geometry.vertices.length ) { + + geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); + + } + + } + + geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); + + } + + function createSkin ( geometry, ctrl, applyBindShape ) { + + var skinCtrl = controllers[ ctrl.url ]; + + if ( !skinCtrl || !skinCtrl.skin ) { + + console.log( "could not find skin controller!" ); + return; + + } + + if ( !ctrl.skeleton || !ctrl.skeleton.length ) { + + console.log( "could not find the skeleton for the skin!" ); + return; + + } + + var skin = skinCtrl.skin; + var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); + var hierarchy = []; + + applyBindShape = applyBindShape !== undefined ? applyBindShape : true; + + var bones = []; + geometry.skinWeights = []; + geometry.skinIndices = []; + + //createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 ); + //createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights ); + + /* + geometry.animation = { + name: 'take_001', + fps: 30, + length: 2, + JIT: true, + hierarchy: hierarchy + }; + */ + + if ( applyBindShape ) { + + for ( var i = 0; i < geometry.vertices.length; i ++ ) { + + geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); + + } + + } + + } + + function setupSkeleton ( node, bones, frame, parent ) { + + node.world = node.world || new THREE.Matrix4(); + node.localworld = node.localworld || new THREE.Matrix4(); + node.world.copy( node.matrix ); + node.localworld.copy( node.matrix ); + + if ( node.channels && node.channels.length ) { + + var channel = node.channels[ 0 ]; + var m = channel.sampler.output[ frame ]; + + if ( m instanceof THREE.Matrix4 ) { + + node.world.copy( m ); + node.localworld.copy(m); + if (frame === 0) + node.matrix.copy(m); + } + + } + + if ( parent ) { + + node.world.multiplyMatrices( parent, node.world ); + + } + + bones.push( node ); + + for ( var i = 0; i < node.nodes.length; i ++ ) { + + setupSkeleton( node.nodes[ i ], bones, frame, node.world ); + + } + + } + + function setupSkinningMatrices ( bones, skin ) { + + // FIXME: this is dumb... + + for ( var i = 0; i < bones.length; i ++ ) { + + var bone = bones[ i ]; + var found = -1; + + if ( bone.type != 'JOINT' ) continue; + + for ( var j = 0; j < skin.joints.length; j ++ ) { + + if ( bone.sid === skin.joints[ j ] ) { + + found = j; + break; + + } + + } + + if ( found >= 0 ) { + + var inv = skin.invBindMatrices[ found ]; + + bone.invBindMatrix = inv; + bone.skinningMatrix = new THREE.Matrix4(); + bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) + bone.animatrix = new THREE.Matrix4(); + + bone.animatrix.copy(bone.localworld); + bone.weights = []; + + for ( var j = 0; j < skin.weights.length; j ++ ) { + + for (var k = 0; k < skin.weights[ j ].length; k ++ ) { + + var w = skin.weights[ j ][ k ]; + + if ( w.joint === found ) { + + bone.weights.push( w ); + + } + + } + + } + + } else { + + console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); + + bone.skinningMatrix = new THREE.Matrix4(); + bone.weights = []; + + } + } + + } + + //Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix + function flattenSkeleton(skeleton) { + + var list = []; + var walk = function(parentid, node, list) { + + var bone = {}; + bone.name = node.sid; + bone.parent = parentid; + bone.matrix = node.matrix; + var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; + bone.matrix.decompose(data[0], data[1], data[2]); + + bone.pos = [ data[0].x,data[0].y,data[0].z ]; + + bone.scl = [ data[2].x,data[2].y,data[2].z ]; + bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; + list.push(bone); + + for (var i in node.nodes) { + + walk(node.sid, node.nodes[i], list); + + } + + }; + + walk(-1, skeleton, list); + return list; + + } + + //Move the vertices into the pose that is proper for the start of the animation + function skinToBindPose(geometry,skeleton,skinController) { + + var bones = []; + setupSkeleton( skeleton, bones, -1 ); + setupSkinningMatrices( bones, skinController.skin ); + var v = new THREE.Vector3(); + var skinned = []; + + for (var i = 0; i < geometry.vertices.length; i ++) { + + skinned.push(new THREE.Vector3()); + + } + + for ( i = 0; i < bones.length; i ++ ) { + + if ( bones[ i ].type != 'JOINT' ) continue; + + for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { + + var w = bones[ i ].weights[ j ]; + var vidx = w.index; + var weight = w.weight; + + var o = geometry.vertices[vidx]; + var s = skinned[vidx]; + + v.x = o.x; + v.y = o.y; + v.z = o.z; + + v.applyMatrix4( bones[i].skinningMatrix ); + + s.x += (v.x * weight); + s.y += (v.y * weight); + s.z += (v.z * weight); + } + + } + + for (var i = 0; i < geometry.vertices.length; i ++) { + + geometry.vertices[i] = skinned[i]; + + } + + } + + function applySkin ( geometry, instanceCtrl, frame ) { + + var skinController = controllers[ instanceCtrl.url ]; + + frame = frame !== undefined ? frame : 40; + + if ( !skinController || !skinController.skin ) { + + console.log( 'ColladaLoader: Could not find skin controller.' ); + return; + + } + + if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { + + console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); + return; + + } + + var animationBounds = calcAnimationBounds(); + var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); + + //flatten the skeleton into a list of bones + var bonelist = flattenSkeleton(skeleton); + var joints = skinController.skin.joints; + + //sort that list so that the order reflects the order in the joint list + var sortedbones = []; + for (var i = 0; i < joints.length; i ++) { + + for (var j = 0; j < bonelist.length; j ++) { + + if (bonelist[j].name === joints[i]) { + + sortedbones[i] = bonelist[j]; + + } + + } + + } + + //hook up the parents by index instead of name + for (var i = 0; i < sortedbones.length; i ++) { + + for (var j = 0; j < sortedbones.length; j ++) { + + if (sortedbones[i].parent === sortedbones[j].name) { + + sortedbones[i].parent = j; + + } + + } + + } + + + var i, j, w, vidx, weight; + var v = new THREE.Vector3(), o, s; + + // move vertices to bind shape + for ( i = 0; i < geometry.vertices.length; i ++ ) { + geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); + } + + var skinIndices = []; + var skinWeights = []; + var weights = skinController.skin.weights; + + // hook up the skin weights + // TODO - this might be a good place to choose greatest 4 weights + for ( var i =0; i < weights.length; i ++ ) { + + var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); + var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); + + skinIndices.push(indicies); + skinWeights.push(weight); + + } + + geometry.skinIndices = skinIndices; + geometry.skinWeights = skinWeights; + geometry.bones = sortedbones; + // process animation, or simply pose the rig if no animation + + //create an animation for the animated bones + //NOTE: this has no effect when using morphtargets + var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; + + for (var j = 0; j < sortedbones.length; j ++) { + + animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); + + } + + console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); + + + + skinToBindPose(geometry, skeleton, skinController); + + + for ( frame = 0; frame < animationBounds.frames; frame ++ ) { + + var bones = []; + var skinned = []; + // process the frame and setup the rig with a fresh + // transform, possibly from the bone's animation channel(s) + + setupSkeleton( skeleton, bones, frame ); + setupSkinningMatrices( bones, skinController.skin ); + + for (var i = 0; i < bones.length; i ++) { + + for (var j = 0; j < animationdata.hierarchy.length; j ++) { + + if (animationdata.hierarchy[j].name === bones[i].sid) { + + var key = {}; + key.time = (frame / 30); + key.matrix = bones[i].animatrix; + + if (frame === 0) + bones[i].matrix = key.matrix; + + var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; + key.matrix.decompose(data[0], data[1], data[2]); + + key.pos = [ data[0].x,data[0].y,data[0].z ]; + + key.scl = [ data[2].x,data[2].y,data[2].z ]; + key.rot = data[1]; + + animationdata.hierarchy[j].keys.push(key); + + } + + } + + } + + geometry.animation = animationdata; + + } + + } + + function createKinematics() { + + if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { + kinematics = undefined; + return; + } + + var jointMap = {}; + + var _addToMap = function( jointIndex, parentVisualElement ) { + + var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); + var colladaNode = visualScene.getChildById( parentVisualElementId, true ); + var joint = kinematicsModel.joints[ jointIndex ]; + + scene.traverse(function( node ) { + + if ( node.colladaId == parentVisualElementId ) { + + jointMap[ jointIndex ] = { + node: node, + transforms: colladaNode.transforms, + joint: joint, + position: joint.zeroPosition + }; + + } + + }); + + }; + + kinematics = { + + joints: kinematicsModel && kinematicsModel.joints, + + getJointValue: function( jointIndex ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + return jointData.position; + + } else { + + console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + }, + + setJointValue: function( jointIndex, value ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + var joint = jointData.joint; + + if ( value > joint.limits.max || value < joint.limits.min ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); + + } else if ( joint.static ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); + + } else { + + var threejsNode = jointData.node; + var axis = joint.axis; + var transforms = jointData.transforms; + + var matrix = new THREE.Matrix4(); + var m1 = new THREE.Matrix4(); + + for (i = 0; i < transforms.length; i ++ ) { + + var transform = transforms[ i ]; + + // kinda ghetto joint detection + + if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { + + // apply actual joint value here + switch ( joint.type ) { + + case 'revolute': + + matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); + break; + + case 'prismatic': + + matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); + break; + + default: + + console.warn( 'setJointValue: unknown joint type: ' + joint.type ); + break; + + } + + } else { + + switch ( transform.type ) { + + case 'matrix': + + matrix.multiply( transform.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); + + break; + + } + } + } + + // apply the matrix to the threejs node + var elementsFloat32Arr = matrix.elements; + var elements = Array.prototype.slice.call( elementsFloat32Arr ); + + var elementsRowMajor = [ + elements[ 0 ], + elements[ 4 ], + elements[ 8 ], + elements[ 12 ], + elements[ 1 ], + elements[ 5 ], + elements[ 9 ], + elements[ 13 ], + elements[ 2 ], + elements[ 6 ], + elements[ 10 ], + elements[ 14 ], + elements[ 3 ], + elements[ 7 ], + elements[ 11 ], + elements[ 15 ] + ]; + + threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); + threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); + + jointMap[ jointIndex ].position = value; + + } + + } else { + + console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + } + + }; + + var element = COLLADA.querySelector('scene instance_kinematics_scene'); + + if ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_joint_axis': + + var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); + var axis = child.querySelector('axis param').textContent; + var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); + var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); + + if ( visualTargetElement ) { + var parentVisualElement = visualTargetElement.parentElement; + _addToMap(jointIndex, parentVisualElement); + } + + break; + + default: + + break; + + } + + } + } + + } + + function createSceneGraph ( node, parent ) { + + var obj = new THREE.Object3D(); + var skinned = false; + var skinController; + var morphController; + var i, j; + + // FIXME: controllers + + for ( i = 0; i < node.controllers.length; i ++ ) { + + var controller = controllers[ node.controllers[ i ].url ]; + + switch ( controller.type ) { + + case 'skin': + + if ( geometries[ controller.skin.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.skin.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + skinned = true; + skinController = node.controllers[ i ]; + + } else if ( controllers[ controller.skin.source ] ) { + + // urgh: controller can be chained + // handle the most basic case... + + var second = controllers[ controller.skin.source ]; + morphController = second; + // skinController = node.controllers[i]; + + if ( second.morph && geometries[ second.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = second.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + + } + + } + + break; + + case 'morph': + + if ( geometries[ controller.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + morphController = node.controllers[ i ]; + + } + + console.log( 'ColladaLoader: Morph-controller partially supported.' ); + + default: + break; + + } + + } + + // geometries + + var double_sided_materials = {}; + + for ( i = 0; i < node.geometries.length; i ++ ) { + + var instance_geometry = node.geometries[i]; + var instance_materials = instance_geometry.instance_material; + var geometry = geometries[ instance_geometry.url ]; + var used_materials = {}; + var used_materials_array = []; + var num_materials = 0; + var first_material; + + if ( geometry ) { + + if ( !geometry.mesh || !geometry.mesh.primitives ) + continue; + + if ( obj.name.length === 0 ) { + + obj.name = geometry.id; + + } + + // collect used fx for this geometry-instance + + if ( instance_materials ) { + + for ( j = 0; j < instance_materials.length; j ++ ) { + + var instance_material = instance_materials[ j ]; + var mat = materials[ instance_material.target ]; + var effect_id = mat.instance_effect.url; + var shader = effects[ effect_id ].shader; + var material3js = shader.material; + + if ( geometry.doubleSided ) { + + if ( !( instance_material.symbol in double_sided_materials ) ) { + + var _copied_material = material3js.clone(); + _copied_material.side = THREE.DoubleSide; + double_sided_materials[ instance_material.symbol ] = _copied_material; + + } + + material3js = double_sided_materials[ instance_material.symbol ]; + + } + + material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; + used_materials[ instance_material.symbol ] = num_materials; + used_materials_array.push( material3js ); + first_material = material3js; + first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; + num_materials ++; + + } + + } + + var mesh; + var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); + var geom = geometry.mesh.geometry3js; + + if ( num_materials > 1 ) { + + material = new THREE.MultiMaterial( used_materials_array ); + + for ( j = 0; j < geom.faces.length; j ++ ) { + + var face = geom.faces[ j ]; + face.materialIndex = used_materials[ face.daeMaterial ] + + } + + } + + if ( skinController !== undefined ) { + + + applySkin( geom, skinController ); + + if ( geom.morphTargets.length > 0 ) { + + material.morphTargets = true; + material.skinning = false; + + } else { + + material.morphTargets = false; + material.skinning = true; + + } + + + mesh = new THREE.SkinnedMesh( geom, material, false ); + + + //mesh.skeleton = skinController.skeleton; + //mesh.skinController = controllers[ skinController.url ]; + //mesh.skinInstanceController = skinController; + mesh.name = 'skin_' + skins.length; + + + + //mesh.animationHandle.setKey(0); + skins.push( mesh ); + + } else if ( morphController !== undefined ) { + + createMorph( geom, morphController ); + + material.morphTargets = true; + + mesh = new THREE.Mesh( geom, material ); + mesh.name = 'morph_' + morphs.length; + + morphs.push( mesh ); + + } else { + + if ( geom.isLineStrip === true ) { + + mesh = new THREE.Line( geom ); + + } else { + + mesh = new THREE.Mesh( geom, material ); + + } + + } + + obj.add(mesh); + + } + + } + + for ( i = 0; i < node.cameras.length; i ++ ) { + + var instance_camera = node.cameras[i]; + var cparams = cameras[instance_camera.url]; + + var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), + parseFloat(cparams.znear), parseFloat(cparams.zfar)); + + obj.add(cam); + } + + for ( i = 0; i < node.lights.length; i ++ ) { + + var light = null; + var instance_light = node.lights[i]; + var lparams = lights[instance_light.url]; + + if ( lparams && lparams.technique ) { + + var color = lparams.color.getHex(); + var intensity = lparams.intensity; + var distance = lparams.distance; + var angle = lparams.falloff_angle; + + switch ( lparams.technique ) { + + case 'directional': + + light = new THREE.DirectionalLight( color, intensity, distance ); + light.position.set(0, 0, 1); + break; + + case 'point': + + light = new THREE.PointLight( color, intensity, distance ); + break; + + case 'spot': + + light = new THREE.SpotLight( color, intensity, distance, angle ); + light.position.set(0, 0, 1); + break; + + case 'ambient': + + light = new THREE.AmbientLight( color ); + break; + + } + + } + + if (light) { + obj.add(light); + } + } + + obj.name = node.name || node.id || ""; + obj.colladaId = node.id || ""; + obj.layer = node.layer || ""; + obj.matrix = node.matrix; + obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); + + if ( options.centerGeometry && obj.geometry ) { + + var delta = obj.geometry.center(); + delta.multiply( obj.scale ); + delta.applyQuaternion( obj.quaternion ); + + obj.position.sub( delta ); + + } + + for ( i = 0; i < node.nodes.length; i ++ ) { + + obj.add( createSceneGraph( node.nodes[i], node ) ); + + } + + return obj; + + } + + function getJointId( skin, id ) { + + for ( var i = 0; i < skin.joints.length; i ++ ) { + + if ( skin.joints[ i ] === id ) { + + return i; + + } + + } + + } + + function getLibraryNode( id ) { + + var nodes = COLLADA.querySelectorAll('library_nodes node'); + + for ( var i = 0; i < nodes.length; i++ ) { + + var attObj = nodes[i].attributes.getNamedItem('id'); + + if ( attObj && attObj.value === id ) { + + return nodes[i]; + + } + + } + + return undefined; + + } + + function getChannelsForNode ( node ) { + + var channels = []; + var startTime = 1000000; + var endTime = -1000000; + + for ( var id in animations ) { + + var animation = animations[id]; + + for ( var i = 0; i < animation.channel.length; i ++ ) { + + var channel = animation.channel[i]; + var sampler = animation.sampler[i]; + var id = channel.target.split('/')[0]; + + if ( id == node.id ) { + + sampler.create(); + channel.sampler = sampler; + startTime = Math.min(startTime, sampler.startTime); + endTime = Math.max(endTime, sampler.endTime); + channels.push(channel); + + } + + } + + } + + if ( channels.length ) { + + node.startTime = startTime; + node.endTime = endTime; + + } + + return channels; + + } + + function calcFrameDuration( node ) { + + var minT = 10000000; + + for ( var i = 0; i < node.channels.length; i ++ ) { + + var sampler = node.channels[i].sampler; + + for ( var j = 0; j < sampler.input.length - 1; j ++ ) { + + var t0 = sampler.input[ j ]; + var t1 = sampler.input[ j + 1 ]; + minT = Math.min( minT, t1 - t0 ); + + } + } + + return minT; + + } + + function calcMatrixAt( node, t ) { + + var animated = {}; + + var i, j; + + for ( i = 0; i < node.channels.length; i ++ ) { + + var channel = node.channels[ i ]; + animated[ channel.sid ] = channel; + + } + + var matrix = new THREE.Matrix4(); + + for ( i = 0; i < node.transforms.length; i ++ ) { + + var transform = node.transforms[ i ]; + var channel = animated[ transform.sid ]; + + if ( channel !== undefined ) { + + var sampler = channel.sampler; + var value; + + for ( j = 0; j < sampler.input.length - 1; j ++ ) { + + if ( sampler.input[ j + 1 ] > t ) { + + value = sampler.output[ j ]; + //console.log(value.flatten) + break; + + } + + } + + if ( value !== undefined ) { + + if ( value instanceof THREE.Matrix4 ) { + + matrix.multiplyMatrices( matrix, value ); + + } else { + + // FIXME: handle other types + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } + + return matrix; + + } + + function bakeAnimations ( node ) { + + if ( node.channels && node.channels.length ) { + + var keys = [], + sids = []; + + for ( var i = 0, il = node.channels.length; i < il; i ++ ) { + + var channel = node.channels[i], + fullSid = channel.fullSid, + sampler = channel.sampler, + input = sampler.input, + transform = node.getTransformBySid( channel.sid ), + member; + + if ( channel.arrIndices ) { + + member = []; + + for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { + + member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); + + } + + } else { + + member = getConvertedMember( channel.member ); + + } + + if ( transform ) { + + if ( sids.indexOf( fullSid ) === -1 ) { + + sids.push( fullSid ); + + } + + for ( var j = 0, jl = input.length; j < jl; j ++ ) { + + var time = input[j], + data = sampler.getData( transform.type, j, member ), + key = findKey( keys, time ); + + if ( !key ) { + + key = new Key( time ); + var timeNdx = findTimeNdx( keys, time ); + keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); + + } + + key.addTarget( fullSid, transform, member, data ); + + } + + } else { + + console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); + + } + + } + + // post process + for ( var i = 0; i < sids.length; i ++ ) { + + var sid = sids[ i ]; + + for ( var j = 0; j < keys.length; j ++ ) { + + var key = keys[ j ]; + + if ( !key.hasTarget( sid ) ) { + + interpolateKeys( keys, key, j, sid ); + + } + + } + + } + + node.keys = keys; + node.sids = sids; + + } + + } + + function findKey ( keys, time) { + + var retVal = null; + + for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { + + var key = keys[i]; + + if ( key.time === time ) { + + retVal = key; + + } else if ( key.time > time ) { + + break; + + } + + } + + return retVal; + + } + + function findTimeNdx ( keys, time) { + + var ndx = -1; + + for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { + + var key = keys[i]; + + if ( key.time >= time ) { + + ndx = i; + + } + + } + + return ndx; + + } + + function interpolateKeys ( keys, key, ndx, fullSid ) { + + var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), + nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); + + if ( prevKey && nextKey ) { + + var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), + prevTarget = prevKey.getTarget( fullSid ), + nextData = nextKey.getTarget( fullSid ).data, + prevData = prevTarget.data, + data; + + if ( prevTarget.type === 'matrix' ) { + + data = prevData; + + } else if ( prevData.length ) { + + data = []; + + for ( var i = 0; i < prevData.length; ++ i ) { + + data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); + + } + + } + + // Get next key with given sid + + function getNextKeyWith( keys, fullSid, ndx ) { + + for ( ; ndx < keys.length; ndx ++ ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + } + + // Get previous key with given sid + + function getPrevKeyWith( keys, fullSid, ndx ) { + + ndx = ndx >= 0 ? ndx : ndx + keys.length; + + for ( ; ndx >= 0; ndx -- ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + } + + function _Image() { + + this.id = ""; + this.init_from = ""; + + } + + _Image.prototype.parse = function(element) { + + this.id = element.getAttribute('id'); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'init_from' ) { + + this.init_from = child.textContent; + + } + + } + + return this; + + }; + + function Controller() { + + this.id = ""; + this.name = ""; + this.type = ""; + this.skin = null; + this.morph = null; + + } + + Controller.prototype.parse = function( element ) { + + this.id = element.getAttribute('id'); + this.name = element.getAttribute('name'); + this.type = "none"; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'skin': + + this.skin = (new Skin()).parse(child); + this.type = child.nodeName; + break; + + case 'morph': + + this.morph = (new Morph()).parse(child); + this.type = child.nodeName; + break; + + default: + break; + + } + } + + return this; + + }; + + function Morph() { + + this.method = null; + this.source = null; + this.targets = null; + this.weights = null; + + } + + Morph.prototype.parse = function( element ) { + + var sources = {}; + var inputs = []; + var i; + + this.method = element.getAttribute( 'method' ); + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + + for ( i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + var source = ( new Source() ).parse( child ); + sources[ source.id ] = source; + break; + + case 'targets': + + inputs = this.parseInputs( child ); + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + for ( i = 0; i < inputs.length; i ++ ) { + + var input = inputs[ i ]; + var source = sources[ input.source ]; + + switch ( input.semantic ) { + + case 'MORPH_TARGET': + + this.targets = source.read(); + break; + + case 'MORPH_WEIGHT': + + this.weights = source.read(); + break; + + default: + break; + + } + } + + return this; + + }; + + Morph.prototype.parseInputs = function(element) { + + var inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( (new Input()).parse(child) ); + break; + + default: + break; + } + } + + return inputs; + + }; + + function Skin() { + + this.source = ""; + this.bindShapeMatrix = null; + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + } + + Skin.prototype.parse = function( element ) { + + var sources = {}; + var joints, weights; + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_shape_matrix': + + var f = _floats(child.textContent); + this.bindShapeMatrix = getConvertedMat4( f ); + break; + + case 'source': + + var src = new Source().parse(child); + sources[ src.id ] = src; + break; + + case 'joints': + + joints = child; + break; + + case 'vertex_weights': + + weights = child; + break; + + default: + + console.log( child.nodeName ); + break; + + } + } + + this.parseJoints( joints, sources ); + this.parseWeights( weights, sources ); + + return this; + + }; + + Skin.prototype.parseJoints = function ( element, sources ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + var input = ( new Input() ).parse( child ); + var source = sources[ input.source ]; + + if ( input.semantic === 'JOINT' ) { + + this.joints = source.read(); + + } else if ( input.semantic === 'INV_BIND_MATRIX' ) { + + this.invBindMatrices = source.read(); + + } + + break; + + default: + break; + } + + } + + }; + + Skin.prototype.parseWeights = function ( element, sources ) { + + var v, vcount, inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( ( new Input() ).parse( child ) ); + break; + + case 'v': + + v = _ints( child.textContent ); + break; + + case 'vcount': + + vcount = _ints( child.textContent ); + break; + + default: + break; + + } + + } + + var index = 0; + + for ( var i = 0; i < vcount.length; i ++ ) { + + var numBones = vcount[i]; + var vertex_weights = []; + + for ( var j = 0; j < numBones; j ++ ) { + + var influence = {}; + + for ( var k = 0; k < inputs.length; k ++ ) { + + var input = inputs[ k ]; + var value = v[ index + input.offset ]; + + switch ( input.semantic ) { + + case 'JOINT': + + influence.joint = value;//this.joints[value]; + break; + + case 'WEIGHT': + + influence.weight = sources[ input.source ].data[ value ]; + break; + + default: + break; + + } + + } + + vertex_weights.push( influence ); + index += inputs.length; + } + + for ( var j = 0; j < vertex_weights.length; j ++ ) { + + vertex_weights[ j ].index = i; + + } + + this.weights.push( vertex_weights ); + + } + + }; + + function VisualScene () { + + this.id = ""; + this.name = ""; + this.nodes = []; + this.scene = new THREE.Group(); + + } + + VisualScene.prototype.getChildById = function( id, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildById( id, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.getChildBySid = function( sid, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.parse = function( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.nodes = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Node() { + + this.id = ""; + this.name = ""; + this.sid = ""; + this.nodes = []; + this.controllers = []; + this.transforms = []; + this.geometries = []; + this.channels = []; + this.matrix = new THREE.Matrix4(); + + } + + Node.prototype.getChannelForTransform = function( transformSid ) { + + for ( var i = 0; i < this.channels.length; i ++ ) { + + var channel = this.channels[i]; + var parts = channel.target.split('/'); + var id = parts.shift(); + var sid = parts.shift(); + var dotSyntax = (sid.indexOf(".") >= 0); + var arrSyntax = (sid.indexOf("(") >= 0); + var arrIndices; + var member; + + if ( dotSyntax ) { + + parts = sid.split("."); + sid = parts.shift(); + member = parts.shift(); + + } else if ( arrSyntax ) { + + arrIndices = sid.split("("); + sid = arrIndices.shift(); + + for ( var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); + + } + + } + + if ( sid === transformSid ) { + + channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; + return channel; + + } + + } + + return null; + + }; + + Node.prototype.getChildById = function ( id, recursive ) { + + if ( this.id === id ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildById( id, recursive ); + + if ( n ) { + + return n; + + } + + } + + } + + return null; + + }; + + Node.prototype.getChildBySid = function ( sid, recursive ) { + + if ( this.sid === sid ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( n ) { + + return n; + + } + + } + } + + return null; + + }; + + Node.prototype.getTransformBySid = function ( sid ) { + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; + + } + + return null; + + }; + + Node.prototype.parse = function( element ) { + + var url; + + this.id = element.getAttribute('id'); + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.type = element.getAttribute('type'); + this.layer = element.getAttribute('layer'); + + this.type = this.type === 'JOINT' ? this.type : 'NODE'; + + this.nodes = []; + this.transforms = []; + this.geometries = []; + this.cameras = []; + this.lights = []; + this.controllers = []; + this.matrix = new THREE.Matrix4(); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + case 'instance_camera': + + this.cameras.push( ( new InstanceCamera() ).parse( child ) ); + break; + + case 'instance_controller': + + this.controllers.push( ( new InstanceController() ).parse( child ) ); + break; + + case 'instance_geometry': + + this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); + break; + + case 'instance_light': + + this.lights.push( ( new InstanceLight() ).parse( child ) ); + break; + + case 'instance_node': + + url = child.getAttribute( 'url' ).replace( /^#/, '' ); + var iNode = getLibraryNode( url ); + + if ( iNode ) { + + this.nodes.push( ( new Node() ).parse( iNode )) ; + + } + + break; + + case 'rotate': + case 'translate': + case 'scale': + case 'matrix': + case 'lookat': + case 'skew': + + this.transforms.push( ( new Transform() ).parse( child ) ); + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + this.channels = getChannelsForNode( this ); + bakeAnimations( this ); + + this.updateMatrix(); + + return this; + + }; + + Node.prototype.updateMatrix = function () { + + this.matrix.identity(); + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + this.transforms[ i ].apply( this.matrix ); + + } + + }; + + function Transform () { + + this.sid = ""; + this.type = ""; + this.data = []; + this.obj = null; + + } + + Transform.prototype.parse = function ( element ) { + + this.sid = element.getAttribute( 'sid' ); + this.type = element.nodeName; + this.data = _floats( element.textContent ); + this.convert(); + + return this; + + }; + + Transform.prototype.convert = function () { + + switch ( this.type ) { + + case 'matrix': + + this.obj = getConvertedMat4( this.data ); + break; + + case 'rotate': + + this.angle = THREE.Math.degToRad( this.data[3] ); + + case 'translate': + + fixCoords( this.data, -1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + case 'scale': + + fixCoords( this.data, 1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + default: + console.log( 'Can not convert Transform of type ' + this.type ); + break; + + } + + }; + + Transform.prototype.apply = function () { + + var m1 = new THREE.Matrix4(); + + return function ( matrix ) { + + switch ( this.type ) { + + case 'matrix': + + matrix.multiply( this.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); + + break; + + case 'scale': + + matrix.scale( this.obj ); + + break; + + } + + }; + + }(); + + Transform.prototype.update = function ( data, member ) { + + var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; + + switch ( this.type ) { + + case 'matrix': + + if ( ! member ) { + + this.obj.copy( data ); + + } else if ( member.length === 1 ) { + + switch ( member[ 0 ] ) { + + case 0: + + this.obj.n11 = data[ 0 ]; + this.obj.n21 = data[ 1 ]; + this.obj.n31 = data[ 2 ]; + this.obj.n41 = data[ 3 ]; + + break; + + case 1: + + this.obj.n12 = data[ 0 ]; + this.obj.n22 = data[ 1 ]; + this.obj.n32 = data[ 2 ]; + this.obj.n42 = data[ 3 ]; + + break; + + case 2: + + this.obj.n13 = data[ 0 ]; + this.obj.n23 = data[ 1 ]; + this.obj.n33 = data[ 2 ]; + this.obj.n43 = data[ 3 ]; + + break; + + case 3: + + this.obj.n14 = data[ 0 ]; + this.obj.n24 = data[ 1 ]; + this.obj.n34 = data[ 2 ]; + this.obj.n44 = data[ 3 ]; + + break; + + } + + } else if ( member.length === 2 ) { + + var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); + this.obj[ propName ] = data; + + } else { + + console.log('Incorrect addressing of matrix in transform.'); + + } + + break; + + case 'translate': + case 'scale': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + break; + + } + + break; + + case 'rotate': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + case 'ANGLE': + + this.angle = THREE.Math.degToRad( data ); + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + this.angle = THREE.Math.degToRad( data[ 3 ] ); + break; + + } + break; + + } + + }; + + function InstanceController() { + + this.url = ""; + this.skeleton = []; + this.instance_material = []; + + } + + InstanceController.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + this.skeleton = []; + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'skeleton': + + this.skeleton.push( child.textContent.replace(/^#/, '') ); + break; + + case 'bind_material': + + var instances = child.querySelectorAll('instance_material'); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[j]; + this.instance_material.push( (new InstanceMaterial()).parse(instance) ); + + } + + + break; + + case 'extra': + break; + + default: + break; + + } + } + + return this; + + }; + + function InstanceMaterial () { + + this.symbol = ""; + this.target = ""; + + } + + InstanceMaterial.prototype.parse = function ( element ) { + + this.symbol = element.getAttribute('symbol'); + this.target = element.getAttribute('target').replace(/^#/, ''); + return this; + + }; + + function InstanceGeometry() { + + this.url = ""; + this.instance_material = []; + + } + + InstanceGeometry.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + if ( child.nodeName === 'bind_material' ) { + + var instances = child.querySelectorAll('instance_material'); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[j]; + this.instance_material.push( (new InstanceMaterial()).parse(instance) ); + + } + + break; + + } + + } + + return this; + + }; + + function Geometry() { + + this.id = ""; + this.mesh = null; + + } + + Geometry.prototype.parse = function ( element ) { + + this.id = element.getAttribute('id'); + + extractDoubleSided( this, element ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + + switch ( child.nodeName ) { + + case 'mesh': + + this.mesh = (new Mesh(this)).parse(child); + break; + + case 'extra': + + // console.log( child ); + break; + + default: + break; + } + } + + return this; + + }; + + function Mesh( geometry ) { + + this.geometry = geometry.id; + this.primitives = []; + this.vertices = null; + this.geometry3js = null; + + } + + Mesh.prototype.parse = function ( element ) { + + this.primitives = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'source': + + _source( child ); + break; + + case 'vertices': + + this.vertices = ( new Vertices() ).parse( child ); + break; + + case 'linestrips': + + this.primitives.push( ( new LineStrips().parse( child ) ) ); + break; + + case 'triangles': + + this.primitives.push( ( new Triangles().parse( child ) ) ); + break; + + case 'polygons': + + this.primitives.push( ( new Polygons().parse( child ) ) ); + break; + + case 'polylist': + + this.primitives.push( ( new Polylist().parse( child ) ) ); + break; + + default: + break; + + } + + } + + this.geometry3js = new THREE.Geometry(); + + if ( this.vertices === null ) { + + // TODO (mrdoob): Study case when this is null (carrier.dae) + + return this; + + } + + var vertexData = sources[ this.vertices.input['POSITION'].source ].data; + + for ( var i = 0; i < vertexData.length; i += 3 ) { + + this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); + + } + + for ( var i = 0; i < this.primitives.length; i ++ ) { + + var primitive = this.primitives[ i ]; + primitive.setVertices( this.vertices ); + this.handlePrimitive( primitive, this.geometry3js ); + + } + + if ( this.geometry3js.calcNormals ) { + + this.geometry3js.computeVertexNormals(); + delete this.geometry3js.calcNormals; + + } + + return this; + + }; + + Mesh.prototype.handlePrimitive = function ( primitive, geom ) { + + if ( primitive instanceof LineStrips ) { + + // TODO: Handle indices. Maybe easier with BufferGeometry? + + geom.isLineStrip = true; + return; + + } + + var j, k, pList = primitive.p, inputs = primitive.inputs; + var input, index, idx32; + var source, numParams; + var vcIndex = 0, vcount = 3, maxOffset = 0; + var texture_sets = []; + + for ( j = 0; j < inputs.length; j ++ ) { + + input = inputs[ j ]; + + var offset = input.offset + 1; + maxOffset = (maxOffset < offset) ? offset : maxOffset; + + switch ( input.semantic ) { + + case 'TEXCOORD': + texture_sets.push( input.set ); + break; + + } + + } + + for ( var pCount = 0; pCount < pList.length; ++ pCount ) { + + var p = pList[ pCount ], i = 0; + + while ( i < p.length ) { + + var vs = []; + var ns = []; + var ts = null; + var cs = []; + + if ( primitive.vcount ) { + + vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; + + } else { + + vcount = p.length / maxOffset; + + } + + + for ( j = 0; j < vcount; j ++ ) { + + for ( k = 0; k < inputs.length; k ++ ) { + + input = inputs[ k ]; + source = sources[ input.source ]; + + index = p[ i + ( j * maxOffset ) + input.offset ]; + numParams = source.accessor.params.length; + idx32 = index * numParams; + + switch ( input.semantic ) { + + case 'VERTEX': + + vs.push( index ); + + break; + + case 'NORMAL': + + ns.push( getConvertedVec3( source.data, idx32 ) ); + + break; + + case 'TEXCOORD': + + ts = ts || { }; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); + + break; + + case 'COLOR': + + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + break; + + default: + + break; + + } + + } + + } + + if ( ns.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.NORMAL; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); + + } + + } else { + + geom.calcNormals = true; + + } + + } + + if ( !ts ) { + + ts = { }; + // check the vertices inputs + input = this.vertices.input.TEXCOORD; + + if ( input ) { + + texture_sets.push( input.set ); + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); + + } + + } + + } + + if ( cs.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.COLOR; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + } + + } + + } + + var face = null, faces = [], uv, uvArr; + + if ( vcount === 3 ) { + + faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); + + } else if ( vcount === 4 ) { + + faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); + + faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); + + } else if ( vcount > 4 && options.subdivideFaces ) { + + var clr = cs.length ? cs : new THREE.Color(), + vec1, vec2, vec3, v1, v2, norm; + + // subdivide into multiple Face3s + + for ( k = 1; k < vcount - 1; ) { + + faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) ); + + } + + } + + if ( faces.length ) { + + for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { + + face = faces[ndx]; + face.daeMaterial = primitive.material; + geom.faces.push( face ); + + for ( k = 0; k < texture_sets.length; k ++ ) { + + uv = ts[ texture_sets[k] ]; + + if ( vcount > 4 ) { + + // Grab the right UVs for the vertices in this face + uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; + + } else if ( vcount === 4 ) { + + if ( ndx === 0 ) { + + uvArr = [ uv[0], uv[1], uv[3] ]; + + } else { + + uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; + + } + + } else { + + uvArr = [ uv[0], uv[1], uv[2] ]; + + } + + if ( geom.faceVertexUvs[k] === undefined ) { + + geom.faceVertexUvs[k] = []; + + } + + geom.faceVertexUvs[k].push( uvArr ); + + } + + } + + } else { + + console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); + + } + + i += maxOffset * vcount; + + } + + } + + }; + + function Polygons () { + + this.material = ""; + this.count = 0; + this.inputs = []; + this.vcount = null; + this.p = []; + this.geometry = new THREE.Geometry(); + + } + + Polygons.prototype.setVertices = function ( vertices ) { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + if ( this.inputs[ i ].source === vertices.id ) { + + this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; + + } + + } + + }; + + Polygons.prototype.parse = function ( element ) { + + this.material = element.getAttribute( 'material' ); + this.count = _attr_as_int( element, 'count', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); + break; + + case 'vcount': + + this.vcount = _ints( child.textContent ); + break; + + case 'p': + + this.p.push( _ints( child.textContent ) ); + break; + + case 'ph': + + console.warn( 'polygon holes not yet supported!' ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Polylist () { + + Polygons.call( this ); + + this.vcount = []; + + } + + Polylist.prototype = Object.create( Polygons.prototype ); + Polylist.prototype.constructor = Polylist; + + function LineStrips() { + + Polygons.call( this ); + + this.vcount = 1; + + } + + LineStrips.prototype = Object.create( Polygons.prototype ); + LineStrips.prototype.constructor = LineStrips; + + function Triangles () { + + Polygons.call( this ); + + this.vcount = 3; + + } + + Triangles.prototype = Object.create( Polygons.prototype ); + Triangles.prototype.constructor = Triangles; + + function Accessor() { + + this.source = ""; + this.count = 0; + this.stride = 0; + this.params = []; + + } + + Accessor.prototype.parse = function ( element ) { + + this.params = []; + this.source = element.getAttribute( 'source' ); + this.count = _attr_as_int( element, 'count', 0 ); + this.stride = _attr_as_int( element, 'stride', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'param' ) { + + var param = {}; + param[ 'name' ] = child.getAttribute( 'name' ); + param[ 'type' ] = child.getAttribute( 'type' ); + this.params.push( param ); + + } + + } + + return this; + + }; + + function Vertices() { + + this.input = {}; + + } + + Vertices.prototype.parse = function ( element ) { + + this.id = element.getAttribute('id'); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[i].nodeName === 'input' ) { + + var input = ( new Input() ).parse( element.childNodes[ i ] ); + this.input[ input.semantic ] = input; + + } + + } + + return this; + + }; + + function Input () { + + this.semantic = ""; + this.offset = 0; + this.source = ""; + this.set = 0; + + } + + Input.prototype.parse = function ( element ) { + + this.semantic = element.getAttribute('semantic'); + this.source = element.getAttribute('source').replace(/^#/, ''); + this.set = _attr_as_int(element, 'set', -1); + this.offset = _attr_as_int(element, 'offset', 0); + + if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { + + this.set = 0; + + } + + return this; + + }; + + function Source ( id ) { + + this.id = id; + this.type = null; + + } + + Source.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + + switch ( child.nodeName ) { + + case 'bool_array': + + this.data = _bools( child.textContent ); + this.type = child.nodeName; + break; + + case 'float_array': + + this.data = _floats( child.textContent ); + this.type = child.nodeName; + break; + + case 'int_array': + + this.data = _ints( child.textContent ); + this.type = child.nodeName; + break; + + case 'IDREF_array': + case 'Name_array': + + this.data = _strings( child.textContent ); + this.type = child.nodeName; + break; + + case 'technique_common': + + for ( var j = 0; j < child.childNodes.length; j ++ ) { + + if ( child.childNodes[ j ].nodeName === 'accessor' ) { + + this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); + break; + + } + } + break; + + default: + // console.log(child.nodeName); + break; + + } + + } + + return this; + + }; + + Source.prototype.read = function () { + + var result = []; + + //for (var i = 0; i < this.accessor.params.length; i++) { + + var param = this.accessor.params[ 0 ]; + + //console.log(param.name + " " + param.type); + + switch ( param.type ) { + + case 'IDREF': + case 'Name': case 'name': + case 'float': + + return this.data; + + case 'float4x4': + + for ( var j = 0; j < this.data.length; j += 16 ) { + + var s = this.data.slice( j, j + 16 ); + var m = getConvertedMat4( s ); + result.push( m ); + } + + break; + + default: + + console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); + break; + + } + + //} + + return result; + + }; + + function Material () { + + this.id = ""; + this.name = ""; + this.instance_effect = null; + + } + + Material.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { + + this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); + break; + + } + + } + + return this; + + }; + + function ColorOrTexture () { + + this.color = new THREE.Color(); + this.color.setRGB( Math.random(), Math.random(), Math.random() ); + this.color.a = 1.0; + + this.texture = null; + this.texcoord = null; + this.texOpts = null; + + } + + ColorOrTexture.prototype.isColor = function () { + + return ( this.texture === null ); + + }; + + ColorOrTexture.prototype.isTexture = function () { + + return ( this.texture != null ); + + }; + + ColorOrTexture.prototype.parse = function ( element ) { + + if (element.nodeName === 'transparent') { + + this.opaque = element.getAttribute('opaque'); + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(); + this.color.setRGB( rgba[0], rgba[1], rgba[2] ); + this.color.a = rgba[3]; + break; + + case 'texture': + + this.texture = child.getAttribute('texture'); + this.texcoord = child.getAttribute('texcoord'); + // Defaults from: + // https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension + this.texOpts = { + offsetU: 0, + offsetV: 0, + repeatU: 1, + repeatV: 1, + wrapU: 1, + wrapV: 1 + }; + this.parseTexture( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + ColorOrTexture.prototype.parseTexture = function ( element ) { + + if ( ! element.childNodes ) return this; + + // This should be supported by Maya, 3dsMax, and MotionBuilder + + if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { + + element = element.childNodes[1]; + + if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { + + element = element.childNodes[1]; + + } + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'offsetU': + case 'offsetV': + case 'repeatU': + case 'repeatV': + + this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); + + break; + + case 'wrapU': + case 'wrapV': + + // some dae have a value of true which becomes NaN via parseInt + + if ( child.textContent.toUpperCase() === 'TRUE' ) { + + this.texOpts[ child.nodeName ] = 1; + + } else { + + this.texOpts[ child.nodeName ] = parseInt( child.textContent ); + + } + break; + + default: + + this.texOpts[ child.nodeName ] = child.textContent; + + break; + + } + + } + + return this; + + }; + + function Shader ( type, effect ) { + + this.type = type; + this.effect = effect; + this.material = null; + + } + + Shader.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'emission': + case 'diffuse': + case 'specular': + case 'transparent': + + this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); + break; + + case 'bump': + + // If 'bumptype' is 'heightfield', create a 'bump' property + // Else if 'bumptype' is 'normalmap', create a 'normal' property + // (Default to 'bump') + var bumpType = child.getAttribute( 'bumptype' ); + if ( bumpType ) { + if ( bumpType.toLowerCase() === "heightfield" ) { + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } else if ( bumpType.toLowerCase() === "normalmap" ) { + this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); + } else { + console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } + } else { + console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } + + break; + + case 'shininess': + case 'reflectivity': + case 'index_of_refraction': + case 'transparency': + + var f = child.querySelectorAll('float'); + + if ( f.length > 0 ) + this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); + + break; + + default: + break; + + } + + } + + this.create(); + return this; + + }; + + Shader.prototype.create = function() { + + var props = {}; + + var transparent = false; + + if (this['transparency'] !== undefined && this['transparent'] !== undefined) { + // convert transparent color RBG to average value + var transparentColor = this['transparent']; + var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; + + if (transparencyLevel > 0) { + transparent = true; + props[ 'transparent' ] = true; + props[ 'opacity' ] = 1 - transparencyLevel; + + } + + } + + var keys = { + 'diffuse':'map', + 'ambient':'lightMap', + 'specular':'specularMap', + 'emission':'emissionMap', + 'bump':'bumpMap', + 'normal':'normalMap' + }; + + for ( var prop in this ) { + + switch ( prop ) { + + case 'ambient': + case 'emission': + case 'diffuse': + case 'specular': + case 'bump': + case 'normal': + + var cot = this[ prop ]; + + if ( cot instanceof ColorOrTexture ) { + + if ( cot.isTexture() ) { + + var samplerId = cot.texture; + var sampler = this.effect.sampler[samplerId]; + + if ( sampler !== undefined && sampler.source !== undefined ) { + + var surface = this.effect.surface[sampler.source]; + + if ( surface !== undefined ) { + + var image = images[ surface.init_from ]; + + if ( image ) { + + var url = baseUrl + image.init_from; + + var texture; + var loader = THREE.Loader.Handlers.get( url ); + + if ( loader !== null ) { + + texture = loader.load( url ); + + } else { + + texture = new THREE.Texture(); + + loadTextureImage( texture, url ); + + } + + if ( sampler.wrap_s === "MIRROR" ) { + + texture.wrapS = THREE.MirroredRepeatWrapping; + + } else if ( sampler.wrap_s === "WRAP" || cot.texOpts.wrapU ) { + + texture.wrapS = THREE.RepeatWrapping; + + } else { + + texture.wrapS = THREE.ClampToEdgeWrapping; + + } + + if ( sampler.wrap_t === "MIRROR" ) { + + texture.wrapT = THREE.MirroredRepeatWrapping; + + } else if ( sampler.wrap_t === "WRAP" || cot.texOpts.wrapV ) { + + texture.wrapT = THREE.RepeatWrapping; + + } else { + + texture.wrapT = THREE.ClampToEdgeWrapping; + + } + + texture.offset.x = cot.texOpts.offsetU; + texture.offset.y = cot.texOpts.offsetV; + texture.repeat.x = cot.texOpts.repeatU; + texture.repeat.y = cot.texOpts.repeatV; + props[keys[prop]] = texture; + + // Texture with baked lighting? + if (prop === 'emission') props['emissive'] = 0xffffff; + + } + + } + + } + + } else if ( prop === 'diffuse' || !transparent ) { + + if ( prop === 'emission' ) { + + props[ 'emissive' ] = cot.color.getHex(); + + } else { + + props[ prop ] = cot.color.getHex(); + + } + + } + + } + + break; + + case 'shininess': + + props[ prop ] = this[ prop ]; + break; + + case 'reflectivity': + + props[ prop ] = this[ prop ]; + if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; + props['combine'] = THREE.MixOperation; //mix regular shading with reflective component + break; + + case 'index_of_refraction': + + props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable + if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; + break; + + case 'transparency': + // gets figured out up top + break; + + default: + break; + + } + + } + + props[ 'shading' ] = preferredShading; + props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; + + if ( props.diffuse !== undefined ) { + + props.color = props.diffuse; + delete props.diffuse; + + } + + switch ( this.type ) { + + case 'constant': + + if (props.emissive != undefined) props.color = props.emissive; + this.material = new THREE.MeshBasicMaterial( props ); + break; + + case 'phong': + case 'blinn': + + this.material = new THREE.MeshPhongMaterial( props ); + break; + + case 'lambert': + default: + + this.material = new THREE.MeshLambertMaterial( props ); + break; + + } + + return this.material; + + }; + + function Surface ( effect ) { + + this.effect = effect; + this.init_from = null; + this.format = null; + + } + + Surface.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'init_from': + + this.init_from = child.textContent; + break; + + case 'format': + + this.format = child.textContent; + break; + + default: + + console.log( "unhandled Surface prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Sampler2D ( effect ) { + + this.effect = effect; + this.source = null; + this.wrap_s = null; + this.wrap_t = null; + this.minfilter = null; + this.magfilter = null; + this.mipfilter = null; + + } + + Sampler2D.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + this.source = child.textContent; + break; + + case 'minfilter': + + this.minfilter = child.textContent; + break; + + case 'magfilter': + + this.magfilter = child.textContent; + break; + + case 'mipfilter': + + this.mipfilter = child.textContent; + break; + + case 'wrap_s': + + this.wrap_s = child.textContent; + break; + + case 'wrap_t': + + this.wrap_t = child.textContent; + break; + + default: + + console.log( "unhandled Sampler2D prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Effect () { + + this.id = ""; + this.name = ""; + this.shader = null; + this.surface = {}; + this.sampler = {}; + + } + + Effect.prototype.create = function () { + + if ( this.shader === null ) { + + return null; + + } + + }; + + Effect.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + extractDoubleSided( this, element ); + + this.shader = null; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseTechnique( this.parseProfileCOMMON( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Effect.prototype.parseNewparam = function ( element ) { + + var sid = element.getAttribute( 'sid' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'surface': + + this.surface[sid] = ( new Surface( this ) ).parse( child ); + break; + + case 'sampler2D': + + this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); + break; + + case 'extra': + + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + }; + + Effect.prototype.parseProfileCOMMON = function ( element ) { + + var technique; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseProfileCOMMON( child ); + break; + + case 'technique': + + technique = child; + break; + + case 'newparam': + + this.parseNewparam( child ); + break; + + case 'image': + + var _image = ( new _Image() ).parse( child ); + images[ _image.id ] = _image; + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + return technique; + + }; + + Effect.prototype.parseTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'constant': + case 'lambert': + case 'blinn': + case 'phong': + + this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); + break; + case 'extra': + this.parseExtra(child); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtra = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique': + this.parseExtraTechnique( child ); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtraTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bump': + this.shader.parse( element ); + break; + default: + break; + + } + + } + + }; + + function InstanceEffect () { + + this.url = ""; + + } + + InstanceEffect.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + return this; + + }; + + function Animation() { + + this.id = ""; + this.name = ""; + this.source = {}; + this.sampler = []; + this.channel = []; + + } + + Animation.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.source = {}; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'animation': + + var anim = ( new Animation() ).parse( child ); + + for ( var src in anim.source ) { + + this.source[ src ] = anim.source[ src ]; + + } + + for ( var j = 0; j < anim.channel.length; j ++ ) { + + this.channel.push( anim.channel[ j ] ); + this.sampler.push( anim.sampler[ j ] ); + + } + + break; + + case 'source': + + var src = ( new Source() ).parse( child ); + this.source[ src.id ] = src; + break; + + case 'sampler': + + this.sampler.push( ( new Sampler( this ) ).parse( child ) ); + break; + + case 'channel': + + this.channel.push( ( new Channel( this ) ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Channel( animation ) { + + this.animation = animation; + this.source = ""; + this.target = ""; + this.fullSid = null; + this.sid = null; + this.dotSyntax = null; + this.arrSyntax = null; + this.arrIndices = null; + this.member = null; + + } + + Channel.prototype.parse = function ( element ) { + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.target = element.getAttribute( 'target' ); + + var parts = this.target.split( '/' ); + + var id = parts.shift(); + var sid = parts.shift(); + + var dotSyntax = ( sid.indexOf(".") >= 0 ); + var arrSyntax = ( sid.indexOf("(") >= 0 ); + + if ( dotSyntax ) { + + parts = sid.split("."); + this.sid = parts.shift(); + this.member = parts.shift(); + + } else if ( arrSyntax ) { + + var arrIndices = sid.split("("); + this.sid = arrIndices.shift(); + + for (var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); + + } + + this.arrIndices = arrIndices; + + } else { + + this.sid = sid; + + } + + this.fullSid = sid; + this.dotSyntax = dotSyntax; + this.arrSyntax = arrSyntax; + + return this; + + }; + + function Sampler ( animation ) { + + this.id = ""; + this.animation = animation; + this.inputs = []; + this.input = null; + this.output = null; + this.strideOut = null; + this.interpolation = null; + this.startTime = null; + this.endTime = null; + this.duration = 0; + + } + + Sampler.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( (new Input()).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Sampler.prototype.create = function () { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + var input = this.inputs[ i ]; + var source = this.animation.source[ input.source ]; + + switch ( input.semantic ) { + + case 'INPUT': + + this.input = source.read(); + break; + + case 'OUTPUT': + + this.output = source.read(); + this.strideOut = source.accessor.stride; + break; + + case 'INTERPOLATION': + + this.interpolation = source.read(); + break; + + case 'IN_TANGENT': + + break; + + case 'OUT_TANGENT': + + break; + + default: + + console.log(input.semantic); + break; + + } + + } + + this.startTime = 0; + this.endTime = 0; + this.duration = 0; + + if ( this.input.length ) { + + this.startTime = 100000000; + this.endTime = -100000000; + + for ( var i = 0; i < this.input.length; i ++ ) { + + this.startTime = Math.min( this.startTime, this.input[ i ] ); + this.endTime = Math.max( this.endTime, this.input[ i ] ); + + } + + this.duration = this.endTime - this.startTime; + + } + + }; + + Sampler.prototype.getData = function ( type, ndx, member ) { + + var data; + + if ( type === 'matrix' && this.strideOut === 16 ) { + + data = this.output[ ndx ]; + + } else if ( this.strideOut > 1 ) { + + data = []; + ndx *= this.strideOut; + + for ( var i = 0; i < this.strideOut; ++ i ) { + + data[ i ] = this.output[ ndx + i ]; + + } + + if ( this.strideOut === 3 ) { + + switch ( type ) { + + case 'rotate': + case 'translate': + + fixCoords( data, -1 ); + break; + + case 'scale': + + fixCoords( data, 1 ); + break; + + } + + } else if ( this.strideOut === 4 && type === 'matrix' ) { + + fixCoords( data, -1 ); + + } + + } else { + + data = this.output[ ndx ]; + + if ( member && type === 'translate' ) { + data = getConvertedTranslation( member, data ); + } + + } + + return data; + + }; + + function Key ( time ) { + + this.targets = []; + this.time = time; + + } + + Key.prototype.addTarget = function ( fullSid, transform, member, data ) { + + this.targets.push( { + sid: fullSid, + member: member, + transform: transform, + data: data + } ); + + }; + + Key.prototype.apply = function ( opt_sid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + var target = this.targets[ i ]; + + if ( !opt_sid || target.sid === opt_sid ) { + + target.transform.update( target.data, target.member ); + + } + + } + + }; + + Key.prototype.getTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return this.targets[ i ]; + + } + + } + + return null; + + }; + + Key.prototype.hasTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return true; + + } + + } + + return false; + + }; + + // TODO: Currently only doing linear interpolation. Should support full COLLADA spec. + Key.prototype.interpolate = function ( nextKey, time ) { + + for ( var i = 0, l = this.targets.length; i < l; i ++ ) { + + var target = this.targets[ i ], + nextTarget = nextKey.getTarget( target.sid ), + data; + + if ( target.transform.type !== 'matrix' && nextTarget ) { + + var scale = ( time - this.time ) / ( nextKey.time - this.time ), + nextData = nextTarget.data, + prevData = target.data; + + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; + + if ( prevData.length ) { + + data = []; + + for ( var j = 0; j < prevData.length; ++ j ) { + + data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + } else { + + data = target.data; + + } + + target.transform.update( data, target.member ); + + } + + }; + + // Camera + function Camera() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + } + + Camera.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'optics': + + this.parseOptics( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Camera.prototype.parseOptics = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'technique_common' ) { + + var technique = element.childNodes[ i ]; + + for ( var j = 0; j < technique.childNodes.length; j ++ ) { + + this.technique = technique.childNodes[ j ].nodeName; + + if ( this.technique === 'perspective' ) { + + var perspective = technique.childNodes[ j ]; + + for ( var k = 0; k < perspective.childNodes.length; k ++ ) { + + var param = perspective.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'yfov': + this.yfov = param.textContent; + break; + case 'xfov': + this.xfov = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } else if ( this.technique === 'orthographic' ) { + + var orthographic = technique.childNodes[ j ]; + + for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { + + var param = orthographic.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'xmag': + this.xmag = param.textContent; + break; + case 'ymag': + this.ymag = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } + + } + + } + + } + + return this; + + }; + + function InstanceCamera() { + + this.url = ""; + + } + + InstanceCamera.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + + return this; + + }; + + // Light + + function Light() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + } + + Light.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon( child ); + break; + + case 'technique': + + this.parseTechnique( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Light.prototype.parseCommon = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + switch ( element.childNodes[ i ].nodeName ) { + + case 'directional': + case 'point': + case 'spot': + case 'ambient': + + this.technique = element.childNodes[ i ].nodeName; + + var light = element.childNodes[ i ]; + + for ( var j = 0; j < light.childNodes.length; j ++ ) { + + var child = light.childNodes[j]; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(0); + this.color.setRGB( rgba[0], rgba[1], rgba[2] ); + this.color.a = rgba[3]; + break; + + case 'falloff_angle': + + this.falloff_angle = parseFloat( child.textContent ); + break; + + case 'quadratic_attenuation': + var f = parseFloat( child.textContent ); + this.distance = f ? Math.sqrt( 1 / f ) : 0; + } + + } + + } + + } + + return this; + + }; + + Light.prototype.parseTechnique = function ( element ) { + + this.profile = element.getAttribute( 'profile' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'intensity': + + this.intensity = parseFloat(child.textContent); + break; + + } + + } + + return this; + + }; + + function InstanceLight() { + + this.url = ""; + + } + + InstanceLight.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + + return this; + + }; + + function KinematicsModel( ) { + + this.id = ''; + this.name = ''; + this.joints = []; + this.links = []; + + } + + KinematicsModel.prototype.parse = function( element ) { + + this.id = element.getAttribute('id'); + this.name = element.getAttribute('name'); + this.joints = []; + this.links = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon(child); + break; + + default: + break; + + } + + } + + return this; + + }; + + KinematicsModel.prototype.parseCommon = function( element ) { + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( element.childNodes[ i ].nodeName ) { + + case 'joint': + this.joints.push( (new Joint()).parse(child) ); + break; + + case 'link': + this.links.push( (new Link()).parse(child) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Joint( ) { + + this.sid = ''; + this.name = ''; + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + } + + Joint.prototype.parse = function( element ) { + + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + var axisElement = element.querySelector('axis'); + var _axis = _floats(axisElement.textContent); + this.axis = getConvertedVec3(_axis, 0); + + var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; + var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; + + this.limits = { + min: min, + max: max + }; + + var jointTypes = [ 'prismatic', 'revolute' ]; + for (var i = 0; i < jointTypes.length; i ++ ) { + + var type = jointTypes[ i ]; + + var jointElement = element.querySelector(type); + + if ( jointElement ) { + + this.type = type; + + } + + } + + // if the min is equal to or somehow greater than the max, consider the joint static + if ( this.limits.min >= this.limits.max ) { + + this.static = true; + + } + + this.middlePosition = (this.limits.min + this.limits.max) / 2.0; + return this; + + }; + + function Link( ) { + + this.sid = ''; + this.name = ''; + this.transforms = []; + this.attachments = []; + + } + + Link.prototype.parse = function( element ) { + + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.transforms = []; + this.attachments = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'attachment_full': + this.attachments.push( (new Attachment()).parse(child) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( (new Transform()).parse(child) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function Attachment( ) { + + this.joint = ''; + this.transforms = []; + this.links = []; + + } + + Attachment.prototype.parse = function( element ) { + + this.joint = element.getAttribute('joint').split('/').pop(); + this.links = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'link': + this.links.push( (new Link()).parse(child) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( (new Transform()).parse(child) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function _source( element ) { + + var id = element.getAttribute( 'id' ); + + if ( sources[ id ] != undefined ) { + + return sources[ id ]; + + } + + sources[ id ] = ( new Source(id )).parse( element ); + return sources[ id ]; + + } + + function _nsResolver( nsPrefix ) { + + if ( nsPrefix === "dae" ) { + + return "http://www.collada.org/2005/11/COLLADASchema"; + + } + + return null; + + } + + function _bools( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); + + } + + return data; + + } + + function _floats( str ) { + + var raw = _strings(str); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseFloat( raw[ i ] ) ); + + } + + return data; + + } + + function _ints( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseInt( raw[ i ], 10 ) ); + + } + + return data; + + } + + function _strings( str ) { + + return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; + + } + + function _trimString( str ) { + + return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); + + } + + function _attr_as_float( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseFloat( element.getAttribute( name ) ); + + } else { + + return defaultValue; + + } + + } + + function _attr_as_int( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseInt( element.getAttribute( name ), 10) ; + + } else { + + return defaultValue; + + } + + } + + function _attr_as_string( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return element.getAttribute( name ); + + } else { + + return defaultValue; + + } + + } + + function _format_float( f, num ) { + + if ( f === undefined ) { + + var s = '0.'; + + while ( s.length < num + 2 ) { + + s += '0'; + + } + + return s; + + } + + num = num || 2; + + var parts = f.toString().split( '.' ); + parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; + + while ( parts[ 1 ].length < num ) { + + parts[ 1 ] += '0'; + + } + + return parts.join( '.' ); + + } + + function loadTextureImage ( texture, url ) { + + var loader = new THREE.ImageLoader(); + + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + } ); + + } + + function extractDoubleSided( obj, element ) { + + obj.doubleSided = false; + + var node = element.querySelectorAll('extra double_sided')[0]; + + if ( node ) { + + if ( node && parseInt( node.textContent, 10 ) === 1 ) { + + obj.doubleSided = true; + + } + + } + + } + + // Up axis conversion + + function setUpConversion() { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + upConversion = null; + + } else { + + switch ( colladaUp ) { + + case 'X': + + upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; + break; + + case 'Y': + + upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; + break; + + case 'Z': + + upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; + break; + + } + + } + + } + + function fixCoords( data, sign ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return; + + } + + switch ( upConversion ) { + + case 'XtoY': + + var tmp = data[ 0 ]; + data[ 0 ] = sign * data[ 1 ]; + data[ 1 ] = tmp; + break; + + case 'XtoZ': + + var tmp = data[ 2 ]; + data[ 2 ] = data[ 1 ]; + data[ 1 ] = data[ 0 ]; + data[ 0 ] = tmp; + break; + + case 'YtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = sign * tmp; + break; + + case 'YtoZ': + + var tmp = data[ 1 ]; + data[ 1 ] = sign * data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoY': + + var tmp = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = sign * tmp; + break; + + } + + } + + function getConvertedTranslation( axis, data ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return data; + + } + + switch ( axis ) { + case 'X': + data = upConversion === 'XtoY' ? data * -1 : data; + break; + case 'Y': + data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; + break; + case 'Z': + data = upConversion === 'ZtoY' ? data * -1 : data ; + break; + default: + break; + } + + return data; + } + + function getConvertedVec3( data, offset ) { + + var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; + fixCoords( arr, -1 ); + return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); + + } + + function getConvertedMat4( data ) { + + if ( options.convertUpAxis ) { + + // First fix rotation and scale + + // Columns first + var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; + fixCoords( arr, -1 ); + data[ 0 ] = arr[ 0 ]; + data[ 4 ] = arr[ 1 ]; + data[ 8 ] = arr[ 2 ]; + arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; + fixCoords( arr, -1 ); + data[ 1 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 9 ] = arr[ 2 ]; + arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; + fixCoords( arr, -1 ); + data[ 2 ] = arr[ 0 ]; + data[ 6 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + // Rows second + arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; + fixCoords( arr, -1 ); + data[ 0 ] = arr[ 0 ]; + data[ 1 ] = arr[ 1 ]; + data[ 2 ] = arr[ 2 ]; + arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; + fixCoords( arr, -1 ); + data[ 4 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 6 ] = arr[ 2 ]; + arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; + fixCoords( arr, -1 ); + data[ 8 ] = arr[ 0 ]; + data[ 9 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + + // Now fix translation + arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; + fixCoords( arr, -1 ); + data[ 3 ] = arr[ 0 ]; + data[ 7 ] = arr[ 1 ]; + data[ 11 ] = arr[ 2 ]; + + } + + return new THREE.Matrix4().set( + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], + data[12], data[13], data[14], data[15] + ); + + } + + function getConvertedIndex( index ) { + + if ( index > -1 && index < 3 ) { + + var members = [ 'X', 'Y', 'Z' ], + indices = { X: 0, Y: 1, Z: 2 }; + + index = getConvertedMember( members[ index ] ); + index = indices[ index ]; + + } + + return index; + + } + + function getConvertedMember( member ) { + + if ( options.convertUpAxis ) { + + switch ( member ) { + + case 'X': + + switch ( upConversion ) { + + case 'XtoY': + case 'XtoZ': + case 'YtoX': + + member = 'Y'; + break; + + case 'ZtoX': + + member = 'Z'; + break; + + } + + break; + + case 'Y': + + switch ( upConversion ) { + + case 'XtoY': + case 'YtoX': + case 'ZtoX': + + member = 'X'; + break; + + case 'XtoZ': + case 'YtoZ': + case 'ZtoY': + + member = 'Z'; + break; + + } + + break; + + case 'Z': + + switch ( upConversion ) { + + case 'XtoZ': + + member = 'X'; + break; + + case 'YtoZ': + case 'ZtoX': + case 'ZtoY': + + member = 'Y'; + break; + + } + + break; + + } + + } + + return member; + + } + + return { + + load: load, + parse: parse, + setPreferredShading: setPreferredShading, + applySkin: applySkin, + geometries : geometries, + options: options + + }; + +}; +// File: utils/GLTF2Loader.js +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + * @author Don McCurdy / https://www.donmccurdy.com + */ + +THREE.GLTF2Loader = ( function () { + + function GLTF2Loader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + GLTF2Loader.prototype = { + + constructor: GLTF2Loader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); + + var loader = new THREE.FileLoader( scope.manager ); + + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( data ) { + + scope.parse( data, onLoad, path ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setPath: function ( value ) { + + this.path = value; + + }, + + parse: function ( data, callback, path ) { + + var content; + var extensions = {}; + + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { + + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + + } else { + + content = convertUint8ArrayToString( new Uint8Array( data ) ); + + } + + var json = JSON.parse( content ); + + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + + } + + console.time( 'GLTF2Loader' ); + + var parser = new GLTFParser( json, extensions, { + + path: path || this.path, + crossOrigin: this.crossOrigin + + } ); + + parser.parse( function ( scene, scenes, cameras, animations ) { + + console.timeEnd( 'GLTF2Loader' ); + + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + + callback( glTF ); + + } ); + + } + + }; + + /* GLTFREGISTRY */ + + function GLTFRegistry() { + + var objects = {}; + + return { + + get: function ( key ) { + + return objects[ key ]; + + }, + + add: function ( key, object ) { + + objects[ key ] = object; + + }, + + remove: function ( key ) { + + delete objects[ key ]; + + }, + + removeAll: function () { + + objects = {}; + + }, + + update: function ( scene, camera ) { + + for ( var name in objects ) { + + var object = objects[ name ]; + + if ( object.update ) { + + object.update( scene, camera ); + + } + + } + + } + + }; + + } + + /* GLTFSHADER */ + + function GLTFShader( targetNode, allNodes ) { + + var boundUniforms = {}; + + // bind each uniform to its source node + + var uniforms = targetNode.material.uniforms; + + for ( var uniformId in uniforms ) { + + var uniform = uniforms[ uniformId ]; + + if ( uniform.semantic ) { + + var sourceNodeRef = uniform.node; + + var sourceNode = targetNode; + + if ( sourceNodeRef ) { + + sourceNode = allNodes[ sourceNodeRef ]; + + } + + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + + } + + } + + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + + } + + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + + var boundUniforms = this.boundUniforms; + + for ( var name in boundUniforms ) { + + var boundUniform = boundUniforms[ name ]; + + switch ( boundUniform.semantic ) { + + case "MODELVIEW": + + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + + case "MODELVIEWINVERSETRANSPOSE": + + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + + case "PROJECTION": + + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + + case "JOINTMATRIX": + + var m4v = boundUniform.uniform.value; + + for ( var mi = 0; mi < m4v.length; mi ++ ) { + + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + + } + + break; + + default : + + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + + } + + } + + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' + }; + + /* MATERIALS COMMON EXTENSION */ + + function GLTFMaterialsCommonExtension( json ) { + + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + + this.lights = {}; + + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + + for ( var lightId in lights ) { + + var light = lights[ lightId ]; + var lightNode; + + var lightParams = light[ light.type ]; + var color = new THREE.Color().fromArray( lightParams.color ); + + switch ( light.type ) { + + case "directional": + lightNode = new THREE.DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "point": + lightNode = new THREE.PointLight( color ); + break; + + case "spot": + lightNode = new THREE.SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "ambient": + lightNode = new THREE.AmbientLight( color ); + break; + + } + + if ( lightNode ) { + + this.lights[ lightId ] = lightNode; + + } + + } + + } + + /* BINARY EXTENSION */ + + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; + var BINARY_EXTENSION_HEADER_LENGTH = 12; + var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; + + function GLTFBinaryExtension( data ) { + + this.name = EXTENSIONS.KHR_BINARY_GLTF; + this.content = null; + this.body = null; + + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + + this.header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ) + }; + + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { + + throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); + + } else if ( this.header.version < 2.0 ) { + + throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); + + } + + var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); + var chunkIndex = 0; + + while ( chunkIndex < chunkView.byteLength ) { + + var chunkLength = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + var chunkType = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { + + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); + this.content = convertUint8ArrayToString( contentArray ); + + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { + + var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; + this.body = data.slice( byteOffset, byteOffset + chunkLength ); + + } + + // Clients must ignore chunks with unknown types. + + chunkIndex += chunkLength; + + } + + if ( this.content === null ) { + + throw new Error( 'GLTF2Loader: JSON content not found.' ); + + } + + } + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + + /* CONSTANTS */ + + var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 + }; + + var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: THREE.Matrix3, + 35676: THREE.Matrix4, + 35664: THREE.Vector2, + 35665: THREE.Vector3, + 35666: THREE.Vector4, + 35678: THREE.Texture + }; + + var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + + var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipMapNearestFilter, + 9985: THREE.LinearMipMapNearestFilter, + 9986: THREE.NearestMipMapLinearFilter, + 9987: THREE.LinearMipMapLinearFilter + }; + + var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping + }; + + var WEBGL_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + + function _each( object, callback, thisObj ) { + + if ( !object ) { + return Promise.resolve(); + } + + var results; + var fns = []; + + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + + results = []; + + var length = object.length; + + for ( var idx = 0; idx < length; idx ++ ) { + + var value = callback.call( thisObj || this, object[ idx ], idx ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function( key, value ) { + + results[ key ] = value; + + }.bind( this, idx )); + + } else { + + results[ idx ] = value; + + } + + } + + } + + } else { + + results = {}; + + for ( var key in object ) { + + if ( object.hasOwnProperty( key ) ) { + + var value = callback.call( thisObj || this, object[ key ], key ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function( key, value ) { + + results[ key ] = value; + + }.bind( this, key )); + + } else { + + results[ key ] = value; + + } + + } + + } + + } + + } + + return Promise.all( fns ).then( function() { + + return results; + + }); + + } + + function resolveURL( url, path ) { + + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + + return url; + + } + + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + + return url; + + } + + // Blob URL + if ( /^blob:.*$/i.test( url ) ) { + + return url; + + } + + // Relative URL + return ( path || '' ) + url; + + } + + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + function convertUint8ArrayToString( array ) { + + var s = ''; + + for ( var i = 0; i < array.length; i ++ ) { + + s += String.fromCharCode( array[ i ] ); + + } + + return s; + + } + + // Three.js seems too dependent on attribute names so globally + // replace those in the shader code + function replaceTHREEShaderAttributes( shaderText, technique ) { + + // Expected technique attributes + var attributes = {}; + + for ( var attributeId in technique.attributes ) { + + var pname = technique.attributes[ attributeId ]; + + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + + } + + // Figure out which attributes to change in technique + + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + + for ( var attributeId in attributes ) { + + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + + params[ attributeId ] = shaderParam; + + } + + } + + for ( var pname in params ) { + + var param = params[ pname ]; + var semantic = param.semantic; + + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + + switch ( semantic ) { + + case 'POSITION': + + shaderText = shaderText.replace( regEx, 'position' ); + break; + + case 'NORMAL': + + shaderText = shaderText.replace( regEx, 'normal' ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + shaderText = shaderText.replace( regEx, 'uv' ); + break; + + case 'TEXCOORD_1': + + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + shaderText = shaderText.replace( regEx, 'color' ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + + } + + } + + return shaderText; + + } + + function createDefaultMaterial() { + + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + + } + + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + + this.isDeferredShaderMaterial = true; + + this.params = params; + + } + + DeferredShaderMaterial.prototype.create = function () { + + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + + for ( var uniformId in this.params.uniforms ) { + + var originalUniform = this.params.uniforms[ uniformId ]; + + if ( originalUniform.value instanceof THREE.Texture ) { + + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + + } + + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + + } + + this.params.uniforms = uniforms; + + return new THREE.RawShaderMaterial( this.params ); + + }; + + /* GLTF PARSER */ + + function GLTFParser( json, extensions, options ) { + + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + + // loader object cache + this.cache = new GLTFRegistry(); + + } + + GLTFParser.prototype._withDependencies = function ( dependencies ) { + + var _dependencies = {}; + + for ( var i = 0; i < dependencies.length; i ++ ) { + + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + + var cached = this.cache.get( dependency ); + + if ( cached !== undefined ) { + + _dependencies[ dependency ] = cached; + + } else if ( this[ fnName ] ) { + + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + + _dependencies[ dependency ] = fn; + + } + + } + + return _each( _dependencies, function ( dependency ) { + + return dependency; + + } ); + + }; + + GLTFParser.prototype.parse = function ( callback ) { + + var json = this.json; + + // Clear the loader cache + this.cache.removeAll(); + + // Fire the callback on complete + this._withDependencies( [ + + "scenes", + "cameras", + "animations" + + ] ).then( function ( dependencies ) { + + var scenes = []; + + for ( var name in dependencies.scenes ) { + + scenes.push( dependencies.scenes[ name ] ); + + } + + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + + var cameras = []; + + for ( var name in dependencies.cameras ) { + + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + + } + + var animations = []; + + for ( var name in dependencies.animations ) { + + animations.push( dependencies.animations[ name ] ); + + } + + callback( scene, scenes, cameras, animations ); + + } ); + + }; + + GLTFParser.prototype.loadShaders = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.shaders, function ( shader ) { + + if ( shader.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ shader.bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + + resolve( shaderText ); + + } ); + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadBuffers = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return _each( json.buffers, function ( buffer, name ) { + + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + + // If present, GLB container is required to be the first buffer. + if ( buffer.uri === undefined && name === 0 ) { + + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + + resolve( buffer ); + + } ); + + } ); + + } else { + + console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); + + } + + } ); + + }; + + GLTFParser.prototype.loadBufferViews = function () { + + var json = this.json; + + return this._withDependencies( [ + + "buffers" + + ] ).then( function ( dependencies ) { + + return _each( json.bufferViews, function ( bufferView ) { + + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAccessors = function () { + + var json = this.json; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.accessors, function ( accessor ) { + + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + + var array; + + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + + // Use the full buffer if it's interleaved. + array = new TypedArray( arraybuffer ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + + } else { + + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + + return new THREE.BufferAttribute( array, itemSize ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadTextures = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.textures, function ( texture ) { + + if ( texture.source !== undefined ) { + + return new Promise( function ( resolve ) { + + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + + var urlCreator; + + if ( source.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ source.bufferView ]; + var blob = new Blob( [ bufferView ], { type: source.mimeType } ); + urlCreator = window.URL || window.webkitURL; + sourceUri = urlCreator.createObjectURL( blob ); + + } + + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + + if ( textureLoader === null ) { + + textureLoader = new THREE.TextureLoader(); + + } + + textureLoader.setCrossOrigin( options.crossOrigin ); + + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + + if ( urlCreator !== undefined ) { + + urlCreator.revokeObjectURL( sourceUri ); + + } + + _texture.flipY = false; + + if ( texture.name !== undefined ) _texture.name = texture.name; + + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + + console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + + } + + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + + if ( texture.sampler !== undefined ) { + + var sampler = json.samplers[ texture.sampler ]; + + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + + } + + resolve( _texture ); + + }, undefined, function () { + + resolve(); + + } ); + + } ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMaterials = function () { + + var json = this.json; + + return this._withDependencies( [ + + "shaders", + "textures" + + ] ).then( function ( dependencies ) { + + return _each( json.materials, function ( material ) { + + var materialType; + var materialValues = {}; + var materialParams = {}; + + var khr_material; + + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + + } + + if ( khr_material ) { + + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + + switch ( khr_material.technique ) { + + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + + } + + keys.forEach( function( v ) { + + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + + } ); + + if ( khr_material.doubleSided || materialValues.doubleSided ) { + + materialParams.side = THREE.DoubleSide; + + } + + if ( khr_material.transparent || materialValues.transparent ) { + + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + + } + + } else if ( material.technique === undefined ) { + + if ( material.pbrMetallicRoughness !== undefined ) { + + // specification + // https://github.com/sbtron/glTF/blob/30de0b365d1566b1bbd8b9c140f9e995d3203226/specification/2.0/README.md#metallic-roughness-material + + materialType = THREE.MeshStandardMaterial; + + if ( material.pbrMetallicRoughness !== undefined ) { + + var metallicRoughness = material.pbrMetallicRoughness; + + materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + + var array = metallicRoughness.baseColorFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( metallicRoughness.baseColorTexture !== undefined ) { + + materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; + + } + + if ( materialParams.opacity < 1.0 || + ( materialParams.map !== undefined && + ( materialParams.map.format === THREE.AlphaFormat || + materialParams.map.format === THREE.RGBAFormat || + materialParams.map.format === THREE.LuminanceAlphaFormat ) ) ) { + + materialParams.transparent = true; + + } + + materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; + materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; + + if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { + + var textureIndex = metallicRoughness.metallicRoughnessTexture.index; + + // Note that currently metalnessMap would be entirely ignored because + // Three.js and glTF specification use different texture channels for metalness + // (Blue: Three.js, Red: glTF). + // But glTF specification team is discussing if they can change. + // Let's keep an eye on it so far. + // + // https://github.com/KhronosGroup/glTF/issues/857 + materialParams.metalnessMap = dependencies.textures[ textureIndex ]; + materialParams.roughnessMap = dependencies.textures[ textureIndex ]; + + } + + } + + } else { + + materialType = THREE.MeshPhongMaterial; + + } + + if ( material.normalTexture !== undefined ) { + + materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; + + } + + if ( material.occlusionTexture !== undefined ) { + + materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; + + } + + if ( material.emissiveTexture !== undefined ) { + + materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; + + } + + materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); + + if ( material.emissiveFactor !== undefined ) { + + materialParams.emissive.fromArray( material.emissiveFactor ); + + } + + Object.assign( materialValues, material.values ); + + } else { + + materialType = DeferredShaderMaterial; + + var technique = json.techniques[ material.technique ]; + + materialParams.uniforms = {}; + + var program = json.programs[ technique.program ]; + + if ( program ) { + + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + + if ( ! materialParams.fragmentShader ) { + + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = THREE.MeshPhongMaterial; + + } + + var vertexShader = dependencies.shaders[ program.vertexShader ]; + + if ( ! vertexShader ) { + + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = THREE.MeshPhongMaterial; + + } + + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + + var uniforms = technique.uniforms; + + for ( var uniformId in uniforms ) { + + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + + var ptype = shaderParam.type; + + if ( WEBGL_TYPE[ ptype ] ) { + + var pcount = shaderParam.count; + var value; + + if ( material.values !== undefined ) value = material.values[ pname ]; + + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + + switch ( ptype ) { + + case WEBGL_CONSTANTS.FLOAT: + + uvalue = shaderParam.value; + + if ( pname == "transparency" ) { + + materialParams.transparent = true; + + } + + if ( value !== undefined ) { + + uvalue = value; + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + + if ( shaderParam && shaderParam.value ) { + + uvalue.fromArray( shaderParam.value ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_MAT2: + + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + + case WEBGL_CONSTANTS.FLOAT_MAT4: + + if ( pcount ) { + + uvalue = new Array( pcount ); + + for ( var mi = 0; mi < pcount; mi ++ ) { + + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + + } + + if ( shaderParam && shaderParam.value ) { + + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } else { + + if ( shaderParam && shaderParam.value ) { + + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } + + break; + + case WEBGL_CONSTANTS.SAMPLER_2D: + + if ( value !== undefined ) { + + uvalue = dependencies.textures[ value ]; + + } else if ( shaderParam.value !== undefined ) { + + uvalue = dependencies.textures[ shaderParam.value ]; + + } else { + + uvalue = null; + + } + + break; + + } + + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + + } else { + + throw new Error( "Unknown shader uniform param type: " + ptype ); + + } + + } + + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + + for ( var i = 0, il = enables.length; i < il; i ++ ) { + + var enable = enables[ i ]; + + switch ( STATES_ENABLES[ enable ] ) { + + case 'CULL_FACE': + + enableCullFace = true; + + break; + + case 'DEPTH_TEST': + + enableDepthTest = true; + + break; + + case 'BLEND': + + enableBlend = true; + + break; + + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + + break; + + default: + + throw new Error( "Unknown technique.states.enable: " + enable ); + + } + + } + + if ( enableCullFace ) { + + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + + } else { + + materialParams.side = THREE.DoubleSide; + + } + + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + + var blendEquationSeparate = functions.blendEquationSeparate; + + if ( blendEquationSeparate !== undefined ) { + + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + + } else { + + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + + } + + var blendFuncSeparate = functions.blendFuncSeparate; + + if ( blendFuncSeparate !== undefined ) { + + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + + } else { + + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + + } + + } + + } + + if ( Array.isArray( materialValues.diffuse ) ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); + + } else if ( typeof( materialValues.diffuse ) === 'string' ) { + + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + + } + + delete materialParams.diffuse; + + if ( typeof( materialValues.reflective ) === 'string' ) { + + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + + } + + if ( typeof( materialValues.bump ) === 'string' ) { + + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + + } + + if ( Array.isArray( materialValues.emission ) ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + + } else { + + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + + } + + } else if ( typeof( materialValues.emission ) === 'string' ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.map = dependencies.textures[ materialValues.emission ]; + + } else { + + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + + } + + } + + if ( Array.isArray( materialValues.specular ) ) { + + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + + } else if ( typeof( materialValues.specular ) === 'string' ) { + + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + + } + + if ( materialValues.shininess !== undefined ) { + + materialParams.shininess = materialValues.shininess; + + } + + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + + return _material; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMeshes = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "materials" + + ] ).then( function ( dependencies ) { + + return _each( json.meshes, function ( mesh ) { + + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + + if ( mesh.extras ) group.userData = mesh.extras; + + var primitives = mesh.primitives || []; + + for ( var name in primitives ) { + + var primitive = primitives[ name ]; + + var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + + var geometry; + + var meshNode; + + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( attributeEntry === undefined ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'NORMAL': + + geometry.addAttribute( 'normal', bufferAttribute ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + geometry.addAttribute( 'uv', bufferAttribute ); + break; + + case 'TEXCOORD_1': + + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + geometry.addAttribute( 'color', bufferAttribute ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + } + + meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( ! attributeEntry ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + meshNode = new THREE.LineSegments( geometry, material ); + + } else { + + meshNode = new THREE.Line( geometry, material ); + + } + + } else { + + throw new Error( "Only triangular and line primitives are supported" ); + + } + + if ( geometry.attributes.color !== undefined ) { + + material.vertexColors = THREE.VertexColors; + material.needsUpdate = true; + + } + + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } + + return group; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadCameras = function () { + + var json = this.json; + + return _each( json.cameras, function ( camera ) { + + if ( camera.type == "perspective" && camera.perspective ) { + + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } else if ( camera.type == "orthographic" && camera.orthographic ) { + + var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } + + } ); + + }; + + GLTFParser.prototype.loadSkins = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors" + + ] ).then( function ( dependencies ) { + + return _each( json.skins, function ( skin ) { + + var bindShapeMatrix = new THREE.Matrix4(); + + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + + return _skin; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAnimations = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.animations, function ( animation, animationId ) { + + var tracks = []; + + for ( var channelId in animation.channels ) { + + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + + if ( sampler ) { + + var target = channel.target; + var name = target.node || target.id; // NOTE: target.id is deprecated. + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + + var node = dependencies.nodes[ name ]; + + if ( node ) { + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + + } + + } + + } + + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + + return new THREE.AnimationClip( name, undefined, tracks ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadNodes = function () { + + var json = this.json; + var extensions = this.extensions; + var scope = this; + + return _each( json.nodes, function ( node ) { + + var matrix = new THREE.Matrix4(); + + var _node; + + if ( node.jointName ) { + + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + + } else { + + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + + } + + if ( node.extras ) _node.userData = node.extras; + + if ( node.matrix !== undefined ) { + + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + + } else { + + if ( node.translation !== undefined ) { + + _node.position.fromArray( node.translation ); + + } + + if ( node.rotation !== undefined ) { + + _node.quaternion.fromArray( node.rotation ); + + } + + if ( node.scale !== undefined ) { + + _node.scale.fromArray( node.scale ); + + } + + } + + return _node; + + } ).then( function ( __nodes ) { + + return scope._withDependencies( [ + + "meshes", + "skins", + "cameras" + + ] ).then( function ( dependencies ) { + + return _each( __nodes, function ( _node, nodeId ) { + + var node = json.nodes[ nodeId ]; + + var meshes; + + if ( node.mesh !== undefined) { + + meshes = [ node.mesh ]; + + } else if ( node.meshes !== undefined ) { + + console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); + + meshes = node.meshes; + + } + + if ( meshes !== undefined ) { + + for ( var meshId in meshes ) { + + var mesh = meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + + if ( group === undefined ) { + + console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); + continue; + + } + + for ( var childrenId in group.children ) { + + var child = group.children[ childrenId ]; + + // clone Mesh to add to _node + + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + + var material; + + if ( originalMaterial.isDeferredShaderMaterial ) { + + originalMaterial = material = originalMaterial.create(); + + } else { + + material = originalMaterial; + + } + + switch ( child.type ) { + + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + + default: + child = new THREE.Mesh( originalGeometry, material ); + + } + + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var skinEntry; + + if ( node.skin !== undefined ) { + + skinEntry = dependencies.skins[ node.skin ]; + + } + + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + + var getJointNode = function ( jointId ) { + + var keys = Object.keys( __nodes ); + + for ( var i = 0, il = keys.length; i < il; i ++ ) { + + var n = __nodes[ keys[ i ] ]; + + if ( n.jointName === jointId ) return n; + + } + + return null; + + }; + + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var bones = []; + var boneInverses = []; + + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + + if ( jointNode ) { + + bones.push( jointNode ); + + var m = skinEntry.inverseBindMatrices.array; + var mat = new THREE.Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + + } else { + + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + + } + + } + + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + + var buildBoneGraph = function ( parentJson, parentObject, property ) { + + var children = parentJson[ property ]; + + if ( children === undefined ) return; + + for ( var i = 0, il = children.length; i < il; i ++ ) { + + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + + } + + } + + }; + + buildBoneGraph( node, child, 'skeletons' ); + + } + + _node.add( child ); + + } + + } + + } + + if ( node.camera !== undefined ) { + + var camera = dependencies.cameras[ node.camera ]; + + _node.add( camera ); + + } + + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + + _node.add( light ); + + } + + return _node; + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadScenes = function () { + + var json = this.json; + + // scene node hierachy builder + + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + + var node = json.nodes[ nodeId ]; + + if ( node.children ) { + + var children = node.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + + } + + } + + } + + return this._withDependencies( [ + + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.scenes, function ( scene ) { + + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + + if ( scene.extras ) _scene.userData = scene.extras; + + var nodes = scene.nodes || []; + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + + } + + _scene.traverse( function ( child ) { + + // Register raw material meshes with GLTF2Loader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + + } + + } ); + + return _scene; + + } ); + + } ); + + }; + + return GLTF2Loader; + +} )(); +// File: utils/GLTFLoader.js +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + */ + +THREE.GLTFLoader = ( function () { + function GLTFLoader( manager ) { + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + } + GLTFLoader.prototype = { + constructor: GLTFLoader, + load: function ( url, onLoad, onProgress, onError ) { + var scope = this; + var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); + var loader = new THREE.FileLoader( scope.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( data ) { + scope.parse( data, onLoad, path ); + }, onProgress, onError ); + }, + setCrossOrigin: function ( value ) { + this.crossOrigin = value; + }, + setPath: function ( value ) { + this.path = value; + }, + parse: function ( data, callback, path ) { + var content; + var extensions = {}; + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + } else { + content = convertUint8ArrayToString( new Uint8Array( data ) ); + } + var json = JSON.parse( content ); + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + } + console.time( 'GLTFLoader' ); + var parser = new GLTFParser( json, extensions, { + path: path || this.path, + crossOrigin: this.crossOrigin + } ); + parser.parse( function ( scene, scenes, cameras, animations ) { + console.timeEnd( 'GLTFLoader' ); + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + callback( glTF ); + } ); + } + }; + + /* GLTFREGISTRY */ + function GLTFRegistry() { + var objects = {}; + return { + get: function ( key ) { + return objects[ key ]; + }, + add: function ( key, object ) { + objects[ key ] = object; + }, + remove: function ( key ) { + delete objects[ key ]; + }, + removeAll: function () { + objects = {}; + }, + update: function ( scene, camera ) { + for ( var name in objects ) { + var object = objects[ name ]; + if ( object.update ) { + object.update( scene, camera ); + } + } + } + }; + } + + /* GLTFSHADERS */ + GLTFLoader.Shaders = { + update: function () { + console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); + } + }; + + /* GLTFSHADER */ + function GLTFShader( targetNode, allNodes ) { + var boundUniforms = {}; + // bind each uniform to its source node + var uniforms = targetNode.material.uniforms; + for ( var uniformId in uniforms ) { + var uniform = uniforms[ uniformId ]; + if ( uniform.semantic ) { + var sourceNodeRef = uniform.node; + var sourceNode = targetNode; + if ( sourceNodeRef ) { + sourceNode = allNodes[ sourceNodeRef ]; + } + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + } + } + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + } + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + var boundUniforms = this.boundUniforms; + for ( var name in boundUniforms ) { + var boundUniform = boundUniforms[ name ]; + switch ( boundUniform.semantic ) { + case "MODELVIEW": + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + case "MODELVIEWINVERSETRANSPOSE": + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + case "PROJECTION": + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + case "JOINTMATRIX": + var m4v = boundUniform.uniform.value; + for ( var mi = 0; mi < m4v.length; mi ++ ) { + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + } + break; + default : + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + } + } + }; + + /* ANIMATION */ + GLTFLoader.Animations = { + update: function () { + console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); + } + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' + }; + + /* MATERIALS COMMON EXTENSION */ + function GLTFMaterialsCommonExtension( json ) { + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + this.lights = {}; + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + for ( var lightId in lights ) { + var light = lights[ lightId ]; + var lightNode; + var lightParams = light[ light.type ]; + var color = new THREE.Color().fromArray( lightParams.color ); + switch ( light.type ) { + case "directional": + lightNode = new THREE.DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + case "point": + lightNode = new THREE.PointLight( color ); + break; + case "spot": + lightNode = new THREE.SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + case "ambient": + lightNode = new THREE.AmbientLight( color ); + break; + } + if ( lightNode ) { + this.lights[ lightId ] = lightNode; + } + } + } + + /* BINARY EXTENSION */ + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; + var BINARY_EXTENSION_HEADER_LENGTH = 20; + function GLTFBinaryExtension( data ) { + this.name = EXTENSIONS.KHR_BINARY_GLTF; + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + var header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ), + contentLength: headerView.getUint32( 12, true ), + contentFormat: headerView.getUint32( 16, true ) + }; + for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { + var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; + if ( header[ key ] !== value ) { + throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); + } + } + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); + this.header = header; + this.content = convertUint8ArrayToString( contentArray ); + this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); + } + GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { + var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + }; + GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) { + var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; + var bufferView = bufferViews[ metadata.bufferView ]; + var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) ); + return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData ); + }; + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + /* CONSTANTS */ + var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 + }; + var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: THREE.Matrix3, + 35676: THREE.Matrix4, + 35664: THREE.Vector2, + 35665: THREE.Vector3, + 35666: THREE.Vector4, + 35678: THREE.Texture + }; + var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipMapNearestFilter, + 9985: THREE.LinearMipMapNearestFilter, + 9986: THREE.NearestMipMapLinearFilter, + 9987: THREE.LinearMipMapLinearFilter + }; + var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping + }; + var WEBGL_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + function _each( object, callback, thisObj ) { + if ( !object ) { + return Promise.resolve(); + } + var results; + var fns = []; + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + results = []; + var length = object.length; + for ( var idx = 0; idx < length; idx ++ ) { + var value = callback.call( thisObj || this, object[ idx ], idx ); + if ( value ) { + fns.push( value ); + if ( value instanceof Promise ) { + value.then( function( key, value ) { + results[ key ] = value; + }.bind( this, idx )); + } else { + results[ idx ] = value; + } + } + } + } else { + results = {}; + for ( var key in object ) { + if ( object.hasOwnProperty( key ) ) { + var value = callback.call( thisObj || this, object[ key ], key ); + if ( value ) { + fns.push( value ); + if ( value instanceof Promise ) { + value.then( function( key, value ) { + results[ key ] = value; + }.bind( this, key )); + } else { + results[ key ] = value; + } + } + } + } + } + return Promise.all( fns ).then( function() { + return results; + }); + } + function resolveURL( url, path ) { + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + return url; + } + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + return url; + } + // Relative URL + return ( path || '' ) + url; + } + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + function convertUint8ArrayToString( array ) { + var s = ''; + for ( var i = 0; i < array.length; i ++ ) { + s += String.fromCharCode( array[ i ] ); + } + return s; + } + // Three.js seems too dependent on attribute names so globally + // replace those in the shader code + function replaceTHREEShaderAttributes( shaderText, technique ) { + // Expected technique attributes + var attributes = {}; + for ( var attributeId in technique.attributes ) { + var pname = technique.attributes[ attributeId ]; + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + } + // Figure out which attributes to change in technique + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + for ( var attributeId in attributes ) { + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + params[ attributeId ] = shaderParam; + } + } + for ( var pname in params ) { + var param = params[ pname ]; + var semantic = param.semantic; + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + switch ( semantic ) { + case "POSITION": + shaderText = shaderText.replace( regEx, 'position' ); + break; + case "NORMAL": + shaderText = shaderText.replace( regEx, 'normal' ); + break; + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + shaderText = shaderText.replace( regEx, 'uv' ); + break; + case 'TEXCOORD_1': + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + shaderText = shaderText.replace( regEx, 'color' ); + break; + case "WEIGHT": + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + case "JOINT": + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + } + } + return shaderText; + } + function createDefaultMaterial() { + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + } + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + this.isDeferredShaderMaterial = true; + this.params = params; + } + DeferredShaderMaterial.prototype.create = function () { + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + for ( var uniformId in this.params.uniforms ) { + var originalUniform = this.params.uniforms[ uniformId ]; + if ( originalUniform.value instanceof THREE.Texture ) { + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + } + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + } + this.params.uniforms = uniforms; + return new THREE.RawShaderMaterial( this.params ); + }; + + /* GLTF PARSER */ + function GLTFParser( json, extensions, options ) { + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + // loader object cache + this.cache = new GLTFRegistry(); + } + GLTFParser.prototype._withDependencies = function ( dependencies ) { + var _dependencies = {}; + for ( var i = 0; i < dependencies.length; i ++ ) { + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + var cached = this.cache.get( dependency ); + if ( cached !== undefined ) { + _dependencies[ dependency ] = cached; + } else if ( this[ fnName ] ) { + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + _dependencies[ dependency ] = fn; + } + } + return _each( _dependencies, function ( dependency ) { + return dependency; + } ); + }; + GLTFParser.prototype.parse = function ( callback ) { + var json = this.json; + // Clear the loader cache + this.cache.removeAll(); + // Fire the callback on complete + this._withDependencies( [ + "scenes", + "cameras", + "animations" + ] ).then( function ( dependencies ) { + var scenes = []; + for ( var name in dependencies.scenes ) { + scenes.push( dependencies.scenes[ name ] ); + } + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + var cameras = []; + for ( var name in dependencies.cameras ) { + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + } + var animations = []; + for ( var name in dependencies.animations ) { + animations.push( dependencies.animations[ name ] ); + } + callback( scene, scenes, cameras, animations ); + } ); + }; + GLTFParser.prototype.loadShaders = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.shaders, function ( shader ) { + if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); + } + return new Promise( function ( resolve ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + resolve( shaderText ); + } ); + } ); + } ); + } ); + }; + GLTFParser.prototype.loadBuffers = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return _each( json.buffers, function ( buffer, name ) { + if ( name === BINARY_EXTENSION_BUFFER_NAME ) { + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + } + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + return new Promise( function ( resolve ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + resolve( buffer ); + } ); + } ); + } else { + console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); + } + } ); + }; + GLTFParser.prototype.loadBufferViews = function () { + var json = this.json; + return this._withDependencies( [ + "buffers" + ] ).then( function ( dependencies ) { + return _each( json.bufferViews, function ( bufferView ) { + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + } ); + } ); + }; + GLTFParser.prototype.loadAccessors = function () { + var json = this.json; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.accessors, function ( accessor ) { + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + // Use the full buffer if it's interleaved. + var array = new TypedArray( arraybuffer ); + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + } else { + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + return new THREE.BufferAttribute( array, itemSize ); + } + } ); + } ); + }; + GLTFParser.prototype.loadTextures = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.textures, function ( texture ) { + if ( texture.source ) { + return new Promise( function ( resolve ) { + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); + } + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + if ( textureLoader === null ) { + textureLoader = new THREE.TextureLoader(); + } + textureLoader.setCrossOrigin( options.crossOrigin ); + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + _texture.flipY = false; + if ( texture.name !== undefined ) _texture.name = texture.name; + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + } + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + if ( texture.sampler ) { + var sampler = json.samplers[ texture.sampler ]; + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + } + resolve( _texture ); + }, undefined, function () { + resolve(); + } ); + } ); + } + } ); + } ); + }; + GLTFParser.prototype.loadMaterials = function () { + var json = this.json; + return this._withDependencies( [ + "shaders", + "textures" + ] ).then( function ( dependencies ) { + return _each( json.materials, function ( material ) { + var materialType; + var materialValues = {}; + var materialParams = {}; + var khr_material; + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + } + if ( khr_material ) { + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + switch ( khr_material.technique ) { + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + } + keys.forEach( function( v ) { + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + } ); + if ( khr_material.doubleSided || materialValues.doubleSided ) { + materialParams.side = THREE.DoubleSide; + } + if ( khr_material.transparent || materialValues.transparent ) { + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + } + } else if ( material.technique === undefined ) { + materialType = THREE.MeshPhongMaterial; + Object.assign( materialValues, material.values ); + } else { + materialType = DeferredShaderMaterial; + var technique = json.techniques[ material.technique ]; + materialParams.uniforms = {}; + var program = json.programs[ technique.program ]; + if ( program ) { + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + if ( ! materialParams.fragmentShader ) { + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = THREE.MeshPhongMaterial; + } + var vertexShader = dependencies.shaders[ program.vertexShader ]; + if ( ! vertexShader ) { + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = THREE.MeshPhongMaterial; + } + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + var uniforms = technique.uniforms; + for ( var uniformId in uniforms ) { + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + var ptype = shaderParam.type; + if ( WEBGL_TYPE[ ptype ] ) { + var pcount = shaderParam.count; + var value; + if ( material.values !== undefined ) value = material.values[ pname ]; + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + switch ( ptype ) { + case WEBGL_CONSTANTS.FLOAT: + uvalue = shaderParam.value; + if ( pname == "transparency" ) { + materialParams.transparent = true; + } + if ( value !== undefined ) { + uvalue = value; + } + break; + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + if ( shaderParam && shaderParam.value ) { + uvalue.fromArray( shaderParam.value ); + } + if ( value ) { + uvalue.fromArray( value ); + } + break; + case WEBGL_CONSTANTS.FLOAT_MAT2: + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + case WEBGL_CONSTANTS.FLOAT_MAT4: + if ( pcount ) { + uvalue = new Array( pcount ); + for ( var mi = 0; mi < pcount; mi ++ ) { + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + } + if ( shaderParam && shaderParam.value ) { + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + } + if ( value ) { + uvalue.fromArray( value ); + } + } else { + if ( shaderParam && shaderParam.value ) { + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + } + if ( value ) { + uvalue.fromArray( value ); + } + } + break; + case WEBGL_CONSTANTS.SAMPLER_2D: + if ( value !== undefined ) { + uvalue = dependencies.textures[ value ]; + } else if ( shaderParam.value !== undefined ) { + uvalue = dependencies.textures[ shaderParam.value ]; + } else { + uvalue = null; + } + break; + } + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + } else { + throw new Error( "Unknown shader uniform param type: " + ptype ); + } + } + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + for ( var i = 0, il = enables.length; i < il; i ++ ) { + var enable = enables[ i ]; + switch ( STATES_ENABLES[ enable ] ) { + case 'CULL_FACE': + enableCullFace = true; + break; + case 'DEPTH_TEST': + enableDepthTest = true; + break; + case 'BLEND': + enableBlend = true; + break; + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + break; + default: + throw new Error( "Unknown technique.states.enable: " + enable ); + } + } + if ( enableCullFace ) { + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + } else { + materialParams.side = THREE.DoubleSide; + } + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + var blendEquationSeparate = functions.blendEquationSeparate; + if ( blendEquationSeparate !== undefined ) { + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + } else { + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + } + var blendFuncSeparate = functions.blendFuncSeparate; + if ( blendFuncSeparate !== undefined ) { + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + } else { + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + } + } + } + if ( Array.isArray( materialValues.diffuse ) ) { + materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); + } else if ( typeof( materialValues.diffuse ) === 'string' ) { + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + } + delete materialParams.diffuse; + if ( typeof( materialValues.reflective ) === 'string' ) { + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + } + if ( typeof( materialValues.bump ) === 'string' ) { + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + } + if ( Array.isArray( materialValues.emission ) ) { + if ( materialType === THREE.MeshBasicMaterial ) { + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + } else { + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + } + } else if ( typeof( materialValues.emission ) === 'string' ) { + if ( materialType === THREE.MeshBasicMaterial ) { + materialParams.map = dependencies.textures[ materialValues.emission ]; + } else { + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + } + } + if ( Array.isArray( materialValues.specular ) ) { + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + } else if ( typeof( materialValues.specular ) === 'string' ) { + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + } + if ( materialValues.shininess !== undefined ) { + materialParams.shininess = materialValues.shininess; + } + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + return _material; + } ); + } ); + }; + GLTFParser.prototype.loadMeshes = function () { + var json = this.json; + return this._withDependencies( [ + "accessors", + "materials" + ] ).then( function ( dependencies ) { + return _each( json.meshes, function ( mesh ) { + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + if ( mesh.extras ) group.userData = mesh.extras; + var primitives = mesh.primitives || []; + for ( var name in primitives ) { + var primitive = primitives[ name ]; + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + var geometry = new THREE.BufferGeometry(); + var attributes = primitive.attributes; + for ( var attributeId in attributes ) { + var attributeEntry = attributes[ attributeId ]; + if ( ! attributeEntry ) return; + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + switch ( attributeId ) { + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + case 'NORMAL': + geometry.addAttribute( 'normal', bufferAttribute ); + break; + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + geometry.addAttribute( 'uv', bufferAttribute ); + break; + case 'TEXCOORD_1': + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + case 'WEIGHT': + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + case 'JOINT': + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + } + } + if ( primitive.indices ) { + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + } + var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + var meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + meshNode.name = ( name === "0" ? group.name : group.name + name ); + if ( primitive.extras ) meshNode.userData = primitive.extras; + group.add( meshNode ); + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + var geometry = new THREE.BufferGeometry(); + var attributes = primitive.attributes; + for ( var attributeId in attributes ) { + var attributeEntry = attributes[ attributeId ]; + if ( ! attributeEntry ) return; + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + switch ( attributeId ) { + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + } + } + var material = dependencies.materials[ primitive.material ]; + var meshNode; + if ( primitive.indices ) { + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + meshNode = new THREE.LineSegments( geometry, material ); + } else { + meshNode = new THREE.Line( geometry, material ); + } + meshNode.name = ( name === "0" ? group.name : group.name + name ); + if ( primitive.extras ) meshNode.userData = primitive.extras; + group.add( meshNode ); + } else { + console.warn( "Only triangular and line primitives are supported" ); + } + } + return group; + } ); + } ); + }; + GLTFParser.prototype.loadCameras = function () { + var json = this.json; + return _each( json.cameras, function ( camera ) { + if ( camera.type == "perspective" && camera.perspective ) { + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + if ( camera.extras ) _camera.userData = camera.extras; + return _camera; + } else if ( camera.type == "orthographic" && camera.orthographic ) { + var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + if ( camera.extras ) _camera.userData = camera.extras; + return _camera; + } + } ); + }; + GLTFParser.prototype.loadSkins = function () { + var json = this.json; + return this._withDependencies( [ + "accessors" + ] ).then( function ( dependencies ) { + return _each( json.skins, function ( skin ) { + var bindShapeMatrix = new THREE.Matrix4(); + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + return _skin; + } ); + } ); + }; + GLTFParser.prototype.loadAnimations = function () { + var json = this.json; + return this._withDependencies( [ + "accessors", + "nodes" + ] ).then( function ( dependencies ) { + return _each( json.animations, function ( animation, animationId ) { + var tracks = []; + for ( var channelId in animation.channels ) { + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + if ( sampler ) { + var target = channel.target; + var name = target.id; + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + var node = dependencies.nodes[ name ]; + if ( node ) { + node.updateMatrix(); + node.matrixAutoUpdate = true; + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + } + } + } + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + return new THREE.AnimationClip( name, undefined, tracks ); + } ); + } ); + }; + GLTFParser.prototype.loadNodes = function () { + var json = this.json; + var extensions = this.extensions; + var scope = this; + return _each( json.nodes, function ( node ) { + var matrix = new THREE.Matrix4(); + var _node; + if ( node.jointName ) { + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + } else { + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + } + if ( node.extras ) _node.userData = node.extras; + if ( node.matrix !== undefined ) { + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + } else { + if ( node.translation !== undefined ) { + _node.position.fromArray( node.translation ); + } + if ( node.rotation !== undefined ) { + _node.quaternion.fromArray( node.rotation ); + } + if ( node.scale !== undefined ) { + _node.scale.fromArray( node.scale ); + } + } + return _node; + } ).then( function ( __nodes ) { + return scope._withDependencies( [ + "meshes", + "skins", + "cameras" + ] ).then( function ( dependencies ) { + return _each( __nodes, function ( _node, nodeId ) { + var node = json.nodes[ nodeId ]; + if ( node.meshes !== undefined ) { + for ( var meshId in node.meshes ) { + var mesh = node.meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + if ( group === undefined ) { + console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); + continue; + } + for ( var childrenId in group.children ) { + var child = group.children[ childrenId ]; + // clone Mesh to add to _node + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + var material; + if ( originalMaterial.isDeferredShaderMaterial ) { + originalMaterial = material = originalMaterial.create(); + } else { + material = originalMaterial; + } + switch ( child.type ) { + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + default: + child = new THREE.Mesh( originalGeometry, material ); + } + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + var skinEntry; + if ( node.skin ) { + skinEntry = dependencies.skins[ node.skin ]; + } + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + var getJointNode = function ( jointId ) { + var keys = Object.keys( __nodes ); + for ( var i = 0, il = keys.length; i < il; i ++ ) { + var n = __nodes[ keys[ i ] ]; + if ( n.jointName === jointId ) return n; + } + return null; + }; + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + var bones = []; + var boneInverses = []; + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + if ( jointNode ) { + bones.push( jointNode ); + var m = skinEntry.inverseBindMatrices.array; + var mat = new THREE.Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + } else { + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + } + } + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + var buildBoneGraph = function ( parentJson, parentObject, property ) { + var children = parentJson[ property ]; + if ( children === undefined ) return; + for ( var i = 0, il = children.length; i < il; i ++ ) { + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + } + } + }; + buildBoneGraph( node, child, 'skeletons' ); + } + _node.add( child ); + } + } + } + if ( node.camera !== undefined ) { + var camera = dependencies.cameras[ node.camera ]; + _node.add( camera ); + } + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + _node.add( light ); + } + return _node; + } ); + } ); + } ); + }; + GLTFParser.prototype.loadScenes = function () { + var json = this.json; + // scene node hierachy builder + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + var node = json.nodes[ nodeId ]; + if ( node.children ) { + var children = node.children; + for ( var i = 0, l = children.length; i < l; i ++ ) { + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + } + } + } + return this._withDependencies( [ + "nodes" + ] ).then( function ( dependencies ) { + return _each( json.scenes, function ( scene ) { + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + if ( scene.extras ) _scene.userData = scene.extras; + var nodes = scene.nodes || []; + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + } + _scene.traverse( function ( child ) { + // Register raw material meshes with GLTFLoader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + } + } ); + return _scene; + } ); + } ); + }; + return GLTFLoader; +} )(); +// File: utils/JSONParser.js +"use strict"; + +var JSONParser = function() +{ +} + +JSONParser.prototype.constructor = JSONParser; + + /** + * Load X3D JSON into an element. + * jsobj - the JavaScript object to convert to DOM. + */ +JSONParser.prototype.parseJavaScript = function(jsobj) { + var child = this.CreateElement('scene'); + this.ConvertToX3DOM(jsobj, "", child); + console.log(jsobj, child); + return child; + }; + + // 'http://www.web3d.org/specifications/x3d-namespace' + + // Load X3D JavaScript object into XML or DOM + + /** + * Yet another way to set an attribute on an element. does not allow you to + * set JSON schema or encoding. + */ +JSONParser.prototype.elementSetAttribute = function(element, key, value) { + if (key === 'SON schema') { + // JSON Schema + } else if (key === 'ncoding') { + // encoding, UTF-8, UTF-16 or UTF-32 + } else { + if (typeof element.setAttribute === 'function') { + element.setAttribute(key, value); + } + } + }; + + /** + * converts children of object to DOM. + */ +JSONParser.prototype.ConvertChildren = function(parentkey, object, element) { + var key; + + for (key in object) { + if (typeof object[key] === 'object') { + if (isNaN(parseInt(key))) { + this.ConvertObject(key, object, element, parentkey.substr(1)); + } else { + this.ConvertToX3DOM(object[key], key, element, parentkey.substr(1)); + } + } + } + }; + + /** + * a method to create and element with tagnam key to DOM in a namespace. If + * containerField is set, then the containerField is set in the elemetn. + */ +JSONParser.prototype.CreateElement = function(key, containerField) { + var child = document.createElement(key); + if (typeof containerField !== 'undefined') { + this.elementSetAttribute(child, 'containerField', containerField); + } + return child; + }; + + /** + * a way to create a CDATA function or script in HTML, by using a DOM parser. + */ +JSONParser.prototype.CDATACreateFunction = function(document, element, str) { + var y = str.replace(/\\"/g, "\\\"") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/&/g, "&"); + do { + str = y; + y = str.replace(/'([^'\r\n]*)\n([^']*)'/g, "'$1\\n$2'"); + if (str !== y) { + console.log("CDATA Replacing",str,"with",y); + } + } while (y != str); + var domParser = new DOMParser(); + var cdataStr = ''; // has to be wrapped into an element + var scriptDoc = domParser .parseFromString (cdataStr, 'application/xml'); + var cdata = scriptDoc .children[0] .childNodes[1]; // space after script is childNode[0] + element .appendChild(cdata); + }; + + /** + * convert the object at object[key] to DOM. + */ +JSONParser.prototype.ConvertObject = function(key, object, element, containerField) { + var child; + if (object !== null && typeof object[key] === 'object') { + if (key.substr(0,1) === '@') { + this.ConvertToX3DOM(object[key], key, element); + } else if (key.substr(0,1) === '-') { + this.ConvertChildren(key, object[key], element); + } else if (key === '#comment') { + for (var c in object[key]) { + child = document.createComment(this.CommentStringToXML(object[key][c])); + element.appendChild(child); + } + } else if (key === '#text') { + child = document.createTextNode(object[key].join("")); + element.appendChild(child); + } else if (key === '#sourceText') { + this.CDATACreateFunction(document, element, object[key].join("\r\n")+"\r\n"); + } else { + if (key === 'connect' || key === 'fieldValue' || key === 'field' || key === 'meta' || key === 'component') { + for (var childkey in object[key]) { // for each field + if (typeof object[key][childkey] === 'object') { + child = this.CreateElement(key, containerField); + this.ConvertToX3DOM(object[key][childkey], childkey, child); + element.appendChild(child); + element.appendChild(document.createTextNode("\n")); + } + } + } else { + child = this.CreateElement(key, containerField); + this.ConvertToX3DOM(object[key], key, child); + element.appendChild(child); + element.appendChild(document.createTextNode("\n")); + } + } + } + }; + + /** + * convert a comment string in JavaScript to XML. Pass the string + */ +JSONParser.prototype.CommentStringToXML = function(str) { + var y = str; + str = str.replace(/\\\\/g, '\\'); + if (y !== str) { + console.log("X3DJSONLD replacing", y, "with", str); + } + return str; + }; + + /** + * convert an SFString to XML. + */ +JSONParser.prototype.SFStringToXML = function(str) { + var y = str; + /* + str = (""+str).replace(/\\\\/g, '\\\\'); + str = str.replace(/\\\\\\\\/g, '\\\\'); + str = str.replace(/(\\+)"/g, '\\"'); + */ + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/"/g, '\\\"'); + if (y !== str) { + console.log("X3DJSONLD [] replacing", y, "with", str); + } + return str; + }; + + /** + * convert a JSON String to XML. + */ +JSONParser.prototype.JSONStringToXML = function(str) { + var y = str; + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/\n/g, '\\n'); + if (y !== str) { + console.log("X3DJSONLD replacing", y, "with", str); + } + return str; + }; + + /** + * main routine for converting a JavaScript object to DOM. + * object is the object to convert. + * parentkey is the key of the object in the parent. + * element is the parent element. + * containerField is a possible containerField. + */ +JSONParser.prototype.ConvertToX3DOM = function(object, parentkey, element, containerField) { + var key; + var localArray = []; + var isArray = false; + var arrayOfStrings = false; + for (key in object) { + if (isNaN(parseInt(key))) { + isArray = false; + } else { + isArray = true; + } + if (isArray) { + if (typeof object[key] === 'number') { + localArray.push(object[key]); + } else if (typeof object[key] === 'string') { + localArray.push(object[key]); + arrayOfStrings = true; + } else if (typeof object[key] === 'boolean') { + localArray.push(object[key]); + } else if (typeof object[key] === 'object') { + /* + if (object[key] != null && typeof object[key].join === 'function') { + localArray.push(object[key].join(" ")); + } + */ + this.ConvertToX3DOM(object[key], key, element); + } else if (typeof object[key] === 'undefined') { + } else { + console.error("Unknown type found in array "+typeof object[key]); + } + } else if (typeof object[key] === 'object') { + // This is where the whole thing starts + if (key === 'scene') { + this.ConvertToX3DOM(object[key], key, element); + } else { + this.ConvertObject(key, object, element); + } + } else if (typeof object[key] === 'number') { + this.elementSetAttribute(element, key.substr(1),object[key]); + } else if (typeof object[key] === 'string') { + if (key === '#comment') { + var child = document.createComment(this.CommentStringToXML(object[key])); + element.appendChild(child); + } else if (key === '#text') { + var child = document.createTextNode(object[key]); + element.appendChild(child); + } else { + // ordinary string attributes + this.elementSetAttribute(element, key.substr(1), this.JSONStringToXML(object[key])); + } + } else if (typeof object[key] === 'boolean') { + this.elementSetAttribute(element, key.substr(1),object[key]); + } else if (typeof object[key] === 'undefined') { + } else { + console.error("Unknown type found in object "+typeof object[key]); + console.error(object); + } + } + if (isArray) { + if (parentkey.substr(0,1) === '@') { + if (arrayOfStrings) { + arrayOfStrings = false; + for (var str in localArray) { + localArray[str] = this.SFStringToXML(localArray[str]); + } + this.elementSetAttribute(element, parentkey.substr(1),'"'+localArray.join('" "')+'"'); + } else { + // if non string array + this.elementSetAttribute(element, parentkey.substr(1),localArray.join(" ")); + } + } + isArray = false; + } + return element; + }; +// File: utils/LoadManager.js +/* + * For use with XSeen JavaScript Library + * http://tools.realism.com/... + * + * Licensed under MIT or GNU in the same manner as XSeen + * + * (C)2017 Daly Realiusm, Los Angeles + * + */ + +/* + * Manages all download requests. + * Requests are queued up and processed to the maximum limit (.MaxRequests) + * Use this for processing text (X3D, XML, JSON, HTML) files. + * Not really setup for binary files (.jpg, png, etc.) + * + * Requires jQuery -- should work on removing that... + * + */ + +function LoadManager () { + this.urlQueue = []; + this.urlNext = -1; + this.MaxRequests = 3; + this.totalRequests = 0; + this.totalResponses = 0; + this.requestCount = 0; + var lmThat = this; + + this.load = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadText = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'text', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadHtml = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'html', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadXml = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'xml', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadJson = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'json', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadImage = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'image', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.success = function (response, string, xhr) { + if (typeof(xhr._loadManager.success) !== undefined) { + xhr._loadManager.success (response, xhr._loadManager.userdata, xhr); + } + } + + this.failure = function (xhr, errorCode, errorText) { + if (typeof(xhr._loadManager.failure) !== undefined) { + xhr._loadManager.failure (xhr, xhr._loadManager.userdata, errorCode, errorText); + } + } + + this.requestComplete = function (event, xhr, settings) { + lmThat.requestCount --; + lmThat.totalResponses++; + lmThat.loadNextUrl(); + } + + this.loadNextUrl = function () { + if (this.requestCount >= this.MaxRequests) {return; } + if (this.urlNext >= this.urlQueue.length || this.urlNext < 0) { + this.urlNext = -1; + for (var i=0; i 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.CODE_SLASH: + slashes[ slashesPointer++ ] = i; + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.CODE_LF: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); + slashesPointer = 0; + bufferPointer = 0; + break; + + case Consts.CODE_CR: + break; + + default: + word += String.fromCharCode( code ); + break; + } + } + }; + + /** + * Parse the provided text + * @memberOf Parser + * + * @param {string} text OBJ data as string + */ + Parser.prototype.parseText = function ( text ) { + var length = text.length; + var buffer = new Array( 32 ); + var bufferPointer = 0; + var slashes = new Array( 32 ); + var slashesPointer = 0; + var reachedFaces = false; + var char; + var word = ''; + for ( var i = 0; i < length; i++ ) { + + char = text[ i ]; + switch ( char ) { + case Consts.STRING_SPACE: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.STRING_SLASH: + slashes[ slashesPointer++ ] = i; + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.STRING_LF: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); + slashesPointer = 0; + bufferPointer = 0; + break; + + case Consts.STRING_CR: + break; + + default: + word += char; + } + } + }; + + Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) { + if ( bufferPointer < 1 ) return reachedFaces; + + var bufferLength = bufferPointer - 1; + switch ( buffer[ 0 ] ) { + case Consts.LINE_V: + + // object complete instance required if reached faces already (= reached next block of v) + if ( reachedFaces ) { + + this.processCompletedObject( null, this.rawObject.groupName ); + reachedFaces = false; + + } + this.rawObject.pushVertex( buffer ); + break; + + case Consts.LINE_VT: + this.rawObject.pushUv( buffer ); + break; + + case Consts.LINE_VN: + this.rawObject.pushNormal( buffer ); + break; + + case Consts.LINE_F: + reachedFaces = true; + /* + * 0: "f vertex/uv/normal ..." + * 1: "f vertex/uv ..." + * 2: "f vertex//normal ..." + * 3: "f vertex ..." + */ + var haveQuad = bufferLength % 4 === 0; + if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVn( buffer ); + } else { + this.rawObject.buildFaceVVn( buffer ); + } + + } else if ( bufferLength === slashesPointer * 2 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVt( buffer ); + } else { + this.rawObject.buildFaceVVt( buffer ); + } + + } else if ( bufferLength * 2 === slashesPointer * 3 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVtVn( buffer ); + } else { + this.rawObject.buildFaceVVtVn( buffer ); + } + + } else { + + if ( haveQuad ) { + this.rawObject.buildQuadV( buffer ); + } else { + this.rawObject.buildFaceV( buffer ); + } + + } + break; + + case Consts.LINE_L: + if ( bufferLength === slashesPointer * 2 ) { + + this.rawObject.buildLineVvt( buffer ); + + } else { + + this.rawObject.buildLineV( buffer ); + + } + break; + + case Consts.LINE_S: + this.rawObject.pushSmoothingGroup( buffer[ 1 ] ); + break; + + case Consts.LINE_G: + this.processCompletedGroup( buffer[ 1 ] ); + break; + + case Consts.LINE_O: + if ( this.rawObject.vertices.length > 0 ) { + + this.processCompletedObject( buffer[ 1 ], null ); + reachedFaces = false; + + } else { + + this.rawObject.pushObject( buffer[ 1 ] ); + + } + break; + + case Consts.LINE_MTLLIB: + this.rawObject.pushMtllib( buffer[ 1 ] ); + break; + + case Consts.LINE_USEMTL: + this.rawObject.pushUsemtl( buffer[ 1 ] ); + break; + + default: + break; + } + return reachedFaces; + }; + + Parser.prototype.processCompletedObject = function ( objectName, groupName ) { + this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + this.inputObjectCount++; + this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName ); + }; + + Parser.prototype.processCompletedGroup = function ( groupName ) { + var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + if ( notEmpty ) { + + this.inputObjectCount ++; + this.rawObject = this.rawObject.newInstanceFromGroup( groupName ); + + } else { + + // if a group was set that did not lead to object creation in finalize, then the group name has to be updated + this.rawObject.pushGroup( groupName ); + + } + }; + + Parser.prototype.finalize = function () { + this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + this.inputObjectCount++; + }; + + return Parser; + })(); + + /** + * {@link RawObject} is only used by {@link Parser}. + * The user of OBJLoader2 does not need to care about this class. + * It is defined publicly for inclusion in web worker based OBJ loader ({@link THREE.OBJLoader2.WWOBJLoader2}) + */ + var RawObject = (function () { + + function RawObject( objectName, groupName, mtllibName ) { + this.globalVertexOffset = 1; + this.globalUvOffset = 1; + this.globalNormalOffset = 1; + + this.vertices = []; + this.normals = []; + this.uvs = []; + + // faces are stored according combined index of group, material and smoothingGroup (0 or not) + this.mtllibName = Validator.verifyInput( mtllibName, 'none' ); + this.objectName = Validator.verifyInput( objectName, 'none' ); + this.groupName = Validator.verifyInput( groupName, 'none' ); + this.activeMtlName = 'none'; + this.activeSmoothingGroup = 1; + + this.mtlCount = 0; + this.smoothingGroupCount = 0; + + this.rawObjectDescriptions = []; + // this default index is required as it is possible to define faces without 'g' or 'usemtl' + var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; + } + + RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) { + return materialName + '|' + smoothingGroup; + }; + + RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) { + var newRawObject = new RawObject( objectName, groupName, this.mtllibName ); + + // move indices forward + newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; + newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; + newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; + + return newRawObject; + }; + + RawObject.prototype.newInstanceFromGroup = function ( groupName ) { + var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName ); + + // keep current buffers and indices forward + newRawObject.vertices = this.vertices; + newRawObject.uvs = this.uvs; + newRawObject.normals = this.normals; + newRawObject.globalVertexOffset = this.globalVertexOffset; + newRawObject.globalUvOffset = this.globalUvOffset; + newRawObject.globalNormalOffset = this.globalNormalOffset; + + return newRawObject; + }; + + RawObject.prototype.pushVertex = function ( buffer ) { + this.vertices.push( parseFloat( buffer[ 1 ] ) ); + this.vertices.push( parseFloat( buffer[ 2 ] ) ); + this.vertices.push( parseFloat( buffer[ 3 ] ) ); + }; + + RawObject.prototype.pushUv = function ( buffer ) { + this.uvs.push( parseFloat( buffer[ 1 ] ) ); + this.uvs.push( parseFloat( buffer[ 2 ] ) ); + }; + + RawObject.prototype.pushNormal = function ( buffer ) { + this.normals.push( parseFloat( buffer[ 1 ] ) ); + this.normals.push( parseFloat( buffer[ 2 ] ) ); + this.normals.push( parseFloat( buffer[ 3 ] ) ); + }; + + RawObject.prototype.pushObject = function ( objectName ) { + this.objectName = objectName; + }; + + RawObject.prototype.pushMtllib = function ( mtllibName ) { + this.mtllibName = mtllibName; + }; + + RawObject.prototype.pushGroup = function ( groupName ) { + this.groupName = groupName; + this.verifyIndex(); + }; + + RawObject.prototype.pushUsemtl = function ( mtlName ) { + if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; + this.activeMtlName = mtlName; + this.mtlCount++; + + this.verifyIndex(); + }; + + RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) { + var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup; + if ( this.activeSmoothingGroup === normalized ) return; + this.activeSmoothingGroup = normalized; + this.smoothingGroupCount++; + + this.verifyIndex(); + }; + + RawObject.prototype.verifyIndex = function () { + var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 ); + this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ]; + if ( ! Validator.isValid( this.rawObjectDescriptionInUse ) ) { + + this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; + + } + }; + + RawObject.prototype.buildQuadVVtVn = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] ); + this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] ); + this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] ); + } + }; + + RawObject.prototype.buildQuadVVt = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); + this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); + } + }; + + RawObject.prototype.buildQuadVVn = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); + this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); + } + }; + + RawObject.prototype.buildQuadV = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] ); + } + }; + + RawObject.prototype.buildFaceVVtVn = function ( indexArray ) { + for ( var i = 1; i < 10; i += 3 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVt( indexArray[ i + 1 ] ); + this.attachFaceVn( indexArray[ i + 2 ] ); + } + }; + + RawObject.prototype.buildFaceVVt = function ( indexArray ) { + for ( var i = 1; i < 7; i += 2 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVt( indexArray[ i + 1 ] ); + } + }; + + RawObject.prototype.buildFaceVVn = function ( indexArray ) { + for ( var i = 1; i < 7; i += 2 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVn( indexArray[ i + 1 ] ); + } + }; + + RawObject.prototype.buildFaceV = function ( indexArray ) { + for ( var i = 1; i < 4; i ++ ) { + this.attachFaceV_( indexArray[ i ] ); + } + }; + + RawObject.prototype.attachFaceV_ = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalVertexOffset ) * 3; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.vertices.push( this.vertices[ index++ ] ); + rodiu.vertices.push( this.vertices[ index++ ] ); + rodiu.vertices.push( this.vertices[ index ] ); + }; + + RawObject.prototype.attachFaceVt = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalUvOffset ) * 2; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.uvs.push( this.uvs[ index++ ] ); + rodiu.uvs.push( this.uvs[ index ] ); + }; + + RawObject.prototype.attachFaceVn = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalNormalOffset ) * 3; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.normals.push( this.normals[ index++ ] ); + rodiu.normals.push( this.normals[ index++ ] ); + rodiu.normals.push( this.normals[ index ] ); + }; + + /* + * Support for lines with or without texture. irst element in indexArray is the line identification + * 0: "f vertex/uv vertex/uv ..." + * 1: "f vertex vertex ..." + */ + RawObject.prototype.buildLineVvt = function ( lineArray ) { + var length = lineArray.length; + for ( var i = 1; i < length; i ++ ) { + this.vertices.push( parseInt( lineArray[ i ] ) ); + this.uvs.push( parseInt( lineArray[ i ] ) ); + } + }; + + RawObject.prototype.buildLineV = function ( lineArray ) { + var length = lineArray.length; + for ( var i = 1; i < length; i++ ) { + this.vertices.push( parseInt( lineArray[ i ] ) ); + } + }; + + /** + * Clear any empty rawObjectDescription and calculate absolute vertex, normal and uv counts + */ + RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) { + var temp = this.rawObjectDescriptions; + this.rawObjectDescriptions = []; + var rawObjectDescription; + var index = 0; + var absoluteVertexCount = 0; + var absoluteNormalCount = 0; + var absoluteUvCount = 0; + + for ( var name in temp ) { + + rawObjectDescription = temp[ name ]; + if ( rawObjectDescription.vertices.length > 0 ) { + + if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName; + this.rawObjectDescriptions[ index++ ] = rawObjectDescription; + absoluteVertexCount += rawObjectDescription.vertices.length; + absoluteUvCount += rawObjectDescription.uvs.length; + absoluteNormalCount += rawObjectDescription.normals.length; + + } + } + + // don not continue if no result + var notEmpty = false; + if ( index > 0 ) { + + if ( debug ) this.createReport( inputObjectCount, true ); + meshCreator.buildMesh( + this.rawObjectDescriptions, + inputObjectCount, + absoluteVertexCount, + absoluteNormalCount, + absoluteUvCount + ); + notEmpty = true; + + } + return notEmpty; + }; + + RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) { + var report = { + name: this.objectName ? this.objectName : 'groups', + mtllibName: this.mtllibName, + vertexCount: this.vertices.length / 3, + normalCount: this.normals.length / 3, + uvCount: this.uvs.length / 2, + smoothingGroupCount: this.smoothingGroupCount, + mtlCount: this.mtlCount, + rawObjectDescriptions: this.rawObjectDescriptions.length + }; + + if ( printDirectly ) { + console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name ); + console.log( 'Mtllib name: ' + report.mtllibName ); + console.log( 'Vertex count: ' + report.vertexCount ); + console.log( 'Normal count: ' + report.normalCount ); + console.log( 'UV count: ' + report.uvCount ); + console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount ); + console.log( 'Material count: ' + report.mtlCount ); + console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions ); + console.log( '' ); + } + + return report; + }; + + return RawObject; + })(); + + /** + * Descriptive information and data (vertices, normals, uvs) to passed on to mesh building function. + * @class + * + * @param {string} objectName Name of the mesh + * @param {string} groupName Name of the group + * @param {string} materialName Name of the material + * @param {number} smoothingGroup Normalized smoothingGroup (0: THREE.FlatShading, 1: THREE.SmoothShading) + */ + var RawObjectDescription = (function () { + + function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) { + this.objectName = objectName; + this.groupName = groupName; + this.materialName = materialName; + this.smoothingGroup = smoothingGroup; + this.vertices = []; + this.uvs = []; + this.normals = []; + } + + return RawObjectDescription; + })(); + + /** + * MeshCreator is used to transform RawObjectDescriptions to THREE.Mesh + * + * @class + */ + var MeshCreator = (function () { + + function MeshCreator() { + this.sceneGraphBaseNode = null; + this.materials = null; + this.debug = false; + this.globalObjectCount = 1; + + this.validated = false; + } + + MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) { + this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, this.sceneGraphBaseNode ); + this.sceneGraphBaseNode = Validator.verifyInput( this.sceneGraphBaseNode, new THREE.Group() ); + }; + + MeshCreator.prototype.setMaterials = function ( materials ) { + this.materials = Validator.verifyInput( materials, this.materials ); + this.materials = Validator.verifyInput( this.materials, { materials: [] } ); + }; + + MeshCreator.prototype.setDebug = function ( debug ) { + if ( debug === true || debug === false ) this.debug = debug; + }; + + MeshCreator.prototype.validate = function () { + if ( this.validated ) return; + + this.setSceneGraphBaseNode( null ); + this.setMaterials( null ); + this.setDebug( null ); + this.globalObjectCount = 1; + }; + + MeshCreator.prototype.finalize = function () { + this.sceneGraphBaseNode = null; + this.materials = null; + this.validated = false; + }; + + /** + * This is an internal function, but due to its importance to Parser it is documented. + * RawObjectDescriptions are transformed to THREE.Mesh. + * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check). + * This method shall be overridden by the web worker implementation + * + * @param {RawObjectDescription[]} rawObjectDescriptions Array of descriptive information and data (vertices, normals, uvs) about the parsed object(s) + * @param {number} inputObjectCount Number of objects already retrieved from OBJ + * @param {number} absoluteVertexCount Sum of all vertices of all rawObjectDescriptions + * @param {number} absoluteNormalCount Sum of all normals of all rawObjectDescriptions + * @param {number} absoluteUvCount Sum of all uvs of all rawObjectDescriptions + */ + MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) { + + if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount ); + + var bufferGeometry = new THREE.BufferGeometry(); + var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 ); + bufferGeometry.addAttribute( 'position', vertexBA ); + + var normalBA; + if ( absoluteNormalCount > 0 ) { + + normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 ); + bufferGeometry.addAttribute( 'normal', normalBA ); + + } + var uvBA; + if ( absoluteUvCount > 0 ) { + + uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 ); + bufferGeometry.addAttribute( 'uv', uvBA ); + + } + + if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount ); + + var rawObjectDescription; + var material; + var materialName; + var createMultiMaterial = rawObjectDescriptions.length > 1; + var materials = []; + var materialIndex = 0; + var materialIndexMapping = []; + var selectedMaterialIndex; + + var vertexBAOffset = 0; + var vertexGroupOffset = 0; + var vertexLength; + var normalOffset = 0; + var uvOffset = 0; + + for ( var oodIndex in rawObjectDescriptions ) { + rawObjectDescription = rawObjectDescriptions[ oodIndex ]; + + materialName = rawObjectDescription.materialName; + material = this.materials[ materialName ]; + if ( ! material ) { + + material = this.materials[ 'defaultMaterial' ]; + if ( ! material ) { + + material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} ); + material.name = 'defaultMaterial'; + this.materials[ 'defaultMaterial' ] = material; + + } + console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' ); + + } + // clone material in case flat shading is needed due to smoothingGroup 0 + if ( rawObjectDescription.smoothingGroup === 0 ) { + + materialName = material.name + '_flat'; + var materialClone = this.materials[ materialName ]; + if ( ! materialClone ) { + + materialClone = material.clone(); + materialClone.name = materialName; + materialClone.shading = THREE.FlatShading; + this.materials[ materialName ] = name; + + } + + } + + vertexLength = rawObjectDescription.vertices.length; + if ( createMultiMaterial ) { + + // re-use material if already used before. Reduces materials array size and eliminates duplicates + selectedMaterialIndex = materialIndexMapping[ materialName ]; + if ( ! selectedMaterialIndex ) { + + selectedMaterialIndex = materialIndex; + materialIndexMapping[ materialName ] = materialIndex; + materials.push( material ); + materialIndex++; + + } + + bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex ); + vertexGroupOffset += vertexLength / 3; + } + + vertexBA.set( rawObjectDescription.vertices, vertexBAOffset ); + vertexBAOffset += vertexLength; + + if ( normalBA ) { + + normalBA.set( rawObjectDescription.normals, normalOffset ); + normalOffset += rawObjectDescription.normals.length; + + } + if ( uvBA ) { + + uvBA.set( rawObjectDescription.uvs, uvOffset ); + uvOffset += rawObjectDescription.uvs.length; + + } + if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex ); + + } + if ( ! normalBA ) bufferGeometry.computeVertexNormals(); + + if ( createMultiMaterial ) material = materials; + var mesh = new THREE.Mesh( bufferGeometry, material ); + this.sceneGraphBaseNode.add( mesh ); + + this.globalObjectCount++; + }; + + MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) { + console.log( + ' Output Object no.: ' + this.globalObjectCount + + '\n objectName: ' + rawObjectDescription.objectName + + '\n groupName: ' + rawObjectDescription.groupName + + '\n materialName: ' + rawObjectDescription.materialName + + '\n materialIndex: ' + selectedMaterialIndex + + '\n smoothingGroup: ' + rawObjectDescription.smoothingGroup + + '\n #vertices: ' + rawObjectDescription.vertices.length / 3 + + '\n #uvs: ' + rawObjectDescription.uvs.length / 2 + + '\n #normals: ' + rawObjectDescription.normals.length / 3 + ); + }; + + return MeshCreator; + })(); + + OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) { + var workerCode = ''; + workerCode += funcBuildObject( 'Consts', Consts ); + workerCode += funcBuildObject( 'Validator', Validator ); + workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); + workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject ); + workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription ); + return workerCode; + }; + + return OBJLoader2; +})(); +// File: init/Definitions.js +/* + * XSeen JavaScript Library + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + * + * Based on code originally provided by + * Philip Taylor: http://philip.html5.org + */ + +/** + * The Namespace container for x3dom objects. + * @namespace x3dom + * + * Removed THREE loaders + loaders: { + 'file' : new THREE.FileLoader(), + 'image' : 0, + }, + + * */ +var xseen = { + canvases : [], + sceneInfo : [], + nodeDefinitions : {}, + parseTable : {}, + node : {}, + utils : {}, + eventManager : {}, + Events : {}, + Navigation : {}, + + loadMgr : {}, + loader : { + 'Null' : '', + 'ColladaLoader' : '', + 'GltfLegacy' : '', + 'GltfLoader' : '', + 'ObjLoader' : '', + 'ImageLoader' : '', + 'X3dLoader' : '', + }, + loadProgress : function (xhr) { + if (xhr.total != 0) { + console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); + } + }, + loadError : function (xhr, userdata, code, message) { + console.error('An error happened on '+userdata.e.id+'\n'+code+'\n'+message); + }, + loadMime : { + '' : {name: 'Null', loader: 'Null'}, + 'dae' : {name: 'Collada', loader: 'ColladaLoader'}, + 'glb' : {name: 'glTF Binary', loader: 'GltfLoader'}, + 'glbl' : {name: 'glTF Binary', loader: 'GltfLegacy'}, + 'gltf' : {name: 'glTF JSON', loader: 'GltfLoader'}, + 'obj' : {name: 'OBJ', loader: 'ObjLoader'}, + 'png' : {name: 'PNG', loader: 'ImageLoader'}, + 'jpg' : {name: 'JPEG', loader: 'ImageLoader'}, + 'jpeg' : {name: 'JPEG', loader: 'ImageLoader'}, + 'gif' : {name: 'GIF', loader: 'ImageLoader'}, + 'x3d' : {name: 'X3D XML', loader: 'X3dLoader'}, + }, +// helper + array_to_object : function (a) { + var o = {}; + for(var i=0;i ' + obj.type + ' (' + obj.name + ')'); + for (var i=0; i + var validParams = xseen.array_to_object([ + 'showLog', + 'showStat', + 'showProgress', + 'PrimitiveQuality', + 'components', + 'loadpath', + 'disableDoubleClick', + 'backend', + 'altImg', + 'runtimeEnabled', + 'keysEnabled', + 'showTouchpoints', + 'disableTouch', + 'maxActiveDownloads' + ]); + var components, prefix; + var showLoggingConsole = false; + + // for each XSeens element + for (var i=0; i < xseens.length; i++) { + + // default parameters + settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'false'); + settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'true'); + settings.setProperty("showStat", xseens[i].getAttribute("showStat") || 'false'); + settings.setProperty("showProgress", xseens[i].getAttribute("showProgress") || 'true'); + settings.setProperty("PrimitiveQuality", xseens[i].getAttribute("PrimitiveQuality") || 'High'); + + // for each param element inside the X3D element + // add settings to properties object + params = xseens[i].getElementsByTagName('PARAM'); + for (var j=0; j < params.length; j++) { + if (params[j].getAttribute('name') in validParams) { + settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value')); + } else { + //xseen.debug.logError("Unknown parameter: " + params[j].getAttribute('name')); + } + } + + // enable log + if (settings.getProperty('showLog') === 'true') { + showLoggingConsole = true; + } + + if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) { + // load components from params or default to x3d attribute + components = settings.getProperty('components', xseens[i].getAttribute("components")); + if (components) { + prefix = settings.getProperty('loadpath', xseens[i].getAttribute("loadpath")); + components = components.trim().split(','); + for (j=0; j < components.length; j++) { + xseen.loadJS(components[j] + ".js", prefix); + } + } + + // src=foo.x3d adding inline node, not a good idea, but... + if (xseens[i].getAttribute("src")) { + var _scene = document.createElement("scene"); + var _inl = document.createElement("Inline"); + _inl.setAttribute("url", xseens[i].getAttribute("src")); + _scene.appendChild(_inl); + xseens[i].appendChild(_scene); + } + } + } + // }}} + + if (showLoggingConsole == true) { + xseen.debug.activate(true); + } else { + xseen.debug.activate(false); + } + + // Convert the collection into a simple array (is this necessary?) +/* Don't think so -- commented out + xseens = Array.map(xseens, function (n) { + n.hasRuntime = true; + return n; + }); + */ + + if (xseen.versionInfo !== undefined) { + xseen.debug.logInfo("XSeen version " + xseen.versionInfo.version + ", " + + "Date " + xseen.versionInfo.date); + xseen.debug.logInfo(xseen.versionInfo.splashText); + } + + //xseen.debug.logInfo("Found " + xseen.length + " XSeen nodes"); + + + // Create a HTML canvas for every XSeen scene and wrap it with + // an X3D canvas and load the content + var x_element; + var x_canvas; + var altDiv, altP, aLnk, altImg; + var t0, t1; + + for (var i=0; i < xseens.length; i++) + { + x_element = xseens[i]; // The XSeen DOM element + + x_canvas = new THREE.Scene(); // May need addtl info if multiple: xseen.X3DCanvas(x_element, xseen.canvases.length); + xseen.canvases.push(x_canvas); // TODO: Need to handle failure to initialize? + t0 = new Date().getTime(); + +/* + * Handle opening tag attributes + * divHeight + * divWidth + * turntable Indicates if view automatically rotates (independent of navigation) + */ + + var divWidth = x_element.getAttribute('width'); + var divHeight = x_element.getAttribute('height'); + if (divHeight + divWidth < 100) { + divHeight = 450; + divWidth = 800; + } else if (divHeight < 50) { + divHeight = Math.floor(divWidth/2) + 50; + } else if (divWidth < 50) { + divWidth = divHeight * 2 - 100; + } + var turntable = (x_element.getAttribute('turntable') || '').toLowerCase(); + if (turntable == 'on' || turntable == 'yes' || turntable == 'y' || turntable == '1') { + turntable = true; + } else { + turntable = false; + } + turntable = false; +/* + * Removed because camera is stored in the Scene node (x_element._xseen.renderer.camera + * Leave variable definition so other code works... + var x_camera = new THREE.PerspectiveCamera( 75, divWidth / divHeight, 0.1, 1000 ); + x_camera.position.x = 0; + x_camera.position.z = 10; + */ + var x_camera = {}; + var x_renderer = new THREE.WebGLRenderer(); + x_renderer.setSize (divWidth, divHeight); + //x_element.appendChild (x_renderer.domElement); + + // Stereo camera effect + // from http://charliegerard.github.io/blog/Virtual-Reality-ThreeJs/ + var x_effect = new THREE.StereoEffect(x_renderer); + x_renderer.controls = {'update' : function() {return;}}; + +/* + * Add event handler to XSeen tag (x_element) + * These handle all mouse/cursor/button controls when the cursor is + * in the XSeen region of the page + */ + + x_element.addEventListener ('dblclick', xseen.Events.canvasHandler, true); + x_element.addEventListener ('click', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mousedown', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mousemove', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mouseup', xseen.Events.canvasHandler, true); + x_element.addEventListener ('xseen', xseen.Events.XSeenHandler); // Last chance for XSeen handling of event + x_element.addEventListener ('change', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event +/* + x_element.addEventListener ('mousedown', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + x_element.addEventListener ('mouseup', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + x_element.addEventListener ('mousemove', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + */ + xseen.sceneInfo.push ({ + 'size' : {'width':divWidth, 'height':divHeight}, + 'scene' : x_canvas, + 'renderer' : x_renderer, + 'effect' : x_effect, + 'camera' : [x_camera], + 'turntable' : turntable, + 'mixers' : [], + 'clock' : new THREE.Clock(), + 'element' : x_element, + 'selectable': [], + 'stacks' : [], + 'tmp' : {activeViewpoint:false}, + 'xseen' : xseen, + }); + x_element._xseen = {}; + x_element._xseen.children = []; + x_element._xseen.sceneInfo = xseen.sceneInfo[xseen.sceneInfo.length-1]; + + t1 = new Date().getTime() - t0; + xseen.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms."); + } + + var ready = (function(eventType) { + var evt = null; + + if (document.createEvent) { + evt = document.createEvent("Events"); + evt.initEvent(eventType, true, true); + document.dispatchEvent(evt); + } else if (document.createEventObject) { + evt = document.createEventObject(); + // http://stackoverflow.com/questions/1874866/how-to-fire-onload-event-on-document-in-ie + document.body.fireEvent('on' + eventType, evt); + } + })('load'); + + // for each X-Scene tag, parse and load the contents + var t=[]; + for (var i=0; i root element that was added after document load. */ + xseen.reload = function() { + onload(); + }; + + /* FIX PROBLEM IN CHROME - HACK - searching for better solution !!! */ + if (navigator.userAgent.indexOf("Chrome") != -1) { + document.__getElementsByTagName = document.getElementsByTagName; + + document.getElementsByTagName = function(tag) { + var obj = []; + var elems = this.__getElementsByTagName("*"); + + if(tag =="*"){ + obj = elems; + } else { + tag = tag.toUpperCase(); + for (var i = 0; i < elems.length; i++) { + var tagName = elems[i].tagName.toUpperCase(); + if (tagName === tag) { + obj.push(elems[i]); + } + } + } + + return obj; + }; + + document.__getElementById = document.getElementById; + document.getElementById = function(id) { + var obj = this.__getElementById(id); + + if (!obj) { + var elems = this.__getElementsByTagName("*"); + for (var i=0; i + * MODE_NAVIGATION events are 'captured' (not bubbled) and drive the camera position + * + * In MODE_SELECT + * mousedown sets redispatch to TRUE + * click Activates + * dblclick ?? + * mouseup terminates select + * mousemove sets redispatch to FALSE + * In all cases, recreate event as type='xseen' and dispatch from geometry when + * redispatch is TRUE. + */ + canvasHandler: function (ev) + { + //console.log ('Primary canvas event handler for event type: ' + ev.type); + var sceneInfo = ev.currentTarget._xseen.sceneInfo; + var localXseen = sceneInfo.xseen; + var lEvents = localXseen.Events; + var type = ev.type; + if (type == 'mousedown') { + lEvents.redispatch = true; + lEvents.mode = lEvents.MODE_SELECT; + lEvents.mouse.x = (ev.clientX / 800) * 2 -1; // TODO: Use real XSeen display sizes + lEvents.mouse.y = (ev.clientY / 450) * 2 -1; + // + lEvents.raycaster.setFromCamera(lEvents.mouse, sceneInfo.element._xseen.renderer.activeCamera); + var hitGeometryList = lEvents.raycaster.intersectObjects (sceneInfo.selectable, true); + if (hitGeometryList.length != 0) { + lEvents.object = hitGeometryList[0]; + } else { + lEvents.object = {}; + lEvents.redispatch = false; + lEvents.mode = lEvents.MODE_NAVIGATION; + } + } + if ((lEvents.redispatch || type == 'click' || type == 'dblclick') && typeof(lEvents.object.object) !== 'undefined') { + // Generate an XSeen (Custom)Event of the same type and dispatch it + var newEv = lEvents.createEvent (ev, lEvents.object); + lEvents.object.object.userData.dispatchEvent(newEv); + ev.stopPropagation(); // No propagation beyond this tag + } else { + //console.log ('Navigation mode...'); + } + if (type == 'mouseup') { + lEvents.redispatch = false; + lEvents.mode = lEvents.MODE_NAVIGATION; + } + }, + + createEvent: function (ev, selectedObject) + { + var properties = { + 'detail': { // This object contains all of the XSeen data + 'type': ev.type, + 'originalType': ev.type, + 'originator': selectedObject.object.userData, + 'position': { + 'x': selectedObject.point.x, + 'y': selectedObject.point.y, + 'z': selectedObject.point.z, + }, + 'normal': { + 'x': 0, + 'y': 0, + 'z': 0, + }, + 'uv': { + 'x': selectedObject.uv.x, + 'y': selectedObject.uv.y, + }, + 'screenX': ev.screenX, + 'screenY': ev.screenY, + 'clientX': ev.clientX, + 'clientY': ev.clientY, + 'ctrlKey': ev.ctrlKey, + 'shiftKey': ev.shiftKey, + 'altKey': ev.altKey, + 'metaKey': ev.metaKey, + 'button': ev.button, + 'buttons': ev.buttons, + }, + 'bubbles': ev.bubbles, + 'cancelable': ev.cancelable, + 'composed': ev.composed, + }; + + var newEvent = new CustomEvent('xseen', properties); + return newEvent; + }, + // Uses method described in https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + // to change 'this' for the handler method. Want 'this' to refer to the target node. + addHandler: function (route, source, eventName, destination, field) + { + var handler = {}; + handler.route = route; // Route element + handler.source = source; // Source element + handler.type = eventName; // Event type + handler.destination = destination; // Destination element + handler.field = field; // Destination field structure + handler.handler = destination._xseen.handlers[field.handlerName]; + this.routes.push (handler); + if (typeof(source._xseen) === 'undefined') { // DOM event + source.addEventListener (eventName, function(ev) { + handler.handler(ev) + }); + } else { // XSeen event + source.addEventListener ('xseen', function(ev) { + //console.log ('New event of original type: |'+ev.detail.originalType+'|; Desired type: |'+handler.type+'|'); + if (ev.detail.originalType == handler.type) { + handler.handler(ev) + } + }); + } + }, + + // Generic notification handler for XSeen's canvas + XSeenHandler: function (ev) + { + //console.log ('XSeen DEBUG Event Bubble handler ('+ev.type+'/'+ev.eventPhase+').'); + }, + XSeenDebugHandler : function (ev) + { + console.log ('XSeen DEBUG Event Capture handler ('+ev.type+'/'+ev.eventPhase+').'); + }, + }; + + this.debug = { + INFO: "INFO", + WARNING: "WARNING", + ERROR: "ERROR", + EXCEPTION: "EXCEPTION", + + // determines whether debugging/logging is active. If set to "false" + // no debugging messages will be logged. + isActive: false, + + // stores if firebug is available + isFirebugAvailable: false, + + // stores if the xseen.debug object is initialized already + isSetup: false, + + // stores if xseen.debug object is append already (Need for IE integration) + isAppend: false, + + // stores the number of lines logged + numLinesLogged: 0, + + // the maximum number of lines to log in order to prevent + // the browser to slow down + maxLinesToLog: 10000, + + // the container div for the logging messages + logContainer: null, + + /** @brief Setup the xseen.debug object. + + Checks for firebug and creates the container div for the logging + messages. + */ + setup: function() { + // If debugging is already setup simply return + if (xseen.debug.isSetup) { return; } + + // Check for firebug console + try { + if (window.console.firebug !== undefined) { + xseen.debug.isFirebugAvailable = true; + } + } + catch (err) { + xseen.debug.isFirebugAvailable = false; + } + + xseen.debug.setupLogContainer(); + + // setup should be setup only once, thus store if we done that already + xseen.debug.isSetup = true; + }, + + /** @brief Activates the log + */ + activate: function(visible) { + xseen.debug.isActive = true; + + //var aDiv = document.createElement("div"); + //aDiv.style.clear = "both"; + //aDiv.appendChild(document.createTextNode("\r\n")); + //aDiv.style.display = (visible) ? "block" : "none"; + xseen.debug.logContainer.style.display = (visible) ? "block" : "none"; + + //Need this HACK for IE/Flash integration. IE don't have a document.body at this time when starting Flash-Backend + if(!xseen.debug.isAppend) { + if(navigator.appName == "Microsoft Internet Explorer") { + //document.documentElement.appendChild(aDiv); + xseen.debug.logContainer.style.marginLeft = "8px"; + document.documentElement.appendChild(xseen.debug.logContainer); + }else{ + //document.body.appendChild(aDiv); + document.body.appendChild(xseen.debug.logContainer); + } + xseen.debug.isAppend = true; + } + }, + + /** @brief Inserts a container div for the logging messages into the HTML page + */ + setupLogContainer: function() { + xseen.debug.logContainer = document.createElement("div"); + xseen.debug.logContainer.id = "xseen_logdiv"; + xseen.debug.logContainer.setAttribute("class", "xseen-logContainer"); + xseen.debug.logContainer.style.clear = "both"; + //document.body.appendChild(xseen.debug.logContainer); + }, + + /** @brief Generic logging function which does all the work. + + @param msg the log message + @param logType the type of the log message. One of INFO, WARNING, ERROR + or EXCEPTION. + */ + doLog: function(msg, logType) { + + // If logging is deactivated do nothing and simply return + if (!xseen.debug.isActive) { return; } + + // If we have reached the maximum number of logged lines output + // a warning message + if (xseen.debug.numLinesLogged === xseen.debug.maxLinesToLog) { + msg = "Maximum number of log lines (=" + xseen.debug.maxLinesToLog + ") reached. Deactivating logging..."; + } + + // If the maximum number of log lines is exceeded do not log anything + // but simply return + if (xseen.debug.numLinesLogged > xseen.debug.maxLinesToLog) { return; } + + // Output a log line to the HTML page + var node = document.createElement("p"); + node.style.margin = 0; + switch (logType) { + case xseen.debug.INFO: + node.style.color = "#009900"; + break; + case xseen.debug.WARNING: + node.style.color = "#cd853f"; + break; + case xseen.debug.ERROR: + node.style.color = "#ff4500"; + break; + case xseen.debug.EXCEPTION: + node.style.color = "#ffff00"; + break; + default: + node.style.color = "#009900"; + break; + } + + // not sure if try/catch solves problem http://sourceforge.net/apps/trac/x3dom/ticket/52 + // but due to no avail of ATI gfxcard can't test + try { + node.innerHTML = logType + ": " + msg; + xseen.debug.logContainer.insertBefore(node, xseen.debug.logContainer.firstChild); + } catch (err) { + if (window.console.firebug !== undefined) { + window.console.warn(msg); + } + } + + // Use firebug's console if available + if (xseen.debug.isFirebugAvailable) { + switch (logType) { + case xseen.debug.INFO: + window.console.info(msg); + break; + case xseen.debug.WARNING: + window.console.warn(msg); + break; + case xseen.debug.ERROR: + window.console.error(msg); + break; + case xseen.debug.EXCEPTION: + window.console.debug(msg); + break; + default: + break; + } + } + + xseen.debug.numLinesLogged++; + }, + + /** Log an info message. */ + logInfo: function(msg) { + xseen.debug.doLog(msg, xseen.debug.INFO); + }, + + /** Log a warning message. */ + logWarning: function(msg) { + xseen.debug.doLog(msg, xseen.debug.WARNING); + }, + + /** Log an error message. */ + logError: function(msg) { + xseen.debug.doLog(msg, xseen.debug.ERROR); + }, + + /** Log an exception message. */ + logException: function(msg) { + xseen.debug.doLog(msg, xseen.debug.EXCEPTION); + }, + + /** Log an assertion. */ + assert: function(c, msg) { + if (!c) { + xseen.debug.doLog("Assertion failed in " + xseen.debug.assert.caller.name + ': ' + msg, xseen.debug.ERROR); + } + }, + + /** + Checks the type of a given object. + + @param obj the object to check. + @returns one of; "boolean", "number", "string", "object", + "function", or "null". + */ + typeOf: function (obj) { + var type = typeof obj; + return type === "object" && !obj ? "null" : type; + }, + + /** + Checks if a property of a specified object has the given type. + + @param obj the object to check. + @param name the property name. + @param type the property type (optional, default is "function"). + @returns true if the property exists and has the specified type, + otherwise false. + */ + exists: function (obj, name, type) { + type = type || "function"; + return (obj ? this.typeOf(obj[name]) : "null") === type; + }, + + /** + Dumps all members of the given object. + */ + dumpFields: function (node) { + var str = ""; + for (var fName in node) { + str += (fName + ", "); + } + str += '\n'; + xseen.debug.logInfo(str); + return str; + } + }; +// Call the setup function to... umm, well, setup xseen.debug + this.debug.setup(); + + }; +// File: ./Nav-Viewpoint.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.Navigation.(label); + * Computes the new viewing location for the specific mode. + * + * Each Navigation method takes the following parameters: + * speed Floating point value indicating motion speed. + * Units are distance per milli-second for linear motion or + * revolutions (2*pi) per milli-second for angular motion + * deltaT Time since last update in milli-seconds + * TODO: This is not true for the Turntable class of camera motion -- which isn't really Navigation anyway + * scene The 'sceneInfo' object for this HTML instance + * camera The current (active) camera (aka scene.element._xseen.renderer.activeCamera) + * + * Navigation is the user-controlled process of moving in the 3D world. + * + */ + +xseen.Navigation = { + 'TwoPi' : 2 * Math.PI, + 'none' : function () {}, // Does not allow user-controlled navigation + + 'turntable' : function (speed, deltaT, scene, camera) + { + var T, radians, radius, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + radius = vp.fields._radius0; + camera.position.x = radius * Math.sin(radians) + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.position.z = radius * Math.cos(radians); + camera.lookAt(scene.ORIGIN); + }, + + 'tilt' : function (speed, deltaT, scene, camera) + { + var T, radians, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.lookAt(scene.ORIGIN); + }, + + 'setup' : { + 'none' : function () {return null;}, + + 'orbit' : function (camera, renderer) + { + var controls; + controls = new THREE.OrbitControls( camera, renderer.domElement ); + //controls.addEventListener( 'change', render ); // remove when using animation loop + // enable animation loop when using damping or autorotation + //controls.enableDamping = true; + //controls.dampingFactor = 0.25; + controls.enableZoom = false; + controls.enableZoom = true; + return controls; + }, + + 'trackball' : function (camera, renderer) + { + var controls; + controls = new THREE.TrackballControls(camera, renderer.domElement); + + // These are from the example code at https://github.com/mrdoob/three.js/blob/master/examples/misc_controls_trackball.html + controls.rotateSpeed = 1.0; + controls.zoomSpeed = 1.2; + controls.panSpeed = 0.8; + controls.noZoom = false; + controls.noPan = false; + controls.staticMoving = true; + controls.dynamicDampingFactor = 0.3; + controls.keys = [ 65, 83, 68 ]; + + // Render function is 'xseen.renderFrame' + // remove when using animation loop + //controls.addEventListener( 'change', xseen.renderFrame ); + return controls; + }, + }, +}; +// File: ./Navigation.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.Navigation.(label); + * Computes the new viewing location for the specific mode. + * + * Each Navigation method takes the following parameters: + * speed Floating point value indicating motion speed. + * Units are distance per milli-second for linear motion or + * revolutions (2*pi) per milli-second for angular motion + * deltaT Time since last update in milli-seconds + * TODO: This is not true for the Turntable class of camera motion -- which isn't really Navigation anyway + * scene The 'sceneInfo' object for this HTML instance + * camera The current (active) camera (aka scene.element._xseen.renderer.activeCamera) + * + * Navigation is the user-controlled process of moving in the 3D world. + * + */ + +xseen.Navigation = { + 'TwoPi' : 2 * Math.PI, + 'none' : function () {}, // Does not allow user-controlled navigation + + 'turntable' : function (speed, deltaT, scene, camera) + { + var T, radians, radius, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + radius = vp.fields._radius0; + camera.position.x = radius * Math.sin(radians) + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.position.z = radius * Math.cos(radians); + camera.lookAt(scene.ORIGIN); + }, + + 'tilt' : function (speed, deltaT, scene, camera) + { + var T, radians, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.lookAt(scene.ORIGIN); + }, + + 'setup' : { + 'none' : function () {return null;}, + + 'orbit' : function (camera, renderer) + { + var controls; + controls = new THREE.OrbitControls( camera, renderer.domElement ); + //controls.addEventListener( 'change', render ); // remove when using animation loop + // enable animation loop when using damping or autorotation + //controls.enableDamping = true; + //controls.dampingFactor = 0.25; + controls.enableZoom = false; + controls.enableZoom = true; + return controls; + }, + }, +}; +// File: ./NodeDefinitions.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.nodes. is the definition of + * All internal variables are stored in ._internal. All functions start with '_' + * + * This is a bare-bones setup. There is no error checking - missing arguments or + * methods that do not exist (e.g., .init) + * + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump (_dumpTable) would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + * Fields are added with the .addField method. It takes its values from the argument list + * or an object passed as the first argument. The properties of the argument are: + * name - the name of the field. This is converted to lowercase before use + * datatype - the datatype of the field. There must be a method in xseen.types by this name + * defaultValue - the default value of the field to be used if the field is not present or incorrectly defined. + * If this argument is an array, then it is the set of allowed values. The first element is the default. + * enumerated - the list of allowed values when the datatype only allows specific values for this field (optional) + * animatable - Flag (T/F) indicating if the field is animatable. Generally speaking, enumerated fieles are not animatable + */ + +xseen.nodes = { + '_defineNode' : function(nodeName, nodeComponent, nodeMethod) { + //methodBase = 'xseen.node.'; + var methodBase = ''; + node = { + 'tag' : nodeName, + 'taglc' : nodeName.toLowerCase(), + 'component' : nodeComponent, + 'method' : methodBase + nodeMethod, + 'fields' : [], + 'fieldIndex': [], + 'addField' : function (fieldObj, datatype, defaultValue) { + var fieldName, namelc, enumerated, animatable; + if (typeof(fieldObj) === 'object') { + fieldName = fieldObj.name; + datatype = fieldObj.datatype; + defaultValue = fieldObj.defaultValue; + enumerated = (typeof(fieldObj.enumerated) === 'undefined') ? [] : fieldObj.enumerated; + animatable = (typeof(fieldObj.animatable) === 'undefined') ? false : fieldObj.animatable; + } else { + fieldName = fieldObj; + animatable = false; + if (typeof(defaultValue) == 'array') { + enumerated = defaultValue; + defaultValue = enumerated[0]; + } else { + enumerated = []; + } + } + namelc = fieldName.toLowerCase(); + this.fields.push ({ + 'field' : fieldName, + 'fieldlc' : namelc, + 'type' : datatype, + 'default' : defaultValue, + 'enumeration' : enumerated, + 'animatable' : animatable, + 'clone' : this.cloneField, + 'setFieldName' : this.setFieldName, + }); + this.fieldIndex[namelc] = this.fields.length-1; + return this; + }, + 'addNode' : function () { + xseen.parseTable[this.taglc] = this; + }, + 'cloneField' : function () { + var newFieldObject = { + 'field' : this.field, + 'fieldlc' : this.fieldlc, + 'type' : this.type, + 'default' : 0, + 'enumeration' : [], + 'animatable' : this.animatable, + 'clone' : this.clone, + 'setFieldName' : this.setFieldName, + }; + for (var i=0; i xseen.node[xseen.nodeDefinitions[nodeName].method].endParse (element, parent); + } + //xseen.debug.logInfo(" reached bottom, heading back up from |" + nodeName + "|"); +} +// File: ./Properties.js +/* + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + * + * Based on code originally provided by + * Philip Taylor: http://philip.html5.org + */ + + +xseen.Properties = function() { + this.properties = {}; +}; + +xseen.Properties.prototype.setProperty = function(name, value) { + xseen.debug.logInfo("Properties: Setting property '"+ name + "' to value '" + value + "'"); + this.properties[name] = value; +}; + +xseen.Properties.prototype.getProperty = function(name, def) { + if (this.properties[name]) { + return this.properties[name] + } else { + return def; + } +}; + +xseen.Properties.prototype.merge = function(other) { + for (var attrname in other.properties) { + this.properties[attrname] = other.properties[attrname]; + } +}; + +xseen.Properties.prototype.toString = function() { + var str = ""; + for (var name in this.properties) { + str += "Name: " + name + " Value: " + this.properties[name] + "\n"; + } + return str; +}; +// File: ./StackHandler.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.utlis.StackHandler(label); + * Creates a new stack that is managed by this class + * + * Note that the Push-Down stack (aka FILO) is implemented as a reverse list + * so that the Array methods .push and .pop can be used. The end of the array + * is the "top-most" element in the stack. + */ + +xseen.utils.StackHandler = function (label) { + this._internals = {}; // Internal class storage + this._internals.label = label; // Unique user-supplied name for this stack + this._internals.stack = []; // Maintains the stack. Last entry on stack is active + this._internals.active = -1; // Index of currently active list element + this._internals.activeNode = {}; // Entry of currently active list element + this._internals.defaultNode = {}; // The default entry to be active if nothing else is + + this._setActiveNode = function() { // Sets the entry specified by nodeId as active + this._internals.active = this._internals.stack.length-1; + if (this._internals.active >= 0) { + this._internals.activeNode = this._internals.stack[this._internals.active]; + } else { + this._internals.activeNode = this._internals.defaultNode; + } + } + + this.init = function() { // Clears existing stack + this._internals.stack = []; + } + + this.pushDown = function(node) { // Push new node onto stack and make active + this._internals.stack.push (node); + this._setActiveNode(); + } + + this.popOff = function() { // Pop node off stack and make next one active + this._internals.stack.pop(); + this._setActiveNode(); + } + + this.getActive = function() { + return this._internals.activeNode; + } + + this.setDefault = function(node) { + this._internals.defaultNode = node; + if (Object.keys(this._internals.activeNode).length === 0) { + this._internals.activeNode = this._internals.defaultNode; + } + } +} +// File: ./Types.js +/* + * xseen.types contains the datatype and conversion utilities. These convert one format to another. + * Any method ending in 'toX' where 'X' is some datatype is a conversion to that type + * Other methods convert from string with space-spearated values + */ +xseen.types = { + 'Deg2Rad' : Math.PI / 180, + + 'SFFloat' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return value; + }, + + 'SFInt' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return Math.round(value); + }, + + 'SFBool' : function (value, def) + { + if (value === null) {return def;} + if (value) {return true;} + if (!value) {return false;} + return def; + }, + + 'SFTime' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return value; + }, + + 'SFVec3f' : function (value, def) + { + if (value === null) {return def;} + var v3 = value.split(' '); + if (v3.length < 3 || Number.isNaN(v3[0]) || Number.isNaN(v3[1]) || Number.isNaN(v3[2])) { + return def; + } + return [v3[0]-0, v3[1]-0, v3[2]-0]; + }, + + 'SFVec2f' : function (value, def) + { + if (value === null) {return def;} + var v2 = value.split(' '); + if (v2.length != 2 || Number.isNaN(v2[0]) || Number.isNaN(v2[1])) { + return def; + } + return [v2[0]-0, v2[1]-0]; + }, + + 'SFRotation' : function (value, def) + { + if (value === null) {return def;} + var v4 = value.split(' '); + if (v4.length != 4 || Number.isNaN(v4[0]) || Number.isNaN(v4[1]) || Number.isNaN(v4[2]) || Number.isNaN(v4[3])) { + return def; + } + var result = { + 'vector' : [v4[0], v4[1], v4[2], v4[3]], + 'axis_angle' : [{'x': v4[0], 'y': v4[1], 'z': v4[2]}, v4[3]], + }; + return result; + }, + + 'SFColor' : function (value, defaultString) + { + var v3 = this.SFVec3f(value, defaultString); + v3[0] = Math.min(Math.max(v3[0], 0.0), 1.0); + v3[1] = Math.min(Math.max(v3[1], 0.0), 1.0); + v3[2] = Math.min(Math.max(v3[2], 0.0), 1.0); + return v3; + }, + + 'SFString' : function (value, def) + { + if (value === null) {value = def;} + return value; + }, + +// For MF* types, a default of '' means to return an empty array on parsing error + 'MFFloat' : function (value, def) + { + var defReturn = (def == '') ? [] : def; + if (value === null) {return defReturn;} + var mi = value.split(' '); + var rv = []; + for (var i=0; iDocumentation."]; +/* + * All X3D and A-Frame pre-defined solids, fixed camera, directional light, Material texture only, glTF model loader with animations, Assets and reuse, Viewpoint, Background, Lighting, Image Texture, [Indexed]TriangleSet, IndexedFaceSet, [Indexed]QuadSet
\nNext work
  • Event Model/Animation
  • Extrusion
  • Navigation
", + * + * All of the following are ALPHA releases for V0.4.x + * V0.4.0+13 Feature -- events (from HTML to XSeen) + * V0.4.1+14 Fix - minor text correction in xseen.node.geometry__TriangulateFix (nodes-x3d_Geometry.js) + * V0.4.1+15 Modified build.pl to increase compression by removing block comments + * V0.4.1+16 Feature -- XSeen events (from XSeen to HTML) + * V0.4.2+17 Feature -- XSeen internals events (from XSeen to XSeen) with changes to fix previous event handling + * V0.4.2+18 Feature -- Split screen VR display + * V0.4.3+19 Rebuild and fix loading caused by new Stereo library + * V0.4.3+20 Feature -- Navigation (orbit), including Stack update for Viewpoint and restructuring the rendering loop + * V0.4.3+21 Feature -- Changed handling of Viewpoint to include camera motion + * V0.4.4+22 Fix -- Internal event handling in passing on events of the proper type + * V0.4.5+23 Feature -- Navigation (trackball) + * V0.4.5+24 Fix -- when there is no navigation + * V0.4.6+25 Update -- JSON Parser (contributed by John Carlson) + * + * In progress + */ + var version = { + major : Major, + minor : Minor, + patch : Patch, + preRelease : PreRelease, + release : Release, + version : '', + date : RDate, + splashText : SplashText + }; +// Using the scheme at http://semver.org/ + version.version = version.major + '.' + version.minor + '.' + version.patch; + version.version += (version.preRelease != '') ? '-' + version.preRelease : ''; + version.version += (version.release != '') ? '+' + version.release : ''; + return version; +} +// File: nodes/nodes-af.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code for A-Frame nodes + + +xseen.node.core_NOOP = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.parsing = function (s, e) { + xseen.debug.logInfo ('Parsing init details stub for ' + s); +} + +xseen.node.af_Entity = { + 'init' : function (e,p) + { + xseen.node.parsing('A-Frame Entity'); + }, + 'fin' : function (e,p) {} +}; +xseen.node.af_Assets = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.af_AssetItem = { + 'init' : function (e,p) // Only field is SRC. + { + }, + 'fin' : function (e,p) {} +}; +xseen.node.af_Mixin = { + 'init' : function (e,p) // Lots of fields -- all nebelous until used + { + }, + 'fin' : function (e,p) {} +}; + + + +xseen.node.af_Appearance = function (e) { + var parameters = { + 'aoMap' : e._xseen.fields['ambient-occlusion-map'], + 'aoMapIntensity' : e._xseen.fields['ambient-occlusion-map-intensity'], + 'color' : e._xseen.fields['color'], + 'displacementMap' : e._xseen.fields['displacement-map'], + 'displacementScale' : e._xseen.fields['displacement-scale'], + 'displacementBias' : e._xseen.fields['displacement-bias'], + 'envMap' : e._xseen.fields['env-map'], + 'normalMap' : e._xseen.fields['normal-map'], + 'normalScale' : e._xseen.fields['normal-scale'], + 'wireframe' : e._xseen.fields['wireframe'], + 'wireframeLinewidth' : e._xseen.fields['wireframe-linewidth'], + }; + var material = new THREE.MeshPhongMaterial(parameters); + return material; +/* + * === All Entries === +.aoMap +.aoMapIntensity +.color + .combine +.displacementMap +.displacementScale +.displacementBias + .emissive + .emissiveMap + .emissiveIntensity +.envMap + .lightMap + .lightMapIntensity + .map + .morphNormals + .morphTargets +.normalMap +.normalScale + .reflectivity + .refractionRatio + .shininess + .skinning + .specular + .specularMap +.wireframe + .wireframeLinecap + .wireframeLinejoin +.wireframeLinewidth +/////////////////////////////////////////////////////////////////////////////// +e._xseen.fields['ambient-occlusion-map'] +e._xseen.fields['ambient-occlusion-map-intensity'] + e._xseen.fields['ambient-occlusion-texture-offset'] + e._xseen.fields['ambient-occlusion-texture-repeat'] +e._xseen.fields['color'] +e._xseen.fields['displacement-bias'] +e._xseen.fields['displacement-map'] +e._xseen.fields['displacement-scale'] + e._xseen.fields['displacement-texture-offset'] + e._xseen.fields['displacement-texture-repeat'] +e._xseen.fields['env-map'] + e._xseen.fields['fog'] + e._xseen.fields['metalness'] +e._xseen.fields['normal-map'] +e._xseen.fields['normal-scale'] + e._xseen.fields['normal-texture-offset'] + e._xseen.fields['normal-texture-repeat'] + e._xseen.fields['repeat'] + e._xseen.fields['roughness'] + e._xseen.fields['spherical-env-map'] + e._xseen.fields['src'] +e._xseen.fields['wireframe'] +e._xseen.fields['wireframe-linewidth'] + + * === Unused Entries === + .combine + .emissive + .emissiveMap + .emissiveIntensity + .lightMap + .lightMapIntensity + .map + .morphNormals + .morphTargets + .reflectivity + .refractionRatio + .shininess + .skinning + .specular + .specularMap + .wireframeLinecap + .wireframeLinejoin +/////////////////////////////////////////////////////////////////////////////// + e._xseen.fields['ambient-occlusion-texture-offset'] + e._xseen.fields['ambient-occlusion-texture-repeat'] + e._xseen.fields['displacement-texture-offset'] + e._xseen.fields['displacement-texture-repeat'] + e._xseen.fields['fog'] + e._xseen.fields['metalness'] + e._xseen.fields['normal-texture-offset'] + e._xseen.fields['normal-texture-repeat'] + e._xseen.fields['repeat'] + e._xseen.fields['roughness'] + e._xseen.fields['spherical-env-map'] + e._xseen.fields['src'] + */ +} + +xseen.node.af_Box = { + 'init' : function (e,p) + { + var geometry = new THREE.BoxGeometry( + e._xseen.fields.width, + e._xseen.fields.height, + e._xseen.fields.depth, + e._xseen.fields['segments-width'], + e._xseen.fields['segments-height'], + e._xseen.fields['segments-depth'] + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Cone = { + 'init' : function (e,p) + { + var geometry = new THREE.ConeGeometry( + e._xseen.fields.radius, + e._xseen.fields.height, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-height'], + e._xseen.fields['open-ended'], + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Cylinder = { + 'init' : function (e,p) + { + var geometry = new THREE.CylinderGeometry( + e._xseen.fields['radius-top'], + e._xseen.fields['radius-bottom'], + e._xseen.fields.height, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-height'], + e._xseen.fields['open-ended'], + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Dodecahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.DodecahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Icosahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.IcosahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Octahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.OctahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Sphere = { + 'init' : function (e,p) + { + var geometry = new THREE.SphereGeometry( + e._xseen.fields.radius, + e._xseen.fields['segments-width'], + e._xseen.fields['segments-height'], + e._xseen.fields['phi-start'] * xseen.types.Deg2Rad, + e._xseen.fields['phi-length'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Tetrahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.TetrahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Torus = { + 'init' : function (e,p) + { + var geometry = new THREE.TorusGeometry( + e._xseen.fields.radius, + e._xseen.fields.tube, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-tubular'], + e._xseen.fields.arc * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; +// File: nodes/nodes-Viewing.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Control Node definitions + + +xseen.node.unk_Viewpoint = { + 'init' : function (e,p) + { // This should really go in a separate push-down list for Viewpoints + e._xseen.fields._radius0 = Math.sqrt( e._xseen.fields.position[0]*e._xseen.fields.position[0] + + e._xseen.fields.position[1]*e._xseen.fields.position[1] + + e._xseen.fields.position[2]*e._xseen.fields.position[2]); + e._xseen.domNode = e; // Back-link to node if needed later on + e._xseen.position = new THREE.Vector3(e._xseen.fields.position[0], e._xseen.fields.position[1], e._xseen.fields.position[2]); + e._xseen.type = e._xseen.fields.cameratype; + e._xseen.motion = e._xseen.fields.motion; + e._xseen.motionspeed = e._xseen.fields.motionspeed * 1000; + if (e._xseen.motion == 'turntable' || e._xseen.motion == 'tilt') {e._xseen.motionspeed = 1.0/e._xseen.motionspeed;} + + if (!e._xseen.sceneInfo.tmp.activeViewpoint) { + e._xseen.sceneInfo.stacks.Viewpoints.pushDown(e._xseen); + e._xseen.sceneInfo.tmp.activeViewpoint = true; + } + + e._xseen.handlers = {}; + e._xseen.handlers.setactive = this.setactive; + }, + 'fin' : function (e,p) {}, + + 'setactive' : function (ev) + { + var xseenNode = this.destination._xseen; + xseenNode.sceneInfo.stacks.Viewpoints.pushDown(xseenNode); // TODO: This is probably not the right way to change VP in the stack + xseenNode.sceneInfo.element._xseen.renderer.activeCamera = + xseenNode.sceneInfo.element._xseen.renderer.cameras[xseenNode.fields.type]; + xseenNode.sceneInfo.element._xseen.renderer.activeRender = + xseenNode.sceneInfo.element._xseen.renderer.renderEffects[xseenNode.fields.type]; + if (xseenNode.fields.type != 'stereo') { + xseenNode.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, xseenNode.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); + } + }, +}; + +xseen.node.controls_Navigation = { + 'init' : function (e,p) + { // This should really go in a separate push-down list for Viewpoints + + e._xseen.domNode = e; // Back-link to node if needed later on + e._xseen.speed = e._xseen.fields.speed; + if (e._xseen.setup == 'examine') {e._xseen.setup == 'trackball';} + //e._xseen.type = e._xseen.fields.type; + e._xseen.type = 'none'; + e._xseen.setup = e._xseen.fields.type; + if (!(e._xseen.setup == 'orbit' || e._xseen.setup == 'trackball')) {e._xseen.setup = 'none';} + + if (!e._xseen.sceneInfo.tmp.activeNavigation) { + e._xseen.sceneInfo.stacks.Navigation.pushDown(e._xseen); + e._xseen.sceneInfo.tmp.activeNavigation = true; + } + + e._xseen.handlers = {}; + e._xseen.handlers.setactive = this.setactive; + }, + 'fin' : function (e,p) {}, + + 'setactive' : function (ev) + { +/* + this.destination._xseen.sceneInfo.stacks.Viewpoints.pushDown(this.destination); // TODO: This is probably not the right way to change VP in the stack + this.destination._xseen.sceneInfo.element._xseen.renderer.activeCamera = + this.destination._xseen.sceneInfo.element._xseen.renderer.cameras[this.destination._xseen.fields.type]; + this.destination._xseen.sceneInfo.element._xseen.renderer.activeRender = + this.destination._xseen.sceneInfo.element._xseen.renderer.renderEffects[this.destination._xseen.fields.type]; + if (this.destination._xseen.fields.type != 'stereo') { + this.destination._xseen.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, this.destination._xseen.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); + } + */ + }, +}; + +xseen.node.lighting_Light = { + 'init' : function (e,p) + { + var color = xseen.types.Color3toInt (e._xseen.fields.color); + var intensity = e._xseen.fields.intensity - 0; + var lamp, type=e._xseen.fields.type.toLowerCase(); +/* + if (typeof(p._xseen.children) == 'undefined') { + console.log('Parent of Light does not have children...'); + p._xseen.children = []; + } + */ + + if (type == 'point') { + // Ignored field -- e._xseen.fields.location + lamp = new THREE.PointLight (color, intensity); + lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); + lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); + + } else if (type == 'spot') { + lamp = new THREE.SpotLight (color, intensity); + lamp.position.set(0-e._xseen.fields.direction[0], 0-e._xseen.fields.direction[1], 0-e._xseen.fields.direction[2]); + lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); + lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); + lamp.angle = Math.max(0.0, Math.min(1.5707963267948966192313216916398, e._xseen.fields.cutoffangle)); + lamp.penumbra = 1 - Math.max(0.0, Math.min(lamp.angle, e._xseen.fields.beamwidth)) / lamp.angle; + + } else { // DirectionalLight (by default) + lamp = new THREE.DirectionalLight (color, intensity); + lamp.position.x = 0-e._xseen.fields.direction[0]; + lamp.position.y = 0-e._xseen.fields.direction[1]; + lamp.position.z = 0-e._xseen.fields.direction[2]; + } + p._xseen.children.push(lamp); + lamp = null; + } + , + 'fin' : function (e,p) + { + } +}; +// File: nodes/nodes-x3d_Appearance.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.appearance_Material = { + 'init' : function (e,p) + { + var transparency = e._xseen.fields.transparency - 0; + var shininess = e._xseen.fields.shininess - 0; + var colorDiffuse = xseen.types.Color3toInt (e._xseen.fields.diffusecolor); + var colorEmissive = xseen.types.Color3toInt (e._xseen.fields.emissivecolor); + var colorSpecular = xseen.types.Color3toInt (e._xseen.fields.specularcolor); + p._xseen.material = new THREE.MeshPhongMaterial( { +// p._xseen.material = new THREE.MeshBasicMaterial( { + 'color' : colorDiffuse, + 'emissive' : colorEmissive, + 'specular' : colorSpecular, + 'shininess' : shininess, + 'opacity' : 1.0-transparency, + 'transparent' : (transparency > 0.0) ? true : false + } ); + e._xseen.animate['diffusecolor'] = p._xseen.material.color; + e._xseen.animate['emissivecolor'] = p._xseen.material.emissive; + e._xseen.animate['specularcolor'] = p._xseen.material.specular; + e._xseen.animate['transparency'] = p._xseen.material.opacity; + e._xseen.animate['shininess'] = p._xseen.material.shininess; + }, + 'fin' : function (e,p) {} +}; + +xseen.node.appearance_ImageTexture = { + 'init' : function (e,p) + { + p._xseen.texture = xseen.loader.ImageLoader.load(e._xseen.fields.url); + p._xseen.texture.wrapS = (e._xseen.fields.repeats) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + p._xseen.texture.wrapT = (e._xseen.fields.repeatt) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + }, + 'fin' : function (e,p) {} +}; + +xseen.node.appearance_Appearance = { + 'init' : function (e,p) {}, + + 'fin' : function (e,p) + { + if (typeof(e._xseen.texture) !== 'undefined' && e._xseen.texture !== null) { + e._xseen.material.map = e._xseen.texture; + } + p._xseen.appearance = e._xseen.material; + } +}; +// File: nodes/nodes-x3d_Geometry.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.geometry_Coordinate = { + 'init' : function (e,p) + { + var vertices = []; + for (var i=0; i= 0 where each triple defines a triangle face. It is assumed that the + * indices define the face in counter-clockwise order when looking at the face. + * vertices - an array of THREE.Vector3 points + */ +xseen.node.geometry__Indexed3 = function (indices, vertices, normals=[], color=[]) { + var i, face, normal=[], faceCount=0, n; + var useNormals = (normals.length > 0) ? true : false; + var useColor = (color.length > 0) ? true : false; + var maxIndex = Math.max.apply(null, indices); + var minIndex = Math.min.apply(null, indices); + if (maxIndex >= vertices.length) { + console.log ('Maximum index ('+maxIndex+') exceeds vertex count ('+vertices.length+'). No geometry is created'); + return; + } + if (useNormals && maxIndex >= normals.length) { + console.log ('Maximum index ('+maxIndex+') exceeds normal count ('+normals.length+'). No geometry is created'); + return; + } + if (useColor && maxIndex >= color.length) { + console.log ('Maximum index ('+maxIndex+') exceeds color count ('+color.length+'). No geometry is created'); + return; + } + if (minIndex < 0) { + console.log ('Minimum index ('+minIndex+') less than zero. No geometry is created'); + return; + } + if (indices.length % 3 != 0) { + console.log ('Number of indices ('+indices.length+') not divisible by 3. No geometry is created'); + return; + } + + var geometry = new THREE.Geometry(); + var normal_pz = new THREE.Vector3 (0, 0, 1); + var normal_mz = new THREE.Vector3 (0, 0, -1); + for (var i=0; i 0) {clip.duration = e._xseen.fields.duration;} + e._xseen.mixer.clipAction( clip ).play(); + } ); + } else { // Play a specific animation + var clip = THREE.AnimationClip.findByName(response.animations, e._xseen.fields.playonload); + var action = e._xseen.mixer.clipAction (clip); + action.play(); + } + } + } + } +}; + + +xseen.node.x_Route = { + 'init' : function (e,p) + { + var dest = e._xseen.fields.destination; + var hand = e._xseen.fields.handler; + var externalHandler = false; + + // Make sure sufficient data is provided + if (e._xseen.fields.source == '' || + typeof(window[hand]) !== 'function' && + (dest == '' || e._xseen.fields.event == '' || e._xseen.fields.field == '')) { + xseen.debug.logError ('Route node missing field. No route setup. Source: '+e._xseen.fields.source+'.'+e._xseen.fields.event+'; Destination: '+dest+'.'+e._xseen.fields.field+'; Handler: '+hand); + return; + } else if (typeof(window[hand]) === 'function') { + externalHandler = true; + } + + // For toNode routing, check existence of source and destination elements + var eSource = document.getElementById (e._xseen.fields.source); + if (! externalHandler) { + var eDestination = document.getElementById (dest); + if (typeof(eSource) === 'undefined' || typeof(eDestination) === 'undefined') { + xseen.debug.logError ('Source or Destination node does not exist. No route setup'); + return; + } + // Get field information -- perhaps there is some use in the Animate node? + var fField = xseen.nodes._getFieldInfo (eDestination.nodeName, e._xseen.fields.field); + if (typeof(fField) === 'undefined' || !fField.good) { + xseen.debug.logError ('Destination field does not exist or incorrectly specified. No route setup'); + return; + } + // Set up listener on source node for specified event. The listener code is the 'set' method for the + // node. It is passed the DOM 'event' data structure. Since there may be more than one node of the type + // specified by 'destination', the event handler is attached to the node in e._xseen.handlers. This is done + // when the node is parsed + xseen.Events.addHandler (e, eSource, e._xseen.fields.event, eDestination, fField); + +/* + * External (to XSeen) event handler + * TODO: limit the events to those requested if e._xseen.fields.event != 'xseen' + * This probably requires an intermediatiary event handler + */ + } else { + var handler = window[hand]; + eSource.addEventListener ('xseen', handler); + } + }, + + 'fin' : function (e,p) + { + }, + 'evHandler' : function (u) + { + var de = u.e; + var df = u.f; + return de._xseen.handlers[df.handlerName]; + }, +}; +// File: nodes/nodes.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.core_NOOP = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.core_WorldInfo = { + 'init' : function (e,p) {parsing('WorldInfo', e)}, + 'fin' : function (e,p) {} +}; + +function parsing (s, e) { + xseen.debug.logInfo ('Parsing init details stub for ' + s); +} + + +xseen.node.unk_Shape = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) + { +// if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + if (typeof(e._xseen.materialProperty) !== 'undefined') { + e._xseen.appearance.vertexColors = THREE.VertexColors; + //e._xseen.appearance.vertexColors = THREE.FaceColors; + e._xseen.appearance._needsUpdate = true; + e._xseen.appearance.needsUpdate = true; + } + var mesh = new THREE.Mesh (e._xseen.geometry, e._xseen.appearance); + mesh.userData = e; + p._xseen.children.push(mesh); + p._xseen.sceneInfo.selectable.push(mesh); + mesh = null; + } +}; + +xseen.node.grouping_Transform = { + 'init' : function (e,p) + { + var group = new THREE.Group(); + if (e.nodeName == "TRANSFORM") { + var rotation = xseen.types.Rotation2Quat(e._xseen.fields.rotation); + group.name = 'Transform children [' + e.id + ']'; + group.position.x = e._xseen.fields.translation[0]; + group.position.y = e._xseen.fields.translation[1]; + group.position.z = e._xseen.fields.translation[2]; + group.scale.x = e._xseen.fields.scale[0]; + group.scale.y = e._xseen.fields.scale[1]; + group.scale.z = e._xseen.fields.scale[2]; + group.quaternion.x = rotation.x; + group.quaternion.y = rotation.y; + group.quaternion.z = rotation.z; + group.quaternion.w = rotation.w; + + e._xseen.animate['translation'] = group.position; + e._xseen.animate['rotation'] = group.quaternion; + e._xseen.animate['scale'] = group.scale; + } + e._xseen.sceneNode = group; + }, + 'fin' : function (e,p) + { + // Apply transform to all objects in e._xseen.children + e._xseen.children.forEach (function (child, ndx, wholeThing) + { + e._xseen.sceneNode.add(child); + }); + p._xseen.children.push(e._xseen.sceneNode); + } +}; + +xseen.node.networking_Inline = { + 'init' : function (e,p) + { + if (typeof(e._xseen.processedUrl) === 'undefined' || !e._xseen.requestedUrl) { + var uri = xseen.parseUrl (e._xseen.fields.url); + var type = uri.extension; + e._xseen.loadGroup = new THREE.Group(); + e._xseen.loadGroup.name = 'Inline content [' + e.id + ']'; + console.log ('Created Inline Group with UUID ' + e._xseen.loadGroup.uuid); + var userdata = {'requestType':'x3d', 'e':e, 'p':p} + if (type.toLowerCase() == 'json') { + userdata.requestType = 'json'; + xseen.loadMgr.loadJson (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); + } else { + xseen.loadMgr.loadXml (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); + } + e._xseen.requestedUrl = true; + } + //if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(e._xseen.loadGroup); + console.log ('Using Inline Group with UUID ' + e._xseen.loadGroup.uuid); + }, + 'fin' : function (e,p) + { + }, + + 'loadSuccess' : + function (response, userdata, xhr) { + userdata.e._xseen.processedUrl = true; + userdata.e._xseen.loadResponse = response; + console.log("download successful for "+userdata.e.id); + if (userdata.requestType == 'json') { + var tmp = {'scene': response}; + response = null; + response = (new JSONParser()).parseJavaScript(tmp); + } + var start = {'_xseen':0}; + var findSceneTag = function (fragment) { + if (typeof(fragment._xseen) === 'undefined') {fragment._xseen = {'childCount': -1};} + if (fragment.nodeName.toLowerCase() == 'scene') { + start = fragment; + return; + } else if (fragment.children.length > 0) { + for (fragment._xseen.childCount=0; fragment._xseen.childCount 0) { + userdata.e.appendChild(start.children[0]); + } + xseen.Parse(userdata.e, userdata.p, userdata.p._xseen.sceneInfo); + userdata.e._xseen.children.forEach (function (child, ndx, wholeThing) + { + userdata.e._xseen.loadGroup.add(child); +console.log ('...Adding ' + child.type + ' (' + child.name + ') to Inline Group? with UUID ' + userdata.e._xseen.loadGroup.uuid + ' (' + userdata.e._xseen.loadGroup.name + ')'); + }); + userdata.p._xseen.sceneInfo.scene.updateMatrixWorld(); + //xseen.debug.logInfo("Complete work on Inline..."); + } else { + console.log("Found illegal X3D file -- no 'scene' tag"); + } + // Parse (start, userdata.p)... + } +}; + +/* + * Most of this stuff is only done once per XSeen element. Loading of Inline contents should not + * repeat the definitions and canvas creation + */ +xseen.node.core_Scene = { + 'DEFAULT' : { + 'Viewpoint' : { + 'Position' : new THREE.Vector3 (0, 0, 10), + 'Orientation' : '0 1 0 0', // TODO: fix (and below) when handling orientation + 'Type' : 'perpsective', + 'Motion' : 'none', + 'MotionSpeed' : 1.0, + }, + 'Navigation' : { + 'Speed' : 1.0, // 16 spr (1 revolution per 16 seconds), in mseconds. + 'Type' : 'none', + 'Setup' : 'none', + } + }, + 'init' : function (e,p) + { + // Create default Viewpoint and Navigation + xseen.sceneInfo[0].stacks.Viewpoints.setDefault( + { + 'position' : this.DEFAULT.Viewpoint.Position, + 'type' : this.DEFAULT.Viewpoint.Type, + 'motion' : this.DEFAULT.Viewpoint.Motion, + 'motionspeed': this.DEFAULT.Viewpoint.MotionSpeed / 1000, + 'domNode' : e, + 'fields' : {}, + } + ); + xseen.sceneInfo[0].stacks.Navigation.setDefault( + { + 'speed' : this.DEFAULT.Navigation.Speed / 1000, + 'type' : this.DEFAULT.Navigation.Type, + 'setup' : this.DEFAULT.Navigation.Setup, + 'domNode' : e, + 'fields' : {}, + } + ); + + var width = e._xseen.sceneInfo.size.width; + var height = e._xseen.sceneInfo.size.height; + var x_renderer = new THREE.WebGLRenderer(); + x_renderer.setSize (width, height); + var perspectiveCamera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 ); + var orthoCamera = new THREE.OrthographicCamera( 75, width / height, 0.1, 1000 ); + //perspectiveCamera.translateX(this.DEFAULT.Viewpoint.Position.x).translateY(this.DEFAULT.Viewpoint.Position.y).translateZ(this.DEFAULT.Viewpoint.Position.z); // Default position + //orthoCamera.translateX(this.DEFAULT.Viewpoint.Position.x).translateY(this.DEFAULT.Viewpoint.Position.y).translateZ(this.DEFAULT.Viewpoint.Position.z); // Default position + perspectiveCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position + perspectiveCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position + perspectiveCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position + orthoCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position + orthoCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position + orthoCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position + + // Stereo viewing effect + // from http://charliegerard.github.io/blog/Virtual-Reality-ThreeJs/ + var x_effect = new THREE.StereoEffect(x_renderer); + + e.appendChild (x_renderer.domElement); + e._xseen.renderer = { + 'canvas' : e._xseen.sceneInfo.scene, + 'width' : width, + 'height' : height, + 'cameras' : { + 'perspective' : perspectiveCamera, + 'ortho' : orthoCamera, + 'stereo' : perspectiveCamera, + }, // Removed .sceneInfo camera because this node defines the camera + 'effects' : x_effect, + 'renderEffects' : { + 'normal' : x_renderer, + 'perspective' : x_renderer, + 'ortho' : x_renderer, + 'stereo' : x_effect, + }, + 'activeRender' : {}, + 'activeCamera' : {}, + 'controls' : {}, // Used for navigation + }; + e._xseen.renderer.activeRender = e._xseen.renderer.renderEffects.normal; + e._xseen.renderer.activeCamera = e._xseen.renderer.cameras.perspective; + }, + +/* + * This appears now to be working!!! + * + * Late loading content is not getting inserted into the scene graph for rendering. Need to read + * THREE docs about how to do that. + * Camera will need to be redone. Existing camera is treated as a special child. A separate camera + * should be established and Viewpoint nodes define "photostops" rather than a camera. The camera is + * in effect, parented to the "photostop". This probably needs to list of Viewpoints discussed in the + * X3D specification. + */ + 'fin' : function (e,p) + { + // Render all Children + //xseen.renderNewChildren (e._xseen.children, e._xseen.renderer.canvas); + e._xseen.children.forEach (function (child, ndx, wholeThing) + { + console.log('Adding child of type ' + child.type + ' (' + child.name + ')'); + e._xseen.renderer.canvas.add(child); + }); + xseen.dumpSceneGraph (); +// e._xseen.renderer.renderer.render( e._xseen.renderer.canvas, e._xseen.renderer.camera ); +// xseen.debug.logInfo("Rendered all elements -- Starting animation"); +/* + * TODO: Need to get current top-of-stack for all stack-bound nodes and set them as active. + * This only happens the initial time for each XSeen tag in the main HTML file + * + * At this time, only Viewpoint is stack-bound. Probably need to stack just the ._xseen object. + * Also, .fields.position is the initial specified location; not the navigated/animated one + */ + var vp = xseen.sceneInfo[0].stacks.Viewpoints.getActive(); + var nav = xseen.sceneInfo[0].stacks.Navigation.getActive(); + var currentCamera = e._xseen.renderer.activeCamera; + var currentRenderer = e._xseen.renderer.activeRender; + currentCamera.position.x = vp.position.x; + currentCamera.position.y = vp.position.y; + currentCamera.position.z = vp.position.z; + e._xseen.renderer.controls = xseen.Navigation.setup[nav.setup] (currentCamera, currentRenderer); + xseen.debug.logInfo("Ready to kick off rendering loop"); + xseen.renderFrame(); + }, + +}; + +xseen.node.env_Background = { + 'init' : function (e,p) + { + var color = new THREE.Color(e._xseen.fields.skycolor[0], e._xseen.fields.skycolor[1], e._xseen.fields.skycolor[2]); + var textureCube = new THREE.CubeTextureLoader() + .load ([e._xseen.fields.srcright, + e._xseen.fields.srcleft, + e._xseen.fields.srctop, + e._xseen.fields.srcbottom, + e._xseen.fields.srcfront, + e._xseen.fields.srcback], + this.loadSuccess({'e':e, 'p':p}) + ); + e._xseen.sceneInfo.scene.background = color; +/* + var material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } ); + var size = 1; + //var geometry = new THREE.BoxGeometry(200, 200, 2); + var geometry = new THREE.Geometry(); + geometry.vertices.push ( + new THREE.Vector3(-size, -size, size), + new THREE.Vector3( size, -size, size), + new THREE.Vector3( size, -size, -size), + new THREE.Vector3(-size, -size, -size), + new THREE.Vector3(-size, size, size), + new THREE.Vector3( size, size, size), + new THREE.Vector3( size, size, -size), + new THREE.Vector3(-size, size, -size) + ); + + geometry.faces.push ( // external facing geometry + new THREE.Face3(0, 1, 5), + new THREE.Face3(0, 5, 4), + new THREE.Face3(1, 2, 6), + new THREE.Face3(1, 6, 5), + new THREE.Face3(2, 3, 7), + new THREE.Face3(2, 7, 6), + new THREE.Face3(3, 0, 4), + new THREE.Face3(3, 4, 7), + new THREE.Face3(4, 5, 6), + new THREE.Face3(4, 6, 7), + new THREE.Face3(0, 2, 1), + new THREE.Face3(0, 3, 2), + ); + geometry.computeBoundingSphere(); + var mesh = new THREE.Mesh (geometry, material); + e._xseen.sceneInfo.element._xseen.renderer.canvas.add(mesh); +*/ + }, + + 'fin' : function (e,p) + { + p._xseen.appearance = e._xseen.material; + }, + + 'loadSuccess' : function (userdata) + { + var e = userdata.e; + var p = userdata.p; + return function (textureCube) + { + e._xseen.processedUrl = true; + e._xseen.loadTexture = textureCube; + e._xseen.sceneInfo.scene.background = textureCube; + } + }, + +}; +// File: nodes/nodes_Animate.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.x_Animate = { + 'init' : function (e,p) + { + var delay = e._xseen.fields.delay * 1000; // Convert to milliseconds + var duration = e._xseen.fields.duration * 1000; // Convert to milliseconds + var repeat = (e._xseen.fields.repeat < 0) ? Infinity : e._xseen.fields.repeat; + var interpolator = e._xseen.fields.interpolator; + var easing = e._xseen.fields.easing; + + var fields = xseen.parseTable[p.localName.toLowerCase()].fields; + var fieldIndex = xseen.parseTable[p.localName.toLowerCase()].fieldIndex; + var toField = e._xseen.fields.field; + var toFieldIndex = fieldIndex[toField]; + if (typeof(fields[toFieldIndex]) === 'undefined') { + xseen.debug.logInfo("Field '" + toField + "' not found in parent (" + p.localName.toLowerCase() + "). No animation performed."); + return; + } + var fieldObject = fields[toFieldIndex].clone().setFieldName('to'); // Parse table entry for 'toField' + var to = xseen.nodes._parseField(fieldObject, e); // Parsed data -- need to convert to THREE format + +// Convert 'to' to the datatype of 'field' and set interpolation type. + var interpolation; + if (fieldObject.type == 'SFVec3f') { + interpolation = TWEEN.Interpolation.Linear; + to = xseen.types.Vector3(to); + xseen.debug.logInfo("Interpolating field '" + toField + "' as 3-space."); + + } else if (fieldObject.type == 'SFColor') { + interpolation = this.Interpolator.color; + to = new THREE.Color (xseen.types.Color3toInt(to)); + xseen.debug.logInfo("Interpolation field '" + toField + "' as color."); + + } else if (fieldObject.type == 'SFRotation') { + interpolation = this.Interpolator.slerp; + to = xseen.types.Rotation2Quat(to); + xseen.debug.logInfo("Interpolation field '" + toField + "' as rotation."); + + } else { + xseen.debug.logInfo("Field '" + toField + "' not converted to THREE format. No animation performed."); + return; + } + var fieldTHREE = p._xseen.animate[toField]; // THREE field for animation + + var tween = new TWEEN.Tween(fieldTHREE) + .to(to, duration) + .delay(delay) + .repeat(repeat) + .interpolation(interpolation); + var easingType = e._xseen.fields.easingtype; + easingType = easingType.charAt(0).toUpperCase() + easingType.slice(1); + easing = (easingType != 'Linear' && easing == '') ? 'inout' : easing; + if (easing != '') { + easing = easing.replace('in', 'In').replace('out', 'Out'); + easingType = (easingType == 'Linear') ? 'Quadratic' : easingType; + e._xseen.fields.easing = easing; + e._xseen.fields.easingtype = easingType; + tween.easing(TWEEN.Easing[easingType][easing]); + } + +/* + * Put animation-specific data in node (e._xseen) so it can be accessed on events (through 'this') + * This includes initial value and field + * All handlers (goes into .handlers) + * TWEEN object + */ + e._xseen.initialValue = fieldTHREE.clone(); + e._xseen.animatingField = fieldTHREE; + e._xseen.handlers = {}; + e._xseen.handlers.setstart = this.setstart; + e._xseen.handlers.setstop = this.setstop; + e._xseen.handlers.setpause = this.setpause; + e._xseen.handlers.setresetstart = this.setresetstart; + e._xseen.animating = tween; + p._xseen.animation.push (tween); + tween.start(); + }, + 'fin' : function (e,p) {}, + 'setstart' : function (ev) + { + console.log ('Starting animation'); + this.destination._xseen.animating.start(); + }, + 'setstop' : function (ev) + { + console.log ('Stopping animation'); + this.destination._xseen.animating.stop(); + }, +/* + * TODO: Update TWEEN to support real pause & resume. + * Pause needs to hold current position + * Resume needs to restart the timer to current time so there is no "jump" + */ + 'setpause' : function (ev) + { + console.log ('Pausing (really stopping) animation'); + this.destination._xseen.animating.stop(); + }, + 'setresetstart' : function (ev) // TODO: Create seperate 'reset' method + { + console.log ('Reset and start animation'); + this.destination._xseen.animatingField = this.destination._xseen.initialValue; + this.destination._xseen.animating.start(); + }, + +/* + * Various interpolator functions for use with different data types + * All are designed to be used within TWEEN and take two arguments + * v A vector of way points (key values) that define the interpolated path + * k The interpolating factor that defines how far along the path for the current result + * + * Functions + * slerp - Linear in quaterian space (though not yet) + * color - Linear in color space (currently HSL as used by THREE) + * + */ + 'Interpolator' : { + 'slerp' : function (v,k) + { + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + + if (k < 0) { + return v[0].slerp(v[1], f); + } + + if (k > 1) { + return v[m].slerp(v[m-1], m-f); + } + + return v[i].slerp (v[i + 1 > m ? m : i + 1], f-i); + }, + 'color' : function (v,k) + { + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + var fn = this.slerpCompute; + + if (k < 0) { + return v[0].lerp(v[1], f); + } + if (k > 1) { + return v[m].lerp(v[m-1], m-f); + } + return v[i].lerp (v[i + 1 > m ? m : i + 1], f - i); + }, + }, +}; +// File: nodes/_Definitions-aframe.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen._addAframeAppearance = function (node) { + node + .addField('ambient-occlusion-map', 'SFString', '') + .addField('ambient-occlusion-map-intensity', 'SFFloat', 1) + .addField('ambient-occlusion-texture-offset', 'SFVec2f', '0 0') + .addField('ambient-occlusion-texture-repeat', 'SFVec2f', '1 1') + .addField('color', 'Color', '#FFF') + .addField('displacement-bias', 'SFFloat', 0.5) + .addField('displacement-map', 'SFString', '') + .addField('displacement-scale', 'SFFloat', 1) + .addField('displacement-texture-offset', 'SFVec2f', '0 0') + .addField('displacement-texture-repeat', 'SFVec2f', '1 1') + .addField('env-map', 'SFString', '') + .addField('fog', 'SFBool', true) + .addField('metalness', 'SFFloat', 0) + .addField('normal-map', 'SFString', '') + .addField('normal-scale', 'SFVec2f', '1 1') + .addField('normal-texture-offset', 'SFVec2f', '0 0') + .addField('normal-texture-repeat', 'SFVec2f', '1 1') + .addField('repeat', 'SFVec2f', '1 1') + .addField('roughness', 'SFFloat', 0.5) + .addField('spherical-env-map', 'SFString', '') + .addField('src', 'SFString', '') + .addField('wireframe', 'SFBool', false) + .addField('wireframe-linewidth', 'SFInt', 2) + .addNode(); +} + +xseen.nodes._defineNode('a-entity', 'A-Frame', 'af_Entity') + .addField('geometry', 'SFString', '') + .addField('material', 'SFString', '') + .addField('light', 'SFString', '') + .addNode(); + +var node; +node = xseen.nodes._defineNode('a-box', 'A-Frame', 'af_Box') + .addField('depth', 'SFFloat', 1) + .addField('height', 'SFFloat', 1) + .addField('width', 'SFFloat', 512) + .addField('segments-depth', 'SFInt', 1) + .addField('segments-height', 'SFInt', 1) + .addField('segments-width', 'SFInt', 1); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-cone', 'A-Frame', 'af_Cone') + .addField('height', 'SFFloat', 1) + .addField('radius', 'SFFloat', 1) + .addField('open-ended', 'SFBool', false) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 1) + .addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-cylinder', 'A-Frame', 'af_Cylinder') + .addField('height', 'SFFloat', 1) + .addField('open-ended', 'SFBool', false) + .addField('radius-bottom', 'SFFloat', 1) + .addField('radius-top', 'SFFloat', 1) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 1) + .addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-dodecahedron', 'A-Frame', 'af_Dodecahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-icosahedron', 'A-Frame', 'af_Icosahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-octahedron', 'A-Frame', 'af_Octahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-sphere', 'A-Frame', 'af_Sphere') + .addField('radius', 'SFFloat', 1) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 180) + .addField('phi-start', 'SFFloat', 0) + .addField('phi-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 18) + .addField('segments-width', 'SFInt', 36); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-tetrahedron', 'A-Frame', 'af_Tetrahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-torus', 'A-Frame', 'af_Torus') + .addField('radius', 'SFFloat', 2) + .addField('tube', 'SFFloat', 1) + .addField('arc', 'SFFloat', 360) + .addField('segments-radial', 'SFInt', 8) + .addField('segments-tubular', 'SFInt', 6); +xseen._addAframeAppearance (node); + +/* + * Asset management system nodes + */ +xseen.nodes._defineNode('a-assets', 'A-Frame', 'af_Assets') + .addNode(); +xseen.nodes._defineNode('a-asset-item', 'A-Frame', 'af_AssetItem') + .addField('src', 'SFString', '') + .addNode(); +xseen.nodes._defineNode('a-mixin', 'A-Frame', 'af_Mixin') + .addField('*', 'SFString', '') + .addNode(); +// File: nodes/_Definitions-x3d.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen.nodes._defineNode('Cone', 'Geometry3D', 'geometry3D_Cone') + .addField('bottomRadius', 'SFFloat', 1) + .addField('height', 'SFFloat', 2) + .addField('bottom', 'SFBool', true) + .addField('side', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode('Box', 'Geometry3D', 'geometry3D_Box') + .addField('size', 'SFVec3f', [1,1,1]) + .addNode(); + +xseen.nodes._defineNode('Sphere', 'Geometry3D', 'geometry3D_Sphere') + .addField('radius', 'SFFloat', '1') + .addNode(); + +xseen.nodes._defineNode('Cylinder', 'Geometry3D', 'geometry3D_Cylinder') + .addField('radius', 'SFFloat', 1) + .addField('height', 'SFFloat', 2) + .addField('bottom', 'SFBool', true) + .addField('side', 'SFBool', true) + .addField('top', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode ('Material', 'Appearance', 'appearance_Material') + .addField({name:'diffuseColor', datatype:'SFColor', defaultValue:[.8,.8,.8], animatable:true}) + .addField({name:'emissiveColor',datatype: 'SFColor', defaultValue:[0,0,0], animatable:true}) + .addField({name:'specularColor', datatype:'SFColor', defaultValue:[0,0,0], animatable:true}) + .addField({name:'transparency', datatype:'SFFloat', defaultValue:'0', animatable:true}) + .addField({name:'shininess', datatype:'SFFloat', defaultValue:'0', animatable:true}) + .addNode(); + +xseen.nodes._defineNode ('Transform', 'Grouping', 'grouping_Transform') + .addField({name:'translation', datatype:'SFVec3f', defaultValue:[0,0,0], animatable:true}) + .addField({name:'scale', datatype:'SFVec3f', defaultValue:[1,1,1], animatable:true}) + .addField({name:'rotation', datatype:'SFRotation', defaultValue:xseen.types.SFRotation('0 1 0 0',''), animatable:true}) + .addNode(); +xseen.nodes._defineNode ('Group', 'Grouping', 'grouping_Transform') + .addNode(); + +xseen.nodes._defineNode ('Light', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) // DirectionalLight + .addField('location', 'SFVec3f', [0,0,0]) // PointLight & SpotLight + .addField('radius', 'SFFloat', '100') // PointLight & SpotLight + .addField('attenuation', 'SFVec3f', [1,0,0]) // PointLight & SpotLight + .addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // SpotLight + .addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // SpotLight + .addField('color', 'SFColor', [1,1,1]) // General + .addField('intensity', 'SFFloat', '1') // General + .addField({name:'type', datatype:'EnumerateString', defaultValue:'Directional', enumerated:['Directional', 'Spot', 'Point'], animatable:true}) + .addNode(); +xseen.nodes._defineNode ('DirectionalLight', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Directional') + .addNode(); +xseen.nodes._defineNode ('PointLight', 'Lighting', 'lighting_Light') + .addField('location', 'SFVec3f', [0,0,0]) + .addField('radius', 'SFFloat', '100') + .addField('attenuation', 'SFVec3f', [1,0,0]) + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Point') + .addNode(); +xseen.nodes._defineNode ('SpotLight', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) + .addField('radius', 'SFFloat', '100') + .addField('attenuation', 'SFVec3f', [1,0,0]) + .addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // pi/4 + .addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // pi/2 + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Spot') + .addNode(); + +xseen.nodes._defineNode ('Viewpoint', 'Controls', 'unk_Viewpoint') + .addField('position', 'SFVec3f', '0 0 10') + .addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) + .addField('description', 'SFString', '') + .addField({name:'cameratype', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) + .addField({name:'type', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) + .addField({name:'motion', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'turntable', 'tilt'], animatable:false}) + .addField('motionspeed', 'SFFloat', 16) + .addField('active', 'SFBool', true) // incoming event + .addNode(); +xseen.nodes._defineNode ('NavigationMode', 'Controls', 'controls_Navigation') + .addField('speed', 'SFFloat', 1.) + .addField({name:'type', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'orbit', 'fly', 'examine', 'trackball'], animatable:false}) + .addNode(); +xseen.nodes._defineNode ('Camera', 'Controls', 'unk_Viewpoint') + .addField('position', 'SFVec3f', [0,0,10]) + .addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) + .addNode(); + +xseen.nodes._defineNode ('Inline', 'Networking', 'networking_Inline') + .addField('url', 'SFString', '') + .addNode(); + +xseen.nodes._defineNode ('scene', 'Core', 'core_Scene') + .addNode(); +xseen.nodes._defineNode ('canvas', 'Core', 'core_NOOP') + .addNode(); +xseen.nodes._defineNode ('WorldInfo', 'Core', 'core_WorldInfo') + .addNode(); +xseen.nodes._defineNode ('Appearance', 'Appearance', 'appearance_Appearance') + .addNode(); +xseen.nodes._defineNode ('ImageTexture', 'Appearance', 'appearance_ImageTexture') + .addField('url', 'SFString', '') + .addField('repeatS', 'SFBool', true) + .addField('repeatT', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode ('Shape', 'Shape', 'unk_Shape') + .addNode(); +xseen.nodes._defineNode('Background', 'Environmental', 'env_Background') + .addField('skyColor', 'SFColor', [0,0,0]) + .addField('srcFront', 'SFString', '') + .addField('srcBack', 'SFString', '') + .addField('srcTop', 'SFString', '') + .addField('srcBottom', 'SFString', '') + .addField('srcLeft', 'SFString', '') + .addField('srcRight', 'SFString', '') + .addField('backgroundIsCube', 'SFBool', 'true') + .addNode(); + +xseen.nodes._defineNode('TriangleSet', 'Geometry', 'geometry_TriangleSet') + .addField('ccw', 'SFBool', 'true') + .addField('colorPerVertex', 'SFBool', 'true') + .addField('solid', 'SFBool', 'true') + .addNode(); +xseen.nodes._defineNode('IndexedTriangleSet', 'Geometry', 'geometry_IndexedTriangleSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('index', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('Coordinate', 'Geometry', 'geometry_Coordinate') + .addField('point', 'MFVec3f', []) + .addNode(); +xseen.nodes._defineNode('Normal', 'Geometry', 'geometry_Normal') + .addField('vector', 'MFVec3f', []) + .addNode(); +xseen.nodes._defineNode('Color', 'Geometry', 'geometry_Color') + .addField('color', 'MFColor', []) + .addNode(); +xseen.nodes._defineNode('IndexedFaceSet', 'Geometry', 'geometry_IndexedFaceSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('coordIndex', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('IndexedQuadSet', 'Geometry', 'geometry_IndexedQuadSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('index', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('QuadSet', 'Geometry', 'geometry_QuadSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addNode(); + +//xseen.nodes._dumpTable(); +// File: nodes/_Definitions-xseen.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen.nodes._defineNode('model', 'XSeen', 'x_Model') + .addField('src', 'SFString', '') + .addField('playonload', 'SFString', '') + .addField('duration', 'SFFloat', '-1') + .addNode(); + +xseen.nodes._defineNode('animate', 'XSeen', 'x_Animate') + .addField('field', 'SFString', '') + .addField('to', 'MFFloat', '') // Needs to be 'field' datatype. That is not known until node-parse. For now insist on numeric array + .addField('delay', 'SFTime', 0) + .addField('duration', 'SFTime', 0) + .addField('repeat', 'SFInt', 0) + .addField({name:'interpolator', datatype:'EnumerateString', defaultValue:'position', enumerated:['position', 'rotation', 'color'], animatable:false}) + .addField({name:'Easing', datatype:'EnumerateString', defaultValue:'', enumerated:['', 'in', 'out', 'inout'], animatable:false}) + .addField({name:'EasingType', datatype:'EnumerateString', defaultValue:'linear', enumerated:['linear', 'quadratic', 'sinusoidal', 'exponential', 'elastic', 'bounce'], animatable:false}) + .addField('start', 'SFBool', true) // incoming event, need to set timer trigger + .addField('stop', 'SFBool', true) // incoming event, need to set timer trigger + .addField('resetstart', 'SFBool', true) // incoming event, need to set timer trigger + .addField('pause', 'SFBool', true) // incoming event, need to set timer trigger + .addNode(); + +xseen.nodes._defineNode('route', 'XSeen', 'x_Route') + .addField('source', 'SFString', '') + .addField('event', 'SFString', '') + .addField('destination', 'SFString', '') + .addField('field', 'SFString', '') + .addField('handler', 'SFString', '') + .addNode(); + + +// Dump parse table +//xseen.nodes._dumpTable(); \ No newline at end of file diff --git a/Release/XSeen.0.4.6+25_0e9dbd1.min.js b/Release/XSeen.0.4.6+25_0e9dbd1.min.js new file mode 100644 index 0000000..94b8ab5 --- /dev/null +++ b/Release/XSeen.0.4.6+25_0e9dbd1.min.js @@ -0,0 +1,9290 @@ +/* + * XSeen V0.4.6+25_0e9dbd1 + * Built Tue Jul 11 08:10:44 2017 + * + +Dual licensed under the MIT and GPL licenses. + +==[MIT]==================================================================== +Copyright (c) 2017, Daly Realism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +==[GPL]==================================================================== + +XSeen - Declarative 3D for HTML + +Copyright (C) 2017, Daly Realism + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +=== COPYRIGHT +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (C) 2017, Daly Realism for XSeen +Copyright, Fraunhofer for X3DOM +Copyright, Mozilla for A-Frame +Copyright, THREE and Khronos for various parts of THREE.js +Copyright (C) 2017, John Carlson for JSON->XML converter (JSONParser.js) + +=== +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + */ +THREE.ColladaLoader = function () { +var COLLADA = null; +var scene = null; +var visualScene; +var kinematicsModel; +var readyCallbackFunc = null; +var sources = {}; +var images = {}; +var animations = {}; +var controllers = {}; +var geometries = {}; +var materials = {}; +var effects = {}; +var cameras = {}; +var lights = {}; +var animData; +var kinematics; +var visualScenes; +var kinematicsModels; +var baseUrl; +var morphs; +var skins; +var flip_uv = true; +var preferredShading = THREE.SmoothShading; +var options = { +centerGeometry: false, +convertUpAxis: false, +subdivideFaces: true, +upAxis: 'Y', +defaultEnvMap: null +}; +var colladaUnit = 1.0; +var colladaUp = 'Y'; +var upConversion = null; +function load ( url, readyCallback, progressCallback, failCallback ) { +var length = 0; +if ( document.implementation && document.implementation.createDocument ) { +var request = new XMLHttpRequest(); +request.onreadystatechange = function() { +if ( request.readyState === 4 ) { +if ( request.status === 0 || request.status === 200 ) { +if ( request.response ) { +readyCallbackFunc = readyCallback; +parse( request.response, undefined, url ); +} else { +if ( failCallback ) { +failCallback( { type: 'error', url: url } ); +} else { +console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); +} +} +}else{ +if( failCallback ){ +failCallback( { type: 'error', url: url } ); +}else{ +console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' ); +} +} +} else if ( request.readyState === 3 ) { +if ( progressCallback ) { +if ( length === 0 ) { +length = request.getResponseHeader( "Content-Length" ); +} +progressCallback( { total: length, loaded: request.responseText.length } ); +} +} +}; +request.open( "GET", url, true ); +request.send( null ); +} else { +alert( "Don't know how to parse XML!" ); +} +} +function parse( text, callBack, url ) { +COLLADA = new DOMParser().parseFromString( text, 'text/xml' ); +callBack = callBack || readyCallbackFunc; +if ( url !== undefined ) { +var parts = url.split( '/' ); +parts.pop(); +baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; +} +parseAsset(); +setUpConversion(); +images = parseLib( "library_images image", _Image, "image" ); +materials = parseLib( "library_materials material", Material, "material" ); +effects = parseLib( "library_effects effect", Effect, "effect" ); +geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); +cameras = parseLib( "library_cameras camera", Camera, "camera" ); +lights = parseLib( "library_lights light", Light, "light" ); +controllers = parseLib( "library_controllers controller", Controller, "controller" ); +animations = parseLib( "library_animations animation", Animation, "animation" ); +visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); +kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); +morphs = []; +skins = []; +visualScene = parseScene(); +scene = new THREE.Group(); +for ( var i = 0; i < visualScene.nodes.length; i ++ ) { +scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); +} +scene.scale.multiplyScalar( colladaUnit ); +createAnimations(); +kinematicsModel = parseKinematicsModel(); +createKinematics(); +var result = { +scene: scene, +morphs: morphs, +skins: skins, +animations: animData, +kinematics: kinematics, +dae: { +images: images, +materials: materials, +cameras: cameras, +lights: lights, +effects: effects, +geometries: geometries, +controllers: controllers, +animations: animations, +visualScenes: visualScenes, +visualScene: visualScene, +scene: visualScene, +kinematicsModels: kinematicsModels, +kinematicsModel: kinematicsModel +} +}; +if ( callBack ) { +callBack( result ); +} +return result; +} +function setPreferredShading ( shading ) { +preferredShading = shading; +} +function parseAsset () { +var elements = COLLADA.querySelectorAll('asset'); +var element = elements[0]; +if ( element && element.childNodes ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'unit': +var meter = child.getAttribute( 'meter' ); +if ( meter ) { +colladaUnit = parseFloat( meter ); +} +break; +case 'up_axis': +colladaUp = child.textContent.charAt(0); +break; +} +} +} +} +function parseLib ( q, classSpec, prefix ) { +var elements = COLLADA.querySelectorAll(q); +var lib = {}; +var i = 0; +var elementsLength = elements.length; +for ( var j = 0; j < elementsLength; j ++ ) { +var element = elements[j]; +var daeElement = ( new classSpec() ).parse( element ); +if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); +lib[ daeElement.id ] = daeElement; +} +return lib; +} +function parseScene() { +var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; +if ( sceneElement ) { +var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); +return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; +} else { +return null; +} +} +function parseKinematicsModel() { +var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; +if ( kinematicsModelElement ) { +var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); +return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; +} else { +return null; +} +} +function createAnimations() { +animData = []; +recurseHierarchy( scene ); +} +function recurseHierarchy( node ) { +var n = visualScene.getChildById( node.colladaId, true ), +newData = null; +if ( n && n.keys ) { +newData = { +fps: 60, +hierarchy: [ { +node: n, +keys: n.keys, +sids: n.sids +} ], +node: node, +name: 'animation_' + node.name, +length: 0 +}; +animData.push(newData); +for ( var i = 0, il = n.keys.length; i < il; i ++ ) { +newData.length = Math.max( newData.length, n.keys[i].time ); +} +} else { +newData = { +hierarchy: [ { +keys: [], +sids: [] +} ] +} +} +for ( var i = 0, il = node.children.length; i < il; i ++ ) { +var d = recurseHierarchy( node.children[i] ); +for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { +newData.hierarchy.push( { +keys: [], +sids: [] +} ); +} +} +return newData; +} +function calcAnimationBounds () { +var start = 1000000; +var end = -start; +var frames = 0; +var ID; +for ( var id in animations ) { +var animation = animations[ id ]; +ID = ID || animation.id; +for ( var i = 0; i < animation.sampler.length; i ++ ) { +var sampler = animation.sampler[ i ]; +sampler.create(); +start = Math.min( start, sampler.startTime ); +end = Math.max( end, sampler.endTime ); +frames = Math.max( frames, sampler.input.length ); +} +} +return { start:start, end:end, frames:frames,ID:ID }; +} +function createMorph ( geometry, ctrl ) { +var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; +if ( !morphCtrl || !morphCtrl.morph ) { +console.log("could not find morph controller!"); +return; +} +var morph = morphCtrl.morph; +for ( var i = 0; i < morph.targets.length; i ++ ) { +var target_id = morph.targets[ i ]; +var daeGeometry = geometries[ target_id ]; +if ( !daeGeometry.mesh || +!daeGeometry.mesh.primitives || +!daeGeometry.mesh.primitives.length ) { +continue; +} +var target = daeGeometry.mesh.primitives[ 0 ].geometry; +if ( target.vertices.length === geometry.vertices.length ) { +geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); +} +} +geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); +} +function createSkin ( geometry, ctrl, applyBindShape ) { +var skinCtrl = controllers[ ctrl.url ]; +if ( !skinCtrl || !skinCtrl.skin ) { +console.log( "could not find skin controller!" ); +return; +} +if ( !ctrl.skeleton || !ctrl.skeleton.length ) { +console.log( "could not find the skeleton for the skin!" ); +return; +} +var skin = skinCtrl.skin; +var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); +var hierarchy = []; +applyBindShape = applyBindShape !== undefined ? applyBindShape : true; +var bones = []; +geometry.skinWeights = []; +geometry.skinIndices = []; +if ( applyBindShape ) { +for ( var i = 0; i < geometry.vertices.length; i ++ ) { +geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); +} +} +} +function setupSkeleton ( node, bones, frame, parent ) { +node.world = node.world || new THREE.Matrix4(); +node.localworld = node.localworld || new THREE.Matrix4(); +node.world.copy( node.matrix ); +node.localworld.copy( node.matrix ); +if ( node.channels && node.channels.length ) { +var channel = node.channels[ 0 ]; +var m = channel.sampler.output[ frame ]; +if ( m instanceof THREE.Matrix4 ) { +node.world.copy( m ); +node.localworld.copy(m); +if (frame === 0) +node.matrix.copy(m); +} +} +if ( parent ) { +node.world.multiplyMatrices( parent, node.world ); +} +bones.push( node ); +for ( var i = 0; i < node.nodes.length; i ++ ) { +setupSkeleton( node.nodes[ i ], bones, frame, node.world ); +} +} +function setupSkinningMatrices ( bones, skin ) { +for ( var i = 0; i < bones.length; i ++ ) { +var bone = bones[ i ]; +var found = -1; +if ( bone.type != 'JOINT' ) continue; +for ( var j = 0; j < skin.joints.length; j ++ ) { +if ( bone.sid === skin.joints[ j ] ) { +found = j; +break; +} +} +if ( found >= 0 ) { +var inv = skin.invBindMatrices[ found ]; +bone.invBindMatrix = inv; +bone.skinningMatrix = new THREE.Matrix4(); +bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) +bone.animatrix = new THREE.Matrix4(); +bone.animatrix.copy(bone.localworld); +bone.weights = []; +for ( var j = 0; j < skin.weights.length; j ++ ) { +for (var k = 0; k < skin.weights[ j ].length; k ++ ) { +var w = skin.weights[ j ][ k ]; +if ( w.joint === found ) { +bone.weights.push( w ); +} +} +} +} else { +console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); +bone.skinningMatrix = new THREE.Matrix4(); +bone.weights = []; +} +} +} +function flattenSkeleton(skeleton) { +var list = []; +var walk = function(parentid, node, list) { +var bone = {}; +bone.name = node.sid; +bone.parent = parentid; +bone.matrix = node.matrix; +var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; +bone.matrix.decompose(data[0], data[1], data[2]); +bone.pos = [ data[0].x,data[0].y,data[0].z ]; +bone.scl = [ data[2].x,data[2].y,data[2].z ]; +bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; +list.push(bone); +for (var i in node.nodes) { +walk(node.sid, node.nodes[i], list); +} +}; +walk(-1, skeleton, list); +return list; +} +function skinToBindPose(geometry,skeleton,skinController) { +var bones = []; +setupSkeleton( skeleton, bones, -1 ); +setupSkinningMatrices( bones, skinController.skin ); +var v = new THREE.Vector3(); +var skinned = []; +for (var i = 0; i < geometry.vertices.length; i ++) { +skinned.push(new THREE.Vector3()); +} +for ( i = 0; i < bones.length; i ++ ) { +if ( bones[ i ].type != 'JOINT' ) continue; +for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { +var w = bones[ i ].weights[ j ]; +var vidx = w.index; +var weight = w.weight; +var o = geometry.vertices[vidx]; +var s = skinned[vidx]; +v.x = o.x; +v.y = o.y; +v.z = o.z; +v.applyMatrix4( bones[i].skinningMatrix ); +s.x += (v.x * weight); +s.y += (v.y * weight); +s.z += (v.z * weight); +} +} +for (var i = 0; i < geometry.vertices.length; i ++) { +geometry.vertices[i] = skinned[i]; +} +} +function applySkin ( geometry, instanceCtrl, frame ) { +var skinController = controllers[ instanceCtrl.url ]; +frame = frame !== undefined ? frame : 40; +if ( !skinController || !skinController.skin ) { +console.log( 'ColladaLoader: Could not find skin controller.' ); +return; +} +if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { +console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); +return; +} +var animationBounds = calcAnimationBounds(); +var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); +var bonelist = flattenSkeleton(skeleton); +var joints = skinController.skin.joints; +var sortedbones = []; +for (var i = 0; i < joints.length; i ++) { +for (var j = 0; j < bonelist.length; j ++) { +if (bonelist[j].name === joints[i]) { +sortedbones[i] = bonelist[j]; +} +} +} +for (var i = 0; i < sortedbones.length; i ++) { +for (var j = 0; j < sortedbones.length; j ++) { +if (sortedbones[i].parent === sortedbones[j].name) { +sortedbones[i].parent = j; +} +} +} +var i, j, w, vidx, weight; +var v = new THREE.Vector3(), o, s; +for ( i = 0; i < geometry.vertices.length; i ++ ) { +geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); +} +var skinIndices = []; +var skinWeights = []; +var weights = skinController.skin.weights; +for ( var i =0; i < weights.length; i ++ ) { +var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); +var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); +skinIndices.push(indicies); +skinWeights.push(weight); +} +geometry.skinIndices = skinIndices; +geometry.skinWeights = skinWeights; +geometry.bones = sortedbones; +var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; +for (var j = 0; j < sortedbones.length; j ++) { +animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); +} +console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); +skinToBindPose(geometry, skeleton, skinController); +for ( frame = 0; frame < animationBounds.frames; frame ++ ) { +var bones = []; +var skinned = []; +setupSkeleton( skeleton, bones, frame ); +setupSkinningMatrices( bones, skinController.skin ); +for (var i = 0; i < bones.length; i ++) { +for (var j = 0; j < animationdata.hierarchy.length; j ++) { +if (animationdata.hierarchy[j].name === bones[i].sid) { +var key = {}; +key.time = (frame / 30); +key.matrix = bones[i].animatrix; +if (frame === 0) +bones[i].matrix = key.matrix; +var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; +key.matrix.decompose(data[0], data[1], data[2]); +key.pos = [ data[0].x,data[0].y,data[0].z ]; +key.scl = [ data[2].x,data[2].y,data[2].z ]; +key.rot = data[1]; +animationdata.hierarchy[j].keys.push(key); +} +} +} +geometry.animation = animationdata; +} +} +function createKinematics() { +if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { +kinematics = undefined; +return; +} +var jointMap = {}; +var _addToMap = function( jointIndex, parentVisualElement ) { +var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); +var colladaNode = visualScene.getChildById( parentVisualElementId, true ); +var joint = kinematicsModel.joints[ jointIndex ]; +scene.traverse(function( node ) { +if ( node.colladaId == parentVisualElementId ) { +jointMap[ jointIndex ] = { +node: node, +transforms: colladaNode.transforms, +joint: joint, +position: joint.zeroPosition +}; +} +}); +}; +kinematics = { +joints: kinematicsModel && kinematicsModel.joints, +getJointValue: function( jointIndex ) { +var jointData = jointMap[ jointIndex ]; +if ( jointData ) { +return jointData.position; +} else { +console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); +} +}, +setJointValue: function( jointIndex, value ) { +var jointData = jointMap[ jointIndex ]; +if ( jointData ) { +var joint = jointData.joint; +if ( value > joint.limits.max || value < joint.limits.min ) { +console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); +} else if ( joint.static ) { +console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); +} else { +var threejsNode = jointData.node; +var axis = joint.axis; +var transforms = jointData.transforms; +var matrix = new THREE.Matrix4(); +var m1 = new THREE.Matrix4(); +for (i = 0; i < transforms.length; i ++ ) { +var transform = transforms[ i ]; +if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { +switch ( joint.type ) { +case 'revolute': +matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); +break; +case 'prismatic': +matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); +break; +default: +console.warn( 'setJointValue: unknown joint type: ' + joint.type ); +break; +} +} else { +switch ( transform.type ) { +case 'matrix': +matrix.multiply( transform.obj ); +break; +case 'translate': +matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); +break; +case 'rotate': +matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); +break; +} +} +} +var elementsFloat32Arr = matrix.elements; +var elements = Array.prototype.slice.call( elementsFloat32Arr ); +var elementsRowMajor = [ +elements[ 0 ], +elements[ 4 ], +elements[ 8 ], +elements[ 12 ], +elements[ 1 ], +elements[ 5 ], +elements[ 9 ], +elements[ 13 ], +elements[ 2 ], +elements[ 6 ], +elements[ 10 ], +elements[ 14 ], +elements[ 3 ], +elements[ 7 ], +elements[ 11 ], +elements[ 15 ] +]; +threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); +threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); +jointMap[ jointIndex ].position = value; +} +} else { +console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); +} +} +}; +var element = COLLADA.querySelector('scene instance_kinematics_scene'); +if ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bind_joint_axis': +var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); +var axis = child.querySelector('axis param').textContent; +var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); +var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); +if ( visualTargetElement ) { +var parentVisualElement = visualTargetElement.parentElement; +_addToMap(jointIndex, parentVisualElement); +} +break; +default: +break; +} +} +} +} +function createSceneGraph ( node, parent ) { +var obj = new THREE.Object3D(); +var skinned = false; +var skinController; +var morphController; +var i, j; +for ( i = 0; i < node.controllers.length; i ++ ) { +var controller = controllers[ node.controllers[ i ].url ]; +switch ( controller.type ) { +case 'skin': +if ( geometries[ controller.skin.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = controller.skin.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +skinned = true; +skinController = node.controllers[ i ]; +} else if ( controllers[ controller.skin.source ] ) { +var second = controllers[ controller.skin.source ]; +morphController = second; +if ( second.morph && geometries[ second.morph.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = second.morph.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +} +} +break; +case 'morph': +if ( geometries[ controller.morph.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = controller.morph.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +morphController = node.controllers[ i ]; +} +console.log( 'ColladaLoader: Morph-controller partially supported.' ); +default: +break; +} +} +var double_sided_materials = {}; +for ( i = 0; i < node.geometries.length; i ++ ) { +var instance_geometry = node.geometries[i]; +var instance_materials = instance_geometry.instance_material; +var geometry = geometries[ instance_geometry.url ]; +var used_materials = {}; +var used_materials_array = []; +var num_materials = 0; +var first_material; +if ( geometry ) { +if ( !geometry.mesh || !geometry.mesh.primitives ) +continue; +if ( obj.name.length === 0 ) { +obj.name = geometry.id; +} +if ( instance_materials ) { +for ( j = 0; j < instance_materials.length; j ++ ) { +var instance_material = instance_materials[ j ]; +var mat = materials[ instance_material.target ]; +var effect_id = mat.instance_effect.url; +var shader = effects[ effect_id ].shader; +var material3js = shader.material; +if ( geometry.doubleSided ) { +if ( !( instance_material.symbol in double_sided_materials ) ) { +var _copied_material = material3js.clone(); +_copied_material.side = THREE.DoubleSide; +double_sided_materials[ instance_material.symbol ] = _copied_material; +} +material3js = double_sided_materials[ instance_material.symbol ]; +} +material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; +used_materials[ instance_material.symbol ] = num_materials; +used_materials_array.push( material3js ); +first_material = material3js; +first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; +num_materials ++; +} +} +var mesh; +var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); +var geom = geometry.mesh.geometry3js; +if ( num_materials > 1 ) { +material = new THREE.MultiMaterial( used_materials_array ); +for ( j = 0; j < geom.faces.length; j ++ ) { +var face = geom.faces[ j ]; +face.materialIndex = used_materials[ face.daeMaterial ] +} +} +if ( skinController !== undefined ) { +applySkin( geom, skinController ); +if ( geom.morphTargets.length > 0 ) { +material.morphTargets = true; +material.skinning = false; +} else { +material.morphTargets = false; +material.skinning = true; +} +mesh = new THREE.SkinnedMesh( geom, material, false ); +mesh.name = 'skin_' + skins.length; +skins.push( mesh ); +} else if ( morphController !== undefined ) { +createMorph( geom, morphController ); +material.morphTargets = true; +mesh = new THREE.Mesh( geom, material ); +mesh.name = 'morph_' + morphs.length; +morphs.push( mesh ); +} else { +if ( geom.isLineStrip === true ) { +mesh = new THREE.Line( geom ); +} else { +mesh = new THREE.Mesh( geom, material ); +} +} +obj.add(mesh); +} +} +for ( i = 0; i < node.cameras.length; i ++ ) { +var instance_camera = node.cameras[i]; +var cparams = cameras[instance_camera.url]; +var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), +parseFloat(cparams.znear), parseFloat(cparams.zfar)); +obj.add(cam); +} +for ( i = 0; i < node.lights.length; i ++ ) { +var light = null; +var instance_light = node.lights[i]; +var lparams = lights[instance_light.url]; +if ( lparams && lparams.technique ) { +var color = lparams.color.getHex(); +var intensity = lparams.intensity; +var distance = lparams.distance; +var angle = lparams.falloff_angle; +switch ( lparams.technique ) { +case 'directional': +light = new THREE.DirectionalLight( color, intensity, distance ); +light.position.set(0, 0, 1); +break; +case 'point': +light = new THREE.PointLight( color, intensity, distance ); +break; +case 'spot': +light = new THREE.SpotLight( color, intensity, distance, angle ); +light.position.set(0, 0, 1); +break; +case 'ambient': +light = new THREE.AmbientLight( color ); +break; +} +} +if (light) { +obj.add(light); +} +} +obj.name = node.name || node.id || ""; +obj.colladaId = node.id || ""; +obj.layer = node.layer || ""; +obj.matrix = node.matrix; +obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); +if ( options.centerGeometry && obj.geometry ) { +var delta = obj.geometry.center(); +delta.multiply( obj.scale ); +delta.applyQuaternion( obj.quaternion ); +obj.position.sub( delta ); +} +for ( i = 0; i < node.nodes.length; i ++ ) { +obj.add( createSceneGraph( node.nodes[i], node ) ); +} +return obj; +} +function getJointId( skin, id ) { +for ( var i = 0; i < skin.joints.length; i ++ ) { +if ( skin.joints[ i ] === id ) { +return i; +} +} +} +function getLibraryNode( id ) { +var nodes = COLLADA.querySelectorAll('library_nodes node'); +for ( var i = 0; i < nodes.length; i++ ) { +var attObj = nodes[i].attributes.getNamedItem('id'); +if ( attObj && attObj.value === id ) { +return nodes[i]; +} +} +return undefined; +} +function getChannelsForNode ( node ) { +var channels = []; +var startTime = 1000000; +var endTime = -1000000; +for ( var id in animations ) { +var animation = animations[id]; +for ( var i = 0; i < animation.channel.length; i ++ ) { +var channel = animation.channel[i]; +var sampler = animation.sampler[i]; +var id = channel.target.split('/')[0]; +if ( id == node.id ) { +sampler.create(); +channel.sampler = sampler; +startTime = Math.min(startTime, sampler.startTime); +endTime = Math.max(endTime, sampler.endTime); +channels.push(channel); +} +} +} +if ( channels.length ) { +node.startTime = startTime; +node.endTime = endTime; +} +return channels; +} +function calcFrameDuration( node ) { +var minT = 10000000; +for ( var i = 0; i < node.channels.length; i ++ ) { +var sampler = node.channels[i].sampler; +for ( var j = 0; j < sampler.input.length - 1; j ++ ) { +var t0 = sampler.input[ j ]; +var t1 = sampler.input[ j + 1 ]; +minT = Math.min( minT, t1 - t0 ); +} +} +return minT; +} +function calcMatrixAt( node, t ) { +var animated = {}; +var i, j; +for ( i = 0; i < node.channels.length; i ++ ) { +var channel = node.channels[ i ]; +animated[ channel.sid ] = channel; +} +var matrix = new THREE.Matrix4(); +for ( i = 0; i < node.transforms.length; i ++ ) { +var transform = node.transforms[ i ]; +var channel = animated[ transform.sid ]; +if ( channel !== undefined ) { +var sampler = channel.sampler; +var value; +for ( j = 0; j < sampler.input.length - 1; j ++ ) { +if ( sampler.input[ j + 1 ] > t ) { +value = sampler.output[ j ]; +break; +} +} +if ( value !== undefined ) { +if ( value instanceof THREE.Matrix4 ) { +matrix.multiplyMatrices( matrix, value ); +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} +return matrix; +} +function bakeAnimations ( node ) { +if ( node.channels && node.channels.length ) { +var keys = [], +sids = []; +for ( var i = 0, il = node.channels.length; i < il; i ++ ) { +var channel = node.channels[i], +fullSid = channel.fullSid, +sampler = channel.sampler, +input = sampler.input, +transform = node.getTransformBySid( channel.sid ), +member; +if ( channel.arrIndices ) { +member = []; +for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { +member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); +} +} else { +member = getConvertedMember( channel.member ); +} +if ( transform ) { +if ( sids.indexOf( fullSid ) === -1 ) { +sids.push( fullSid ); +} +for ( var j = 0, jl = input.length; j < jl; j ++ ) { +var time = input[j], +data = sampler.getData( transform.type, j, member ), +key = findKey( keys, time ); +if ( !key ) { +key = new Key( time ); +var timeNdx = findTimeNdx( keys, time ); +keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); +} +key.addTarget( fullSid, transform, member, data ); +} +} else { +console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); +} +} +for ( var i = 0; i < sids.length; i ++ ) { +var sid = sids[ i ]; +for ( var j = 0; j < keys.length; j ++ ) { +var key = keys[ j ]; +if ( !key.hasTarget( sid ) ) { +interpolateKeys( keys, key, j, sid ); +} +} +} +node.keys = keys; +node.sids = sids; +} +} +function findKey ( keys, time) { +var retVal = null; +for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { +var key = keys[i]; +if ( key.time === time ) { +retVal = key; +} else if ( key.time > time ) { +break; +} +} +return retVal; +} +function findTimeNdx ( keys, time) { +var ndx = -1; +for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { +var key = keys[i]; +if ( key.time >= time ) { +ndx = i; +} +} +return ndx; +} +function interpolateKeys ( keys, key, ndx, fullSid ) { +var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), +nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); +if ( prevKey && nextKey ) { +var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), +prevTarget = prevKey.getTarget( fullSid ), +nextData = nextKey.getTarget( fullSid ).data, +prevData = prevTarget.data, +data; +if ( prevTarget.type === 'matrix' ) { +data = prevData; +} else if ( prevData.length ) { +data = []; +for ( var i = 0; i < prevData.length; ++ i ) { +data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; +} +} else { +data = prevData + ( nextData - prevData ) * scale; +} +key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); +} +} +function getNextKeyWith( keys, fullSid, ndx ) { +for ( ; ndx < keys.length; ndx ++ ) { +var key = keys[ ndx ]; +if ( key.hasTarget( fullSid ) ) { +return key; +} +} +return null; +} +function getPrevKeyWith( keys, fullSid, ndx ) { +ndx = ndx >= 0 ? ndx : ndx + keys.length; +for ( ; ndx >= 0; ndx -- ) { +var key = keys[ ndx ]; +if ( key.hasTarget( fullSid ) ) { +return key; +} +} +return null; +} +function _Image() { +this.id = ""; +this.init_from = ""; +} +_Image.prototype.parse = function(element) { +this.id = element.getAttribute('id'); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeName === 'init_from' ) { +this.init_from = child.textContent; +} +} +return this; +}; +function Controller() { +this.id = ""; +this.name = ""; +this.type = ""; +this.skin = null; +this.morph = null; +} +Controller.prototype.parse = function( element ) { +this.id = element.getAttribute('id'); +this.name = element.getAttribute('name'); +this.type = "none"; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'skin': +this.skin = (new Skin()).parse(child); +this.type = child.nodeName; +break; +case 'morph': +this.morph = (new Morph()).parse(child); +this.type = child.nodeName; +break; +default: +break; +} +} +return this; +}; +function Morph() { +this.method = null; +this.source = null; +this.targets = null; +this.weights = null; +} +Morph.prototype.parse = function( element ) { +var sources = {}; +var inputs = []; +var i; +this.method = element.getAttribute( 'method' ); +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +for ( i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'source': +var source = ( new Source() ).parse( child ); +sources[ source.id ] = source; +break; +case 'targets': +inputs = this.parseInputs( child ); +break; +default: +console.log( child.nodeName ); +break; +} +} +for ( i = 0; i < inputs.length; i ++ ) { +var input = inputs[ i ]; +var source = sources[ input.source ]; +switch ( input.semantic ) { +case 'MORPH_TARGET': +this.targets = source.read(); +break; +case 'MORPH_WEIGHT': +this.weights = source.read(); +break; +default: +break; +} +} +return this; +}; +Morph.prototype.parseInputs = function(element) { +var inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1) continue; +switch ( child.nodeName ) { +case 'input': +inputs.push( (new Input()).parse(child) ); +break; +default: +break; +} +} +return inputs; +}; +function Skin() { +this.source = ""; +this.bindShapeMatrix = null; +this.invBindMatrices = []; +this.joints = []; +this.weights = []; +} +Skin.prototype.parse = function( element ) { +var sources = {}; +var joints, weights; +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +this.invBindMatrices = []; +this.joints = []; +this.weights = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bind_shape_matrix': +var f = _floats(child.textContent); +this.bindShapeMatrix = getConvertedMat4( f ); +break; +case 'source': +var src = new Source().parse(child); +sources[ src.id ] = src; +break; +case 'joints': +joints = child; +break; +case 'vertex_weights': +weights = child; +break; +default: +console.log( child.nodeName ); +break; +} +} +this.parseJoints( joints, sources ); +this.parseWeights( weights, sources ); +return this; +}; +Skin.prototype.parseJoints = function ( element, sources ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +var input = ( new Input() ).parse( child ); +var source = sources[ input.source ]; +if ( input.semantic === 'JOINT' ) { +this.joints = source.read(); +} else if ( input.semantic === 'INV_BIND_MATRIX' ) { +this.invBindMatrices = source.read(); +} +break; +default: +break; +} +} +}; +Skin.prototype.parseWeights = function ( element, sources ) { +var v, vcount, inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +inputs.push( ( new Input() ).parse( child ) ); +break; +case 'v': +v = _ints( child.textContent ); +break; +case 'vcount': +vcount = _ints( child.textContent ); +break; +default: +break; +} +} +var index = 0; +for ( var i = 0; i < vcount.length; i ++ ) { +var numBones = vcount[i]; +var vertex_weights = []; +for ( var j = 0; j < numBones; j ++ ) { +var influence = {}; +for ( var k = 0; k < inputs.length; k ++ ) { +var input = inputs[ k ]; +var value = v[ index + input.offset ]; +switch ( input.semantic ) { +case 'JOINT': +influence.joint = value;//this.joints[value]; +break; +case 'WEIGHT': +influence.weight = sources[ input.source ].data[ value ]; +break; +default: +break; +} +} +vertex_weights.push( influence ); +index += inputs.length; +} +for ( var j = 0; j < vertex_weights.length; j ++ ) { +vertex_weights[ j ].index = i; +} +this.weights.push( vertex_weights ); +} +}; +function VisualScene () { +this.id = ""; +this.name = ""; +this.nodes = []; +this.scene = new THREE.Group(); +} +VisualScene.prototype.getChildById = function( id, recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var node = this.nodes[ i ].getChildById( id, recursive ); +if ( node ) { +return node; +} +} +return null; +}; +VisualScene.prototype.getChildBySid = function( sid, recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var node = this.nodes[ i ].getChildBySid( sid, recursive ); +if ( node ) { +return node; +} +} +return null; +}; +VisualScene.prototype.parse = function( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +this.nodes = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'node': +this.nodes.push( ( new Node() ).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +function Node() { +this.id = ""; +this.name = ""; +this.sid = ""; +this.nodes = []; +this.controllers = []; +this.transforms = []; +this.geometries = []; +this.channels = []; +this.matrix = new THREE.Matrix4(); +} +Node.prototype.getChannelForTransform = function( transformSid ) { +for ( var i = 0; i < this.channels.length; i ++ ) { +var channel = this.channels[i]; +var parts = channel.target.split('/'); +var id = parts.shift(); +var sid = parts.shift(); +var dotSyntax = (sid.indexOf(".") >= 0); +var arrSyntax = (sid.indexOf("(") >= 0); +var arrIndices; +var member; +if ( dotSyntax ) { +parts = sid.split("."); +sid = parts.shift(); +member = parts.shift(); +} else if ( arrSyntax ) { +arrIndices = sid.split("("); +sid = arrIndices.shift(); +for ( var j = 0; j < arrIndices.length; j ++ ) { +arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); +} +} +if ( sid === transformSid ) { +channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; +return channel; +} +} +return null; +}; +Node.prototype.getChildById = function ( id, recursive ) { +if ( this.id === id ) { +return this; +} +if ( recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var n = this.nodes[ i ].getChildById( id, recursive ); +if ( n ) { +return n; +} +} +} +return null; +}; +Node.prototype.getChildBySid = function ( sid, recursive ) { +if ( this.sid === sid ) { +return this; +} +if ( recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var n = this.nodes[ i ].getChildBySid( sid, recursive ); +if ( n ) { +return n; +} +} +} +return null; +}; +Node.prototype.getTransformBySid = function ( sid ) { +for ( var i = 0; i < this.transforms.length; i ++ ) { +if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; +} +return null; +}; +Node.prototype.parse = function( element ) { +var url; +this.id = element.getAttribute('id'); +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.type = element.getAttribute('type'); +this.layer = element.getAttribute('layer'); +this.type = this.type === 'JOINT' ? this.type : 'NODE'; +this.nodes = []; +this.transforms = []; +this.geometries = []; +this.cameras = []; +this.lights = []; +this.controllers = []; +this.matrix = new THREE.Matrix4(); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'node': +this.nodes.push( ( new Node() ).parse( child ) ); +break; +case 'instance_camera': +this.cameras.push( ( new InstanceCamera() ).parse( child ) ); +break; +case 'instance_controller': +this.controllers.push( ( new InstanceController() ).parse( child ) ); +break; +case 'instance_geometry': +this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); +break; +case 'instance_light': +this.lights.push( ( new InstanceLight() ).parse( child ) ); +break; +case 'instance_node': +url = child.getAttribute( 'url' ).replace( /^#/, '' ); +var iNode = getLibraryNode( url ); +if ( iNode ) { +this.nodes.push( ( new Node() ).parse( iNode )) ; +} +break; +case 'rotate': +case 'translate': +case 'scale': +case 'matrix': +case 'lookat': +case 'skew': +this.transforms.push( ( new Transform() ).parse( child ) ); +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +this.channels = getChannelsForNode( this ); +bakeAnimations( this ); +this.updateMatrix(); +return this; +}; +Node.prototype.updateMatrix = function () { +this.matrix.identity(); +for ( var i = 0; i < this.transforms.length; i ++ ) { +this.transforms[ i ].apply( this.matrix ); +} +}; +function Transform () { +this.sid = ""; +this.type = ""; +this.data = []; +this.obj = null; +} +Transform.prototype.parse = function ( element ) { +this.sid = element.getAttribute( 'sid' ); +this.type = element.nodeName; +this.data = _floats( element.textContent ); +this.convert(); +return this; +}; +Transform.prototype.convert = function () { +switch ( this.type ) { +case 'matrix': +this.obj = getConvertedMat4( this.data ); +break; +case 'rotate': +this.angle = THREE.Math.degToRad( this.data[3] ); +case 'translate': +fixCoords( this.data, -1 ); +this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); +break; +case 'scale': +fixCoords( this.data, 1 ); +this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); +break; +default: +console.log( 'Can not convert Transform of type ' + this.type ); +break; +} +}; +Transform.prototype.apply = function () { +var m1 = new THREE.Matrix4(); +return function ( matrix ) { +switch ( this.type ) { +case 'matrix': +matrix.multiply( this.obj ); +break; +case 'translate': +matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); +break; +case 'rotate': +matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); +break; +case 'scale': +matrix.scale( this.obj ); +break; +} +}; +}(); +Transform.prototype.update = function ( data, member ) { +var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; +switch ( this.type ) { +case 'matrix': +if ( ! member ) { +this.obj.copy( data ); +} else if ( member.length === 1 ) { +switch ( member[ 0 ] ) { +case 0: +this.obj.n11 = data[ 0 ]; +this.obj.n21 = data[ 1 ]; +this.obj.n31 = data[ 2 ]; +this.obj.n41 = data[ 3 ]; +break; +case 1: +this.obj.n12 = data[ 0 ]; +this.obj.n22 = data[ 1 ]; +this.obj.n32 = data[ 2 ]; +this.obj.n42 = data[ 3 ]; +break; +case 2: +this.obj.n13 = data[ 0 ]; +this.obj.n23 = data[ 1 ]; +this.obj.n33 = data[ 2 ]; +this.obj.n43 = data[ 3 ]; +break; +case 3: +this.obj.n14 = data[ 0 ]; +this.obj.n24 = data[ 1 ]; +this.obj.n34 = data[ 2 ]; +this.obj.n44 = data[ 3 ]; +break; +} +} else if ( member.length === 2 ) { +var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); +this.obj[ propName ] = data; +} else { +console.log('Incorrect addressing of matrix in transform.'); +} +break; +case 'translate': +case 'scale': +if ( Object.prototype.toString.call( member ) === '[object Array]' ) { +member = members[ member[ 0 ] ]; +} +switch ( member ) { +case 'X': +this.obj.x = data; +break; +case 'Y': +this.obj.y = data; +break; +case 'Z': +this.obj.z = data; +break; +default: +this.obj.x = data[ 0 ]; +this.obj.y = data[ 1 ]; +this.obj.z = data[ 2 ]; +break; +} +break; +case 'rotate': +if ( Object.prototype.toString.call( member ) === '[object Array]' ) { +member = members[ member[ 0 ] ]; +} +switch ( member ) { +case 'X': +this.obj.x = data; +break; +case 'Y': +this.obj.y = data; +break; +case 'Z': +this.obj.z = data; +break; +case 'ANGLE': +this.angle = THREE.Math.degToRad( data ); +break; +default: +this.obj.x = data[ 0 ]; +this.obj.y = data[ 1 ]; +this.obj.z = data[ 2 ]; +this.angle = THREE.Math.degToRad( data[ 3 ] ); +break; +} +break; +} +}; +function InstanceController() { +this.url = ""; +this.skeleton = []; +this.instance_material = []; +} +InstanceController.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +this.skeleton = []; +this.instance_material = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType !== 1 ) continue; +switch ( child.nodeName ) { +case 'skeleton': +this.skeleton.push( child.textContent.replace(/^#/, '') ); +break; +case 'bind_material': +var instances = child.querySelectorAll('instance_material'); +for ( var j = 0; j < instances.length; j ++ ) { +var instance = instances[j]; +this.instance_material.push( (new InstanceMaterial()).parse(instance) ); +} +break; +case 'extra': +break; +default: +break; +} +} +return this; +}; +function InstanceMaterial () { +this.symbol = ""; +this.target = ""; +} +InstanceMaterial.prototype.parse = function ( element ) { +this.symbol = element.getAttribute('symbol'); +this.target = element.getAttribute('target').replace(/^#/, ''); +return this; +}; +function InstanceGeometry() { +this.url = ""; +this.instance_material = []; +} +InstanceGeometry.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +this.instance_material = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +if ( child.nodeName === 'bind_material' ) { +var instances = child.querySelectorAll('instance_material'); +for ( var j = 0; j < instances.length; j ++ ) { +var instance = instances[j]; +this.instance_material.push( (new InstanceMaterial()).parse(instance) ); +} +break; +} +} +return this; +}; +function Geometry() { +this.id = ""; +this.mesh = null; +} +Geometry.prototype.parse = function ( element ) { +this.id = element.getAttribute('id'); +extractDoubleSided( this, element ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +switch ( child.nodeName ) { +case 'mesh': +this.mesh = (new Mesh(this)).parse(child); +break; +case 'extra': +break; +default: +break; +} +} +return this; +}; +function Mesh( geometry ) { +this.geometry = geometry.id; +this.primitives = []; +this.vertices = null; +this.geometry3js = null; +} +Mesh.prototype.parse = function ( element ) { +this.primitives = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'source': +_source( child ); +break; +case 'vertices': +this.vertices = ( new Vertices() ).parse( child ); +break; +case 'linestrips': +this.primitives.push( ( new LineStrips().parse( child ) ) ); +break; +case 'triangles': +this.primitives.push( ( new Triangles().parse( child ) ) ); +break; +case 'polygons': +this.primitives.push( ( new Polygons().parse( child ) ) ); +break; +case 'polylist': +this.primitives.push( ( new Polylist().parse( child ) ) ); +break; +default: +break; +} +} +this.geometry3js = new THREE.Geometry(); +if ( this.vertices === null ) { +return this; +} +var vertexData = sources[ this.vertices.input['POSITION'].source ].data; +for ( var i = 0; i < vertexData.length; i += 3 ) { +this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); +} +for ( var i = 0; i < this.primitives.length; i ++ ) { +var primitive = this.primitives[ i ]; +primitive.setVertices( this.vertices ); +this.handlePrimitive( primitive, this.geometry3js ); +} +if ( this.geometry3js.calcNormals ) { +this.geometry3js.computeVertexNormals(); +delete this.geometry3js.calcNormals; +} +return this; +}; +Mesh.prototype.handlePrimitive = function ( primitive, geom ) { +if ( primitive instanceof LineStrips ) { +geom.isLineStrip = true; +return; +} +var j, k, pList = primitive.p, inputs = primitive.inputs; +var input, index, idx32; +var source, numParams; +var vcIndex = 0, vcount = 3, maxOffset = 0; +var texture_sets = []; +for ( j = 0; j < inputs.length; j ++ ) { +input = inputs[ j ]; +var offset = input.offset + 1; +maxOffset = (maxOffset < offset) ? offset : maxOffset; +switch ( input.semantic ) { +case 'TEXCOORD': +texture_sets.push( input.set ); +break; +} +} +for ( var pCount = 0; pCount < pList.length; ++ pCount ) { +var p = pList[ pCount ], i = 0; +while ( i < p.length ) { +var vs = []; +var ns = []; +var ts = null; +var cs = []; +if ( primitive.vcount ) { +vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; +} else { +vcount = p.length / maxOffset; +} +for ( j = 0; j < vcount; j ++ ) { +for ( k = 0; k < inputs.length; k ++ ) { +input = inputs[ k ]; +source = sources[ input.source ]; +index = p[ i + ( j * maxOffset ) + input.offset ]; +numParams = source.accessor.params.length; +idx32 = index * numParams; +switch ( input.semantic ) { +case 'VERTEX': +vs.push( index ); +break; +case 'NORMAL': +ns.push( getConvertedVec3( source.data, idx32 ) ); +break; +case 'TEXCOORD': +ts = ts || { }; +if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; +ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); +break; +case 'COLOR': +cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); +break; +default: +break; +} +} +} +if ( ns.length === 0 ) { +input = this.vertices.input.NORMAL; +if ( input ) { +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); +} +} else { +geom.calcNormals = true; +} +} +if ( !ts ) { +ts = { }; +input = this.vertices.input.TEXCOORD; +if ( input ) { +texture_sets.push( input.set ); +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +idx32 = vs[ ndx ] * numParams; +if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; +ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); +} +} +} +if ( cs.length === 0 ) { +input = this.vertices.input.COLOR; +if ( input ) { +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +idx32 = vs[ ndx ] * numParams; +cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); +} +} +} +var face = null, faces = [], uv, uvArr; +if ( vcount === 3 ) { +faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); +} else if ( vcount === 4 ) { +faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); +faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); +} else if ( vcount > 4 && options.subdivideFaces ) { +var clr = cs.length ? cs : new THREE.Color(), +vec1, vec2, vec3, v1, v2, norm; +for ( k = 1; k < vcount - 1; ) { +faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) ); +} +} +if ( faces.length ) { +for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { +face = faces[ndx]; +face.daeMaterial = primitive.material; +geom.faces.push( face ); +for ( k = 0; k < texture_sets.length; k ++ ) { +uv = ts[ texture_sets[k] ]; +if ( vcount > 4 ) { +uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; +} else if ( vcount === 4 ) { +if ( ndx === 0 ) { +uvArr = [ uv[0], uv[1], uv[3] ]; +} else { +uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; +} +} else { +uvArr = [ uv[0], uv[1], uv[2] ]; +} +if ( geom.faceVertexUvs[k] === undefined ) { +geom.faceVertexUvs[k] = []; +} +geom.faceVertexUvs[k].push( uvArr ); +} +} +} else { +console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); +} +i += maxOffset * vcount; +} +} +}; +function Polygons () { +this.material = ""; +this.count = 0; +this.inputs = []; +this.vcount = null; +this.p = []; +this.geometry = new THREE.Geometry(); +} +Polygons.prototype.setVertices = function ( vertices ) { +for ( var i = 0; i < this.inputs.length; i ++ ) { +if ( this.inputs[ i ].source === vertices.id ) { +this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; +} +} +}; +Polygons.prototype.parse = function ( element ) { +this.material = element.getAttribute( 'material' ); +this.count = _attr_as_int( element, 'count', 0 ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'input': +this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); +break; +case 'vcount': +this.vcount = _ints( child.textContent ); +break; +case 'p': +this.p.push( _ints( child.textContent ) ); +break; +case 'ph': +console.warn( 'polygon holes not yet supported!' ); +break; +default: +break; +} +} +return this; +}; +function Polylist () { +Polygons.call( this ); +this.vcount = []; +} +Polylist.prototype = Object.create( Polygons.prototype ); +Polylist.prototype.constructor = Polylist; +function LineStrips() { +Polygons.call( this ); +this.vcount = 1; +} +LineStrips.prototype = Object.create( Polygons.prototype ); +LineStrips.prototype.constructor = LineStrips; +function Triangles () { +Polygons.call( this ); +this.vcount = 3; +} +Triangles.prototype = Object.create( Polygons.prototype ); +Triangles.prototype.constructor = Triangles; +function Accessor() { +this.source = ""; +this.count = 0; +this.stride = 0; +this.params = []; +} +Accessor.prototype.parse = function ( element ) { +this.params = []; +this.source = element.getAttribute( 'source' ); +this.count = _attr_as_int( element, 'count', 0 ); +this.stride = _attr_as_int( element, 'stride', 0 ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeName === 'param' ) { +var param = {}; +param[ 'name' ] = child.getAttribute( 'name' ); +param[ 'type' ] = child.getAttribute( 'type' ); +this.params.push( param ); +} +} +return this; +}; +function Vertices() { +this.input = {}; +} +Vertices.prototype.parse = function ( element ) { +this.id = element.getAttribute('id'); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[i].nodeName === 'input' ) { +var input = ( new Input() ).parse( element.childNodes[ i ] ); +this.input[ input.semantic ] = input; +} +} +return this; +}; +function Input () { +this.semantic = ""; +this.offset = 0; +this.source = ""; +this.set = 0; +} +Input.prototype.parse = function ( element ) { +this.semantic = element.getAttribute('semantic'); +this.source = element.getAttribute('source').replace(/^#/, ''); +this.set = _attr_as_int(element, 'set', -1); +this.offset = _attr_as_int(element, 'offset', 0); +if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { +this.set = 0; +} +return this; +}; +function Source ( id ) { +this.id = id; +this.type = null; +} +Source.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +switch ( child.nodeName ) { +case 'bool_array': +this.data = _bools( child.textContent ); +this.type = child.nodeName; +break; +case 'float_array': +this.data = _floats( child.textContent ); +this.type = child.nodeName; +break; +case 'int_array': +this.data = _ints( child.textContent ); +this.type = child.nodeName; +break; +case 'IDREF_array': +case 'Name_array': +this.data = _strings( child.textContent ); +this.type = child.nodeName; +break; +case 'technique_common': +for ( var j = 0; j < child.childNodes.length; j ++ ) { +if ( child.childNodes[ j ].nodeName === 'accessor' ) { +this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); +break; +} +} +break; +default: +break; +} +} +return this; +}; +Source.prototype.read = function () { +var result = []; +var param = this.accessor.params[ 0 ]; +switch ( param.type ) { +case 'IDREF': +case 'Name': case 'name': +case 'float': +return this.data; +case 'float4x4': +for ( var j = 0; j < this.data.length; j += 16 ) { +var s = this.data.slice( j, j + 16 ); +var m = getConvertedMat4( s ); +result.push( m ); +} +break; +default: +console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); +break; +} +return result; +}; +function Material () { +this.id = ""; +this.name = ""; +this.instance_effect = null; +} +Material.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { +this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); +break; +} +} +return this; +}; +function ColorOrTexture () { +this.color = new THREE.Color(); +this.color.setRGB( Math.random(), Math.random(), Math.random() ); +this.color.a = 1.0; +this.texture = null; +this.texcoord = null; +this.texOpts = null; +} +ColorOrTexture.prototype.isColor = function () { +return ( this.texture === null ); +}; +ColorOrTexture.prototype.isTexture = function () { +return ( this.texture != null ); +}; +ColorOrTexture.prototype.parse = function ( element ) { +if (element.nodeName === 'transparent') { +this.opaque = element.getAttribute('opaque'); +} +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'color': +var rgba = _floats( child.textContent ); +this.color = new THREE.Color(); +this.color.setRGB( rgba[0], rgba[1], rgba[2] ); +this.color.a = rgba[3]; +break; +case 'texture': +this.texture = child.getAttribute('texture'); +this.texcoord = child.getAttribute('texcoord'); +this.texOpts = { +offsetU: 0, +offsetV: 0, +repeatU: 1, +repeatV: 1, +wrapU: 1, +wrapV: 1 +}; +this.parseTexture( child ); +break; +default: +break; +} +} +return this; +}; +ColorOrTexture.prototype.parseTexture = function ( element ) { +if ( ! element.childNodes ) return this; +if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { +element = element.childNodes[1]; +if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { +element = element.childNodes[1]; +} +} +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'offsetU': +case 'offsetV': +case 'repeatU': +case 'repeatV': +this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); +break; +case 'wrapU': +case 'wrapV': +if ( child.textContent.toUpperCase() === 'TRUE' ) { +this.texOpts[ child.nodeName ] = 1; +} else { +this.texOpts[ child.nodeName ] = parseInt( child.textContent ); +} +break; +default: +this.texOpts[ child.nodeName ] = child.textContent; +break; +} +} +return this; +}; +function Shader ( type, effect ) { +this.type = type; +this.effect = effect; +this.material = null; +} +Shader.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'emission': +case 'diffuse': +case 'specular': +case 'transparent': +this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); +break; +case 'bump': +var bumpType = child.getAttribute( 'bumptype' ); +if ( bumpType ) { +if ( bumpType.toLowerCase() === "heightfield" ) { +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} else if ( bumpType.toLowerCase() === "normalmap" ) { +this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); +} else { +console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} +} else { +console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} +break; +case 'shininess': +case 'reflectivity': +case 'index_of_refraction': +case 'transparency': +var f = child.querySelectorAll('float'); +if ( f.length > 0 ) +this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); +break; +default: +break; +} +} +this.create(); +return this; +}; +Shader.prototype.create = function() { +var props = {}; +var transparent = false; +if (this['transparency'] !== undefined && this['transparent'] !== undefined) { +var transparentColor = this['transparent']; +var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; +if (transparencyLevel > 0) { +transparent = true; +props[ 'transparent' ] = true; +props[ 'opacity' ] = 1 - transparencyLevel; +} +} +var keys = { +'diffuse':'map', +'ambient':'lightMap', +'specular':'specularMap', +'emission':'emissionMap', +'bump':'bumpMap', +'normal':'normalMap' +}; +for ( var prop in this ) { +switch ( prop ) { +case 'ambient': +case 'emission': +case 'diffuse': +case 'specular': +case 'bump': +case 'normal': +var cot = this[ prop ]; +if ( cot instanceof ColorOrTexture ) { +if ( cot.isTexture() ) { +var samplerId = cot.texture; +var sampler = this.effect.sampler[samplerId]; +if ( sampler !== undefined && sampler.source !== undefined ) { +var surface = this.effect.surface[sampler.source]; +if ( surface !== undefined ) { +var image = images[ surface.init_from ]; +if ( image ) { +var url = baseUrl + image.init_from; +var texture; +var loader = THREE.Loader.Handlers.get( url ); +if ( loader !== null ) { +texture = loader.load( url ); +} else { +texture = new THREE.Texture(); +loadTextureImage( texture, url ); +} +if ( sampler.wrap_s === "MIRROR" ) { +texture.wrapS = THREE.MirroredRepeatWrapping; +} else if ( sampler.wrap_s === "WRAP" || cot.texOpts.wrapU ) { +texture.wrapS = THREE.RepeatWrapping; +} else { +texture.wrapS = THREE.ClampToEdgeWrapping; +} +if ( sampler.wrap_t === "MIRROR" ) { +texture.wrapT = THREE.MirroredRepeatWrapping; +} else if ( sampler.wrap_t === "WRAP" || cot.texOpts.wrapV ) { +texture.wrapT = THREE.RepeatWrapping; +} else { +texture.wrapT = THREE.ClampToEdgeWrapping; +} +texture.offset.x = cot.texOpts.offsetU; +texture.offset.y = cot.texOpts.offsetV; +texture.repeat.x = cot.texOpts.repeatU; +texture.repeat.y = cot.texOpts.repeatV; +props[keys[prop]] = texture; +if (prop === 'emission') props['emissive'] = 0xffffff; +} +} +} +} else if ( prop === 'diffuse' || !transparent ) { +if ( prop === 'emission' ) { +props[ 'emissive' ] = cot.color.getHex(); +} else { +props[ prop ] = cot.color.getHex(); +} +} +} +break; +case 'shininess': +props[ prop ] = this[ prop ]; +break; +case 'reflectivity': +props[ prop ] = this[ prop ]; +if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; +props['combine'] = THREE.MixOperation; //mix regular shading with reflective component +break; +case 'index_of_refraction': +props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable +if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; +break; +case 'transparency': +break; +default: +break; +} +} +props[ 'shading' ] = preferredShading; +props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; +if ( props.diffuse !== undefined ) { +props.color = props.diffuse; +delete props.diffuse; +} +switch ( this.type ) { +case 'constant': +if (props.emissive != undefined) props.color = props.emissive; +this.material = new THREE.MeshBasicMaterial( props ); +break; +case 'phong': +case 'blinn': +this.material = new THREE.MeshPhongMaterial( props ); +break; +case 'lambert': +default: +this.material = new THREE.MeshLambertMaterial( props ); +break; +} +return this.material; +}; +function Surface ( effect ) { +this.effect = effect; +this.init_from = null; +this.format = null; +} +Surface.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'init_from': +this.init_from = child.textContent; +break; +case 'format': +this.format = child.textContent; +break; +default: +console.log( "unhandled Surface prop: " + child.nodeName ); +break; +} +} +return this; +}; +function Sampler2D ( effect ) { +this.effect = effect; +this.source = null; +this.wrap_s = null; +this.wrap_t = null; +this.minfilter = null; +this.magfilter = null; +this.mipfilter = null; +} +Sampler2D.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'source': +this.source = child.textContent; +break; +case 'minfilter': +this.minfilter = child.textContent; +break; +case 'magfilter': +this.magfilter = child.textContent; +break; +case 'mipfilter': +this.mipfilter = child.textContent; +break; +case 'wrap_s': +this.wrap_s = child.textContent; +break; +case 'wrap_t': +this.wrap_t = child.textContent; +break; +default: +console.log( "unhandled Sampler2D prop: " + child.nodeName ); +break; +} +} +return this; +}; +function Effect () { +this.id = ""; +this.name = ""; +this.shader = null; +this.surface = {}; +this.sampler = {}; +} +Effect.prototype.create = function () { +if ( this.shader === null ) { +return null; +} +}; +Effect.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +extractDoubleSided( this, element ); +this.shader = null; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'profile_COMMON': +this.parseTechnique( this.parseProfileCOMMON( child ) ); +break; +default: +break; +} +} +return this; +}; +Effect.prototype.parseNewparam = function ( element ) { +var sid = element.getAttribute( 'sid' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'surface': +this.surface[sid] = ( new Surface( this ) ).parse( child ); +break; +case 'sampler2D': +this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +}; +Effect.prototype.parseProfileCOMMON = function ( element ) { +var technique; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'profile_COMMON': +this.parseProfileCOMMON( child ); +break; +case 'technique': +technique = child; +break; +case 'newparam': +this.parseNewparam( child ); +break; +case 'image': +var _image = ( new _Image() ).parse( child ); +images[ _image.id ] = _image; +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +return technique; +}; +Effect.prototype.parseTechnique = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'constant': +case 'lambert': +case 'blinn': +case 'phong': +this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); +break; +case 'extra': +this.parseExtra(child); +break; +default: +break; +} +} +}; +Effect.prototype.parseExtra = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique': +this.parseExtraTechnique( child ); +break; +default: +break; +} +} +}; +Effect.prototype.parseExtraTechnique = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bump': +this.shader.parse( element ); +break; +default: +break; +} +} +}; +function InstanceEffect () { +this.url = ""; +} +InstanceEffect.prototype.parse = function ( element ) { +this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); +return this; +}; +function Animation() { +this.id = ""; +this.name = ""; +this.source = {}; +this.sampler = []; +this.channel = []; +} +Animation.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +this.source = {}; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'animation': +var anim = ( new Animation() ).parse( child ); +for ( var src in anim.source ) { +this.source[ src ] = anim.source[ src ]; +} +for ( var j = 0; j < anim.channel.length; j ++ ) { +this.channel.push( anim.channel[ j ] ); +this.sampler.push( anim.sampler[ j ] ); +} +break; +case 'source': +var src = ( new Source() ).parse( child ); +this.source[ src.id ] = src; +break; +case 'sampler': +this.sampler.push( ( new Sampler( this ) ).parse( child ) ); +break; +case 'channel': +this.channel.push( ( new Channel( this ) ).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +function Channel( animation ) { +this.animation = animation; +this.source = ""; +this.target = ""; +this.fullSid = null; +this.sid = null; +this.dotSyntax = null; +this.arrSyntax = null; +this.arrIndices = null; +this.member = null; +} +Channel.prototype.parse = function ( element ) { +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +this.target = element.getAttribute( 'target' ); +var parts = this.target.split( '/' ); +var id = parts.shift(); +var sid = parts.shift(); +var dotSyntax = ( sid.indexOf(".") >= 0 ); +var arrSyntax = ( sid.indexOf("(") >= 0 ); +if ( dotSyntax ) { +parts = sid.split("."); +this.sid = parts.shift(); +this.member = parts.shift(); +} else if ( arrSyntax ) { +var arrIndices = sid.split("("); +this.sid = arrIndices.shift(); +for (var j = 0; j < arrIndices.length; j ++ ) { +arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); +} +this.arrIndices = arrIndices; +} else { +this.sid = sid; +} +this.fullSid = sid; +this.dotSyntax = dotSyntax; +this.arrSyntax = arrSyntax; +return this; +}; +function Sampler ( animation ) { +this.id = ""; +this.animation = animation; +this.inputs = []; +this.input = null; +this.output = null; +this.strideOut = null; +this.interpolation = null; +this.startTime = null; +this.endTime = null; +this.duration = 0; +} +Sampler.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +this.inputs.push( (new Input()).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +Sampler.prototype.create = function () { +for ( var i = 0; i < this.inputs.length; i ++ ) { +var input = this.inputs[ i ]; +var source = this.animation.source[ input.source ]; +switch ( input.semantic ) { +case 'INPUT': +this.input = source.read(); +break; +case 'OUTPUT': +this.output = source.read(); +this.strideOut = source.accessor.stride; +break; +case 'INTERPOLATION': +this.interpolation = source.read(); +break; +case 'IN_TANGENT': +break; +case 'OUT_TANGENT': +break; +default: +console.log(input.semantic); +break; +} +} +this.startTime = 0; +this.endTime = 0; +this.duration = 0; +if ( this.input.length ) { +this.startTime = 100000000; +this.endTime = -100000000; +for ( var i = 0; i < this.input.length; i ++ ) { +this.startTime = Math.min( this.startTime, this.input[ i ] ); +this.endTime = Math.max( this.endTime, this.input[ i ] ); +} +this.duration = this.endTime - this.startTime; +} +}; +Sampler.prototype.getData = function ( type, ndx, member ) { +var data; +if ( type === 'matrix' && this.strideOut === 16 ) { +data = this.output[ ndx ]; +} else if ( this.strideOut > 1 ) { +data = []; +ndx *= this.strideOut; +for ( var i = 0; i < this.strideOut; ++ i ) { +data[ i ] = this.output[ ndx + i ]; +} +if ( this.strideOut === 3 ) { +switch ( type ) { +case 'rotate': +case 'translate': +fixCoords( data, -1 ); +break; +case 'scale': +fixCoords( data, 1 ); +break; +} +} else if ( this.strideOut === 4 && type === 'matrix' ) { +fixCoords( data, -1 ); +} +} else { +data = this.output[ ndx ]; +if ( member && type === 'translate' ) { +data = getConvertedTranslation( member, data ); +} +} +return data; +}; +function Key ( time ) { +this.targets = []; +this.time = time; +} +Key.prototype.addTarget = function ( fullSid, transform, member, data ) { +this.targets.push( { +sid: fullSid, +member: member, +transform: transform, +data: data +} ); +}; +Key.prototype.apply = function ( opt_sid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +var target = this.targets[ i ]; +if ( !opt_sid || target.sid === opt_sid ) { +target.transform.update( target.data, target.member ); +} +} +}; +Key.prototype.getTarget = function ( fullSid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +if ( this.targets[ i ].sid === fullSid ) { +return this.targets[ i ]; +} +} +return null; +}; +Key.prototype.hasTarget = function ( fullSid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +if ( this.targets[ i ].sid === fullSid ) { +return true; +} +} +return false; +}; +Key.prototype.interpolate = function ( nextKey, time ) { +for ( var i = 0, l = this.targets.length; i < l; i ++ ) { +var target = this.targets[ i ], +nextTarget = nextKey.getTarget( target.sid ), +data; +if ( target.transform.type !== 'matrix' && nextTarget ) { +var scale = ( time - this.time ) / ( nextKey.time - this.time ), +nextData = nextTarget.data, +prevData = target.data; +if ( scale < 0 ) scale = 0; +if ( scale > 1 ) scale = 1; +if ( prevData.length ) { +data = []; +for ( var j = 0; j < prevData.length; ++ j ) { +data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; +} +} else { +data = prevData + ( nextData - prevData ) * scale; +} +} else { +data = target.data; +} +target.transform.update( data, target.member ); +} +}; +function Camera() { +this.id = ""; +this.name = ""; +this.technique = ""; +} +Camera.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'optics': +this.parseOptics( child ); +break; +default: +break; +} +} +return this; +}; +Camera.prototype.parseOptics = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[ i ].nodeName === 'technique_common' ) { +var technique = element.childNodes[ i ]; +for ( var j = 0; j < technique.childNodes.length; j ++ ) { +this.technique = technique.childNodes[ j ].nodeName; +if ( this.technique === 'perspective' ) { +var perspective = technique.childNodes[ j ]; +for ( var k = 0; k < perspective.childNodes.length; k ++ ) { +var param = perspective.childNodes[ k ]; +switch ( param.nodeName ) { +case 'yfov': +this.yfov = param.textContent; +break; +case 'xfov': +this.xfov = param.textContent; +break; +case 'znear': +this.znear = param.textContent; +break; +case 'zfar': +this.zfar = param.textContent; +break; +case 'aspect_ratio': +this.aspect_ratio = param.textContent; +break; +} +} +} else if ( this.technique === 'orthographic' ) { +var orthographic = technique.childNodes[ j ]; +for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { +var param = orthographic.childNodes[ k ]; +switch ( param.nodeName ) { +case 'xmag': +this.xmag = param.textContent; +break; +case 'ymag': +this.ymag = param.textContent; +break; +case 'znear': +this.znear = param.textContent; +break; +case 'zfar': +this.zfar = param.textContent; +break; +case 'aspect_ratio': +this.aspect_ratio = param.textContent; +break; +} +} +} +} +} +} +return this; +}; +function InstanceCamera() { +this.url = ""; +} +InstanceCamera.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +return this; +}; +function Light() { +this.id = ""; +this.name = ""; +this.technique = ""; +} +Light.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique_common': +this.parseCommon( child ); +break; +case 'technique': +this.parseTechnique( child ); +break; +default: +break; +} +} +return this; +}; +Light.prototype.parseCommon = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +switch ( element.childNodes[ i ].nodeName ) { +case 'directional': +case 'point': +case 'spot': +case 'ambient': +this.technique = element.childNodes[ i ].nodeName; +var light = element.childNodes[ i ]; +for ( var j = 0; j < light.childNodes.length; j ++ ) { +var child = light.childNodes[j]; +switch ( child.nodeName ) { +case 'color': +var rgba = _floats( child.textContent ); +this.color = new THREE.Color(0); +this.color.setRGB( rgba[0], rgba[1], rgba[2] ); +this.color.a = rgba[3]; +break; +case 'falloff_angle': +this.falloff_angle = parseFloat( child.textContent ); +break; +case 'quadratic_attenuation': +var f = parseFloat( child.textContent ); +this.distance = f ? Math.sqrt( 1 / f ) : 0; +} +} +} +} +return this; +}; +Light.prototype.parseTechnique = function ( element ) { +this.profile = element.getAttribute( 'profile' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'intensity': +this.intensity = parseFloat(child.textContent); +break; +} +} +return this; +}; +function InstanceLight() { +this.url = ""; +} +InstanceLight.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +return this; +}; +function KinematicsModel( ) { +this.id = ''; +this.name = ''; +this.joints = []; +this.links = []; +} +KinematicsModel.prototype.parse = function( element ) { +this.id = element.getAttribute('id'); +this.name = element.getAttribute('name'); +this.joints = []; +this.links = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique_common': +this.parseCommon(child); +break; +default: +break; +} +} +return this; +}; +KinematicsModel.prototype.parseCommon = function( element ) { +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( element.childNodes[ i ].nodeName ) { +case 'joint': +this.joints.push( (new Joint()).parse(child) ); +break; +case 'link': +this.links.push( (new Link()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function Joint( ) { +this.sid = ''; +this.name = ''; +this.axis = new THREE.Vector3(); +this.limits = { +min: 0, +max: 0 +}; +this.type = ''; +this.static = false; +this.zeroPosition = 0.0; +this.middlePosition = 0.0; +} +Joint.prototype.parse = function( element ) { +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.axis = new THREE.Vector3(); +this.limits = { +min: 0, +max: 0 +}; +this.type = ''; +this.static = false; +this.zeroPosition = 0.0; +this.middlePosition = 0.0; +var axisElement = element.querySelector('axis'); +var _axis = _floats(axisElement.textContent); +this.axis = getConvertedVec3(_axis, 0); +var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; +var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; +this.limits = { +min: min, +max: max +}; +var jointTypes = [ 'prismatic', 'revolute' ]; +for (var i = 0; i < jointTypes.length; i ++ ) { +var type = jointTypes[ i ]; +var jointElement = element.querySelector(type); +if ( jointElement ) { +this.type = type; +} +} +if ( this.limits.min >= this.limits.max ) { +this.static = true; +} +this.middlePosition = (this.limits.min + this.limits.max) / 2.0; +return this; +}; +function Link( ) { +this.sid = ''; +this.name = ''; +this.transforms = []; +this.attachments = []; +} +Link.prototype.parse = function( element ) { +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.transforms = []; +this.attachments = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'attachment_full': +this.attachments.push( (new Attachment()).parse(child) ); +break; +case 'rotate': +case 'translate': +case 'matrix': +this.transforms.push( (new Transform()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function Attachment( ) { +this.joint = ''; +this.transforms = []; +this.links = []; +} +Attachment.prototype.parse = function( element ) { +this.joint = element.getAttribute('joint').split('/').pop(); +this.links = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'link': +this.links.push( (new Link()).parse(child) ); +break; +case 'rotate': +case 'translate': +case 'matrix': +this.transforms.push( (new Transform()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function _source( element ) { +var id = element.getAttribute( 'id' ); +if ( sources[ id ] != undefined ) { +return sources[ id ]; +} +sources[ id ] = ( new Source(id )).parse( element ); +return sources[ id ]; +} +function _nsResolver( nsPrefix ) { +if ( nsPrefix === "dae" ) { +return "http://www.collada.org/2005/11/COLLADASchema"; +} +return null; +} +function _bools( str ) { +var raw = _strings( str ); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); +} +return data; +} +function _floats( str ) { +var raw = _strings(str); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( parseFloat( raw[ i ] ) ); +} +return data; +} +function _ints( str ) { +var raw = _strings( str ); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( parseInt( raw[ i ], 10 ) ); +} +return data; +} +function _strings( str ) { +return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; +} +function _trimString( str ) { +return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); +} +function _attr_as_float( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return parseFloat( element.getAttribute( name ) ); +} else { +return defaultValue; +} +} +function _attr_as_int( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return parseInt( element.getAttribute( name ), 10) ; +} else { +return defaultValue; +} +} +function _attr_as_string( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return element.getAttribute( name ); +} else { +return defaultValue; +} +} +function _format_float( f, num ) { +if ( f === undefined ) { +var s = '0.'; +while ( s.length < num + 2 ) { +s += '0'; +} +return s; +} +num = num || 2; +var parts = f.toString().split( '.' ); +parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; +while ( parts[ 1 ].length < num ) { +parts[ 1 ] += '0'; +} +return parts.join( '.' ); +} +function loadTextureImage ( texture, url ) { +var loader = new THREE.ImageLoader(); +loader.load( url, function ( image ) { +texture.image = image; +texture.needsUpdate = true; +} ); +} +function extractDoubleSided( obj, element ) { +obj.doubleSided = false; +var node = element.querySelectorAll('extra double_sided')[0]; +if ( node ) { +if ( node && parseInt( node.textContent, 10 ) === 1 ) { +obj.doubleSided = true; +} +} +} +function setUpConversion() { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +upConversion = null; +} else { +switch ( colladaUp ) { +case 'X': +upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; +break; +case 'Y': +upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; +break; +case 'Z': +upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; +break; +} +} +} +function fixCoords( data, sign ) { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +return; +} +switch ( upConversion ) { +case 'XtoY': +var tmp = data[ 0 ]; +data[ 0 ] = sign * data[ 1 ]; +data[ 1 ] = tmp; +break; +case 'XtoZ': +var tmp = data[ 2 ]; +data[ 2 ] = data[ 1 ]; +data[ 1 ] = data[ 0 ]; +data[ 0 ] = tmp; +break; +case 'YtoX': +var tmp = data[ 0 ]; +data[ 0 ] = data[ 1 ]; +data[ 1 ] = sign * tmp; +break; +case 'YtoZ': +var tmp = data[ 1 ]; +data[ 1 ] = sign * data[ 2 ]; +data[ 2 ] = tmp; +break; +case 'ZtoX': +var tmp = data[ 0 ]; +data[ 0 ] = data[ 1 ]; +data[ 1 ] = data[ 2 ]; +data[ 2 ] = tmp; +break; +case 'ZtoY': +var tmp = data[ 1 ]; +data[ 1 ] = data[ 2 ]; +data[ 2 ] = sign * tmp; +break; +} +} +function getConvertedTranslation( axis, data ) { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +return data; +} +switch ( axis ) { +case 'X': +data = upConversion === 'XtoY' ? data * -1 : data; +break; +case 'Y': +data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; +break; +case 'Z': +data = upConversion === 'ZtoY' ? data * -1 : data ; +break; +default: +break; +} +return data; +} +function getConvertedVec3( data, offset ) { +var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; +fixCoords( arr, -1 ); +return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); +} +function getConvertedMat4( data ) { +if ( options.convertUpAxis ) { +var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; +fixCoords( arr, -1 ); +data[ 0 ] = arr[ 0 ]; +data[ 4 ] = arr[ 1 ]; +data[ 8 ] = arr[ 2 ]; +arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; +fixCoords( arr, -1 ); +data[ 1 ] = arr[ 0 ]; +data[ 5 ] = arr[ 1 ]; +data[ 9 ] = arr[ 2 ]; +arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; +fixCoords( arr, -1 ); +data[ 2 ] = arr[ 0 ]; +data[ 6 ] = arr[ 1 ]; +data[ 10 ] = arr[ 2 ]; +arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; +fixCoords( arr, -1 ); +data[ 0 ] = arr[ 0 ]; +data[ 1 ] = arr[ 1 ]; +data[ 2 ] = arr[ 2 ]; +arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; +fixCoords( arr, -1 ); +data[ 4 ] = arr[ 0 ]; +data[ 5 ] = arr[ 1 ]; +data[ 6 ] = arr[ 2 ]; +arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; +fixCoords( arr, -1 ); +data[ 8 ] = arr[ 0 ]; +data[ 9 ] = arr[ 1 ]; +data[ 10 ] = arr[ 2 ]; +arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; +fixCoords( arr, -1 ); +data[ 3 ] = arr[ 0 ]; +data[ 7 ] = arr[ 1 ]; +data[ 11 ] = arr[ 2 ]; +} +return new THREE.Matrix4().set( +data[0], data[1], data[2], data[3], +data[4], data[5], data[6], data[7], +data[8], data[9], data[10], data[11], +data[12], data[13], data[14], data[15] +); +} +function getConvertedIndex( index ) { +if ( index > -1 && index < 3 ) { +var members = [ 'X', 'Y', 'Z' ], +indices = { X: 0, Y: 1, Z: 2 }; +index = getConvertedMember( members[ index ] ); +index = indices[ index ]; +} +return index; +} +function getConvertedMember( member ) { +if ( options.convertUpAxis ) { +switch ( member ) { +case 'X': +switch ( upConversion ) { +case 'XtoY': +case 'XtoZ': +case 'YtoX': +member = 'Y'; +break; +case 'ZtoX': +member = 'Z'; +break; +} +break; +case 'Y': +switch ( upConversion ) { +case 'XtoY': +case 'YtoX': +case 'ZtoX': +member = 'X'; +break; +case 'XtoZ': +case 'YtoZ': +case 'ZtoY': +member = 'Z'; +break; +} +break; +case 'Z': +switch ( upConversion ) { +case 'XtoZ': +member = 'X'; +break; +case 'YtoZ': +case 'ZtoX': +case 'ZtoY': +member = 'Y'; +break; +} +break; +} +} +return member; +} +return { +load: load, +parse: parse, +setPreferredShading: setPreferredShading, +applySkin: applySkin, +geometries : geometries, +options: options +}; +}; +THREE.GLTF2Loader = ( function () { +function GLTF2Loader( manager ) { +this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +} +GLTF2Loader.prototype = { +constructor: GLTF2Loader, +load: function ( url, onLoad, onProgress, onError ) { +var scope = this; +var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); +var loader = new THREE.FileLoader( scope.manager ); +loader.setResponseType( 'arraybuffer' ); +loader.load( url, function ( data ) { +scope.parse( data, onLoad, path ); +}, onProgress, onError ); +}, +setCrossOrigin: function ( value ) { +this.crossOrigin = value; +}, +setPath: function ( value ) { +this.path = value; +}, +parse: function ( data, callback, path ) { +var content; +var extensions = {}; +var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); +if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { +extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); +content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; +} else { +content = convertUint8ArrayToString( new Uint8Array( data ) ); +} +var json = JSON.parse( content ); +if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { +extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); +} +console.time( 'GLTF2Loader' ); +var parser = new GLTFParser( json, extensions, { +path: path || this.path, +crossOrigin: this.crossOrigin +} ); +parser.parse( function ( scene, scenes, cameras, animations ) { +console.timeEnd( 'GLTF2Loader' ); +var glTF = { +"scene": scene, +"scenes": scenes, +"cameras": cameras, +"animations": animations +}; +callback( glTF ); +} ); +} +}; +function GLTFRegistry() { +var objects = {}; +return { +get: function ( key ) { +return objects[ key ]; +}, +add: function ( key, object ) { +objects[ key ] = object; +}, +remove: function ( key ) { +delete objects[ key ]; +}, +removeAll: function () { +objects = {}; +}, +update: function ( scene, camera ) { +for ( var name in objects ) { +var object = objects[ name ]; +if ( object.update ) { +object.update( scene, camera ); +} +} +} +}; +} +function GLTFShader( targetNode, allNodes ) { +var boundUniforms = {}; +var uniforms = targetNode.material.uniforms; +for ( var uniformId in uniforms ) { +var uniform = uniforms[ uniformId ]; +if ( uniform.semantic ) { +var sourceNodeRef = uniform.node; +var sourceNode = targetNode; +if ( sourceNodeRef ) { +sourceNode = allNodes[ sourceNodeRef ]; +} +boundUniforms[ uniformId ] = { +semantic: uniform.semantic, +sourceNode: sourceNode, +targetNode: targetNode, +uniform: uniform +}; +} +} +this.boundUniforms = boundUniforms; +this._m4 = new THREE.Matrix4(); +} +GLTFShader.prototype.update = function ( scene, camera ) { +var boundUniforms = this.boundUniforms; +for ( var name in boundUniforms ) { +var boundUniform = boundUniforms[ name ]; +switch ( boundUniform.semantic ) { +case "MODELVIEW": +var m4 = boundUniform.uniform.value; +m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +break; +case "MODELVIEWINVERSETRANSPOSE": +var m3 = boundUniform.uniform.value; +this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +m3.getNormalMatrix( this._m4 ); +break; +case "PROJECTION": +var m4 = boundUniform.uniform.value; +m4.copy( camera.projectionMatrix ); +break; +case "JOINTMATRIX": +var m4v = boundUniform.uniform.value; +for ( var mi = 0; mi < m4v.length; mi ++ ) { +m4v[ mi ] +.getInverse( boundUniform.sourceNode.matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) +.multiply( boundUniform.targetNode.bindMatrix ); +} +break; +default : +console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); +break; +} +} +}; +var EXTENSIONS = { +KHR_BINARY_GLTF: 'KHR_binary_glTF', +KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; +function GLTFMaterialsCommonExtension( json ) { +this.name = EXTENSIONS.KHR_MATERIALS_COMMON; +this.lights = {}; +var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; +var lights = extension.lights || {}; +for ( var lightId in lights ) { +var light = lights[ lightId ]; +var lightNode; +var lightParams = light[ light.type ]; +var color = new THREE.Color().fromArray( lightParams.color ); +switch ( light.type ) { +case "directional": +lightNode = new THREE.DirectionalLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "point": +lightNode = new THREE.PointLight( color ); +break; +case "spot": +lightNode = new THREE.SpotLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "ambient": +lightNode = new THREE.AmbientLight( color ); +break; +} +if ( lightNode ) { +this.lights[ lightId ] = lightNode; +} +} +} +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; +var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; +var BINARY_EXTENSION_HEADER_LENGTH = 12; +var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; +function GLTFBinaryExtension( data ) { +this.name = EXTENSIONS.KHR_BINARY_GLTF; +this.content = null; +this.body = null; +var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); +this.header = { +magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), +version: headerView.getUint32( 4, true ), +length: headerView.getUint32( 8, true ) +}; +if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { +throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); +} else if ( this.header.version < 2.0 ) { +throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); +} +var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); +var chunkIndex = 0; +while ( chunkIndex < chunkView.byteLength ) { +var chunkLength = chunkView.getUint32( chunkIndex, true ); +chunkIndex += 4; +var chunkType = chunkView.getUint32( chunkIndex, true ); +chunkIndex += 4; +if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { +var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); +this.content = convertUint8ArrayToString( contentArray ); +} else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { +var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; +this.body = data.slice( byteOffset, byteOffset + chunkLength ); +} +chunkIndex += chunkLength; +} +if ( this.content === null ) { +throw new Error( 'GLTF2Loader: JSON content not found.' ); +} +} +var WEBGL_CONSTANTS = { +FLOAT: 5126, +FLOAT_MAT3: 35675, +FLOAT_MAT4: 35676, +FLOAT_VEC2: 35664, +FLOAT_VEC3: 35665, +FLOAT_VEC4: 35666, +LINEAR: 9729, +REPEAT: 10497, +SAMPLER_2D: 35678, +TRIANGLES: 4, +LINES: 1, +UNSIGNED_BYTE: 5121, +UNSIGNED_SHORT: 5123, +VERTEX_SHADER: 35633, +FRAGMENT_SHADER: 35632 +}; +var WEBGL_TYPE = { +5126: Number, +35675: THREE.Matrix3, +35676: THREE.Matrix4, +35664: THREE.Vector2, +35665: THREE.Vector3, +35666: THREE.Vector4, +35678: THREE.Texture +}; +var WEBGL_COMPONENT_TYPES = { +5120: Int8Array, +5121: Uint8Array, +5122: Int16Array, +5123: Uint16Array, +5125: Uint32Array, +5126: Float32Array +}; +var WEBGL_FILTERS = { +9728: THREE.NearestFilter, +9729: THREE.LinearFilter, +9984: THREE.NearestMipMapNearestFilter, +9985: THREE.LinearMipMapNearestFilter, +9986: THREE.NearestMipMapLinearFilter, +9987: THREE.LinearMipMapLinearFilter +}; +var WEBGL_WRAPPINGS = { +33071: THREE.ClampToEdgeWrapping, +33648: THREE.MirroredRepeatWrapping, +10497: THREE.RepeatWrapping +}; +var WEBGL_TEXTURE_FORMATS = { +6406: THREE.AlphaFormat, +6407: THREE.RGBFormat, +6408: THREE.RGBAFormat, +6409: THREE.LuminanceFormat, +6410: THREE.LuminanceAlphaFormat +}; +var WEBGL_TEXTURE_DATATYPES = { +5121: THREE.UnsignedByteType, +32819: THREE.UnsignedShort4444Type, +32820: THREE.UnsignedShort5551Type, +33635: THREE.UnsignedShort565Type +}; +var WEBGL_SIDES = { +1028: THREE.BackSide, // Culling front +1029: THREE.FrontSide // Culling back +}; +var WEBGL_DEPTH_FUNCS = { +512: THREE.NeverDepth, +513: THREE.LessDepth, +514: THREE.EqualDepth, +515: THREE.LessEqualDepth, +516: THREE.GreaterEqualDepth, +517: THREE.NotEqualDepth, +518: THREE.GreaterEqualDepth, +519: THREE.AlwaysDepth +}; +var WEBGL_BLEND_EQUATIONS = { +32774: THREE.AddEquation, +32778: THREE.SubtractEquation, +32779: THREE.ReverseSubtractEquation +}; +var WEBGL_BLEND_FUNCS = { +0: THREE.ZeroFactor, +1: THREE.OneFactor, +768: THREE.SrcColorFactor, +769: THREE.OneMinusSrcColorFactor, +770: THREE.SrcAlphaFactor, +771: THREE.OneMinusSrcAlphaFactor, +772: THREE.DstAlphaFactor, +773: THREE.OneMinusDstAlphaFactor, +774: THREE.DstColorFactor, +775: THREE.OneMinusDstColorFactor, +776: THREE.SrcAlphaSaturateFactor +}; +var WEBGL_TYPE_SIZES = { +'SCALAR': 1, +'VEC2': 2, +'VEC3': 3, +'VEC4': 4, +'MAT2': 4, +'MAT3': 9, +'MAT4': 16 +}; +var PATH_PROPERTIES = { +scale: 'scale', +translation: 'position', +rotation: 'quaternion' +}; +var INTERPOLATION = { +LINEAR: THREE.InterpolateLinear, +STEP: THREE.InterpolateDiscrete +}; +var STATES_ENABLES = { +2884: 'CULL_FACE', +2929: 'DEPTH_TEST', +3042: 'BLEND', +3089: 'SCISSOR_TEST', +32823: 'POLYGON_OFFSET_FILL', +32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; +function _each( object, callback, thisObj ) { +if ( !object ) { +return Promise.resolve(); +} +var results; +var fns = []; +if ( Object.prototype.toString.call( object ) === '[object Array]' ) { +results = []; +var length = object.length; +for ( var idx = 0; idx < length; idx ++ ) { +var value = callback.call( thisObj || this, object[ idx ], idx ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, idx )); +} else { +results[ idx ] = value; +} +} +} +} else { +results = {}; +for ( var key in object ) { +if ( object.hasOwnProperty( key ) ) { +var value = callback.call( thisObj || this, object[ key ], key ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, key )); +} else { +results[ key ] = value; +} +} +} +} +} +return Promise.all( fns ).then( function() { +return results; +}); +} +function resolveURL( url, path ) { +if ( typeof url !== 'string' || url === '' ) +return ''; +if ( /^(https?:)?\/\//i.test( url ) ) { +return url; +} +if ( /^data:.*,.*$/i.test( url ) ) { +return url; +} +if ( /^blob:.*$/i.test( url ) ) { +return url; +} +return ( path || '' ) + url; +} +function convertUint8ArrayToString( array ) { +var s = ''; +for ( var i = 0; i < array.length; i ++ ) { +s += String.fromCharCode( array[ i ] ); +} +return s; +} +function replaceTHREEShaderAttributes( shaderText, technique ) { +var attributes = {}; +for ( var attributeId in technique.attributes ) { +var pname = technique.attributes[ attributeId ]; +var param = technique.parameters[ pname ]; +var atype = param.type; +var semantic = param.semantic; +attributes[ attributeId ] = { +type: atype, +semantic: semantic +}; +} +var shaderParams = technique.parameters; +var shaderAttributes = technique.attributes; +var params = {}; +for ( var attributeId in attributes ) { +var pname = shaderAttributes[ attributeId ]; +var shaderParam = shaderParams[ pname ]; +var semantic = shaderParam.semantic; +if ( semantic ) { +params[ attributeId ] = shaderParam; +} +} +for ( var pname in params ) { +var param = params[ pname ]; +var semantic = param.semantic; +var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); +switch ( semantic ) { +case 'POSITION': +shaderText = shaderText.replace( regEx, 'position' ); +break; +case 'NORMAL': +shaderText = shaderText.replace( regEx, 'normal' ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +shaderText = shaderText.replace( regEx, 'uv' ); +break; +case 'TEXCOORD_1': +shaderText = shaderText.replace( regEx, 'uv2' ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +shaderText = shaderText.replace( regEx, 'color' ); +break; +case 'WEIGHTS_0': +case 'WEIGHT': // WEIGHT semantic deprecated. +shaderText = shaderText.replace( regEx, 'skinWeight' ); +break; +case 'JOINTS_0': +case 'JOINT': // JOINT semantic deprecated. +shaderText = shaderText.replace( regEx, 'skinIndex' ); +break; +} +} +return shaderText; +} +function createDefaultMaterial() { +return new THREE.MeshPhongMaterial( { +color: 0x00000, +emissive: 0x888888, +specular: 0x000000, +shininess: 0, +transparent: false, +depthTest: true, +side: THREE.FrontSide +} ); +} +function DeferredShaderMaterial( params ) { +this.isDeferredShaderMaterial = true; +this.params = params; +} +DeferredShaderMaterial.prototype.create = function () { +var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); +for ( var uniformId in this.params.uniforms ) { +var originalUniform = this.params.uniforms[ uniformId ]; +if ( originalUniform.value instanceof THREE.Texture ) { +uniforms[ uniformId ].value = originalUniform.value; +uniforms[ uniformId ].value.needsUpdate = true; +} +uniforms[ uniformId ].semantic = originalUniform.semantic; +uniforms[ uniformId ].node = originalUniform.node; +} +this.params.uniforms = uniforms; +return new THREE.RawShaderMaterial( this.params ); +}; +function GLTFParser( json, extensions, options ) { +this.json = json || {}; +this.extensions = extensions || {}; +this.options = options || {}; +this.cache = new GLTFRegistry(); +} +GLTFParser.prototype._withDependencies = function ( dependencies ) { +var _dependencies = {}; +for ( var i = 0; i < dependencies.length; i ++ ) { +var dependency = dependencies[ i ]; +var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); +var cached = this.cache.get( dependency ); +if ( cached !== undefined ) { +_dependencies[ dependency ] = cached; +} else if ( this[ fnName ] ) { +var fn = this[ fnName ](); +this.cache.add( dependency, fn ); +_dependencies[ dependency ] = fn; +} +} +return _each( _dependencies, function ( dependency ) { +return dependency; +} ); +}; +GLTFParser.prototype.parse = function ( callback ) { +var json = this.json; +this.cache.removeAll(); +this._withDependencies( [ +"scenes", +"cameras", +"animations" +] ).then( function ( dependencies ) { +var scenes = []; +for ( var name in dependencies.scenes ) { +scenes.push( dependencies.scenes[ name ] ); +} +var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; +var cameras = []; +for ( var name in dependencies.cameras ) { +var camera = dependencies.cameras[ name ]; +cameras.push( camera ); +} +var animations = []; +for ( var name in dependencies.animations ) { +animations.push( dependencies.animations[ name ] ); +} +callback( scene, scenes, cameras, animations ); +} ); +}; +GLTFParser.prototype.loadShaders = function () { +var json = this.json; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.shaders, function ( shader ) { +if ( shader.bufferView !== undefined ) { +var bufferView = dependencies.bufferViews[ shader.bufferView ]; +var array = new Uint8Array( bufferView ); +return convertUint8ArrayToString( array ); +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'text' ); +loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { +resolve( shaderText ); +} ); +} ); +} ); +} ); +}; +GLTFParser.prototype.loadBuffers = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return _each( json.buffers, function ( buffer, name ) { +if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { +if ( buffer.uri === undefined && name === 0 ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'arraybuffer' ); +loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { +resolve( buffer ); +} ); +} ); +} else { +console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); +} +} ); +}; +GLTFParser.prototype.loadBufferViews = function () { +var json = this.json; +return this._withDependencies( [ +"buffers" +] ).then( function ( dependencies ) { +return _each( json.bufferViews, function ( bufferView ) { +var arraybuffer = dependencies.buffers[ bufferView.buffer ]; +var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; +return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); +} ); +} ); +}; +GLTFParser.prototype.loadAccessors = function () { +var json = this.json; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.accessors, function ( accessor ) { +var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; +var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; +var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; +var elementBytes = TypedArray.BYTES_PER_ELEMENT; +var itemBytes = elementBytes * itemSize; +var array; +if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { +array = new TypedArray( arraybuffer ); +var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); +return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); +} else { +array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); +return new THREE.BufferAttribute( array, itemSize ); +} +} ); +} ); +}; +GLTFParser.prototype.loadTextures = function () { +var json = this.json; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.textures, function ( texture ) { +if ( texture.source !== undefined ) { +return new Promise( function ( resolve ) { +var source = json.images[ texture.source ]; +var sourceUri = source.uri; +var urlCreator; +if ( source.bufferView !== undefined ) { +var bufferView = dependencies.bufferViews[ source.bufferView ]; +var blob = new Blob( [ bufferView ], { type: source.mimeType } ); +urlCreator = window.URL || window.webkitURL; +sourceUri = urlCreator.createObjectURL( blob ); +} +var textureLoader = THREE.Loader.Handlers.get( sourceUri ); +if ( textureLoader === null ) { +textureLoader = new THREE.TextureLoader(); +} +textureLoader.setCrossOrigin( options.crossOrigin ); +textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { +if ( urlCreator !== undefined ) { +urlCreator.revokeObjectURL( sourceUri ); +} +_texture.flipY = false; +if ( texture.name !== undefined ) _texture.name = texture.name; +_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; +if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { +console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + +'internalFormat will be forced to be the same value as format.' ); +} +_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; +if ( texture.sampler !== undefined ) { +var sampler = json.samplers[ texture.sampler ]; +_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; +_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; +_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; +_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; +} +resolve( _texture ); +}, undefined, function () { +resolve(); +} ); +} ); +} +} ); +} ); +}; +GLTFParser.prototype.loadMaterials = function () { +var json = this.json; +return this._withDependencies( [ +"shaders", +"textures" +] ).then( function ( dependencies ) { +return _each( json.materials, function ( material ) { +var materialType; +var materialValues = {}; +var materialParams = {}; +var khr_material; +if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { +khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; +} +if ( khr_material ) { +var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; +switch ( khr_material.technique ) { +case 'BLINN' : +case 'PHONG' : +materialType = THREE.MeshPhongMaterial; +keys.push( 'diffuse', 'specular', 'shininess' ); +break; +case 'LAMBERT' : +materialType = THREE.MeshLambertMaterial; +keys.push( 'diffuse' ); +break; +case 'CONSTANT' : +default : +materialType = THREE.MeshBasicMaterial; +break; +} +keys.forEach( function( v ) { +if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; +} ); +if ( khr_material.doubleSided || materialValues.doubleSided ) { +materialParams.side = THREE.DoubleSide; +} +if ( khr_material.transparent || materialValues.transparent ) { +materialParams.transparent = true; +materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; +} +} else if ( material.technique === undefined ) { +if ( material.pbrMetallicRoughness !== undefined ) { +materialType = THREE.MeshStandardMaterial; +if ( material.pbrMetallicRoughness !== undefined ) { +var metallicRoughness = material.pbrMetallicRoughness; +materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); +materialParams.opacity = 1.0; +if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { +var array = metallicRoughness.baseColorFactor; +materialParams.color.fromArray( array ); +materialParams.opacity = array[ 3 ]; +} +if ( metallicRoughness.baseColorTexture !== undefined ) { +materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; +} +if ( materialParams.opacity < 1.0 || +( materialParams.map !== undefined && +( materialParams.map.format === THREE.AlphaFormat || +materialParams.map.format === THREE.RGBAFormat || +materialParams.map.format === THREE.LuminanceAlphaFormat ) ) ) { +materialParams.transparent = true; +} +materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; +materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; +if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { +var textureIndex = metallicRoughness.metallicRoughnessTexture.index; +materialParams.metalnessMap = dependencies.textures[ textureIndex ]; +materialParams.roughnessMap = dependencies.textures[ textureIndex ]; +} +} +} else { +materialType = THREE.MeshPhongMaterial; +} +if ( material.normalTexture !== undefined ) { +materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; +} +if ( material.occlusionTexture !== undefined ) { +materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; +} +if ( material.emissiveTexture !== undefined ) { +materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; +} +materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); +if ( material.emissiveFactor !== undefined ) { +materialParams.emissive.fromArray( material.emissiveFactor ); +} +Object.assign( materialValues, material.values ); +} else { +materialType = DeferredShaderMaterial; +var technique = json.techniques[ material.technique ]; +materialParams.uniforms = {}; +var program = json.programs[ technique.program ]; +if ( program ) { +materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; +if ( ! materialParams.fragmentShader ) { +console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); +materialType = THREE.MeshPhongMaterial; +} +var vertexShader = dependencies.shaders[ program.vertexShader ]; +if ( ! vertexShader ) { +console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); +materialType = THREE.MeshPhongMaterial; +} +materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); +var uniforms = technique.uniforms; +for ( var uniformId in uniforms ) { +var pname = uniforms[ uniformId ]; +var shaderParam = technique.parameters[ pname ]; +var ptype = shaderParam.type; +if ( WEBGL_TYPE[ ptype ] ) { +var pcount = shaderParam.count; +var value; +if ( material.values !== undefined ) value = material.values[ pname ]; +var uvalue = new WEBGL_TYPE[ ptype ](); +var usemantic = shaderParam.semantic; +var unode = shaderParam.node; +switch ( ptype ) { +case WEBGL_CONSTANTS.FLOAT: +uvalue = shaderParam.value; +if ( pname == "transparency" ) { +materialParams.transparent = true; +} +if ( value !== undefined ) { +uvalue = value; +} +break; +case WEBGL_CONSTANTS.FLOAT_VEC2: +case WEBGL_CONSTANTS.FLOAT_VEC3: +case WEBGL_CONSTANTS.FLOAT_VEC4: +case WEBGL_CONSTANTS.FLOAT_MAT3: +if ( shaderParam && shaderParam.value ) { +uvalue.fromArray( shaderParam.value ); +} +if ( value ) { +uvalue.fromArray( value ); +} +break; +case WEBGL_CONSTANTS.FLOAT_MAT2: +console.warn( "FLOAT_MAT2 is not a supported uniform type" ); +break; +case WEBGL_CONSTANTS.FLOAT_MAT4: +if ( pcount ) { +uvalue = new Array( pcount ); +for ( var mi = 0; mi < pcount; mi ++ ) { +uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); +} +if ( shaderParam && shaderParam.value ) { +var m4v = shaderParam.value; +uvalue.fromArray( m4v ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} else { +if ( shaderParam && shaderParam.value ) { +var m4 = shaderParam.value; +uvalue.fromArray( m4 ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} +break; +case WEBGL_CONSTANTS.SAMPLER_2D: +if ( value !== undefined ) { +uvalue = dependencies.textures[ value ]; +} else if ( shaderParam.value !== undefined ) { +uvalue = dependencies.textures[ shaderParam.value ]; +} else { +uvalue = null; +} +break; +} +materialParams.uniforms[ uniformId ] = { +value: uvalue, +semantic: usemantic, +node: unode +}; +} else { +throw new Error( "Unknown shader uniform param type: " + ptype ); +} +} +var states = technique.states || {}; +var enables = states.enable || []; +var functions = states.functions || {}; +var enableCullFace = false; +var enableDepthTest = false; +var enableBlend = false; +for ( var i = 0, il = enables.length; i < il; i ++ ) { +var enable = enables[ i ]; +switch ( STATES_ENABLES[ enable ] ) { +case 'CULL_FACE': +enableCullFace = true; +break; +case 'DEPTH_TEST': +enableDepthTest = true; +break; +case 'BLEND': +enableBlend = true; +break; +case 'SCISSOR_TEST': +case 'POLYGON_OFFSET_FILL': +case 'SAMPLE_ALPHA_TO_COVERAGE': +break; +default: +throw new Error( "Unknown technique.states.enable: " + enable ); +} +} +if ( enableCullFace ) { +materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; +} else { +materialParams.side = THREE.DoubleSide; +} +materialParams.depthTest = enableDepthTest; +materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; +materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; +materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; +materialParams.transparent = enableBlend; +var blendEquationSeparate = functions.blendEquationSeparate; +if ( blendEquationSeparate !== undefined ) { +materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; +materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; +} else { +materialParams.blendEquation = THREE.AddEquation; +materialParams.blendEquationAlpha = THREE.AddEquation; +} +var blendFuncSeparate = functions.blendFuncSeparate; +if ( blendFuncSeparate !== undefined ) { +materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; +materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; +materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; +materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; +} else { +materialParams.blendSrc = THREE.OneFactor; +materialParams.blendDst = THREE.ZeroFactor; +materialParams.blendSrcAlpha = THREE.OneFactor; +materialParams.blendDstAlpha = THREE.ZeroFactor; +} +} +} +if ( Array.isArray( materialValues.diffuse ) ) { +materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); +} else if ( typeof( materialValues.diffuse ) === 'string' ) { +materialParams.map = dependencies.textures[ materialValues.diffuse ]; +} +delete materialParams.diffuse; +if ( typeof( materialValues.reflective ) === 'string' ) { +materialParams.envMap = dependencies.textures[ materialValues.reflective ]; +} +if ( typeof( materialValues.bump ) === 'string' ) { +materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; +} +if ( Array.isArray( materialValues.emission ) ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.color = new THREE.Color().fromArray( materialValues.emission ); +} else { +materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); +} +} else if ( typeof( materialValues.emission ) === 'string' ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.map = dependencies.textures[ materialValues.emission ]; +} else { +materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; +} +} +if ( Array.isArray( materialValues.specular ) ) { +materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); +} else if ( typeof( materialValues.specular ) === 'string' ) { +materialParams.specularMap = dependencies.textures[ materialValues.specular ]; +} +if ( materialValues.shininess !== undefined ) { +materialParams.shininess = materialValues.shininess; +} +var _material = new materialType( materialParams ); +if ( material.name !== undefined ) _material.name = material.name; +return _material; +} ); +} ); +}; +GLTFParser.prototype.loadMeshes = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"materials" +] ).then( function ( dependencies ) { +return _each( json.meshes, function ( mesh ) { +var group = new THREE.Group(); +if ( mesh.name !== undefined ) group.name = mesh.name; +if ( mesh.extras ) group.userData = mesh.extras; +var primitives = mesh.primitives || []; +for ( var name in primitives ) { +var primitive = primitives[ name ]; +var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); +var geometry; +var meshNode; +if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { +geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( attributeEntry === undefined ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'NORMAL': +geometry.addAttribute( 'normal', bufferAttribute ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +geometry.addAttribute( 'uv', bufferAttribute ); +break; +case 'TEXCOORD_1': +geometry.addAttribute( 'uv2', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +case 'WEIGHTS_0': +case 'WEIGHT': // WEIGHT semantic deprecated. +geometry.addAttribute( 'skinWeight', bufferAttribute ); +break; +case 'JOINTS_0': +case 'JOINT': // JOINT semantic deprecated. +geometry.addAttribute( 'skinIndex', bufferAttribute ); +break; +} +} +if ( primitive.indices !== undefined ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +} +meshNode = new THREE.Mesh( geometry, material ); +meshNode.castShadow = true; +} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { +geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +} +} +if ( primitive.indices !== undefined ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +meshNode = new THREE.LineSegments( geometry, material ); +} else { +meshNode = new THREE.Line( geometry, material ); +} +} else { +throw new Error( "Only triangular and line primitives are supported" ); +} +if ( geometry.attributes.color !== undefined ) { +material.vertexColors = THREE.VertexColors; +material.needsUpdate = true; +} +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} +return group; +} ); +} ); +}; +GLTFParser.prototype.loadCameras = function () { +var json = this.json; +return _each( json.cameras, function ( camera ) { +if ( camera.type == "perspective" && camera.perspective ) { +var yfov = camera.perspective.yfov; +var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; +var xfov = yfov * aspectRatio; +var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} else if ( camera.type == "orthographic" && camera.orthographic ) { +var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} +} ); +}; +GLTFParser.prototype.loadSkins = function () { +var json = this.json; +return this._withDependencies( [ +"accessors" +] ).then( function ( dependencies ) { +return _each( json.skins, function ( skin ) { +var bindShapeMatrix = new THREE.Matrix4(); +if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); +var _skin = { +bindShapeMatrix: bindShapeMatrix, +jointNames: skin.jointNames, +inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] +}; +return _skin; +} ); +} ); +}; +GLTFParser.prototype.loadAnimations = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"nodes" +] ).then( function ( dependencies ) { +return _each( json.animations, function ( animation, animationId ) { +var tracks = []; +for ( var channelId in animation.channels ) { +var channel = animation.channels[ channelId ]; +var sampler = animation.samplers[ channel.sampler ]; +if ( sampler ) { +var target = channel.target; +var name = target.node || target.id; // NOTE: target.id is deprecated. +var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; +var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; +var inputAccessor = dependencies.accessors[ input ]; +var outputAccessor = dependencies.accessors[ output ]; +var node = dependencies.nodes[ name ]; +if ( node ) { +node.updateMatrix(); +node.matrixAutoUpdate = true; +var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation +? THREE.QuaternionKeyframeTrack +: THREE.VectorKeyframeTrack; +var targetName = node.name ? node.name : node.uuid; +var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; +tracks.push( new TypedKeyframeTrack( +targetName + '.' + PATH_PROPERTIES[ target.path ], +THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), +THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), +interpolation +) ); +} +} +} +var name = animation.name !== undefined ? animation.name : "animation_" + animationId; +return new THREE.AnimationClip( name, undefined, tracks ); +} ); +} ); +}; +GLTFParser.prototype.loadNodes = function () { +var json = this.json; +var extensions = this.extensions; +var scope = this; +return _each( json.nodes, function ( node ) { +var matrix = new THREE.Matrix4(); +var _node; +if ( node.jointName ) { +_node = new THREE.Bone(); +_node.name = node.name !== undefined ? node.name : node.jointName; +_node.jointName = node.jointName; +} else { +_node = new THREE.Object3D(); +if ( node.name !== undefined ) _node.name = node.name; +} +if ( node.extras ) _node.userData = node.extras; +if ( node.matrix !== undefined ) { +matrix.fromArray( node.matrix ); +_node.applyMatrix( matrix ); +} else { +if ( node.translation !== undefined ) { +_node.position.fromArray( node.translation ); +} +if ( node.rotation !== undefined ) { +_node.quaternion.fromArray( node.rotation ); +} +if ( node.scale !== undefined ) { +_node.scale.fromArray( node.scale ); +} +} +return _node; +} ).then( function ( __nodes ) { +return scope._withDependencies( [ +"meshes", +"skins", +"cameras" +] ).then( function ( dependencies ) { +return _each( __nodes, function ( _node, nodeId ) { +var node = json.nodes[ nodeId ]; +var meshes; +if ( node.mesh !== undefined) { +meshes = [ node.mesh ]; +} else if ( node.meshes !== undefined ) { +console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); +meshes = node.meshes; +} +if ( meshes !== undefined ) { +for ( var meshId in meshes ) { +var mesh = meshes[ meshId ]; +var group = dependencies.meshes[ mesh ]; +if ( group === undefined ) { +console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); +continue; +} +for ( var childrenId in group.children ) { +var child = group.children[ childrenId ]; +var originalMaterial = child.material; +var originalGeometry = child.geometry; +var originalUserData = child.userData; +var originalName = child.name; +var material; +if ( originalMaterial.isDeferredShaderMaterial ) { +originalMaterial = material = originalMaterial.create(); +} else { +material = originalMaterial; +} +switch ( child.type ) { +case 'LineSegments': +child = new THREE.LineSegments( originalGeometry, material ); +break; +case 'LineLoop': +child = new THREE.LineLoop( originalGeometry, material ); +break; +case 'Line': +child = new THREE.Line( originalGeometry, material ); +break; +default: +child = new THREE.Mesh( originalGeometry, material ); +} +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var skinEntry; +if ( node.skin !== undefined ) { +skinEntry = dependencies.skins[ node.skin ]; +} +if ( skinEntry ) { +var getJointNode = function ( jointId ) { +var keys = Object.keys( __nodes ); +for ( var i = 0, il = keys.length; i < il; i ++ ) { +var n = __nodes[ keys[ i ] ]; +if ( n.jointName === jointId ) return n; +} +return null; +}; +var geometry = originalGeometry; +var material = originalMaterial; +material.skinning = true; +child = new THREE.SkinnedMesh( geometry, material, false ); +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var bones = []; +var boneInverses = []; +for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { +var jointId = skinEntry.jointNames[ i ]; +var jointNode = getJointNode( jointId ); +if ( jointNode ) { +bones.push( jointNode ); +var m = skinEntry.inverseBindMatrices.array; +var mat = new THREE.Matrix4().fromArray( m, i * 16 ); +boneInverses.push( mat ); +} else { +console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); +} +} +child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); +var buildBoneGraph = function ( parentJson, parentObject, property ) { +var children = parentJson[ property ]; +if ( children === undefined ) return; +for ( var i = 0, il = children.length; i < il; i ++ ) { +var nodeId = children[ i ]; +var bone = __nodes[ nodeId ]; +var boneJson = json.nodes[ nodeId ]; +if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { +parentObject.add( bone ); +buildBoneGraph( boneJson, bone, 'children' ); +} +} +}; +buildBoneGraph( node, child, 'skeletons' ); +} +_node.add( child ); +} +} +} +if ( node.camera !== undefined ) { +var camera = dependencies.cameras[ node.camera ]; +_node.add( camera ); +} +if ( node.extensions +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { +var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; +var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; +_node.add( light ); +} +return _node; +} ); +} ); +} ); +}; +GLTFParser.prototype.loadScenes = function () { +var json = this.json; +function buildNodeHierachy( nodeId, parentObject, allNodes ) { +var _node = allNodes[ nodeId ]; +parentObject.add( _node ); +var node = json.nodes[ nodeId ]; +if ( node.children ) { +var children = node.children; +for ( var i = 0, l = children.length; i < l; i ++ ) { +var child = children[ i ]; +buildNodeHierachy( child, _node, allNodes ); +} +} +} +return this._withDependencies( [ +"nodes" +] ).then( function ( dependencies ) { +return _each( json.scenes, function ( scene ) { +var _scene = new THREE.Scene(); +if ( scene.name !== undefined ) _scene.name = scene.name; +if ( scene.extras ) _scene.userData = scene.extras; +var nodes = scene.nodes || []; +for ( var i = 0, l = nodes.length; i < l; i ++ ) { +var nodeId = nodes[ i ]; +buildNodeHierachy( nodeId, _scene, dependencies.nodes ); +} +_scene.traverse( function ( child ) { +if ( child.material && child.material.isRawShaderMaterial ) { +child.gltfShader = new GLTFShader( child, dependencies.nodes ); +child.onBeforeRender = function(renderer, scene, camera){ +this.gltfShader.update(scene, camera); +}; +} +} ); +return _scene; +} ); +} ); +}; +return GLTF2Loader; +} )(); +THREE.GLTFLoader = ( function () { +function GLTFLoader( manager ) { +this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +} +GLTFLoader.prototype = { +constructor: GLTFLoader, +load: function ( url, onLoad, onProgress, onError ) { +var scope = this; +var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); +var loader = new THREE.FileLoader( scope.manager ); +loader.setResponseType( 'arraybuffer' ); +loader.load( url, function ( data ) { +scope.parse( data, onLoad, path ); +}, onProgress, onError ); +}, +setCrossOrigin: function ( value ) { +this.crossOrigin = value; +}, +setPath: function ( value ) { +this.path = value; +}, +parse: function ( data, callback, path ) { +var content; +var extensions = {}; +var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); +if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { +extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); +content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; +} else { +content = convertUint8ArrayToString( new Uint8Array( data ) ); +} +var json = JSON.parse( content ); +if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { +extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); +} +console.time( 'GLTFLoader' ); +var parser = new GLTFParser( json, extensions, { +path: path || this.path, +crossOrigin: this.crossOrigin +} ); +parser.parse( function ( scene, scenes, cameras, animations ) { +console.timeEnd( 'GLTFLoader' ); +var glTF = { +"scene": scene, +"scenes": scenes, +"cameras": cameras, +"animations": animations +}; +callback( glTF ); +} ); +} +}; +function GLTFRegistry() { +var objects = {}; +return { +get: function ( key ) { +return objects[ key ]; +}, +add: function ( key, object ) { +objects[ key ] = object; +}, +remove: function ( key ) { +delete objects[ key ]; +}, +removeAll: function () { +objects = {}; +}, +update: function ( scene, camera ) { +for ( var name in objects ) { +var object = objects[ name ]; +if ( object.update ) { +object.update( scene, camera ); +} +} +} +}; +} +GLTFLoader.Shaders = { +update: function () { +console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); +} +}; +function GLTFShader( targetNode, allNodes ) { +var boundUniforms = {}; +var uniforms = targetNode.material.uniforms; +for ( var uniformId in uniforms ) { +var uniform = uniforms[ uniformId ]; +if ( uniform.semantic ) { +var sourceNodeRef = uniform.node; +var sourceNode = targetNode; +if ( sourceNodeRef ) { +sourceNode = allNodes[ sourceNodeRef ]; +} +boundUniforms[ uniformId ] = { +semantic: uniform.semantic, +sourceNode: sourceNode, +targetNode: targetNode, +uniform: uniform +}; +} +} +this.boundUniforms = boundUniforms; +this._m4 = new THREE.Matrix4(); +} +GLTFShader.prototype.update = function ( scene, camera ) { +var boundUniforms = this.boundUniforms; +for ( var name in boundUniforms ) { +var boundUniform = boundUniforms[ name ]; +switch ( boundUniform.semantic ) { +case "MODELVIEW": +var m4 = boundUniform.uniform.value; +m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +break; +case "MODELVIEWINVERSETRANSPOSE": +var m3 = boundUniform.uniform.value; +this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +m3.getNormalMatrix( this._m4 ); +break; +case "PROJECTION": +var m4 = boundUniform.uniform.value; +m4.copy( camera.projectionMatrix ); +break; +case "JOINTMATRIX": +var m4v = boundUniform.uniform.value; +for ( var mi = 0; mi < m4v.length; mi ++ ) { +m4v[ mi ] +.getInverse( boundUniform.sourceNode.matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) +.multiply( boundUniform.targetNode.bindMatrix ); +} +break; +default : +console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); +break; +} +} +}; +GLTFLoader.Animations = { +update: function () { +console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); +} +}; +var EXTENSIONS = { +KHR_BINARY_GLTF: 'KHR_binary_glTF', +KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; +function GLTFMaterialsCommonExtension( json ) { +this.name = EXTENSIONS.KHR_MATERIALS_COMMON; +this.lights = {}; +var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; +var lights = extension.lights || {}; +for ( var lightId in lights ) { +var light = lights[ lightId ]; +var lightNode; +var lightParams = light[ light.type ]; +var color = new THREE.Color().fromArray( lightParams.color ); +switch ( light.type ) { +case "directional": +lightNode = new THREE.DirectionalLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "point": +lightNode = new THREE.PointLight( color ); +break; +case "spot": +lightNode = new THREE.SpotLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "ambient": +lightNode = new THREE.AmbientLight( color ); +break; +} +if ( lightNode ) { +this.lights[ lightId ] = lightNode; +} +} +} +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; +var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; +var BINARY_EXTENSION_HEADER_LENGTH = 20; +function GLTFBinaryExtension( data ) { +this.name = EXTENSIONS.KHR_BINARY_GLTF; +var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); +var header = { +magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), +version: headerView.getUint32( 4, true ), +length: headerView.getUint32( 8, true ), +contentLength: headerView.getUint32( 12, true ), +contentFormat: headerView.getUint32( 16, true ) +}; +for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { +var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; +if ( header[ key ] !== value ) { +throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); +} +} +var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); +this.header = header; +this.content = convertUint8ArrayToString( contentArray ); +this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); +} +GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { +var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; +var array = new Uint8Array( bufferView ); +return convertUint8ArrayToString( array ); +}; +GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) { +var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; +var bufferView = bufferViews[ metadata.bufferView ]; +var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) ); +return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData ); +}; +var WEBGL_CONSTANTS = { +FLOAT: 5126, +FLOAT_MAT3: 35675, +FLOAT_MAT4: 35676, +FLOAT_VEC2: 35664, +FLOAT_VEC3: 35665, +FLOAT_VEC4: 35666, +LINEAR: 9729, +REPEAT: 10497, +SAMPLER_2D: 35678, +TRIANGLES: 4, +LINES: 1, +UNSIGNED_BYTE: 5121, +UNSIGNED_SHORT: 5123, +VERTEX_SHADER: 35633, +FRAGMENT_SHADER: 35632 +}; +var WEBGL_TYPE = { +5126: Number, +35675: THREE.Matrix3, +35676: THREE.Matrix4, +35664: THREE.Vector2, +35665: THREE.Vector3, +35666: THREE.Vector4, +35678: THREE.Texture +}; +var WEBGL_COMPONENT_TYPES = { +5120: Int8Array, +5121: Uint8Array, +5122: Int16Array, +5123: Uint16Array, +5125: Uint32Array, +5126: Float32Array +}; +var WEBGL_FILTERS = { +9728: THREE.NearestFilter, +9729: THREE.LinearFilter, +9984: THREE.NearestMipMapNearestFilter, +9985: THREE.LinearMipMapNearestFilter, +9986: THREE.NearestMipMapLinearFilter, +9987: THREE.LinearMipMapLinearFilter +}; +var WEBGL_WRAPPINGS = { +33071: THREE.ClampToEdgeWrapping, +33648: THREE.MirroredRepeatWrapping, +10497: THREE.RepeatWrapping +}; +var WEBGL_TEXTURE_FORMATS = { +6406: THREE.AlphaFormat, +6407: THREE.RGBFormat, +6408: THREE.RGBAFormat, +6409: THREE.LuminanceFormat, +6410: THREE.LuminanceAlphaFormat +}; +var WEBGL_TEXTURE_DATATYPES = { +5121: THREE.UnsignedByteType, +32819: THREE.UnsignedShort4444Type, +32820: THREE.UnsignedShort5551Type, +33635: THREE.UnsignedShort565Type +}; +var WEBGL_SIDES = { +1028: THREE.BackSide, // Culling front +1029: THREE.FrontSide // Culling back +}; +var WEBGL_DEPTH_FUNCS = { +512: THREE.NeverDepth, +513: THREE.LessDepth, +514: THREE.EqualDepth, +515: THREE.LessEqualDepth, +516: THREE.GreaterEqualDepth, +517: THREE.NotEqualDepth, +518: THREE.GreaterEqualDepth, +519: THREE.AlwaysDepth +}; +var WEBGL_BLEND_EQUATIONS = { +32774: THREE.AddEquation, +32778: THREE.SubtractEquation, +32779: THREE.ReverseSubtractEquation +}; +var WEBGL_BLEND_FUNCS = { +0: THREE.ZeroFactor, +1: THREE.OneFactor, +768: THREE.SrcColorFactor, +769: THREE.OneMinusSrcColorFactor, +770: THREE.SrcAlphaFactor, +771: THREE.OneMinusSrcAlphaFactor, +772: THREE.DstAlphaFactor, +773: THREE.OneMinusDstAlphaFactor, +774: THREE.DstColorFactor, +775: THREE.OneMinusDstColorFactor, +776: THREE.SrcAlphaSaturateFactor +}; +var WEBGL_TYPE_SIZES = { +'SCALAR': 1, +'VEC2': 2, +'VEC3': 3, +'VEC4': 4, +'MAT2': 4, +'MAT3': 9, +'MAT4': 16 +}; +var PATH_PROPERTIES = { +scale: 'scale', +translation: 'position', +rotation: 'quaternion' +}; +var INTERPOLATION = { +LINEAR: THREE.InterpolateLinear, +STEP: THREE.InterpolateDiscrete +}; +var STATES_ENABLES = { +2884: 'CULL_FACE', +2929: 'DEPTH_TEST', +3042: 'BLEND', +3089: 'SCISSOR_TEST', +32823: 'POLYGON_OFFSET_FILL', +32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; +function _each( object, callback, thisObj ) { +if ( !object ) { +return Promise.resolve(); +} +var results; +var fns = []; +if ( Object.prototype.toString.call( object ) === '[object Array]' ) { +results = []; +var length = object.length; +for ( var idx = 0; idx < length; idx ++ ) { +var value = callback.call( thisObj || this, object[ idx ], idx ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, idx )); +} else { +results[ idx ] = value; +} +} +} +} else { +results = {}; +for ( var key in object ) { +if ( object.hasOwnProperty( key ) ) { +var value = callback.call( thisObj || this, object[ key ], key ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, key )); +} else { +results[ key ] = value; +} +} +} +} +} +return Promise.all( fns ).then( function() { +return results; +}); +} +function resolveURL( url, path ) { +if ( typeof url !== 'string' || url === '' ) +return ''; +if ( /^(https?:)?\/\//i.test( url ) ) { +return url; +} +if ( /^data:.*,.*$/i.test( url ) ) { +return url; +} +return ( path || '' ) + url; +} +function convertUint8ArrayToString( array ) { +var s = ''; +for ( var i = 0; i < array.length; i ++ ) { +s += String.fromCharCode( array[ i ] ); +} +return s; +} +function replaceTHREEShaderAttributes( shaderText, technique ) { +var attributes = {}; +for ( var attributeId in technique.attributes ) { +var pname = technique.attributes[ attributeId ]; +var param = technique.parameters[ pname ]; +var atype = param.type; +var semantic = param.semantic; +attributes[ attributeId ] = { +type: atype, +semantic: semantic +}; +} +var shaderParams = technique.parameters; +var shaderAttributes = technique.attributes; +var params = {}; +for ( var attributeId in attributes ) { +var pname = shaderAttributes[ attributeId ]; +var shaderParam = shaderParams[ pname ]; +var semantic = shaderParam.semantic; +if ( semantic ) { +params[ attributeId ] = shaderParam; +} +} +for ( var pname in params ) { +var param = params[ pname ]; +var semantic = param.semantic; +var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); +switch ( semantic ) { +case "POSITION": +shaderText = shaderText.replace( regEx, 'position' ); +break; +case "NORMAL": +shaderText = shaderText.replace( regEx, 'normal' ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +shaderText = shaderText.replace( regEx, 'uv' ); +break; +case 'TEXCOORD_1': +shaderText = shaderText.replace( regEx, 'uv2' ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +shaderText = shaderText.replace( regEx, 'color' ); +break; +case "WEIGHT": +shaderText = shaderText.replace( regEx, 'skinWeight' ); +break; +case "JOINT": +shaderText = shaderText.replace( regEx, 'skinIndex' ); +break; +} +} +return shaderText; +} +function createDefaultMaterial() { +return new THREE.MeshPhongMaterial( { +color: 0x00000, +emissive: 0x888888, +specular: 0x000000, +shininess: 0, +transparent: false, +depthTest: true, +side: THREE.FrontSide +} ); +} +function DeferredShaderMaterial( params ) { +this.isDeferredShaderMaterial = true; +this.params = params; +} +DeferredShaderMaterial.prototype.create = function () { +var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); +for ( var uniformId in this.params.uniforms ) { +var originalUniform = this.params.uniforms[ uniformId ]; +if ( originalUniform.value instanceof THREE.Texture ) { +uniforms[ uniformId ].value = originalUniform.value; +uniforms[ uniformId ].value.needsUpdate = true; +} +uniforms[ uniformId ].semantic = originalUniform.semantic; +uniforms[ uniformId ].node = originalUniform.node; +} +this.params.uniforms = uniforms; +return new THREE.RawShaderMaterial( this.params ); +}; +function GLTFParser( json, extensions, options ) { +this.json = json || {}; +this.extensions = extensions || {}; +this.options = options || {}; +this.cache = new GLTFRegistry(); +} +GLTFParser.prototype._withDependencies = function ( dependencies ) { +var _dependencies = {}; +for ( var i = 0; i < dependencies.length; i ++ ) { +var dependency = dependencies[ i ]; +var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); +var cached = this.cache.get( dependency ); +if ( cached !== undefined ) { +_dependencies[ dependency ] = cached; +} else if ( this[ fnName ] ) { +var fn = this[ fnName ](); +this.cache.add( dependency, fn ); +_dependencies[ dependency ] = fn; +} +} +return _each( _dependencies, function ( dependency ) { +return dependency; +} ); +}; +GLTFParser.prototype.parse = function ( callback ) { +var json = this.json; +this.cache.removeAll(); +this._withDependencies( [ +"scenes", +"cameras", +"animations" +] ).then( function ( dependencies ) { +var scenes = []; +for ( var name in dependencies.scenes ) { +scenes.push( dependencies.scenes[ name ] ); +} +var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; +var cameras = []; +for ( var name in dependencies.cameras ) { +var camera = dependencies.cameras[ name ]; +cameras.push( camera ); +} +var animations = []; +for ( var name in dependencies.animations ) { +animations.push( dependencies.animations[ name ] ); +} +callback( scene, scenes, cameras, animations ); +} ); +}; +GLTFParser.prototype.loadShaders = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.shaders, function ( shader ) { +if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'text' ); +loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { +resolve( shaderText ); +} ); +} ); +} ); +} ); +}; +GLTFParser.prototype.loadBuffers = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return _each( json.buffers, function ( buffer, name ) { +if ( name === BINARY_EXTENSION_BUFFER_NAME ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; +} +if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'arraybuffer' ); +loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { +resolve( buffer ); +} ); +} ); +} else { +console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); +} +} ); +}; +GLTFParser.prototype.loadBufferViews = function () { +var json = this.json; +return this._withDependencies( [ +"buffers" +] ).then( function ( dependencies ) { +return _each( json.bufferViews, function ( bufferView ) { +var arraybuffer = dependencies.buffers[ bufferView.buffer ]; +var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; +return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); +} ); +} ); +}; +GLTFParser.prototype.loadAccessors = function () { +var json = this.json; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.accessors, function ( accessor ) { +var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; +var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; +var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; +var elementBytes = TypedArray.BYTES_PER_ELEMENT; +var itemBytes = elementBytes * itemSize; +if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { +var array = new TypedArray( arraybuffer ); +var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); +return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); +} else { +array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); +return new THREE.BufferAttribute( array, itemSize ); +} +} ); +} ); +}; +GLTFParser.prototype.loadTextures = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.textures, function ( texture ) { +if ( texture.source ) { +return new Promise( function ( resolve ) { +var source = json.images[ texture.source ]; +var sourceUri = source.uri; +if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { +sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); +} +var textureLoader = THREE.Loader.Handlers.get( sourceUri ); +if ( textureLoader === null ) { +textureLoader = new THREE.TextureLoader(); +} +textureLoader.setCrossOrigin( options.crossOrigin ); +textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { +_texture.flipY = false; +if ( texture.name !== undefined ) _texture.name = texture.name; +_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; +if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { +console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + +'internalFormat will be forced to be the same value as format.' ); +} +_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; +if ( texture.sampler ) { +var sampler = json.samplers[ texture.sampler ]; +_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; +_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; +_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; +_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; +} +resolve( _texture ); +}, undefined, function () { +resolve(); +} ); +} ); +} +} ); +} ); +}; +GLTFParser.prototype.loadMaterials = function () { +var json = this.json; +return this._withDependencies( [ +"shaders", +"textures" +] ).then( function ( dependencies ) { +return _each( json.materials, function ( material ) { +var materialType; +var materialValues = {}; +var materialParams = {}; +var khr_material; +if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { +khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; +} +if ( khr_material ) { +var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; +switch ( khr_material.technique ) { +case 'BLINN' : +case 'PHONG' : +materialType = THREE.MeshPhongMaterial; +keys.push( 'diffuse', 'specular', 'shininess' ); +break; +case 'LAMBERT' : +materialType = THREE.MeshLambertMaterial; +keys.push( 'diffuse' ); +break; +case 'CONSTANT' : +default : +materialType = THREE.MeshBasicMaterial; +break; +} +keys.forEach( function( v ) { +if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; +} ); +if ( khr_material.doubleSided || materialValues.doubleSided ) { +materialParams.side = THREE.DoubleSide; +} +if ( khr_material.transparent || materialValues.transparent ) { +materialParams.transparent = true; +materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; +} +} else if ( material.technique === undefined ) { +materialType = THREE.MeshPhongMaterial; +Object.assign( materialValues, material.values ); +} else { +materialType = DeferredShaderMaterial; +var technique = json.techniques[ material.technique ]; +materialParams.uniforms = {}; +var program = json.programs[ technique.program ]; +if ( program ) { +materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; +if ( ! materialParams.fragmentShader ) { +console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); +materialType = THREE.MeshPhongMaterial; +} +var vertexShader = dependencies.shaders[ program.vertexShader ]; +if ( ! vertexShader ) { +console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); +materialType = THREE.MeshPhongMaterial; +} +materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); +var uniforms = technique.uniforms; +for ( var uniformId in uniforms ) { +var pname = uniforms[ uniformId ]; +var shaderParam = technique.parameters[ pname ]; +var ptype = shaderParam.type; +if ( WEBGL_TYPE[ ptype ] ) { +var pcount = shaderParam.count; +var value; +if ( material.values !== undefined ) value = material.values[ pname ]; +var uvalue = new WEBGL_TYPE[ ptype ](); +var usemantic = shaderParam.semantic; +var unode = shaderParam.node; +switch ( ptype ) { +case WEBGL_CONSTANTS.FLOAT: +uvalue = shaderParam.value; +if ( pname == "transparency" ) { +materialParams.transparent = true; +} +if ( value !== undefined ) { +uvalue = value; +} +break; +case WEBGL_CONSTANTS.FLOAT_VEC2: +case WEBGL_CONSTANTS.FLOAT_VEC3: +case WEBGL_CONSTANTS.FLOAT_VEC4: +case WEBGL_CONSTANTS.FLOAT_MAT3: +if ( shaderParam && shaderParam.value ) { +uvalue.fromArray( shaderParam.value ); +} +if ( value ) { +uvalue.fromArray( value ); +} +break; +case WEBGL_CONSTANTS.FLOAT_MAT2: +console.warn( "FLOAT_MAT2 is not a supported uniform type" ); +break; +case WEBGL_CONSTANTS.FLOAT_MAT4: +if ( pcount ) { +uvalue = new Array( pcount ); +for ( var mi = 0; mi < pcount; mi ++ ) { +uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); +} +if ( shaderParam && shaderParam.value ) { +var m4v = shaderParam.value; +uvalue.fromArray( m4v ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} else { +if ( shaderParam && shaderParam.value ) { +var m4 = shaderParam.value; +uvalue.fromArray( m4 ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} +break; +case WEBGL_CONSTANTS.SAMPLER_2D: +if ( value !== undefined ) { +uvalue = dependencies.textures[ value ]; +} else if ( shaderParam.value !== undefined ) { +uvalue = dependencies.textures[ shaderParam.value ]; +} else { +uvalue = null; +} +break; +} +materialParams.uniforms[ uniformId ] = { +value: uvalue, +semantic: usemantic, +node: unode +}; +} else { +throw new Error( "Unknown shader uniform param type: " + ptype ); +} +} +var states = technique.states || {}; +var enables = states.enable || []; +var functions = states.functions || {}; +var enableCullFace = false; +var enableDepthTest = false; +var enableBlend = false; +for ( var i = 0, il = enables.length; i < il; i ++ ) { +var enable = enables[ i ]; +switch ( STATES_ENABLES[ enable ] ) { +case 'CULL_FACE': +enableCullFace = true; +break; +case 'DEPTH_TEST': +enableDepthTest = true; +break; +case 'BLEND': +enableBlend = true; +break; +case 'SCISSOR_TEST': +case 'POLYGON_OFFSET_FILL': +case 'SAMPLE_ALPHA_TO_COVERAGE': +break; +default: +throw new Error( "Unknown technique.states.enable: " + enable ); +} +} +if ( enableCullFace ) { +materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; +} else { +materialParams.side = THREE.DoubleSide; +} +materialParams.depthTest = enableDepthTest; +materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; +materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; +materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; +materialParams.transparent = enableBlend; +var blendEquationSeparate = functions.blendEquationSeparate; +if ( blendEquationSeparate !== undefined ) { +materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; +materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; +} else { +materialParams.blendEquation = THREE.AddEquation; +materialParams.blendEquationAlpha = THREE.AddEquation; +} +var blendFuncSeparate = functions.blendFuncSeparate; +if ( blendFuncSeparate !== undefined ) { +materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; +materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; +materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; +materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; +} else { +materialParams.blendSrc = THREE.OneFactor; +materialParams.blendDst = THREE.ZeroFactor; +materialParams.blendSrcAlpha = THREE.OneFactor; +materialParams.blendDstAlpha = THREE.ZeroFactor; +} +} +} +if ( Array.isArray( materialValues.diffuse ) ) { +materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); +} else if ( typeof( materialValues.diffuse ) === 'string' ) { +materialParams.map = dependencies.textures[ materialValues.diffuse ]; +} +delete materialParams.diffuse; +if ( typeof( materialValues.reflective ) === 'string' ) { +materialParams.envMap = dependencies.textures[ materialValues.reflective ]; +} +if ( typeof( materialValues.bump ) === 'string' ) { +materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; +} +if ( Array.isArray( materialValues.emission ) ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.color = new THREE.Color().fromArray( materialValues.emission ); +} else { +materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); +} +} else if ( typeof( materialValues.emission ) === 'string' ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.map = dependencies.textures[ materialValues.emission ]; +} else { +materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; +} +} +if ( Array.isArray( materialValues.specular ) ) { +materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); +} else if ( typeof( materialValues.specular ) === 'string' ) { +materialParams.specularMap = dependencies.textures[ materialValues.specular ]; +} +if ( materialValues.shininess !== undefined ) { +materialParams.shininess = materialValues.shininess; +} +var _material = new materialType( materialParams ); +if ( material.name !== undefined ) _material.name = material.name; +return _material; +} ); +} ); +}; +GLTFParser.prototype.loadMeshes = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"materials" +] ).then( function ( dependencies ) { +return _each( json.meshes, function ( mesh ) { +var group = new THREE.Group(); +if ( mesh.name !== undefined ) group.name = mesh.name; +if ( mesh.extras ) group.userData = mesh.extras; +var primitives = mesh.primitives || []; +for ( var name in primitives ) { +var primitive = primitives[ name ]; +if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { +var geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'NORMAL': +geometry.addAttribute( 'normal', bufferAttribute ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +geometry.addAttribute( 'uv', bufferAttribute ); +break; +case 'TEXCOORD_1': +geometry.addAttribute( 'uv2', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +case 'WEIGHT': +geometry.addAttribute( 'skinWeight', bufferAttribute ); +break; +case 'JOINT': +geometry.addAttribute( 'skinIndex', bufferAttribute ); +break; +} +} +if ( primitive.indices ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +} +var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); +var meshNode = new THREE.Mesh( geometry, material ); +meshNode.castShadow = true; +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { +var geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +} +} +var material = dependencies.materials[ primitive.material ]; +var meshNode; +if ( primitive.indices ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +meshNode = new THREE.LineSegments( geometry, material ); +} else { +meshNode = new THREE.Line( geometry, material ); +} +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} else { +console.warn( "Only triangular and line primitives are supported" ); +} +} +return group; +} ); +} ); +}; +GLTFParser.prototype.loadCameras = function () { +var json = this.json; +return _each( json.cameras, function ( camera ) { +if ( camera.type == "perspective" && camera.perspective ) { +var yfov = camera.perspective.yfov; +var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; +var xfov = yfov * aspectRatio; +var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} else if ( camera.type == "orthographic" && camera.orthographic ) { +var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} +} ); +}; +GLTFParser.prototype.loadSkins = function () { +var json = this.json; +return this._withDependencies( [ +"accessors" +] ).then( function ( dependencies ) { +return _each( json.skins, function ( skin ) { +var bindShapeMatrix = new THREE.Matrix4(); +if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); +var _skin = { +bindShapeMatrix: bindShapeMatrix, +jointNames: skin.jointNames, +inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] +}; +return _skin; +} ); +} ); +}; +GLTFParser.prototype.loadAnimations = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"nodes" +] ).then( function ( dependencies ) { +return _each( json.animations, function ( animation, animationId ) { +var tracks = []; +for ( var channelId in animation.channels ) { +var channel = animation.channels[ channelId ]; +var sampler = animation.samplers[ channel.sampler ]; +if ( sampler ) { +var target = channel.target; +var name = target.id; +var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; +var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; +var inputAccessor = dependencies.accessors[ input ]; +var outputAccessor = dependencies.accessors[ output ]; +var node = dependencies.nodes[ name ]; +if ( node ) { +node.updateMatrix(); +node.matrixAutoUpdate = true; +var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation +? THREE.QuaternionKeyframeTrack +: THREE.VectorKeyframeTrack; +var targetName = node.name ? node.name : node.uuid; +var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; +tracks.push( new TypedKeyframeTrack( +targetName + '.' + PATH_PROPERTIES[ target.path ], +THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), +THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), +interpolation +) ); +} +} +} +var name = animation.name !== undefined ? animation.name : "animation_" + animationId; +return new THREE.AnimationClip( name, undefined, tracks ); +} ); +} ); +}; +GLTFParser.prototype.loadNodes = function () { +var json = this.json; +var extensions = this.extensions; +var scope = this; +return _each( json.nodes, function ( node ) { +var matrix = new THREE.Matrix4(); +var _node; +if ( node.jointName ) { +_node = new THREE.Bone(); +_node.name = node.name !== undefined ? node.name : node.jointName; +_node.jointName = node.jointName; +} else { +_node = new THREE.Object3D(); +if ( node.name !== undefined ) _node.name = node.name; +} +if ( node.extras ) _node.userData = node.extras; +if ( node.matrix !== undefined ) { +matrix.fromArray( node.matrix ); +_node.applyMatrix( matrix ); +} else { +if ( node.translation !== undefined ) { +_node.position.fromArray( node.translation ); +} +if ( node.rotation !== undefined ) { +_node.quaternion.fromArray( node.rotation ); +} +if ( node.scale !== undefined ) { +_node.scale.fromArray( node.scale ); +} +} +return _node; +} ).then( function ( __nodes ) { +return scope._withDependencies( [ +"meshes", +"skins", +"cameras" +] ).then( function ( dependencies ) { +return _each( __nodes, function ( _node, nodeId ) { +var node = json.nodes[ nodeId ]; +if ( node.meshes !== undefined ) { +for ( var meshId in node.meshes ) { +var mesh = node.meshes[ meshId ]; +var group = dependencies.meshes[ mesh ]; +if ( group === undefined ) { +console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); +continue; +} +for ( var childrenId in group.children ) { +var child = group.children[ childrenId ]; +var originalMaterial = child.material; +var originalGeometry = child.geometry; +var originalUserData = child.userData; +var originalName = child.name; +var material; +if ( originalMaterial.isDeferredShaderMaterial ) { +originalMaterial = material = originalMaterial.create(); +} else { +material = originalMaterial; +} +switch ( child.type ) { +case 'LineSegments': +child = new THREE.LineSegments( originalGeometry, material ); +break; +case 'LineLoop': +child = new THREE.LineLoop( originalGeometry, material ); +break; +case 'Line': +child = new THREE.Line( originalGeometry, material ); +break; +default: +child = new THREE.Mesh( originalGeometry, material ); +} +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var skinEntry; +if ( node.skin ) { +skinEntry = dependencies.skins[ node.skin ]; +} +if ( skinEntry ) { +var getJointNode = function ( jointId ) { +var keys = Object.keys( __nodes ); +for ( var i = 0, il = keys.length; i < il; i ++ ) { +var n = __nodes[ keys[ i ] ]; +if ( n.jointName === jointId ) return n; +} +return null; +}; +var geometry = originalGeometry; +var material = originalMaterial; +material.skinning = true; +child = new THREE.SkinnedMesh( geometry, material, false ); +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var bones = []; +var boneInverses = []; +for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { +var jointId = skinEntry.jointNames[ i ]; +var jointNode = getJointNode( jointId ); +if ( jointNode ) { +bones.push( jointNode ); +var m = skinEntry.inverseBindMatrices.array; +var mat = new THREE.Matrix4().fromArray( m, i * 16 ); +boneInverses.push( mat ); +} else { +console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); +} +} +child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); +var buildBoneGraph = function ( parentJson, parentObject, property ) { +var children = parentJson[ property ]; +if ( children === undefined ) return; +for ( var i = 0, il = children.length; i < il; i ++ ) { +var nodeId = children[ i ]; +var bone = __nodes[ nodeId ]; +var boneJson = json.nodes[ nodeId ]; +if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { +parentObject.add( bone ); +buildBoneGraph( boneJson, bone, 'children' ); +} +} +}; +buildBoneGraph( node, child, 'skeletons' ); +} +_node.add( child ); +} +} +} +if ( node.camera !== undefined ) { +var camera = dependencies.cameras[ node.camera ]; +_node.add( camera ); +} +if ( node.extensions +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { +var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; +var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; +_node.add( light ); +} +return _node; +} ); +} ); +} ); +}; +GLTFParser.prototype.loadScenes = function () { +var json = this.json; +function buildNodeHierachy( nodeId, parentObject, allNodes ) { +var _node = allNodes[ nodeId ]; +parentObject.add( _node ); +var node = json.nodes[ nodeId ]; +if ( node.children ) { +var children = node.children; +for ( var i = 0, l = children.length; i < l; i ++ ) { +var child = children[ i ]; +buildNodeHierachy( child, _node, allNodes ); +} +} +} +return this._withDependencies( [ +"nodes" +] ).then( function ( dependencies ) { +return _each( json.scenes, function ( scene ) { +var _scene = new THREE.Scene(); +if ( scene.name !== undefined ) _scene.name = scene.name; +if ( scene.extras ) _scene.userData = scene.extras; +var nodes = scene.nodes || []; +for ( var i = 0, l = nodes.length; i < l; i ++ ) { +var nodeId = nodes[ i ]; +buildNodeHierachy( nodeId, _scene, dependencies.nodes ); +} +_scene.traverse( function ( child ) { +if ( child.material && child.material.isRawShaderMaterial ) { +child.gltfShader = new GLTFShader( child, dependencies.nodes ); +child.onBeforeRender = function(renderer, scene, camera){ +this.gltfShader.update(scene, camera); +}; +} +} ); +return _scene; +} ); +} ); +}; +return GLTFLoader; +} )(); +"use strict"; +var JSONParser = function() +{ +} +JSONParser.prototype.constructor = JSONParser; +JSONParser.prototype.parseJavaScript = function(jsobj) { +var child = this.CreateElement('scene'); +this.ConvertToX3DOM(jsobj, "", child); +console.log(jsobj, child); +return child; +}; +JSONParser.prototype.elementSetAttribute = function(element, key, value) { +if (key === 'SON schema') { +} else if (key === 'ncoding') { +} else { +if (typeof element.setAttribute === 'function') { +element.setAttribute(key, value); +} +} +}; +JSONParser.prototype.ConvertChildren = function(parentkey, object, element) { +var key; +for (key in object) { +if (typeof object[key] === 'object') { +if (isNaN(parseInt(key))) { +this.ConvertObject(key, object, element, parentkey.substr(1)); +} else { +this.ConvertToX3DOM(object[key], key, element, parentkey.substr(1)); +} +} +} +}; +JSONParser.prototype.CreateElement = function(key, containerField) { +var child = document.createElement(key); +if (typeof containerField !== 'undefined') { +this.elementSetAttribute(child, 'containerField', containerField); +} +return child; +}; +JSONParser.prototype.CDATACreateFunction = function(document, element, str) { +var y = str.replace(/\\"/g, "\\\"") +.replace(/</g, "<") +.replace(/>/g, ">") +.replace(/&/g, "&"); +do { +str = y; +y = str.replace(/'([^'\r\n]*)\n([^']*)'/g, "'$1\\n$2'"); +if (str !== y) { +console.log("CDATA Replacing",str,"with",y); +} +} while (y != str); +var domParser = new DOMParser(); +var cdataStr = ''; // has to be wrapped into an element +var scriptDoc = domParser .parseFromString (cdataStr, 'application/xml'); +var cdata = scriptDoc .children[0] .childNodes[1]; // space after script is childNode[0] +element .appendChild(cdata); +}; +JSONParser.prototype.ConvertObject = function(key, object, element, containerField) { +var child; +if (object !== null && typeof object[key] === 'object') { +if (key.substr(0,1) === '@') { +this.ConvertToX3DOM(object[key], key, element); +} else if (key.substr(0,1) === '-') { +this.ConvertChildren(key, object[key], element); +} else if (key === '#comment') { +for (var c in object[key]) { +child = document.createComment(this.CommentStringToXML(object[key][c])); +element.appendChild(child); +} +} else if (key === '#text') { +child = document.createTextNode(object[key].join("")); +element.appendChild(child); +} else if (key === '#sourceText') { +this.CDATACreateFunction(document, element, object[key].join("\r\n")+"\r\n"); +} else { +if (key === 'connect' || key === 'fieldValue' || key === 'field' || key === 'meta' || key === 'component') { +for (var childkey in object[key]) { // for each field +if (typeof object[key][childkey] === 'object') { +child = this.CreateElement(key, containerField); +this.ConvertToX3DOM(object[key][childkey], childkey, child); +element.appendChild(child); +element.appendChild(document.createTextNode("\n")); +} +} +} else { +child = this.CreateElement(key, containerField); +this.ConvertToX3DOM(object[key], key, child); +element.appendChild(child); +element.appendChild(document.createTextNode("\n")); +} +} +} +}; +JSONParser.prototype.CommentStringToXML = function(str) { +var y = str; +str = str.replace(/\\\\/g, '\\'); +if (y !== str) { +console.log("X3DJSONLD replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.SFStringToXML = function(str) { +var y = str; +str = str.replace(/\\/g, '\\\\'); +str = str.replace(/"/g, '\\\"'); +if (y !== str) { +console.log("X3DJSONLD [] replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.JSONStringToXML = function(str) { +var y = str; +str = str.replace(/\\/g, '\\\\'); +str = str.replace(/\n/g, '\\n'); +if (y !== str) { +console.log("X3DJSONLD replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.ConvertToX3DOM = function(object, parentkey, element, containerField) { +var key; +var localArray = []; +var isArray = false; +var arrayOfStrings = false; +for (key in object) { +if (isNaN(parseInt(key))) { +isArray = false; +} else { +isArray = true; +} +if (isArray) { +if (typeof object[key] === 'number') { +localArray.push(object[key]); +} else if (typeof object[key] === 'string') { +localArray.push(object[key]); +arrayOfStrings = true; +} else if (typeof object[key] === 'boolean') { +localArray.push(object[key]); +} else if (typeof object[key] === 'object') { +this.ConvertToX3DOM(object[key], key, element); +} else if (typeof object[key] === 'undefined') { +} else { +console.error("Unknown type found in array "+typeof object[key]); +} +} else if (typeof object[key] === 'object') { +if (key === 'scene') { +this.ConvertToX3DOM(object[key], key, element); +} else { +this.ConvertObject(key, object, element); +} +} else if (typeof object[key] === 'number') { +this.elementSetAttribute(element, key.substr(1),object[key]); +} else if (typeof object[key] === 'string') { +if (key === '#comment') { +var child = document.createComment(this.CommentStringToXML(object[key])); +element.appendChild(child); +} else if (key === '#text') { +var child = document.createTextNode(object[key]); +element.appendChild(child); +} else { +this.elementSetAttribute(element, key.substr(1), this.JSONStringToXML(object[key])); +} +} else if (typeof object[key] === 'boolean') { +this.elementSetAttribute(element, key.substr(1),object[key]); +} else if (typeof object[key] === 'undefined') { +} else { +console.error("Unknown type found in object "+typeof object[key]); +console.error(object); +} +} +if (isArray) { +if (parentkey.substr(0,1) === '@') { +if (arrayOfStrings) { +arrayOfStrings = false; +for (var str in localArray) { +localArray[str] = this.SFStringToXML(localArray[str]); +} +this.elementSetAttribute(element, parentkey.substr(1),'"'+localArray.join('" "')+'"'); +} else { +this.elementSetAttribute(element, parentkey.substr(1),localArray.join(" ")); +} +} +isArray = false; +} +return element; +}; +function LoadManager () { +this.urlQueue = []; +this.urlNext = -1; +this.MaxRequests = 3; +this.totalRequests = 0; +this.totalResponses = 0; +this.requestCount = 0; +var lmThat = this; +this.load = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadText = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'text', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadHtml = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'html', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadXml = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'xml', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadJson = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'json', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadImage = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'image', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.success = function (response, string, xhr) { +if (typeof(xhr._loadManager.success) !== undefined) { +xhr._loadManager.success (response, xhr._loadManager.userdata, xhr); +} +} +this.failure = function (xhr, errorCode, errorText) { +if (typeof(xhr._loadManager.failure) !== undefined) { +xhr._loadManager.failure (xhr, xhr._loadManager.userdata, errorCode, errorText); +} +} +this.requestComplete = function (event, xhr, settings) { +lmThat.requestCount --; +lmThat.totalResponses++; +lmThat.loadNextUrl(); +} +this.loadNextUrl = function () { +if (this.requestCount >= this.MaxRequests) {return; } +if (this.urlNext >= this.urlQueue.length || this.urlNext < 0) { +this.urlNext = -1; +for (var i=0; i 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.CODE_SLASH: +slashes[ slashesPointer++ ] = i; +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.CODE_LF: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); +slashesPointer = 0; +bufferPointer = 0; +break; +case Consts.CODE_CR: +break; +default: +word += String.fromCharCode( code ); +break; +} +} +}; +Parser.prototype.parseText = function ( text ) { +var length = text.length; +var buffer = new Array( 32 ); +var bufferPointer = 0; +var slashes = new Array( 32 ); +var slashesPointer = 0; +var reachedFaces = false; +var char; +var word = ''; +for ( var i = 0; i < length; i++ ) { +char = text[ i ]; +switch ( char ) { +case Consts.STRING_SPACE: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.STRING_SLASH: +slashes[ slashesPointer++ ] = i; +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.STRING_LF: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); +slashesPointer = 0; +bufferPointer = 0; +break; +case Consts.STRING_CR: +break; +default: +word += char; +} +} +}; +Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) { +if ( bufferPointer < 1 ) return reachedFaces; +var bufferLength = bufferPointer - 1; +switch ( buffer[ 0 ] ) { +case Consts.LINE_V: +if ( reachedFaces ) { +this.processCompletedObject( null, this.rawObject.groupName ); +reachedFaces = false; +} +this.rawObject.pushVertex( buffer ); +break; +case Consts.LINE_VT: +this.rawObject.pushUv( buffer ); +break; +case Consts.LINE_VN: +this.rawObject.pushNormal( buffer ); +break; +case Consts.LINE_F: +reachedFaces = true; +var haveQuad = bufferLength % 4 === 0; +if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVn( buffer ); +} else { +this.rawObject.buildFaceVVn( buffer ); +} +} else if ( bufferLength === slashesPointer * 2 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVt( buffer ); +} else { +this.rawObject.buildFaceVVt( buffer ); +} +} else if ( bufferLength * 2 === slashesPointer * 3 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVtVn( buffer ); +} else { +this.rawObject.buildFaceVVtVn( buffer ); +} +} else { +if ( haveQuad ) { +this.rawObject.buildQuadV( buffer ); +} else { +this.rawObject.buildFaceV( buffer ); +} +} +break; +case Consts.LINE_L: +if ( bufferLength === slashesPointer * 2 ) { +this.rawObject.buildLineVvt( buffer ); +} else { +this.rawObject.buildLineV( buffer ); +} +break; +case Consts.LINE_S: +this.rawObject.pushSmoothingGroup( buffer[ 1 ] ); +break; +case Consts.LINE_G: +this.processCompletedGroup( buffer[ 1 ] ); +break; +case Consts.LINE_O: +if ( this.rawObject.vertices.length > 0 ) { +this.processCompletedObject( buffer[ 1 ], null ); +reachedFaces = false; +} else { +this.rawObject.pushObject( buffer[ 1 ] ); +} +break; +case Consts.LINE_MTLLIB: +this.rawObject.pushMtllib( buffer[ 1 ] ); +break; +case Consts.LINE_USEMTL: +this.rawObject.pushUsemtl( buffer[ 1 ] ); +break; +default: +break; +} +return reachedFaces; +}; +Parser.prototype.processCompletedObject = function ( objectName, groupName ) { +this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +this.inputObjectCount++; +this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName ); +}; +Parser.prototype.processCompletedGroup = function ( groupName ) { +var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +if ( notEmpty ) { +this.inputObjectCount ++; +this.rawObject = this.rawObject.newInstanceFromGroup( groupName ); +} else { +this.rawObject.pushGroup( groupName ); +} +}; +Parser.prototype.finalize = function () { +this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +this.inputObjectCount++; +}; +return Parser; +})(); +var RawObject = (function () { +function RawObject( objectName, groupName, mtllibName ) { +this.globalVertexOffset = 1; +this.globalUvOffset = 1; +this.globalNormalOffset = 1; +this.vertices = []; +this.normals = []; +this.uvs = []; +this.mtllibName = Validator.verifyInput( mtllibName, 'none' ); +this.objectName = Validator.verifyInput( objectName, 'none' ); +this.groupName = Validator.verifyInput( groupName, 'none' ); +this.activeMtlName = 'none'; +this.activeSmoothingGroup = 1; +this.mtlCount = 0; +this.smoothingGroupCount = 0; +this.rawObjectDescriptions = []; +var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; +} +RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) { +return materialName + '|' + smoothingGroup; +}; +RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) { +var newRawObject = new RawObject( objectName, groupName, this.mtllibName ); +newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; +newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; +newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; +return newRawObject; +}; +RawObject.prototype.newInstanceFromGroup = function ( groupName ) { +var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName ); +newRawObject.vertices = this.vertices; +newRawObject.uvs = this.uvs; +newRawObject.normals = this.normals; +newRawObject.globalVertexOffset = this.globalVertexOffset; +newRawObject.globalUvOffset = this.globalUvOffset; +newRawObject.globalNormalOffset = this.globalNormalOffset; +return newRawObject; +}; +RawObject.prototype.pushVertex = function ( buffer ) { +this.vertices.push( parseFloat( buffer[ 1 ] ) ); +this.vertices.push( parseFloat( buffer[ 2 ] ) ); +this.vertices.push( parseFloat( buffer[ 3 ] ) ); +}; +RawObject.prototype.pushUv = function ( buffer ) { +this.uvs.push( parseFloat( buffer[ 1 ] ) ); +this.uvs.push( parseFloat( buffer[ 2 ] ) ); +}; +RawObject.prototype.pushNormal = function ( buffer ) { +this.normals.push( parseFloat( buffer[ 1 ] ) ); +this.normals.push( parseFloat( buffer[ 2 ] ) ); +this.normals.push( parseFloat( buffer[ 3 ] ) ); +}; +RawObject.prototype.pushObject = function ( objectName ) { +this.objectName = objectName; +}; +RawObject.prototype.pushMtllib = function ( mtllibName ) { +this.mtllibName = mtllibName; +}; +RawObject.prototype.pushGroup = function ( groupName ) { +this.groupName = groupName; +this.verifyIndex(); +}; +RawObject.prototype.pushUsemtl = function ( mtlName ) { +if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; +this.activeMtlName = mtlName; +this.mtlCount++; +this.verifyIndex(); +}; +RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) { +var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup; +if ( this.activeSmoothingGroup === normalized ) return; +this.activeSmoothingGroup = normalized; +this.smoothingGroupCount++; +this.verifyIndex(); +}; +RawObject.prototype.verifyIndex = function () { +var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 ); +this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ]; +if ( ! Validator.isValid( this.rawObjectDescriptionInUse ) ) { +this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; +} +}; +RawObject.prototype.buildQuadVVtVn = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] ); +this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] ); +this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] ); +} +}; +RawObject.prototype.buildQuadVVt = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); +this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); +} +}; +RawObject.prototype.buildQuadVVn = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); +this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); +} +}; +RawObject.prototype.buildQuadV = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] ); +} +}; +RawObject.prototype.buildFaceVVtVn = function ( indexArray ) { +for ( var i = 1; i < 10; i += 3 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVt( indexArray[ i + 1 ] ); +this.attachFaceVn( indexArray[ i + 2 ] ); +} +}; +RawObject.prototype.buildFaceVVt = function ( indexArray ) { +for ( var i = 1; i < 7; i += 2 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVt( indexArray[ i + 1 ] ); +} +}; +RawObject.prototype.buildFaceVVn = function ( indexArray ) { +for ( var i = 1; i < 7; i += 2 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVn( indexArray[ i + 1 ] ); +} +}; +RawObject.prototype.buildFaceV = function ( indexArray ) { +for ( var i = 1; i < 4; i ++ ) { +this.attachFaceV_( indexArray[ i ] ); +} +}; +RawObject.prototype.attachFaceV_ = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalVertexOffset ) * 3; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.vertices.push( this.vertices[ index++ ] ); +rodiu.vertices.push( this.vertices[ index++ ] ); +rodiu.vertices.push( this.vertices[ index ] ); +}; +RawObject.prototype.attachFaceVt = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalUvOffset ) * 2; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.uvs.push( this.uvs[ index++ ] ); +rodiu.uvs.push( this.uvs[ index ] ); +}; +RawObject.prototype.attachFaceVn = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalNormalOffset ) * 3; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.normals.push( this.normals[ index++ ] ); +rodiu.normals.push( this.normals[ index++ ] ); +rodiu.normals.push( this.normals[ index ] ); +}; +RawObject.prototype.buildLineVvt = function ( lineArray ) { +var length = lineArray.length; +for ( var i = 1; i < length; i ++ ) { +this.vertices.push( parseInt( lineArray[ i ] ) ); +this.uvs.push( parseInt( lineArray[ i ] ) ); +} +}; +RawObject.prototype.buildLineV = function ( lineArray ) { +var length = lineArray.length; +for ( var i = 1; i < length; i++ ) { +this.vertices.push( parseInt( lineArray[ i ] ) ); +} +}; +RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) { +var temp = this.rawObjectDescriptions; +this.rawObjectDescriptions = []; +var rawObjectDescription; +var index = 0; +var absoluteVertexCount = 0; +var absoluteNormalCount = 0; +var absoluteUvCount = 0; +for ( var name in temp ) { +rawObjectDescription = temp[ name ]; +if ( rawObjectDescription.vertices.length > 0 ) { +if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName; +this.rawObjectDescriptions[ index++ ] = rawObjectDescription; +absoluteVertexCount += rawObjectDescription.vertices.length; +absoluteUvCount += rawObjectDescription.uvs.length; +absoluteNormalCount += rawObjectDescription.normals.length; +} +} +var notEmpty = false; +if ( index > 0 ) { +if ( debug ) this.createReport( inputObjectCount, true ); +meshCreator.buildMesh( +this.rawObjectDescriptions, +inputObjectCount, +absoluteVertexCount, +absoluteNormalCount, +absoluteUvCount +); +notEmpty = true; +} +return notEmpty; +}; +RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) { +var report = { +name: this.objectName ? this.objectName : 'groups', +mtllibName: this.mtllibName, +vertexCount: this.vertices.length / 3, +normalCount: this.normals.length / 3, +uvCount: this.uvs.length / 2, +smoothingGroupCount: this.smoothingGroupCount, +mtlCount: this.mtlCount, +rawObjectDescriptions: this.rawObjectDescriptions.length +}; +if ( printDirectly ) { +console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name ); +console.log( 'Mtllib name: ' + report.mtllibName ); +console.log( 'Vertex count: ' + report.vertexCount ); +console.log( 'Normal count: ' + report.normalCount ); +console.log( 'UV count: ' + report.uvCount ); +console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount ); +console.log( 'Material count: ' + report.mtlCount ); +console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions ); +console.log( '' ); +} +return report; +}; +return RawObject; +})(); +var RawObjectDescription = (function () { +function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) { +this.objectName = objectName; +this.groupName = groupName; +this.materialName = materialName; +this.smoothingGroup = smoothingGroup; +this.vertices = []; +this.uvs = []; +this.normals = []; +} +return RawObjectDescription; +})(); +var MeshCreator = (function () { +function MeshCreator() { +this.sceneGraphBaseNode = null; +this.materials = null; +this.debug = false; +this.globalObjectCount = 1; +this.validated = false; +} +MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) { +this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, this.sceneGraphBaseNode ); +this.sceneGraphBaseNode = Validator.verifyInput( this.sceneGraphBaseNode, new THREE.Group() ); +}; +MeshCreator.prototype.setMaterials = function ( materials ) { +this.materials = Validator.verifyInput( materials, this.materials ); +this.materials = Validator.verifyInput( this.materials, { materials: [] } ); +}; +MeshCreator.prototype.setDebug = function ( debug ) { +if ( debug === true || debug === false ) this.debug = debug; +}; +MeshCreator.prototype.validate = function () { +if ( this.validated ) return; +this.setSceneGraphBaseNode( null ); +this.setMaterials( null ); +this.setDebug( null ); +this.globalObjectCount = 1; +}; +MeshCreator.prototype.finalize = function () { +this.sceneGraphBaseNode = null; +this.materials = null; +this.validated = false; +}; +MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) { +if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount ); +var bufferGeometry = new THREE.BufferGeometry(); +var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 ); +bufferGeometry.addAttribute( 'position', vertexBA ); +var normalBA; +if ( absoluteNormalCount > 0 ) { +normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 ); +bufferGeometry.addAttribute( 'normal', normalBA ); +} +var uvBA; +if ( absoluteUvCount > 0 ) { +uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 ); +bufferGeometry.addAttribute( 'uv', uvBA ); +} +if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount ); +var rawObjectDescription; +var material; +var materialName; +var createMultiMaterial = rawObjectDescriptions.length > 1; +var materials = []; +var materialIndex = 0; +var materialIndexMapping = []; +var selectedMaterialIndex; +var vertexBAOffset = 0; +var vertexGroupOffset = 0; +var vertexLength; +var normalOffset = 0; +var uvOffset = 0; +for ( var oodIndex in rawObjectDescriptions ) { +rawObjectDescription = rawObjectDescriptions[ oodIndex ]; +materialName = rawObjectDescription.materialName; +material = this.materials[ materialName ]; +if ( ! material ) { +material = this.materials[ 'defaultMaterial' ]; +if ( ! material ) { +material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} ); +material.name = 'defaultMaterial'; +this.materials[ 'defaultMaterial' ] = material; +} +console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' ); +} +if ( rawObjectDescription.smoothingGroup === 0 ) { +materialName = material.name + '_flat'; +var materialClone = this.materials[ materialName ]; +if ( ! materialClone ) { +materialClone = material.clone(); +materialClone.name = materialName; +materialClone.shading = THREE.FlatShading; +this.materials[ materialName ] = name; +} +} +vertexLength = rawObjectDescription.vertices.length; +if ( createMultiMaterial ) { +selectedMaterialIndex = materialIndexMapping[ materialName ]; +if ( ! selectedMaterialIndex ) { +selectedMaterialIndex = materialIndex; +materialIndexMapping[ materialName ] = materialIndex; +materials.push( material ); +materialIndex++; +} +bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex ); +vertexGroupOffset += vertexLength / 3; +} +vertexBA.set( rawObjectDescription.vertices, vertexBAOffset ); +vertexBAOffset += vertexLength; +if ( normalBA ) { +normalBA.set( rawObjectDescription.normals, normalOffset ); +normalOffset += rawObjectDescription.normals.length; +} +if ( uvBA ) { +uvBA.set( rawObjectDescription.uvs, uvOffset ); +uvOffset += rawObjectDescription.uvs.length; +} +if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex ); +} +if ( ! normalBA ) bufferGeometry.computeVertexNormals(); +if ( createMultiMaterial ) material = materials; +var mesh = new THREE.Mesh( bufferGeometry, material ); +this.sceneGraphBaseNode.add( mesh ); +this.globalObjectCount++; +}; +MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) { +console.log( +' Output Object no.: ' + this.globalObjectCount + +'\n objectName: ' + rawObjectDescription.objectName + +'\n groupName: ' + rawObjectDescription.groupName + +'\n materialName: ' + rawObjectDescription.materialName + +'\n materialIndex: ' + selectedMaterialIndex + +'\n smoothingGroup: ' + rawObjectDescription.smoothingGroup + +'\n #vertices: ' + rawObjectDescription.vertices.length / 3 + +'\n #uvs: ' + rawObjectDescription.uvs.length / 2 + +'\n #normals: ' + rawObjectDescription.normals.length / 3 +); +}; +return MeshCreator; +})(); +OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) { +var workerCode = ''; +workerCode += funcBuildObject( 'Consts', Consts ); +workerCode += funcBuildObject( 'Validator', Validator ); +workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); +workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject ); +workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription ); +return workerCode; +}; +return OBJLoader2; +})(); +var xseen = { +canvases : [], +sceneInfo : [], +nodeDefinitions : {}, +parseTable : {}, +node : {}, +utils : {}, +eventManager : {}, +Events : {}, +Navigation : {}, +loadMgr : {}, +loader : { +'Null' : '', +'ColladaLoader' : '', +'GltfLegacy' : '', +'GltfLoader' : '', +'ObjLoader' : '', +'ImageLoader' : '', +'X3dLoader' : '', +}, +loadProgress : function (xhr) { +if (xhr.total != 0) { +console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); +} +}, +loadError : function (xhr, userdata, code, message) { +console.error('An error happened on '+userdata.e.id+'\n'+code+'\n'+message); +}, +loadMime : { +'' : {name: 'Null', loader: 'Null'}, +'dae' : {name: 'Collada', loader: 'ColladaLoader'}, +'glb' : {name: 'glTF Binary', loader: 'GltfLoader'}, +'glbl' : {name: 'glTF Binary', loader: 'GltfLegacy'}, +'gltf' : {name: 'glTF JSON', loader: 'GltfLoader'}, +'obj' : {name: 'OBJ', loader: 'ObjLoader'}, +'png' : {name: 'PNG', loader: 'ImageLoader'}, +'jpg' : {name: 'JPEG', loader: 'ImageLoader'}, +'jpeg' : {name: 'JPEG', loader: 'ImageLoader'}, +'gif' : {name: 'GIF', loader: 'ImageLoader'}, +'x3d' : {name: 'X3D XML', loader: 'X3dLoader'}, +}, +array_to_object : function (a) { +var o = {}; +for(var i=0;i ' + obj.type + ' (' + obj.name + ')'); +for (var i=0; i +var validParams = xseen.array_to_object([ +'showLog', +'showStat', +'showProgress', +'PrimitiveQuality', +'components', +'loadpath', +'disableDoubleClick', +'backend', +'altImg', +'runtimeEnabled', +'keysEnabled', +'showTouchpoints', +'disableTouch', +'maxActiveDownloads' +]); +var components, prefix; +var showLoggingConsole = false; +for (var i=0; i < xseens.length; i++) { +settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'false'); +settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'true'); +settings.setProperty("showStat", xseens[i].getAttribute("showStat") || 'false'); +settings.setProperty("showProgress", xseens[i].getAttribute("showProgress") || 'true'); +settings.setProperty("PrimitiveQuality", xseens[i].getAttribute("PrimitiveQuality") || 'High'); +params = xseens[i].getElementsByTagName('PARAM'); +for (var j=0; j < params.length; j++) { +if (params[j].getAttribute('name') in validParams) { +settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value')); +} else { +} +} +if (settings.getProperty('showLog') === 'true') { +showLoggingConsole = true; +} +if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) { +components = settings.getProperty('components', xseens[i].getAttribute("components")); +if (components) { +prefix = settings.getProperty('loadpath', xseens[i].getAttribute("loadpath")); +components = components.trim().split(','); +for (j=0; j < components.length; j++) { +xseen.loadJS(components[j] + ".js", prefix); +} +} +if (xseens[i].getAttribute("src")) { +var _scene = document.createElement("scene"); +var _inl = document.createElement("Inline"); +_inl.setAttribute("url", xseens[i].getAttribute("src")); +_scene.appendChild(_inl); +xseens[i].appendChild(_scene); +} +} +} +if (showLoggingConsole == true) { +xseen.debug.activate(true); +} else { +xseen.debug.activate(false); +} +if (xseen.versionInfo !== undefined) { +xseen.debug.logInfo("XSeen version " + xseen.versionInfo.version + ", " + +"Date " + xseen.versionInfo.date); +xseen.debug.logInfo(xseen.versionInfo.splashText); +} +var x_element; +var x_canvas; +var altDiv, altP, aLnk, altImg; +var t0, t1; +for (var i=0; i < xseens.length; i++) +{ +x_element = xseens[i]; // The XSeen DOM element +x_canvas = new THREE.Scene(); // May need addtl info if multiple: xseen.X3DCanvas(x_element, xseen.canvases.length); +xseen.canvases.push(x_canvas); // TODO: Need to handle failure to initialize? +t0 = new Date().getTime(); +var divWidth = x_element.getAttribute('width'); +var divHeight = x_element.getAttribute('height'); +if (divHeight + divWidth < 100) { +divHeight = 450; +divWidth = 800; +} else if (divHeight < 50) { +divHeight = Math.floor(divWidth/2) + 50; +} else if (divWidth < 50) { +divWidth = divHeight * 2 - 100; +} +var turntable = (x_element.getAttribute('turntable') || '').toLowerCase(); +if (turntable == 'on' || turntable == 'yes' || turntable == 'y' || turntable == '1') { +turntable = true; +} else { +turntable = false; +} +turntable = false; +var x_camera = {}; +var x_renderer = new THREE.WebGLRenderer(); +x_renderer.setSize (divWidth, divHeight); +var x_effect = new THREE.StereoEffect(x_renderer); +x_renderer.controls = {'update' : function() {return;}}; +x_element.addEventListener ('dblclick', xseen.Events.canvasHandler, true); +x_element.addEventListener ('click', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mousedown', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mousemove', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mouseup', xseen.Events.canvasHandler, true); +x_element.addEventListener ('xseen', xseen.Events.XSeenHandler); // Last chance for XSeen handling of event +x_element.addEventListener ('change', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event +xseen.sceneInfo.push ({ +'size' : {'width':divWidth, 'height':divHeight}, +'scene' : x_canvas, +'renderer' : x_renderer, +'effect' : x_effect, +'camera' : [x_camera], +'turntable' : turntable, +'mixers' : [], +'clock' : new THREE.Clock(), +'element' : x_element, +'selectable': [], +'stacks' : [], +'tmp' : {activeViewpoint:false}, +'xseen' : xseen, +}); +x_element._xseen = {}; +x_element._xseen.children = []; +x_element._xseen.sceneInfo = xseen.sceneInfo[xseen.sceneInfo.length-1]; +t1 = new Date().getTime() - t0; +xseen.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms."); +} +var ready = (function(eventType) { +var evt = null; +if (document.createEvent) { +evt = document.createEvent("Events"); +evt.initEvent(eventType, true, true); +document.dispatchEvent(evt); +} else if (document.createEventObject) { +evt = document.createEventObject(); +document.body.fireEvent('on' + eventType, evt); +} +})('load'); +var t=[]; +for (var i=0; i xseen.debug.maxLinesToLog) { return; } +var node = document.createElement("p"); +node.style.margin = 0; +switch (logType) { +case xseen.debug.INFO: +node.style.color = "#009900"; +break; +case xseen.debug.WARNING: +node.style.color = "#cd853f"; +break; +case xseen.debug.ERROR: +node.style.color = "#ff4500"; +break; +case xseen.debug.EXCEPTION: +node.style.color = "#ffff00"; +break; +default: +node.style.color = "#009900"; +break; +} +try { +node.innerHTML = logType + ": " + msg; +xseen.debug.logContainer.insertBefore(node, xseen.debug.logContainer.firstChild); +} catch (err) { +if (window.console.firebug !== undefined) { +window.console.warn(msg); +} +} +if (xseen.debug.isFirebugAvailable) { +switch (logType) { +case xseen.debug.INFO: +window.console.info(msg); +break; +case xseen.debug.WARNING: +window.console.warn(msg); +break; +case xseen.debug.ERROR: +window.console.error(msg); +break; +case xseen.debug.EXCEPTION: +window.console.debug(msg); +break; +default: +break; +} +} +xseen.debug.numLinesLogged++; +}, +logInfo: function(msg) { +xseen.debug.doLog(msg, xseen.debug.INFO); +}, +logWarning: function(msg) { +xseen.debug.doLog(msg, xseen.debug.WARNING); +}, +logError: function(msg) { +xseen.debug.doLog(msg, xseen.debug.ERROR); +}, +logException: function(msg) { +xseen.debug.doLog(msg, xseen.debug.EXCEPTION); +}, +assert: function(c, msg) { +if (!c) { +xseen.debug.doLog("Assertion failed in " + xseen.debug.assert.caller.name + ': ' + msg, xseen.debug.ERROR); +} +}, +typeOf: function (obj) { +var type = typeof obj; +return type === "object" && !obj ? "null" : type; +}, +exists: function (obj, name, type) { +type = type || "function"; +return (obj ? this.typeOf(obj[name]) : "null") === type; +}, +dumpFields: function (node) { +var str = ""; +for (var fName in node) { +str += (fName + ", "); +} +str += '\n'; +xseen.debug.logInfo(str); +return str; +} +}; +this.debug.setup(); +}; +xseen.Navigation = { +'TwoPi' : 2 * Math.PI, +'none' : function () {}, // Does not allow user-controlled navigation +'turntable' : function (speed, deltaT, scene, camera) +{ +var T, radians, radius, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +radius = vp.fields._radius0; +camera.position.x = radius * Math.sin(radians) +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.position.z = radius * Math.cos(radians); +camera.lookAt(scene.ORIGIN); +}, +'tilt' : function (speed, deltaT, scene, camera) +{ +var T, radians, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.lookAt(scene.ORIGIN); +}, +'setup' : { +'none' : function () {return null;}, +'orbit' : function (camera, renderer) +{ +var controls; +controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.enableZoom = false; +controls.enableZoom = true; +return controls; +}, +'trackball' : function (camera, renderer) +{ +var controls; +controls = new THREE.TrackballControls(camera, renderer.domElement); +controls.rotateSpeed = 1.0; +controls.zoomSpeed = 1.2; +controls.panSpeed = 0.8; +controls.noZoom = false; +controls.noPan = false; +controls.staticMoving = true; +controls.dynamicDampingFactor = 0.3; +controls.keys = [ 65, 83, 68 ]; +return controls; +}, +}, +}; +xseen.Navigation = { +'TwoPi' : 2 * Math.PI, +'none' : function () {}, // Does not allow user-controlled navigation +'turntable' : function (speed, deltaT, scene, camera) +{ +var T, radians, radius, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +radius = vp.fields._radius0; +camera.position.x = radius * Math.sin(radians) +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.position.z = radius * Math.cos(radians); +camera.lookAt(scene.ORIGIN); +}, +'tilt' : function (speed, deltaT, scene, camera) +{ +var T, radians, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.lookAt(scene.ORIGIN); +}, +'setup' : { +'none' : function () {return null;}, +'orbit' : function (camera, renderer) +{ +var controls; +controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.enableZoom = false; +controls.enableZoom = true; +return controls; +}, +}, +}; +xseen.nodes = { +'_defineNode' : function(nodeName, nodeComponent, nodeMethod) { +var methodBase = ''; +node = { +'tag' : nodeName, +'taglc' : nodeName.toLowerCase(), +'component' : nodeComponent, +'method' : methodBase + nodeMethod, +'fields' : [], +'fieldIndex': [], +'addField' : function (fieldObj, datatype, defaultValue) { +var fieldName, namelc, enumerated, animatable; +if (typeof(fieldObj) === 'object') { +fieldName = fieldObj.name; +datatype = fieldObj.datatype; +defaultValue = fieldObj.defaultValue; +enumerated = (typeof(fieldObj.enumerated) === 'undefined') ? [] : fieldObj.enumerated; +animatable = (typeof(fieldObj.animatable) === 'undefined') ? false : fieldObj.animatable; +} else { +fieldName = fieldObj; +animatable = false; +if (typeof(defaultValue) == 'array') { +enumerated = defaultValue; +defaultValue = enumerated[0]; +} else { +enumerated = []; +} +} +namelc = fieldName.toLowerCase(); +this.fields.push ({ +'field' : fieldName, +'fieldlc' : namelc, +'type' : datatype, +'default' : defaultValue, +'enumeration' : enumerated, +'animatable' : animatable, +'clone' : this.cloneField, +'setFieldName' : this.setFieldName, +}); +this.fieldIndex[namelc] = this.fields.length-1; +return this; +}, +'addNode' : function () { +xseen.parseTable[this.taglc] = this; +}, +'cloneField' : function () { +var newFieldObject = { +'field' : this.field, +'fieldlc' : this.fieldlc, +'type' : this.type, +'default' : 0, +'enumeration' : [], +'animatable' : this.animatable, +'clone' : this.clone, +'setFieldName' : this.setFieldName, +}; +for (var i=0; i= 0) { +this._internals.activeNode = this._internals.stack[this._internals.active]; +} else { +this._internals.activeNode = this._internals.defaultNode; +} +} +this.init = function() { // Clears existing stack +this._internals.stack = []; +} +this.pushDown = function(node) { // Push new node onto stack and make active +this._internals.stack.push (node); +this._setActiveNode(); +} +this.popOff = function() { // Pop node off stack and make next one active +this._internals.stack.pop(); +this._setActiveNode(); +} +this.getActive = function() { +return this._internals.activeNode; +} +this.setDefault = function(node) { +this._internals.defaultNode = node; +if (Object.keys(this._internals.activeNode).length === 0) { +this._internals.activeNode = this._internals.defaultNode; +} +} +} +xseen.types = { +'Deg2Rad' : Math.PI / 180, +'SFFloat' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return value; +}, +'SFInt' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return Math.round(value); +}, +'SFBool' : function (value, def) +{ +if (value === null) {return def;} +if (value) {return true;} +if (!value) {return false;} +return def; +}, +'SFTime' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return value; +}, +'SFVec3f' : function (value, def) +{ +if (value === null) {return def;} +var v3 = value.split(' '); +if (v3.length < 3 || Number.isNaN(v3[0]) || Number.isNaN(v3[1]) || Number.isNaN(v3[2])) { +return def; +} +return [v3[0]-0, v3[1]-0, v3[2]-0]; +}, +'SFVec2f' : function (value, def) +{ +if (value === null) {return def;} +var v2 = value.split(' '); +if (v2.length != 2 || Number.isNaN(v2[0]) || Number.isNaN(v2[1])) { +return def; +} +return [v2[0]-0, v2[1]-0]; +}, +'SFRotation' : function (value, def) +{ +if (value === null) {return def;} +var v4 = value.split(' '); +if (v4.length != 4 || Number.isNaN(v4[0]) || Number.isNaN(v4[1]) || Number.isNaN(v4[2]) || Number.isNaN(v4[3])) { +return def; +} +var result = { +'vector' : [v4[0], v4[1], v4[2], v4[3]], +'axis_angle' : [{'x': v4[0], 'y': v4[1], 'z': v4[2]}, v4[3]], +}; +return result; +}, +'SFColor' : function (value, defaultString) +{ +var v3 = this.SFVec3f(value, defaultString); +v3[0] = Math.min(Math.max(v3[0], 0.0), 1.0); +v3[1] = Math.min(Math.max(v3[1], 0.0), 1.0); +v3[2] = Math.min(Math.max(v3[2], 0.0), 1.0); +return v3; +}, +'SFString' : function (value, def) +{ +if (value === null) {value = def;} +return value; +}, +'MFFloat' : function (value, def) +{ +var defReturn = (def == '') ? [] : def; +if (value === null) {return defReturn;} +var mi = value.split(' '); +var rv = []; +for (var i=0; iDocumentation."]; +var version = { +major : Major, +minor : Minor, +patch : Patch, +preRelease : PreRelease, +release : Release, +version : '', +date : RDate, +splashText : SplashText +}; +version.version = version.major + '.' + version.minor + '.' + version.patch; +version.version += (version.preRelease != '') ? '-' + version.preRelease : ''; +version.version += (version.release != '') ? '+' + version.release : ''; +return version; +} +xseen.node.core_NOOP = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.parsing = function (s, e) { +xseen.debug.logInfo ('Parsing init details stub for ' + s); +} +xseen.node.af_Entity = { +'init' : function (e,p) +{ +xseen.node.parsing('A-Frame Entity'); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Assets = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.af_AssetItem = { +'init' : function (e,p) // Only field is SRC. +{ +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Mixin = { +'init' : function (e,p) // Lots of fields -- all nebelous until used +{ +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Appearance = function (e) { +var parameters = { +'aoMap' : e._xseen.fields['ambient-occlusion-map'], +'aoMapIntensity' : e._xseen.fields['ambient-occlusion-map-intensity'], +'color' : e._xseen.fields['color'], +'displacementMap' : e._xseen.fields['displacement-map'], +'displacementScale' : e._xseen.fields['displacement-scale'], +'displacementBias' : e._xseen.fields['displacement-bias'], +'envMap' : e._xseen.fields['env-map'], +'normalMap' : e._xseen.fields['normal-map'], +'normalScale' : e._xseen.fields['normal-scale'], +'wireframe' : e._xseen.fields['wireframe'], +'wireframeLinewidth' : e._xseen.fields['wireframe-linewidth'], +}; +var material = new THREE.MeshPhongMaterial(parameters); +return material; +} +xseen.node.af_Box = { +'init' : function (e,p) +{ +var geometry = new THREE.BoxGeometry( +e._xseen.fields.width, +e._xseen.fields.height, +e._xseen.fields.depth, +e._xseen.fields['segments-width'], +e._xseen.fields['segments-height'], +e._xseen.fields['segments-depth'] +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Cone = { +'init' : function (e,p) +{ +var geometry = new THREE.ConeGeometry( +e._xseen.fields.radius, +e._xseen.fields.height, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-height'], +e._xseen.fields['open-ended'], +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Cylinder = { +'init' : function (e,p) +{ +var geometry = new THREE.CylinderGeometry( +e._xseen.fields['radius-top'], +e._xseen.fields['radius-bottom'], +e._xseen.fields.height, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-height'], +e._xseen.fields['open-ended'], +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Dodecahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.DodecahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Icosahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.IcosahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Octahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.OctahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Sphere = { +'init' : function (e,p) +{ +var geometry = new THREE.SphereGeometry( +e._xseen.fields.radius, +e._xseen.fields['segments-width'], +e._xseen.fields['segments-height'], +e._xseen.fields['phi-start'] * xseen.types.Deg2Rad, +e._xseen.fields['phi-length'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Tetrahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.TetrahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Torus = { +'init' : function (e,p) +{ +var geometry = new THREE.TorusGeometry( +e._xseen.fields.radius, +e._xseen.fields.tube, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-tubular'], +e._xseen.fields.arc * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.unk_Viewpoint = { +'init' : function (e,p) +{ // This should really go in a separate push-down list for Viewpoints +e._xseen.fields._radius0 = Math.sqrt( e._xseen.fields.position[0]*e._xseen.fields.position[0] + +e._xseen.fields.position[1]*e._xseen.fields.position[1] + +e._xseen.fields.position[2]*e._xseen.fields.position[2]); +e._xseen.domNode = e; // Back-link to node if needed later on +e._xseen.position = new THREE.Vector3(e._xseen.fields.position[0], e._xseen.fields.position[1], e._xseen.fields.position[2]); +e._xseen.type = e._xseen.fields.cameratype; +e._xseen.motion = e._xseen.fields.motion; +e._xseen.motionspeed = e._xseen.fields.motionspeed * 1000; +if (e._xseen.motion == 'turntable' || e._xseen.motion == 'tilt') {e._xseen.motionspeed = 1.0/e._xseen.motionspeed;} +if (!e._xseen.sceneInfo.tmp.activeViewpoint) { +e._xseen.sceneInfo.stacks.Viewpoints.pushDown(e._xseen); +e._xseen.sceneInfo.tmp.activeViewpoint = true; +} +e._xseen.handlers = {}; +e._xseen.handlers.setactive = this.setactive; +}, +'fin' : function (e,p) {}, +'setactive' : function (ev) +{ +var xseenNode = this.destination._xseen; +xseenNode.sceneInfo.stacks.Viewpoints.pushDown(xseenNode); // TODO: This is probably not the right way to change VP in the stack +xseenNode.sceneInfo.element._xseen.renderer.activeCamera = +xseenNode.sceneInfo.element._xseen.renderer.cameras[xseenNode.fields.type]; +xseenNode.sceneInfo.element._xseen.renderer.activeRender = +xseenNode.sceneInfo.element._xseen.renderer.renderEffects[xseenNode.fields.type]; +if (xseenNode.fields.type != 'stereo') { +xseenNode.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, xseenNode.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); +} +}, +}; +xseen.node.controls_Navigation = { +'init' : function (e,p) +{ // This should really go in a separate push-down list for Viewpoints +e._xseen.domNode = e; // Back-link to node if needed later on +e._xseen.speed = e._xseen.fields.speed; +if (e._xseen.setup == 'examine') {e._xseen.setup == 'trackball';} +e._xseen.type = 'none'; +e._xseen.setup = e._xseen.fields.type; +if (!(e._xseen.setup == 'orbit' || e._xseen.setup == 'trackball')) {e._xseen.setup = 'none';} +if (!e._xseen.sceneInfo.tmp.activeNavigation) { +e._xseen.sceneInfo.stacks.Navigation.pushDown(e._xseen); +e._xseen.sceneInfo.tmp.activeNavigation = true; +} +e._xseen.handlers = {}; +e._xseen.handlers.setactive = this.setactive; +}, +'fin' : function (e,p) {}, +'setactive' : function (ev) +{ +}, +}; +xseen.node.lighting_Light = { +'init' : function (e,p) +{ +var color = xseen.types.Color3toInt (e._xseen.fields.color); +var intensity = e._xseen.fields.intensity - 0; +var lamp, type=e._xseen.fields.type.toLowerCase(); +if (type == 'point') { +lamp = new THREE.PointLight (color, intensity); +lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); +lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); +} else if (type == 'spot') { +lamp = new THREE.SpotLight (color, intensity); +lamp.position.set(0-e._xseen.fields.direction[0], 0-e._xseen.fields.direction[1], 0-e._xseen.fields.direction[2]); +lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); +lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); +lamp.angle = Math.max(0.0, Math.min(1.5707963267948966192313216916398, e._xseen.fields.cutoffangle)); +lamp.penumbra = 1 - Math.max(0.0, Math.min(lamp.angle, e._xseen.fields.beamwidth)) / lamp.angle; +} else { // DirectionalLight (by default) +lamp = new THREE.DirectionalLight (color, intensity); +lamp.position.x = 0-e._xseen.fields.direction[0]; +lamp.position.y = 0-e._xseen.fields.direction[1]; +lamp.position.z = 0-e._xseen.fields.direction[2]; +} +p._xseen.children.push(lamp); +lamp = null; +} +, +'fin' : function (e,p) +{ +} +}; +xseen.node.appearance_Material = { +'init' : function (e,p) +{ +var transparency = e._xseen.fields.transparency - 0; +var shininess = e._xseen.fields.shininess - 0; +var colorDiffuse = xseen.types.Color3toInt (e._xseen.fields.diffusecolor); +var colorEmissive = xseen.types.Color3toInt (e._xseen.fields.emissivecolor); +var colorSpecular = xseen.types.Color3toInt (e._xseen.fields.specularcolor); +p._xseen.material = new THREE.MeshPhongMaterial( { +'color' : colorDiffuse, +'emissive' : colorEmissive, +'specular' : colorSpecular, +'shininess' : shininess, +'opacity' : 1.0-transparency, +'transparent' : (transparency > 0.0) ? true : false +} ); +e._xseen.animate['diffusecolor'] = p._xseen.material.color; +e._xseen.animate['emissivecolor'] = p._xseen.material.emissive; +e._xseen.animate['specularcolor'] = p._xseen.material.specular; +e._xseen.animate['transparency'] = p._xseen.material.opacity; +e._xseen.animate['shininess'] = p._xseen.material.shininess; +}, +'fin' : function (e,p) {} +}; +xseen.node.appearance_ImageTexture = { +'init' : function (e,p) +{ +p._xseen.texture = xseen.loader.ImageLoader.load(e._xseen.fields.url); +p._xseen.texture.wrapS = (e._xseen.fields.repeats) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; +p._xseen.texture.wrapT = (e._xseen.fields.repeatt) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; +}, +'fin' : function (e,p) {} +}; +xseen.node.appearance_Appearance = { +'init' : function (e,p) {}, +'fin' : function (e,p) +{ +if (typeof(e._xseen.texture) !== 'undefined' && e._xseen.texture !== null) { +e._xseen.material.map = e._xseen.texture; +} +p._xseen.appearance = e._xseen.material; +} +}; +xseen.node.geometry_Coordinate = { +'init' : function (e,p) +{ +var vertices = []; +for (var i=0; i 0) ? true : false; +var useColor = (color.length > 0) ? true : false; +var maxIndex = Math.max.apply(null, indices); +var minIndex = Math.min.apply(null, indices); +if (maxIndex >= vertices.length) { +console.log ('Maximum index ('+maxIndex+') exceeds vertex count ('+vertices.length+'). No geometry is created'); +return; +} +if (useNormals && maxIndex >= normals.length) { +console.log ('Maximum index ('+maxIndex+') exceeds normal count ('+normals.length+'). No geometry is created'); +return; +} +if (useColor && maxIndex >= color.length) { +console.log ('Maximum index ('+maxIndex+') exceeds color count ('+color.length+'). No geometry is created'); +return; +} +if (minIndex < 0) { +console.log ('Minimum index ('+minIndex+') less than zero. No geometry is created'); +return; +} +if (indices.length % 3 != 0) { +console.log ('Number of indices ('+indices.length+') not divisible by 3. No geometry is created'); +return; +} +var geometry = new THREE.Geometry(); +var normal_pz = new THREE.Vector3 (0, 0, 1); +var normal_mz = new THREE.Vector3 (0, 0, -1); +for (var i=0; i 0) {clip.duration = e._xseen.fields.duration;} +e._xseen.mixer.clipAction( clip ).play(); +} ); +} else { // Play a specific animation +var clip = THREE.AnimationClip.findByName(response.animations, e._xseen.fields.playonload); +var action = e._xseen.mixer.clipAction (clip); +action.play(); +} +} +} +} +}; +xseen.node.x_Route = { +'init' : function (e,p) +{ +var dest = e._xseen.fields.destination; +var hand = e._xseen.fields.handler; +var externalHandler = false; +if (e._xseen.fields.source == '' || +typeof(window[hand]) !== 'function' && +(dest == '' || e._xseen.fields.event == '' || e._xseen.fields.field == '')) { +xseen.debug.logError ('Route node missing field. No route setup. Source: '+e._xseen.fields.source+'.'+e._xseen.fields.event+'; Destination: '+dest+'.'+e._xseen.fields.field+'; Handler: '+hand); +return; +} else if (typeof(window[hand]) === 'function') { +externalHandler = true; +} +var eSource = document.getElementById (e._xseen.fields.source); +if (! externalHandler) { +var eDestination = document.getElementById (dest); +if (typeof(eSource) === 'undefined' || typeof(eDestination) === 'undefined') { +xseen.debug.logError ('Source or Destination node does not exist. No route setup'); +return; +} +var fField = xseen.nodes._getFieldInfo (eDestination.nodeName, e._xseen.fields.field); +if (typeof(fField) === 'undefined' || !fField.good) { +xseen.debug.logError ('Destination field does not exist or incorrectly specified. No route setup'); +return; +} +xseen.Events.addHandler (e, eSource, e._xseen.fields.event, eDestination, fField); +} else { +var handler = window[hand]; +eSource.addEventListener ('xseen', handler); +} +}, +'fin' : function (e,p) +{ +}, +'evHandler' : function (u) +{ +var de = u.e; +var df = u.f; +return de._xseen.handlers[df.handlerName]; +}, +}; +xseen.node.core_NOOP = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.core_WorldInfo = { +'init' : function (e,p) {parsing('WorldInfo', e)}, +'fin' : function (e,p) {} +}; +function parsing (s, e) { +xseen.debug.logInfo ('Parsing init details stub for ' + s); +} +xseen.node.unk_Shape = { +'init' : function (e,p) {}, +'fin' : function (e,p) +{ +if (typeof(e._xseen.materialProperty) !== 'undefined') { +e._xseen.appearance.vertexColors = THREE.VertexColors; +e._xseen.appearance._needsUpdate = true; +e._xseen.appearance.needsUpdate = true; +} +var mesh = new THREE.Mesh (e._xseen.geometry, e._xseen.appearance); +mesh.userData = e; +p._xseen.children.push(mesh); +p._xseen.sceneInfo.selectable.push(mesh); +mesh = null; +} +}; +xseen.node.grouping_Transform = { +'init' : function (e,p) +{ +var group = new THREE.Group(); +if (e.nodeName == "TRANSFORM") { +var rotation = xseen.types.Rotation2Quat(e._xseen.fields.rotation); +group.name = 'Transform children [' + e.id + ']'; +group.position.x = e._xseen.fields.translation[0]; +group.position.y = e._xseen.fields.translation[1]; +group.position.z = e._xseen.fields.translation[2]; +group.scale.x = e._xseen.fields.scale[0]; +group.scale.y = e._xseen.fields.scale[1]; +group.scale.z = e._xseen.fields.scale[2]; +group.quaternion.x = rotation.x; +group.quaternion.y = rotation.y; +group.quaternion.z = rotation.z; +group.quaternion.w = rotation.w; +e._xseen.animate['translation'] = group.position; +e._xseen.animate['rotation'] = group.quaternion; +e._xseen.animate['scale'] = group.scale; +} +e._xseen.sceneNode = group; +}, +'fin' : function (e,p) +{ +e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +e._xseen.sceneNode.add(child); +}); +p._xseen.children.push(e._xseen.sceneNode); +} +}; +xseen.node.networking_Inline = { +'init' : function (e,p) +{ +if (typeof(e._xseen.processedUrl) === 'undefined' || !e._xseen.requestedUrl) { +var uri = xseen.parseUrl (e._xseen.fields.url); +var type = uri.extension; +e._xseen.loadGroup = new THREE.Group(); +e._xseen.loadGroup.name = 'Inline content [' + e.id + ']'; +console.log ('Created Inline Group with UUID ' + e._xseen.loadGroup.uuid); +var userdata = {'requestType':'x3d', 'e':e, 'p':p} +if (type.toLowerCase() == 'json') { +userdata.requestType = 'json'; +xseen.loadMgr.loadJson (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); +} else { +xseen.loadMgr.loadXml (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); +} +e._xseen.requestedUrl = true; +} +p._xseen.children.push(e._xseen.loadGroup); +console.log ('Using Inline Group with UUID ' + e._xseen.loadGroup.uuid); +}, +'fin' : function (e,p) +{ +}, +'loadSuccess' : +function (response, userdata, xhr) { +userdata.e._xseen.processedUrl = true; +userdata.e._xseen.loadResponse = response; +console.log("download successful for "+userdata.e.id); +if (userdata.requestType == 'json') { +var tmp = {'scene': response}; +response = null; +response = (new JSONParser()).parseJavaScript(tmp); +} +var start = {'_xseen':0}; +var findSceneTag = function (fragment) { +if (typeof(fragment._xseen) === 'undefined') {fragment._xseen = {'childCount': -1};} +if (fragment.nodeName.toLowerCase() == 'scene') { +start = fragment; +return; +} else if (fragment.children.length > 0) { +for (fragment._xseen.childCount=0; fragment._xseen.childCount 0) { +userdata.e.appendChild(start.children[0]); +} +xseen.Parse(userdata.e, userdata.p, userdata.p._xseen.sceneInfo); +userdata.e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +userdata.e._xseen.loadGroup.add(child); +console.log ('...Adding ' + child.type + ' (' + child.name + ') to Inline Group? with UUID ' + userdata.e._xseen.loadGroup.uuid + ' (' + userdata.e._xseen.loadGroup.name + ')'); +}); +userdata.p._xseen.sceneInfo.scene.updateMatrixWorld(); +} else { +console.log("Found illegal X3D file -- no 'scene' tag"); +} +} +}; +xseen.node.core_Scene = { +'DEFAULT' : { +'Viewpoint' : { +'Position' : new THREE.Vector3 (0, 0, 10), +'Orientation' : '0 1 0 0', // TODO: fix (and below) when handling orientation +'Type' : 'perpsective', +'Motion' : 'none', +'MotionSpeed' : 1.0, +}, +'Navigation' : { +'Speed' : 1.0, // 16 spr (1 revolution per 16 seconds), in mseconds. +'Type' : 'none', +'Setup' : 'none', +} +}, +'init' : function (e,p) +{ +xseen.sceneInfo[0].stacks.Viewpoints.setDefault( +{ +'position' : this.DEFAULT.Viewpoint.Position, +'type' : this.DEFAULT.Viewpoint.Type, +'motion' : this.DEFAULT.Viewpoint.Motion, +'motionspeed': this.DEFAULT.Viewpoint.MotionSpeed / 1000, +'domNode' : e, +'fields' : {}, +} +); +xseen.sceneInfo[0].stacks.Navigation.setDefault( +{ +'speed' : this.DEFAULT.Navigation.Speed / 1000, +'type' : this.DEFAULT.Navigation.Type, +'setup' : this.DEFAULT.Navigation.Setup, +'domNode' : e, +'fields' : {}, +} +); +var width = e._xseen.sceneInfo.size.width; +var height = e._xseen.sceneInfo.size.height; +var x_renderer = new THREE.WebGLRenderer(); +x_renderer.setSize (width, height); +var perspectiveCamera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 ); +var orthoCamera = new THREE.OrthographicCamera( 75, width / height, 0.1, 1000 ); +perspectiveCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position +perspectiveCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position +perspectiveCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position +orthoCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position +orthoCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position +orthoCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position +var x_effect = new THREE.StereoEffect(x_renderer); +e.appendChild (x_renderer.domElement); +e._xseen.renderer = { +'canvas' : e._xseen.sceneInfo.scene, +'width' : width, +'height' : height, +'cameras' : { +'perspective' : perspectiveCamera, +'ortho' : orthoCamera, +'stereo' : perspectiveCamera, +}, // Removed .sceneInfo camera because this node defines the camera +'effects' : x_effect, +'renderEffects' : { +'normal' : x_renderer, +'perspective' : x_renderer, +'ortho' : x_renderer, +'stereo' : x_effect, +}, +'activeRender' : {}, +'activeCamera' : {}, +'controls' : {}, // Used for navigation +}; +e._xseen.renderer.activeRender = e._xseen.renderer.renderEffects.normal; +e._xseen.renderer.activeCamera = e._xseen.renderer.cameras.perspective; +}, +'fin' : function (e,p) +{ +e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +console.log('Adding child of type ' + child.type + ' (' + child.name + ')'); +e._xseen.renderer.canvas.add(child); +}); +xseen.dumpSceneGraph (); +var vp = xseen.sceneInfo[0].stacks.Viewpoints.getActive(); +var nav = xseen.sceneInfo[0].stacks.Navigation.getActive(); +var currentCamera = e._xseen.renderer.activeCamera; +var currentRenderer = e._xseen.renderer.activeRender; +currentCamera.position.x = vp.position.x; +currentCamera.position.y = vp.position.y; +currentCamera.position.z = vp.position.z; +e._xseen.renderer.controls = xseen.Navigation.setup[nav.setup] (currentCamera, currentRenderer); +xseen.debug.logInfo("Ready to kick off rendering loop"); +xseen.renderFrame(); +}, +}; +xseen.node.env_Background = { +'init' : function (e,p) +{ +var color = new THREE.Color(e._xseen.fields.skycolor[0], e._xseen.fields.skycolor[1], e._xseen.fields.skycolor[2]); +var textureCube = new THREE.CubeTextureLoader() +.load ([e._xseen.fields.srcright, +e._xseen.fields.srcleft, +e._xseen.fields.srctop, +e._xseen.fields.srcbottom, +e._xseen.fields.srcfront, +e._xseen.fields.srcback], +this.loadSuccess({'e':e, 'p':p}) +); +e._xseen.sceneInfo.scene.background = color; +}, +'fin' : function (e,p) +{ +p._xseen.appearance = e._xseen.material; +}, +'loadSuccess' : function (userdata) +{ +var e = userdata.e; +var p = userdata.p; +return function (textureCube) +{ +e._xseen.processedUrl = true; +e._xseen.loadTexture = textureCube; +e._xseen.sceneInfo.scene.background = textureCube; +} +}, +}; +xseen.node.x_Animate = { +'init' : function (e,p) +{ +var delay = e._xseen.fields.delay * 1000; // Convert to milliseconds +var duration = e._xseen.fields.duration * 1000; // Convert to milliseconds +var repeat = (e._xseen.fields.repeat < 0) ? Infinity : e._xseen.fields.repeat; +var interpolator = e._xseen.fields.interpolator; +var easing = e._xseen.fields.easing; +var fields = xseen.parseTable[p.localName.toLowerCase()].fields; +var fieldIndex = xseen.parseTable[p.localName.toLowerCase()].fieldIndex; +var toField = e._xseen.fields.field; +var toFieldIndex = fieldIndex[toField]; +if (typeof(fields[toFieldIndex]) === 'undefined') { +xseen.debug.logInfo("Field '" + toField + "' not found in parent (" + p.localName.toLowerCase() + "). No animation performed."); +return; +} +var fieldObject = fields[toFieldIndex].clone().setFieldName('to'); // Parse table entry for 'toField' +var to = xseen.nodes._parseField(fieldObject, e); // Parsed data -- need to convert to THREE format +var interpolation; +if (fieldObject.type == 'SFVec3f') { +interpolation = TWEEN.Interpolation.Linear; +to = xseen.types.Vector3(to); +xseen.debug.logInfo("Interpolating field '" + toField + "' as 3-space."); +} else if (fieldObject.type == 'SFColor') { +interpolation = this.Interpolator.color; +to = new THREE.Color (xseen.types.Color3toInt(to)); +xseen.debug.logInfo("Interpolation field '" + toField + "' as color."); +} else if (fieldObject.type == 'SFRotation') { +interpolation = this.Interpolator.slerp; +to = xseen.types.Rotation2Quat(to); +xseen.debug.logInfo("Interpolation field '" + toField + "' as rotation."); +} else { +xseen.debug.logInfo("Field '" + toField + "' not converted to THREE format. No animation performed."); +return; +} +var fieldTHREE = p._xseen.animate[toField]; // THREE field for animation +var tween = new TWEEN.Tween(fieldTHREE) +.to(to, duration) +.delay(delay) +.repeat(repeat) +.interpolation(interpolation); +var easingType = e._xseen.fields.easingtype; +easingType = easingType.charAt(0).toUpperCase() + easingType.slice(1); +easing = (easingType != 'Linear' && easing == '') ? 'inout' : easing; +if (easing != '') { +easing = easing.replace('in', 'In').replace('out', 'Out'); +easingType = (easingType == 'Linear') ? 'Quadratic' : easingType; +e._xseen.fields.easing = easing; +e._xseen.fields.easingtype = easingType; +tween.easing(TWEEN.Easing[easingType][easing]); +} +e._xseen.initialValue = fieldTHREE.clone(); +e._xseen.animatingField = fieldTHREE; +e._xseen.handlers = {}; +e._xseen.handlers.setstart = this.setstart; +e._xseen.handlers.setstop = this.setstop; +e._xseen.handlers.setpause = this.setpause; +e._xseen.handlers.setresetstart = this.setresetstart; +e._xseen.animating = tween; +p._xseen.animation.push (tween); +tween.start(); +}, +'fin' : function (e,p) {}, +'setstart' : function (ev) +{ +console.log ('Starting animation'); +this.destination._xseen.animating.start(); +}, +'setstop' : function (ev) +{ +console.log ('Stopping animation'); +this.destination._xseen.animating.stop(); +}, +'setpause' : function (ev) +{ +console.log ('Pausing (really stopping) animation'); +this.destination._xseen.animating.stop(); +}, +'setresetstart' : function (ev) // TODO: Create seperate 'reset' method +{ +console.log ('Reset and start animation'); +this.destination._xseen.animatingField = this.destination._xseen.initialValue; +this.destination._xseen.animating.start(); +}, +'Interpolator' : { +'slerp' : function (v,k) +{ +var m = v.length - 1; +var f = m * k; +var i = Math.floor(f); +if (k < 0) { +return v[0].slerp(v[1], f); +} +if (k > 1) { +return v[m].slerp(v[m-1], m-f); +} +return v[i].slerp (v[i + 1 > m ? m : i + 1], f-i); +}, +'color' : function (v,k) +{ +var m = v.length - 1; +var f = m * k; +var i = Math.floor(f); +var fn = this.slerpCompute; +if (k < 0) { +return v[0].lerp(v[1], f); +} +if (k > 1) { +return v[m].lerp(v[m-1], m-f); +} +return v[i].lerp (v[i + 1 > m ? m : i + 1], f - i); +}, +}, +}; +xseen._addAframeAppearance = function (node) { +node +.addField('ambient-occlusion-map', 'SFString', '') +.addField('ambient-occlusion-map-intensity', 'SFFloat', 1) +.addField('ambient-occlusion-texture-offset', 'SFVec2f', '0 0') +.addField('ambient-occlusion-texture-repeat', 'SFVec2f', '1 1') +.addField('color', 'Color', '#FFF') +.addField('displacement-bias', 'SFFloat', 0.5) +.addField('displacement-map', 'SFString', '') +.addField('displacement-scale', 'SFFloat', 1) +.addField('displacement-texture-offset', 'SFVec2f', '0 0') +.addField('displacement-texture-repeat', 'SFVec2f', '1 1') +.addField('env-map', 'SFString', '') +.addField('fog', 'SFBool', true) +.addField('metalness', 'SFFloat', 0) +.addField('normal-map', 'SFString', '') +.addField('normal-scale', 'SFVec2f', '1 1') +.addField('normal-texture-offset', 'SFVec2f', '0 0') +.addField('normal-texture-repeat', 'SFVec2f', '1 1') +.addField('repeat', 'SFVec2f', '1 1') +.addField('roughness', 'SFFloat', 0.5) +.addField('spherical-env-map', 'SFString', '') +.addField('src', 'SFString', '') +.addField('wireframe', 'SFBool', false) +.addField('wireframe-linewidth', 'SFInt', 2) +.addNode(); +} +xseen.nodes._defineNode('a-entity', 'A-Frame', 'af_Entity') +.addField('geometry', 'SFString', '') +.addField('material', 'SFString', '') +.addField('light', 'SFString', '') +.addNode(); +var node; +node = xseen.nodes._defineNode('a-box', 'A-Frame', 'af_Box') +.addField('depth', 'SFFloat', 1) +.addField('height', 'SFFloat', 1) +.addField('width', 'SFFloat', 512) +.addField('segments-depth', 'SFInt', 1) +.addField('segments-height', 'SFInt', 1) +.addField('segments-width', 'SFInt', 1); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-cone', 'A-Frame', 'af_Cone') +.addField('height', 'SFFloat', 1) +.addField('radius', 'SFFloat', 1) +.addField('open-ended', 'SFBool', false) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 1) +.addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-cylinder', 'A-Frame', 'af_Cylinder') +.addField('height', 'SFFloat', 1) +.addField('open-ended', 'SFBool', false) +.addField('radius-bottom', 'SFFloat', 1) +.addField('radius-top', 'SFFloat', 1) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 1) +.addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-dodecahedron', 'A-Frame', 'af_Dodecahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-icosahedron', 'A-Frame', 'af_Icosahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-octahedron', 'A-Frame', 'af_Octahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-sphere', 'A-Frame', 'af_Sphere') +.addField('radius', 'SFFloat', 1) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 180) +.addField('phi-start', 'SFFloat', 0) +.addField('phi-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 18) +.addField('segments-width', 'SFInt', 36); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-tetrahedron', 'A-Frame', 'af_Tetrahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-torus', 'A-Frame', 'af_Torus') +.addField('radius', 'SFFloat', 2) +.addField('tube', 'SFFloat', 1) +.addField('arc', 'SFFloat', 360) +.addField('segments-radial', 'SFInt', 8) +.addField('segments-tubular', 'SFInt', 6); +xseen._addAframeAppearance (node); +xseen.nodes._defineNode('a-assets', 'A-Frame', 'af_Assets') +.addNode(); +xseen.nodes._defineNode('a-asset-item', 'A-Frame', 'af_AssetItem') +.addField('src', 'SFString', '') +.addNode(); +xseen.nodes._defineNode('a-mixin', 'A-Frame', 'af_Mixin') +.addField('*', 'SFString', '') +.addNode(); +xseen.nodes._defineNode('Cone', 'Geometry3D', 'geometry3D_Cone') +.addField('bottomRadius', 'SFFloat', 1) +.addField('height', 'SFFloat', 2) +.addField('bottom', 'SFBool', true) +.addField('side', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode('Box', 'Geometry3D', 'geometry3D_Box') +.addField('size', 'SFVec3f', [1,1,1]) +.addNode(); +xseen.nodes._defineNode('Sphere', 'Geometry3D', 'geometry3D_Sphere') +.addField('radius', 'SFFloat', '1') +.addNode(); +xseen.nodes._defineNode('Cylinder', 'Geometry3D', 'geometry3D_Cylinder') +.addField('radius', 'SFFloat', 1) +.addField('height', 'SFFloat', 2) +.addField('bottom', 'SFBool', true) +.addField('side', 'SFBool', true) +.addField('top', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode ('Material', 'Appearance', 'appearance_Material') +.addField({name:'diffuseColor', datatype:'SFColor', defaultValue:[.8,.8,.8], animatable:true}) +.addField({name:'emissiveColor',datatype: 'SFColor', defaultValue:[0,0,0], animatable:true}) +.addField({name:'specularColor', datatype:'SFColor', defaultValue:[0,0,0], animatable:true}) +.addField({name:'transparency', datatype:'SFFloat', defaultValue:'0', animatable:true}) +.addField({name:'shininess', datatype:'SFFloat', defaultValue:'0', animatable:true}) +.addNode(); +xseen.nodes._defineNode ('Transform', 'Grouping', 'grouping_Transform') +.addField({name:'translation', datatype:'SFVec3f', defaultValue:[0,0,0], animatable:true}) +.addField({name:'scale', datatype:'SFVec3f', defaultValue:[1,1,1], animatable:true}) +.addField({name:'rotation', datatype:'SFRotation', defaultValue:xseen.types.SFRotation('0 1 0 0',''), animatable:true}) +.addNode(); +xseen.nodes._defineNode ('Group', 'Grouping', 'grouping_Transform') +.addNode(); +xseen.nodes._defineNode ('Light', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) // DirectionalLight +.addField('location', 'SFVec3f', [0,0,0]) // PointLight & SpotLight +.addField('radius', 'SFFloat', '100') // PointLight & SpotLight +.addField('attenuation', 'SFVec3f', [1,0,0]) // PointLight & SpotLight +.addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // SpotLight +.addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // SpotLight +.addField('color', 'SFColor', [1,1,1]) // General +.addField('intensity', 'SFFloat', '1') // General +.addField({name:'type', datatype:'EnumerateString', defaultValue:'Directional', enumerated:['Directional', 'Spot', 'Point'], animatable:true}) +.addNode(); +xseen.nodes._defineNode ('DirectionalLight', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Directional') +.addNode(); +xseen.nodes._defineNode ('PointLight', 'Lighting', 'lighting_Light') +.addField('location', 'SFVec3f', [0,0,0]) +.addField('radius', 'SFFloat', '100') +.addField('attenuation', 'SFVec3f', [1,0,0]) +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Point') +.addNode(); +xseen.nodes._defineNode ('SpotLight', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) +.addField('radius', 'SFFloat', '100') +.addField('attenuation', 'SFVec3f', [1,0,0]) +.addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // pi/4 +.addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // pi/2 +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Spot') +.addNode(); +xseen.nodes._defineNode ('Viewpoint', 'Controls', 'unk_Viewpoint') +.addField('position', 'SFVec3f', '0 0 10') +.addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) +.addField('description', 'SFString', '') +.addField({name:'cameratype', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) +.addField({name:'type', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) +.addField({name:'motion', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'turntable', 'tilt'], animatable:false}) +.addField('motionspeed', 'SFFloat', 16) +.addField('active', 'SFBool', true) // incoming event +.addNode(); +xseen.nodes._defineNode ('NavigationMode', 'Controls', 'controls_Navigation') +.addField('speed', 'SFFloat', 1.) +.addField({name:'type', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'orbit', 'fly', 'examine', 'trackball'], animatable:false}) +.addNode(); +xseen.nodes._defineNode ('Camera', 'Controls', 'unk_Viewpoint') +.addField('position', 'SFVec3f', [0,0,10]) +.addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) +.addNode(); +xseen.nodes._defineNode ('Inline', 'Networking', 'networking_Inline') +.addField('url', 'SFString', '') +.addNode(); +xseen.nodes._defineNode ('scene', 'Core', 'core_Scene') +.addNode(); +xseen.nodes._defineNode ('canvas', 'Core', 'core_NOOP') +.addNode(); +xseen.nodes._defineNode ('WorldInfo', 'Core', 'core_WorldInfo') +.addNode(); +xseen.nodes._defineNode ('Appearance', 'Appearance', 'appearance_Appearance') +.addNode(); +xseen.nodes._defineNode ('ImageTexture', 'Appearance', 'appearance_ImageTexture') +.addField('url', 'SFString', '') +.addField('repeatS', 'SFBool', true) +.addField('repeatT', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode ('Shape', 'Shape', 'unk_Shape') +.addNode(); +xseen.nodes._defineNode('Background', 'Environmental', 'env_Background') +.addField('skyColor', 'SFColor', [0,0,0]) +.addField('srcFront', 'SFString', '') +.addField('srcBack', 'SFString', '') +.addField('srcTop', 'SFString', '') +.addField('srcBottom', 'SFString', '') +.addField('srcLeft', 'SFString', '') +.addField('srcRight', 'SFString', '') +.addField('backgroundIsCube', 'SFBool', 'true') +.addNode(); +xseen.nodes._defineNode('TriangleSet', 'Geometry', 'geometry_TriangleSet') +.addField('ccw', 'SFBool', 'true') +.addField('colorPerVertex', 'SFBool', 'true') +.addField('solid', 'SFBool', 'true') +.addNode(); +xseen.nodes._defineNode('IndexedTriangleSet', 'Geometry', 'geometry_IndexedTriangleSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('index', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('Coordinate', 'Geometry', 'geometry_Coordinate') +.addField('point', 'MFVec3f', []) +.addNode(); +xseen.nodes._defineNode('Normal', 'Geometry', 'geometry_Normal') +.addField('vector', 'MFVec3f', []) +.addNode(); +xseen.nodes._defineNode('Color', 'Geometry', 'geometry_Color') +.addField('color', 'MFColor', []) +.addNode(); +xseen.nodes._defineNode('IndexedFaceSet', 'Geometry', 'geometry_IndexedFaceSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('coordIndex', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('IndexedQuadSet', 'Geometry', 'geometry_IndexedQuadSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('index', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('QuadSet', 'Geometry', 'geometry_QuadSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode('model', 'XSeen', 'x_Model') +.addField('src', 'SFString', '') +.addField('playonload', 'SFString', '') +.addField('duration', 'SFFloat', '-1') +.addNode(); +xseen.nodes._defineNode('animate', 'XSeen', 'x_Animate') +.addField('field', 'SFString', '') +.addField('to', 'MFFloat', '') // Needs to be 'field' datatype. That is not known until node-parse. For now insist on numeric array +.addField('delay', 'SFTime', 0) +.addField('duration', 'SFTime', 0) +.addField('repeat', 'SFInt', 0) +.addField({name:'interpolator', datatype:'EnumerateString', defaultValue:'position', enumerated:['position', 'rotation', 'color'], animatable:false}) +.addField({name:'Easing', datatype:'EnumerateString', defaultValue:'', enumerated:['', 'in', 'out', 'inout'], animatable:false}) +.addField({name:'EasingType', datatype:'EnumerateString', defaultValue:'linear', enumerated:['linear', 'quadratic', 'sinusoidal', 'exponential', 'elastic', 'bounce'], animatable:false}) +.addField('start', 'SFBool', true) // incoming event, need to set timer trigger +.addField('stop', 'SFBool', true) // incoming event, need to set timer trigger +.addField('resetstart', 'SFBool', true) // incoming event, need to set timer trigger +.addField('pause', 'SFBool', true) // incoming event, need to set timer trigger +.addNode(); +xseen.nodes._defineNode('route', 'XSeen', 'x_Route') +.addField('source', 'SFString', '') +.addField('event', 'SFString', '') +.addField('destination', 'SFString', '') +.addField('field', 'SFString', '') +.addField('handler', 'SFString', '') +.addNode(); \ No newline at end of file diff --git a/Release/XSeen.0.4.6-rc1+25_0e9dbd1.js b/Release/XSeen.0.4.6-rc1+25_0e9dbd1.js new file mode 100644 index 0000000..23f143f --- /dev/null +++ b/Release/XSeen.0.4.6-rc1+25_0e9dbd1.js @@ -0,0 +1,14404 @@ +/* + * XSeen V0.4.6-rc1+25_0e9dbd1 + * Built Tue Jul 11 08:12:44 2017 + * + +Dual licensed under the MIT and GPL licenses. + +==[MIT]==================================================================== +Copyright (c) 2017, Daly Realism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +==[GPL]==================================================================== + +XSeen - Declarative 3D for HTML + +Copyright (C) 2017, Daly Realism + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +=== COPYRIGHT +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (C) 2017, Daly Realism for XSeen +Copyright, Fraunhofer for X3DOM +Copyright, Mozilla for A-Frame +Copyright, THREE and Khronos for various parts of THREE.js +Copyright (C) 2017, John Carlson for JSON->XML converter (JSONParser.js) + +=== +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + */ +// File: utils/ColladaLoader.js +/** +* @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com +* @author Tony Parisi / http://www.tonyparisi.com/ +*/ + +THREE.ColladaLoader = function () { + + var COLLADA = null; + var scene = null; + var visualScene; + var kinematicsModel; + + var readyCallbackFunc = null; + + var sources = {}; + var images = {}; + var animations = {}; + var controllers = {}; + var geometries = {}; + var materials = {}; + var effects = {}; + var cameras = {}; + var lights = {}; + + var animData; + var kinematics; + var visualScenes; + var kinematicsModels; + var baseUrl; + var morphs; + var skins; + + var flip_uv = true; + var preferredShading = THREE.SmoothShading; + + var options = { + // Force Geometry to always be centered at the local origin of the + // containing Mesh. + centerGeometry: false, + + // Axis conversion is done for geometries, animations, and controllers. + // If we ever pull cameras or lights out of the COLLADA file, they'll + // need extra work. + convertUpAxis: false, + + subdivideFaces: true, + + upAxis: 'Y', + + // For reflective or refractive materials we'll use this cubemap + defaultEnvMap: null + + }; + + var colladaUnit = 1.0; + var colladaUp = 'Y'; + var upConversion = null; + + function load ( url, readyCallback, progressCallback, failCallback ) { + + var length = 0; + + if ( document.implementation && document.implementation.createDocument ) { + + var request = new XMLHttpRequest(); + + request.onreadystatechange = function() { + + if ( request.readyState === 4 ) { + + if ( request.status === 0 || request.status === 200 ) { + + if ( request.response ) { + + readyCallbackFunc = readyCallback; + parse( request.response, undefined, url ); + + } else { + + if ( failCallback ) { + + failCallback( { type: 'error', url: url } ); + + } else { + + console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); + + } + + } + + }else{ + + if( failCallback ){ + + failCallback( { type: 'error', url: url } ); + + }else{ + + console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' ); + + } + + } + + } else if ( request.readyState === 3 ) { + + if ( progressCallback ) { + + if ( length === 0 ) { + + length = request.getResponseHeader( "Content-Length" ); + + } + + progressCallback( { total: length, loaded: request.responseText.length } ); + + } + + } + + }; + + request.open( "GET", url, true ); + request.send( null ); + + } else { + + alert( "Don't know how to parse XML!" ); + + } + + } + + function parse( text, callBack, url ) { + + COLLADA = new DOMParser().parseFromString( text, 'text/xml' ); + callBack = callBack || readyCallbackFunc; + + if ( url !== undefined ) { + + var parts = url.split( '/' ); + parts.pop(); + baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; + + } + + parseAsset(); + setUpConversion(); + images = parseLib( "library_images image", _Image, "image" ); + materials = parseLib( "library_materials material", Material, "material" ); + effects = parseLib( "library_effects effect", Effect, "effect" ); + geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); + cameras = parseLib( "library_cameras camera", Camera, "camera" ); + lights = parseLib( "library_lights light", Light, "light" ); + controllers = parseLib( "library_controllers controller", Controller, "controller" ); + animations = parseLib( "library_animations animation", Animation, "animation" ); + visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); + kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); + + morphs = []; + skins = []; + + visualScene = parseScene(); + scene = new THREE.Group(); + + for ( var i = 0; i < visualScene.nodes.length; i ++ ) { + + scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); + + } + + // unit conversion + scene.scale.multiplyScalar( colladaUnit ); + + createAnimations(); + + kinematicsModel = parseKinematicsModel(); + createKinematics(); + + var result = { + + scene: scene, + morphs: morphs, + skins: skins, + animations: animData, + kinematics: kinematics, + dae: { + images: images, + materials: materials, + cameras: cameras, + lights: lights, + effects: effects, + geometries: geometries, + controllers: controllers, + animations: animations, + visualScenes: visualScenes, + visualScene: visualScene, + scene: visualScene, + kinematicsModels: kinematicsModels, + kinematicsModel: kinematicsModel + } + + }; + + if ( callBack ) { + + callBack( result ); + + } + + return result; + + } + + function setPreferredShading ( shading ) { + + preferredShading = shading; + + } + + function parseAsset () { + + var elements = COLLADA.querySelectorAll('asset'); + + var element = elements[0]; + + if ( element && element.childNodes ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'unit': + + var meter = child.getAttribute( 'meter' ); + + if ( meter ) { + + colladaUnit = parseFloat( meter ); + + } + + break; + + case 'up_axis': + + colladaUp = child.textContent.charAt(0); + break; + + } + + } + + } + + } + + function parseLib ( q, classSpec, prefix ) { + + var elements = COLLADA.querySelectorAll(q); + + var lib = {}; + + var i = 0; + + var elementsLength = elements.length; + + for ( var j = 0; j < elementsLength; j ++ ) { + + var element = elements[j]; + var daeElement = ( new classSpec() ).parse( element ); + + if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); + lib[ daeElement.id ] = daeElement; + + } + + return lib; + + } + + function parseScene() { + + var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; + + if ( sceneElement ) { + + var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); + return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; + + } else { + + return null; + + } + + } + + function parseKinematicsModel() { + + var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; + + if ( kinematicsModelElement ) { + + var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); + return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; + + } else { + + return null; + + } + + } + + function createAnimations() { + + animData = []; + + // fill in the keys + recurseHierarchy( scene ); + + } + + function recurseHierarchy( node ) { + + var n = visualScene.getChildById( node.colladaId, true ), + newData = null; + + if ( n && n.keys ) { + + newData = { + fps: 60, + hierarchy: [ { + node: n, + keys: n.keys, + sids: n.sids + } ], + node: node, + name: 'animation_' + node.name, + length: 0 + }; + + animData.push(newData); + + for ( var i = 0, il = n.keys.length; i < il; i ++ ) { + + newData.length = Math.max( newData.length, n.keys[i].time ); + + } + + } else { + + newData = { + hierarchy: [ { + keys: [], + sids: [] + } ] + } + + } + + for ( var i = 0, il = node.children.length; i < il; i ++ ) { + + var d = recurseHierarchy( node.children[i] ); + + for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { + + newData.hierarchy.push( { + keys: [], + sids: [] + } ); + + } + + } + + return newData; + + } + + function calcAnimationBounds () { + + var start = 1000000; + var end = -start; + var frames = 0; + var ID; + for ( var id in animations ) { + + var animation = animations[ id ]; + ID = ID || animation.id; + for ( var i = 0; i < animation.sampler.length; i ++ ) { + + var sampler = animation.sampler[ i ]; + + sampler.create(); + + start = Math.min( start, sampler.startTime ); + end = Math.max( end, sampler.endTime ); + frames = Math.max( frames, sampler.input.length ); + + } + + } + + return { start:start, end:end, frames:frames,ID:ID }; + + } + + function createMorph ( geometry, ctrl ) { + + var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; + + if ( !morphCtrl || !morphCtrl.morph ) { + + console.log("could not find morph controller!"); + return; + + } + + var morph = morphCtrl.morph; + + for ( var i = 0; i < morph.targets.length; i ++ ) { + + var target_id = morph.targets[ i ]; + var daeGeometry = geometries[ target_id ]; + + if ( !daeGeometry.mesh || + !daeGeometry.mesh.primitives || + !daeGeometry.mesh.primitives.length ) { + continue; + } + + var target = daeGeometry.mesh.primitives[ 0 ].geometry; + + if ( target.vertices.length === geometry.vertices.length ) { + + geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); + + } + + } + + geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); + + } + + function createSkin ( geometry, ctrl, applyBindShape ) { + + var skinCtrl = controllers[ ctrl.url ]; + + if ( !skinCtrl || !skinCtrl.skin ) { + + console.log( "could not find skin controller!" ); + return; + + } + + if ( !ctrl.skeleton || !ctrl.skeleton.length ) { + + console.log( "could not find the skeleton for the skin!" ); + return; + + } + + var skin = skinCtrl.skin; + var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); + var hierarchy = []; + + applyBindShape = applyBindShape !== undefined ? applyBindShape : true; + + var bones = []; + geometry.skinWeights = []; + geometry.skinIndices = []; + + //createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 ); + //createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights ); + + /* + geometry.animation = { + name: 'take_001', + fps: 30, + length: 2, + JIT: true, + hierarchy: hierarchy + }; + */ + + if ( applyBindShape ) { + + for ( var i = 0; i < geometry.vertices.length; i ++ ) { + + geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); + + } + + } + + } + + function setupSkeleton ( node, bones, frame, parent ) { + + node.world = node.world || new THREE.Matrix4(); + node.localworld = node.localworld || new THREE.Matrix4(); + node.world.copy( node.matrix ); + node.localworld.copy( node.matrix ); + + if ( node.channels && node.channels.length ) { + + var channel = node.channels[ 0 ]; + var m = channel.sampler.output[ frame ]; + + if ( m instanceof THREE.Matrix4 ) { + + node.world.copy( m ); + node.localworld.copy(m); + if (frame === 0) + node.matrix.copy(m); + } + + } + + if ( parent ) { + + node.world.multiplyMatrices( parent, node.world ); + + } + + bones.push( node ); + + for ( var i = 0; i < node.nodes.length; i ++ ) { + + setupSkeleton( node.nodes[ i ], bones, frame, node.world ); + + } + + } + + function setupSkinningMatrices ( bones, skin ) { + + // FIXME: this is dumb... + + for ( var i = 0; i < bones.length; i ++ ) { + + var bone = bones[ i ]; + var found = -1; + + if ( bone.type != 'JOINT' ) continue; + + for ( var j = 0; j < skin.joints.length; j ++ ) { + + if ( bone.sid === skin.joints[ j ] ) { + + found = j; + break; + + } + + } + + if ( found >= 0 ) { + + var inv = skin.invBindMatrices[ found ]; + + bone.invBindMatrix = inv; + bone.skinningMatrix = new THREE.Matrix4(); + bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) + bone.animatrix = new THREE.Matrix4(); + + bone.animatrix.copy(bone.localworld); + bone.weights = []; + + for ( var j = 0; j < skin.weights.length; j ++ ) { + + for (var k = 0; k < skin.weights[ j ].length; k ++ ) { + + var w = skin.weights[ j ][ k ]; + + if ( w.joint === found ) { + + bone.weights.push( w ); + + } + + } + + } + + } else { + + console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); + + bone.skinningMatrix = new THREE.Matrix4(); + bone.weights = []; + + } + } + + } + + //Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix + function flattenSkeleton(skeleton) { + + var list = []; + var walk = function(parentid, node, list) { + + var bone = {}; + bone.name = node.sid; + bone.parent = parentid; + bone.matrix = node.matrix; + var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; + bone.matrix.decompose(data[0], data[1], data[2]); + + bone.pos = [ data[0].x,data[0].y,data[0].z ]; + + bone.scl = [ data[2].x,data[2].y,data[2].z ]; + bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; + list.push(bone); + + for (var i in node.nodes) { + + walk(node.sid, node.nodes[i], list); + + } + + }; + + walk(-1, skeleton, list); + return list; + + } + + //Move the vertices into the pose that is proper for the start of the animation + function skinToBindPose(geometry,skeleton,skinController) { + + var bones = []; + setupSkeleton( skeleton, bones, -1 ); + setupSkinningMatrices( bones, skinController.skin ); + var v = new THREE.Vector3(); + var skinned = []; + + for (var i = 0; i < geometry.vertices.length; i ++) { + + skinned.push(new THREE.Vector3()); + + } + + for ( i = 0; i < bones.length; i ++ ) { + + if ( bones[ i ].type != 'JOINT' ) continue; + + for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { + + var w = bones[ i ].weights[ j ]; + var vidx = w.index; + var weight = w.weight; + + var o = geometry.vertices[vidx]; + var s = skinned[vidx]; + + v.x = o.x; + v.y = o.y; + v.z = o.z; + + v.applyMatrix4( bones[i].skinningMatrix ); + + s.x += (v.x * weight); + s.y += (v.y * weight); + s.z += (v.z * weight); + } + + } + + for (var i = 0; i < geometry.vertices.length; i ++) { + + geometry.vertices[i] = skinned[i]; + + } + + } + + function applySkin ( geometry, instanceCtrl, frame ) { + + var skinController = controllers[ instanceCtrl.url ]; + + frame = frame !== undefined ? frame : 40; + + if ( !skinController || !skinController.skin ) { + + console.log( 'ColladaLoader: Could not find skin controller.' ); + return; + + } + + if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { + + console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); + return; + + } + + var animationBounds = calcAnimationBounds(); + var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); + + //flatten the skeleton into a list of bones + var bonelist = flattenSkeleton(skeleton); + var joints = skinController.skin.joints; + + //sort that list so that the order reflects the order in the joint list + var sortedbones = []; + for (var i = 0; i < joints.length; i ++) { + + for (var j = 0; j < bonelist.length; j ++) { + + if (bonelist[j].name === joints[i]) { + + sortedbones[i] = bonelist[j]; + + } + + } + + } + + //hook up the parents by index instead of name + for (var i = 0; i < sortedbones.length; i ++) { + + for (var j = 0; j < sortedbones.length; j ++) { + + if (sortedbones[i].parent === sortedbones[j].name) { + + sortedbones[i].parent = j; + + } + + } + + } + + + var i, j, w, vidx, weight; + var v = new THREE.Vector3(), o, s; + + // move vertices to bind shape + for ( i = 0; i < geometry.vertices.length; i ++ ) { + geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); + } + + var skinIndices = []; + var skinWeights = []; + var weights = skinController.skin.weights; + + // hook up the skin weights + // TODO - this might be a good place to choose greatest 4 weights + for ( var i =0; i < weights.length; i ++ ) { + + var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); + var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); + + skinIndices.push(indicies); + skinWeights.push(weight); + + } + + geometry.skinIndices = skinIndices; + geometry.skinWeights = skinWeights; + geometry.bones = sortedbones; + // process animation, or simply pose the rig if no animation + + //create an animation for the animated bones + //NOTE: this has no effect when using morphtargets + var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; + + for (var j = 0; j < sortedbones.length; j ++) { + + animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); + + } + + console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); + + + + skinToBindPose(geometry, skeleton, skinController); + + + for ( frame = 0; frame < animationBounds.frames; frame ++ ) { + + var bones = []; + var skinned = []; + // process the frame and setup the rig with a fresh + // transform, possibly from the bone's animation channel(s) + + setupSkeleton( skeleton, bones, frame ); + setupSkinningMatrices( bones, skinController.skin ); + + for (var i = 0; i < bones.length; i ++) { + + for (var j = 0; j < animationdata.hierarchy.length; j ++) { + + if (animationdata.hierarchy[j].name === bones[i].sid) { + + var key = {}; + key.time = (frame / 30); + key.matrix = bones[i].animatrix; + + if (frame === 0) + bones[i].matrix = key.matrix; + + var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; + key.matrix.decompose(data[0], data[1], data[2]); + + key.pos = [ data[0].x,data[0].y,data[0].z ]; + + key.scl = [ data[2].x,data[2].y,data[2].z ]; + key.rot = data[1]; + + animationdata.hierarchy[j].keys.push(key); + + } + + } + + } + + geometry.animation = animationdata; + + } + + } + + function createKinematics() { + + if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { + kinematics = undefined; + return; + } + + var jointMap = {}; + + var _addToMap = function( jointIndex, parentVisualElement ) { + + var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); + var colladaNode = visualScene.getChildById( parentVisualElementId, true ); + var joint = kinematicsModel.joints[ jointIndex ]; + + scene.traverse(function( node ) { + + if ( node.colladaId == parentVisualElementId ) { + + jointMap[ jointIndex ] = { + node: node, + transforms: colladaNode.transforms, + joint: joint, + position: joint.zeroPosition + }; + + } + + }); + + }; + + kinematics = { + + joints: kinematicsModel && kinematicsModel.joints, + + getJointValue: function( jointIndex ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + return jointData.position; + + } else { + + console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + }, + + setJointValue: function( jointIndex, value ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + var joint = jointData.joint; + + if ( value > joint.limits.max || value < joint.limits.min ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); + + } else if ( joint.static ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); + + } else { + + var threejsNode = jointData.node; + var axis = joint.axis; + var transforms = jointData.transforms; + + var matrix = new THREE.Matrix4(); + var m1 = new THREE.Matrix4(); + + for (i = 0; i < transforms.length; i ++ ) { + + var transform = transforms[ i ]; + + // kinda ghetto joint detection + + if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { + + // apply actual joint value here + switch ( joint.type ) { + + case 'revolute': + + matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); + break; + + case 'prismatic': + + matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); + break; + + default: + + console.warn( 'setJointValue: unknown joint type: ' + joint.type ); + break; + + } + + } else { + + switch ( transform.type ) { + + case 'matrix': + + matrix.multiply( transform.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); + + break; + + } + } + } + + // apply the matrix to the threejs node + var elementsFloat32Arr = matrix.elements; + var elements = Array.prototype.slice.call( elementsFloat32Arr ); + + var elementsRowMajor = [ + elements[ 0 ], + elements[ 4 ], + elements[ 8 ], + elements[ 12 ], + elements[ 1 ], + elements[ 5 ], + elements[ 9 ], + elements[ 13 ], + elements[ 2 ], + elements[ 6 ], + elements[ 10 ], + elements[ 14 ], + elements[ 3 ], + elements[ 7 ], + elements[ 11 ], + elements[ 15 ] + ]; + + threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); + threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); + + jointMap[ jointIndex ].position = value; + + } + + } else { + + console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + } + + }; + + var element = COLLADA.querySelector('scene instance_kinematics_scene'); + + if ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_joint_axis': + + var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); + var axis = child.querySelector('axis param').textContent; + var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); + var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); + + if ( visualTargetElement ) { + var parentVisualElement = visualTargetElement.parentElement; + _addToMap(jointIndex, parentVisualElement); + } + + break; + + default: + + break; + + } + + } + } + + } + + function createSceneGraph ( node, parent ) { + + var obj = new THREE.Object3D(); + var skinned = false; + var skinController; + var morphController; + var i, j; + + // FIXME: controllers + + for ( i = 0; i < node.controllers.length; i ++ ) { + + var controller = controllers[ node.controllers[ i ].url ]; + + switch ( controller.type ) { + + case 'skin': + + if ( geometries[ controller.skin.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.skin.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + skinned = true; + skinController = node.controllers[ i ]; + + } else if ( controllers[ controller.skin.source ] ) { + + // urgh: controller can be chained + // handle the most basic case... + + var second = controllers[ controller.skin.source ]; + morphController = second; + // skinController = node.controllers[i]; + + if ( second.morph && geometries[ second.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = second.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + + } + + } + + break; + + case 'morph': + + if ( geometries[ controller.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + morphController = node.controllers[ i ]; + + } + + console.log( 'ColladaLoader: Morph-controller partially supported.' ); + + default: + break; + + } + + } + + // geometries + + var double_sided_materials = {}; + + for ( i = 0; i < node.geometries.length; i ++ ) { + + var instance_geometry = node.geometries[i]; + var instance_materials = instance_geometry.instance_material; + var geometry = geometries[ instance_geometry.url ]; + var used_materials = {}; + var used_materials_array = []; + var num_materials = 0; + var first_material; + + if ( geometry ) { + + if ( !geometry.mesh || !geometry.mesh.primitives ) + continue; + + if ( obj.name.length === 0 ) { + + obj.name = geometry.id; + + } + + // collect used fx for this geometry-instance + + if ( instance_materials ) { + + for ( j = 0; j < instance_materials.length; j ++ ) { + + var instance_material = instance_materials[ j ]; + var mat = materials[ instance_material.target ]; + var effect_id = mat.instance_effect.url; + var shader = effects[ effect_id ].shader; + var material3js = shader.material; + + if ( geometry.doubleSided ) { + + if ( !( instance_material.symbol in double_sided_materials ) ) { + + var _copied_material = material3js.clone(); + _copied_material.side = THREE.DoubleSide; + double_sided_materials[ instance_material.symbol ] = _copied_material; + + } + + material3js = double_sided_materials[ instance_material.symbol ]; + + } + + material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; + used_materials[ instance_material.symbol ] = num_materials; + used_materials_array.push( material3js ); + first_material = material3js; + first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; + num_materials ++; + + } + + } + + var mesh; + var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); + var geom = geometry.mesh.geometry3js; + + if ( num_materials > 1 ) { + + material = new THREE.MultiMaterial( used_materials_array ); + + for ( j = 0; j < geom.faces.length; j ++ ) { + + var face = geom.faces[ j ]; + face.materialIndex = used_materials[ face.daeMaterial ] + + } + + } + + if ( skinController !== undefined ) { + + + applySkin( geom, skinController ); + + if ( geom.morphTargets.length > 0 ) { + + material.morphTargets = true; + material.skinning = false; + + } else { + + material.morphTargets = false; + material.skinning = true; + + } + + + mesh = new THREE.SkinnedMesh( geom, material, false ); + + + //mesh.skeleton = skinController.skeleton; + //mesh.skinController = controllers[ skinController.url ]; + //mesh.skinInstanceController = skinController; + mesh.name = 'skin_' + skins.length; + + + + //mesh.animationHandle.setKey(0); + skins.push( mesh ); + + } else if ( morphController !== undefined ) { + + createMorph( geom, morphController ); + + material.morphTargets = true; + + mesh = new THREE.Mesh( geom, material ); + mesh.name = 'morph_' + morphs.length; + + morphs.push( mesh ); + + } else { + + if ( geom.isLineStrip === true ) { + + mesh = new THREE.Line( geom ); + + } else { + + mesh = new THREE.Mesh( geom, material ); + + } + + } + + obj.add(mesh); + + } + + } + + for ( i = 0; i < node.cameras.length; i ++ ) { + + var instance_camera = node.cameras[i]; + var cparams = cameras[instance_camera.url]; + + var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), + parseFloat(cparams.znear), parseFloat(cparams.zfar)); + + obj.add(cam); + } + + for ( i = 0; i < node.lights.length; i ++ ) { + + var light = null; + var instance_light = node.lights[i]; + var lparams = lights[instance_light.url]; + + if ( lparams && lparams.technique ) { + + var color = lparams.color.getHex(); + var intensity = lparams.intensity; + var distance = lparams.distance; + var angle = lparams.falloff_angle; + + switch ( lparams.technique ) { + + case 'directional': + + light = new THREE.DirectionalLight( color, intensity, distance ); + light.position.set(0, 0, 1); + break; + + case 'point': + + light = new THREE.PointLight( color, intensity, distance ); + break; + + case 'spot': + + light = new THREE.SpotLight( color, intensity, distance, angle ); + light.position.set(0, 0, 1); + break; + + case 'ambient': + + light = new THREE.AmbientLight( color ); + break; + + } + + } + + if (light) { + obj.add(light); + } + } + + obj.name = node.name || node.id || ""; + obj.colladaId = node.id || ""; + obj.layer = node.layer || ""; + obj.matrix = node.matrix; + obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); + + if ( options.centerGeometry && obj.geometry ) { + + var delta = obj.geometry.center(); + delta.multiply( obj.scale ); + delta.applyQuaternion( obj.quaternion ); + + obj.position.sub( delta ); + + } + + for ( i = 0; i < node.nodes.length; i ++ ) { + + obj.add( createSceneGraph( node.nodes[i], node ) ); + + } + + return obj; + + } + + function getJointId( skin, id ) { + + for ( var i = 0; i < skin.joints.length; i ++ ) { + + if ( skin.joints[ i ] === id ) { + + return i; + + } + + } + + } + + function getLibraryNode( id ) { + + var nodes = COLLADA.querySelectorAll('library_nodes node'); + + for ( var i = 0; i < nodes.length; i++ ) { + + var attObj = nodes[i].attributes.getNamedItem('id'); + + if ( attObj && attObj.value === id ) { + + return nodes[i]; + + } + + } + + return undefined; + + } + + function getChannelsForNode ( node ) { + + var channels = []; + var startTime = 1000000; + var endTime = -1000000; + + for ( var id in animations ) { + + var animation = animations[id]; + + for ( var i = 0; i < animation.channel.length; i ++ ) { + + var channel = animation.channel[i]; + var sampler = animation.sampler[i]; + var id = channel.target.split('/')[0]; + + if ( id == node.id ) { + + sampler.create(); + channel.sampler = sampler; + startTime = Math.min(startTime, sampler.startTime); + endTime = Math.max(endTime, sampler.endTime); + channels.push(channel); + + } + + } + + } + + if ( channels.length ) { + + node.startTime = startTime; + node.endTime = endTime; + + } + + return channels; + + } + + function calcFrameDuration( node ) { + + var minT = 10000000; + + for ( var i = 0; i < node.channels.length; i ++ ) { + + var sampler = node.channels[i].sampler; + + for ( var j = 0; j < sampler.input.length - 1; j ++ ) { + + var t0 = sampler.input[ j ]; + var t1 = sampler.input[ j + 1 ]; + minT = Math.min( minT, t1 - t0 ); + + } + } + + return minT; + + } + + function calcMatrixAt( node, t ) { + + var animated = {}; + + var i, j; + + for ( i = 0; i < node.channels.length; i ++ ) { + + var channel = node.channels[ i ]; + animated[ channel.sid ] = channel; + + } + + var matrix = new THREE.Matrix4(); + + for ( i = 0; i < node.transforms.length; i ++ ) { + + var transform = node.transforms[ i ]; + var channel = animated[ transform.sid ]; + + if ( channel !== undefined ) { + + var sampler = channel.sampler; + var value; + + for ( j = 0; j < sampler.input.length - 1; j ++ ) { + + if ( sampler.input[ j + 1 ] > t ) { + + value = sampler.output[ j ]; + //console.log(value.flatten) + break; + + } + + } + + if ( value !== undefined ) { + + if ( value instanceof THREE.Matrix4 ) { + + matrix.multiplyMatrices( matrix, value ); + + } else { + + // FIXME: handle other types + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } + + return matrix; + + } + + function bakeAnimations ( node ) { + + if ( node.channels && node.channels.length ) { + + var keys = [], + sids = []; + + for ( var i = 0, il = node.channels.length; i < il; i ++ ) { + + var channel = node.channels[i], + fullSid = channel.fullSid, + sampler = channel.sampler, + input = sampler.input, + transform = node.getTransformBySid( channel.sid ), + member; + + if ( channel.arrIndices ) { + + member = []; + + for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { + + member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); + + } + + } else { + + member = getConvertedMember( channel.member ); + + } + + if ( transform ) { + + if ( sids.indexOf( fullSid ) === -1 ) { + + sids.push( fullSid ); + + } + + for ( var j = 0, jl = input.length; j < jl; j ++ ) { + + var time = input[j], + data = sampler.getData( transform.type, j, member ), + key = findKey( keys, time ); + + if ( !key ) { + + key = new Key( time ); + var timeNdx = findTimeNdx( keys, time ); + keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); + + } + + key.addTarget( fullSid, transform, member, data ); + + } + + } else { + + console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); + + } + + } + + // post process + for ( var i = 0; i < sids.length; i ++ ) { + + var sid = sids[ i ]; + + for ( var j = 0; j < keys.length; j ++ ) { + + var key = keys[ j ]; + + if ( !key.hasTarget( sid ) ) { + + interpolateKeys( keys, key, j, sid ); + + } + + } + + } + + node.keys = keys; + node.sids = sids; + + } + + } + + function findKey ( keys, time) { + + var retVal = null; + + for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { + + var key = keys[i]; + + if ( key.time === time ) { + + retVal = key; + + } else if ( key.time > time ) { + + break; + + } + + } + + return retVal; + + } + + function findTimeNdx ( keys, time) { + + var ndx = -1; + + for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { + + var key = keys[i]; + + if ( key.time >= time ) { + + ndx = i; + + } + + } + + return ndx; + + } + + function interpolateKeys ( keys, key, ndx, fullSid ) { + + var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), + nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); + + if ( prevKey && nextKey ) { + + var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), + prevTarget = prevKey.getTarget( fullSid ), + nextData = nextKey.getTarget( fullSid ).data, + prevData = prevTarget.data, + data; + + if ( prevTarget.type === 'matrix' ) { + + data = prevData; + + } else if ( prevData.length ) { + + data = []; + + for ( var i = 0; i < prevData.length; ++ i ) { + + data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); + + } + + } + + // Get next key with given sid + + function getNextKeyWith( keys, fullSid, ndx ) { + + for ( ; ndx < keys.length; ndx ++ ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + } + + // Get previous key with given sid + + function getPrevKeyWith( keys, fullSid, ndx ) { + + ndx = ndx >= 0 ? ndx : ndx + keys.length; + + for ( ; ndx >= 0; ndx -- ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + } + + function _Image() { + + this.id = ""; + this.init_from = ""; + + } + + _Image.prototype.parse = function(element) { + + this.id = element.getAttribute('id'); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'init_from' ) { + + this.init_from = child.textContent; + + } + + } + + return this; + + }; + + function Controller() { + + this.id = ""; + this.name = ""; + this.type = ""; + this.skin = null; + this.morph = null; + + } + + Controller.prototype.parse = function( element ) { + + this.id = element.getAttribute('id'); + this.name = element.getAttribute('name'); + this.type = "none"; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'skin': + + this.skin = (new Skin()).parse(child); + this.type = child.nodeName; + break; + + case 'morph': + + this.morph = (new Morph()).parse(child); + this.type = child.nodeName; + break; + + default: + break; + + } + } + + return this; + + }; + + function Morph() { + + this.method = null; + this.source = null; + this.targets = null; + this.weights = null; + + } + + Morph.prototype.parse = function( element ) { + + var sources = {}; + var inputs = []; + var i; + + this.method = element.getAttribute( 'method' ); + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + + for ( i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + var source = ( new Source() ).parse( child ); + sources[ source.id ] = source; + break; + + case 'targets': + + inputs = this.parseInputs( child ); + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + for ( i = 0; i < inputs.length; i ++ ) { + + var input = inputs[ i ]; + var source = sources[ input.source ]; + + switch ( input.semantic ) { + + case 'MORPH_TARGET': + + this.targets = source.read(); + break; + + case 'MORPH_WEIGHT': + + this.weights = source.read(); + break; + + default: + break; + + } + } + + return this; + + }; + + Morph.prototype.parseInputs = function(element) { + + var inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( (new Input()).parse(child) ); + break; + + default: + break; + } + } + + return inputs; + + }; + + function Skin() { + + this.source = ""; + this.bindShapeMatrix = null; + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + } + + Skin.prototype.parse = function( element ) { + + var sources = {}; + var joints, weights; + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_shape_matrix': + + var f = _floats(child.textContent); + this.bindShapeMatrix = getConvertedMat4( f ); + break; + + case 'source': + + var src = new Source().parse(child); + sources[ src.id ] = src; + break; + + case 'joints': + + joints = child; + break; + + case 'vertex_weights': + + weights = child; + break; + + default: + + console.log( child.nodeName ); + break; + + } + } + + this.parseJoints( joints, sources ); + this.parseWeights( weights, sources ); + + return this; + + }; + + Skin.prototype.parseJoints = function ( element, sources ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + var input = ( new Input() ).parse( child ); + var source = sources[ input.source ]; + + if ( input.semantic === 'JOINT' ) { + + this.joints = source.read(); + + } else if ( input.semantic === 'INV_BIND_MATRIX' ) { + + this.invBindMatrices = source.read(); + + } + + break; + + default: + break; + } + + } + + }; + + Skin.prototype.parseWeights = function ( element, sources ) { + + var v, vcount, inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( ( new Input() ).parse( child ) ); + break; + + case 'v': + + v = _ints( child.textContent ); + break; + + case 'vcount': + + vcount = _ints( child.textContent ); + break; + + default: + break; + + } + + } + + var index = 0; + + for ( var i = 0; i < vcount.length; i ++ ) { + + var numBones = vcount[i]; + var vertex_weights = []; + + for ( var j = 0; j < numBones; j ++ ) { + + var influence = {}; + + for ( var k = 0; k < inputs.length; k ++ ) { + + var input = inputs[ k ]; + var value = v[ index + input.offset ]; + + switch ( input.semantic ) { + + case 'JOINT': + + influence.joint = value;//this.joints[value]; + break; + + case 'WEIGHT': + + influence.weight = sources[ input.source ].data[ value ]; + break; + + default: + break; + + } + + } + + vertex_weights.push( influence ); + index += inputs.length; + } + + for ( var j = 0; j < vertex_weights.length; j ++ ) { + + vertex_weights[ j ].index = i; + + } + + this.weights.push( vertex_weights ); + + } + + }; + + function VisualScene () { + + this.id = ""; + this.name = ""; + this.nodes = []; + this.scene = new THREE.Group(); + + } + + VisualScene.prototype.getChildById = function( id, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildById( id, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.getChildBySid = function( sid, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.parse = function( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.nodes = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Node() { + + this.id = ""; + this.name = ""; + this.sid = ""; + this.nodes = []; + this.controllers = []; + this.transforms = []; + this.geometries = []; + this.channels = []; + this.matrix = new THREE.Matrix4(); + + } + + Node.prototype.getChannelForTransform = function( transformSid ) { + + for ( var i = 0; i < this.channels.length; i ++ ) { + + var channel = this.channels[i]; + var parts = channel.target.split('/'); + var id = parts.shift(); + var sid = parts.shift(); + var dotSyntax = (sid.indexOf(".") >= 0); + var arrSyntax = (sid.indexOf("(") >= 0); + var arrIndices; + var member; + + if ( dotSyntax ) { + + parts = sid.split("."); + sid = parts.shift(); + member = parts.shift(); + + } else if ( arrSyntax ) { + + arrIndices = sid.split("("); + sid = arrIndices.shift(); + + for ( var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); + + } + + } + + if ( sid === transformSid ) { + + channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; + return channel; + + } + + } + + return null; + + }; + + Node.prototype.getChildById = function ( id, recursive ) { + + if ( this.id === id ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildById( id, recursive ); + + if ( n ) { + + return n; + + } + + } + + } + + return null; + + }; + + Node.prototype.getChildBySid = function ( sid, recursive ) { + + if ( this.sid === sid ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( n ) { + + return n; + + } + + } + } + + return null; + + }; + + Node.prototype.getTransformBySid = function ( sid ) { + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; + + } + + return null; + + }; + + Node.prototype.parse = function( element ) { + + var url; + + this.id = element.getAttribute('id'); + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.type = element.getAttribute('type'); + this.layer = element.getAttribute('layer'); + + this.type = this.type === 'JOINT' ? this.type : 'NODE'; + + this.nodes = []; + this.transforms = []; + this.geometries = []; + this.cameras = []; + this.lights = []; + this.controllers = []; + this.matrix = new THREE.Matrix4(); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + case 'instance_camera': + + this.cameras.push( ( new InstanceCamera() ).parse( child ) ); + break; + + case 'instance_controller': + + this.controllers.push( ( new InstanceController() ).parse( child ) ); + break; + + case 'instance_geometry': + + this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); + break; + + case 'instance_light': + + this.lights.push( ( new InstanceLight() ).parse( child ) ); + break; + + case 'instance_node': + + url = child.getAttribute( 'url' ).replace( /^#/, '' ); + var iNode = getLibraryNode( url ); + + if ( iNode ) { + + this.nodes.push( ( new Node() ).parse( iNode )) ; + + } + + break; + + case 'rotate': + case 'translate': + case 'scale': + case 'matrix': + case 'lookat': + case 'skew': + + this.transforms.push( ( new Transform() ).parse( child ) ); + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + this.channels = getChannelsForNode( this ); + bakeAnimations( this ); + + this.updateMatrix(); + + return this; + + }; + + Node.prototype.updateMatrix = function () { + + this.matrix.identity(); + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + this.transforms[ i ].apply( this.matrix ); + + } + + }; + + function Transform () { + + this.sid = ""; + this.type = ""; + this.data = []; + this.obj = null; + + } + + Transform.prototype.parse = function ( element ) { + + this.sid = element.getAttribute( 'sid' ); + this.type = element.nodeName; + this.data = _floats( element.textContent ); + this.convert(); + + return this; + + }; + + Transform.prototype.convert = function () { + + switch ( this.type ) { + + case 'matrix': + + this.obj = getConvertedMat4( this.data ); + break; + + case 'rotate': + + this.angle = THREE.Math.degToRad( this.data[3] ); + + case 'translate': + + fixCoords( this.data, -1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + case 'scale': + + fixCoords( this.data, 1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + default: + console.log( 'Can not convert Transform of type ' + this.type ); + break; + + } + + }; + + Transform.prototype.apply = function () { + + var m1 = new THREE.Matrix4(); + + return function ( matrix ) { + + switch ( this.type ) { + + case 'matrix': + + matrix.multiply( this.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); + + break; + + case 'scale': + + matrix.scale( this.obj ); + + break; + + } + + }; + + }(); + + Transform.prototype.update = function ( data, member ) { + + var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; + + switch ( this.type ) { + + case 'matrix': + + if ( ! member ) { + + this.obj.copy( data ); + + } else if ( member.length === 1 ) { + + switch ( member[ 0 ] ) { + + case 0: + + this.obj.n11 = data[ 0 ]; + this.obj.n21 = data[ 1 ]; + this.obj.n31 = data[ 2 ]; + this.obj.n41 = data[ 3 ]; + + break; + + case 1: + + this.obj.n12 = data[ 0 ]; + this.obj.n22 = data[ 1 ]; + this.obj.n32 = data[ 2 ]; + this.obj.n42 = data[ 3 ]; + + break; + + case 2: + + this.obj.n13 = data[ 0 ]; + this.obj.n23 = data[ 1 ]; + this.obj.n33 = data[ 2 ]; + this.obj.n43 = data[ 3 ]; + + break; + + case 3: + + this.obj.n14 = data[ 0 ]; + this.obj.n24 = data[ 1 ]; + this.obj.n34 = data[ 2 ]; + this.obj.n44 = data[ 3 ]; + + break; + + } + + } else if ( member.length === 2 ) { + + var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); + this.obj[ propName ] = data; + + } else { + + console.log('Incorrect addressing of matrix in transform.'); + + } + + break; + + case 'translate': + case 'scale': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + break; + + } + + break; + + case 'rotate': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + case 'ANGLE': + + this.angle = THREE.Math.degToRad( data ); + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + this.angle = THREE.Math.degToRad( data[ 3 ] ); + break; + + } + break; + + } + + }; + + function InstanceController() { + + this.url = ""; + this.skeleton = []; + this.instance_material = []; + + } + + InstanceController.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + this.skeleton = []; + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'skeleton': + + this.skeleton.push( child.textContent.replace(/^#/, '') ); + break; + + case 'bind_material': + + var instances = child.querySelectorAll('instance_material'); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[j]; + this.instance_material.push( (new InstanceMaterial()).parse(instance) ); + + } + + + break; + + case 'extra': + break; + + default: + break; + + } + } + + return this; + + }; + + function InstanceMaterial () { + + this.symbol = ""; + this.target = ""; + + } + + InstanceMaterial.prototype.parse = function ( element ) { + + this.symbol = element.getAttribute('symbol'); + this.target = element.getAttribute('target').replace(/^#/, ''); + return this; + + }; + + function InstanceGeometry() { + + this.url = ""; + this.instance_material = []; + + } + + InstanceGeometry.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + if ( child.nodeName === 'bind_material' ) { + + var instances = child.querySelectorAll('instance_material'); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[j]; + this.instance_material.push( (new InstanceMaterial()).parse(instance) ); + + } + + break; + + } + + } + + return this; + + }; + + function Geometry() { + + this.id = ""; + this.mesh = null; + + } + + Geometry.prototype.parse = function ( element ) { + + this.id = element.getAttribute('id'); + + extractDoubleSided( this, element ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + + switch ( child.nodeName ) { + + case 'mesh': + + this.mesh = (new Mesh(this)).parse(child); + break; + + case 'extra': + + // console.log( child ); + break; + + default: + break; + } + } + + return this; + + }; + + function Mesh( geometry ) { + + this.geometry = geometry.id; + this.primitives = []; + this.vertices = null; + this.geometry3js = null; + + } + + Mesh.prototype.parse = function ( element ) { + + this.primitives = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'source': + + _source( child ); + break; + + case 'vertices': + + this.vertices = ( new Vertices() ).parse( child ); + break; + + case 'linestrips': + + this.primitives.push( ( new LineStrips().parse( child ) ) ); + break; + + case 'triangles': + + this.primitives.push( ( new Triangles().parse( child ) ) ); + break; + + case 'polygons': + + this.primitives.push( ( new Polygons().parse( child ) ) ); + break; + + case 'polylist': + + this.primitives.push( ( new Polylist().parse( child ) ) ); + break; + + default: + break; + + } + + } + + this.geometry3js = new THREE.Geometry(); + + if ( this.vertices === null ) { + + // TODO (mrdoob): Study case when this is null (carrier.dae) + + return this; + + } + + var vertexData = sources[ this.vertices.input['POSITION'].source ].data; + + for ( var i = 0; i < vertexData.length; i += 3 ) { + + this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); + + } + + for ( var i = 0; i < this.primitives.length; i ++ ) { + + var primitive = this.primitives[ i ]; + primitive.setVertices( this.vertices ); + this.handlePrimitive( primitive, this.geometry3js ); + + } + + if ( this.geometry3js.calcNormals ) { + + this.geometry3js.computeVertexNormals(); + delete this.geometry3js.calcNormals; + + } + + return this; + + }; + + Mesh.prototype.handlePrimitive = function ( primitive, geom ) { + + if ( primitive instanceof LineStrips ) { + + // TODO: Handle indices. Maybe easier with BufferGeometry? + + geom.isLineStrip = true; + return; + + } + + var j, k, pList = primitive.p, inputs = primitive.inputs; + var input, index, idx32; + var source, numParams; + var vcIndex = 0, vcount = 3, maxOffset = 0; + var texture_sets = []; + + for ( j = 0; j < inputs.length; j ++ ) { + + input = inputs[ j ]; + + var offset = input.offset + 1; + maxOffset = (maxOffset < offset) ? offset : maxOffset; + + switch ( input.semantic ) { + + case 'TEXCOORD': + texture_sets.push( input.set ); + break; + + } + + } + + for ( var pCount = 0; pCount < pList.length; ++ pCount ) { + + var p = pList[ pCount ], i = 0; + + while ( i < p.length ) { + + var vs = []; + var ns = []; + var ts = null; + var cs = []; + + if ( primitive.vcount ) { + + vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; + + } else { + + vcount = p.length / maxOffset; + + } + + + for ( j = 0; j < vcount; j ++ ) { + + for ( k = 0; k < inputs.length; k ++ ) { + + input = inputs[ k ]; + source = sources[ input.source ]; + + index = p[ i + ( j * maxOffset ) + input.offset ]; + numParams = source.accessor.params.length; + idx32 = index * numParams; + + switch ( input.semantic ) { + + case 'VERTEX': + + vs.push( index ); + + break; + + case 'NORMAL': + + ns.push( getConvertedVec3( source.data, idx32 ) ); + + break; + + case 'TEXCOORD': + + ts = ts || { }; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); + + break; + + case 'COLOR': + + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + break; + + default: + + break; + + } + + } + + } + + if ( ns.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.NORMAL; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); + + } + + } else { + + geom.calcNormals = true; + + } + + } + + if ( !ts ) { + + ts = { }; + // check the vertices inputs + input = this.vertices.input.TEXCOORD; + + if ( input ) { + + texture_sets.push( input.set ); + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); + + } + + } + + } + + if ( cs.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.COLOR; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + } + + } + + } + + var face = null, faces = [], uv, uvArr; + + if ( vcount === 3 ) { + + faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); + + } else if ( vcount === 4 ) { + + faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); + + faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); + + } else if ( vcount > 4 && options.subdivideFaces ) { + + var clr = cs.length ? cs : new THREE.Color(), + vec1, vec2, vec3, v1, v2, norm; + + // subdivide into multiple Face3s + + for ( k = 1; k < vcount - 1; ) { + + faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) ); + + } + + } + + if ( faces.length ) { + + for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { + + face = faces[ndx]; + face.daeMaterial = primitive.material; + geom.faces.push( face ); + + for ( k = 0; k < texture_sets.length; k ++ ) { + + uv = ts[ texture_sets[k] ]; + + if ( vcount > 4 ) { + + // Grab the right UVs for the vertices in this face + uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; + + } else if ( vcount === 4 ) { + + if ( ndx === 0 ) { + + uvArr = [ uv[0], uv[1], uv[3] ]; + + } else { + + uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; + + } + + } else { + + uvArr = [ uv[0], uv[1], uv[2] ]; + + } + + if ( geom.faceVertexUvs[k] === undefined ) { + + geom.faceVertexUvs[k] = []; + + } + + geom.faceVertexUvs[k].push( uvArr ); + + } + + } + + } else { + + console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); + + } + + i += maxOffset * vcount; + + } + + } + + }; + + function Polygons () { + + this.material = ""; + this.count = 0; + this.inputs = []; + this.vcount = null; + this.p = []; + this.geometry = new THREE.Geometry(); + + } + + Polygons.prototype.setVertices = function ( vertices ) { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + if ( this.inputs[ i ].source === vertices.id ) { + + this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; + + } + + } + + }; + + Polygons.prototype.parse = function ( element ) { + + this.material = element.getAttribute( 'material' ); + this.count = _attr_as_int( element, 'count', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); + break; + + case 'vcount': + + this.vcount = _ints( child.textContent ); + break; + + case 'p': + + this.p.push( _ints( child.textContent ) ); + break; + + case 'ph': + + console.warn( 'polygon holes not yet supported!' ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Polylist () { + + Polygons.call( this ); + + this.vcount = []; + + } + + Polylist.prototype = Object.create( Polygons.prototype ); + Polylist.prototype.constructor = Polylist; + + function LineStrips() { + + Polygons.call( this ); + + this.vcount = 1; + + } + + LineStrips.prototype = Object.create( Polygons.prototype ); + LineStrips.prototype.constructor = LineStrips; + + function Triangles () { + + Polygons.call( this ); + + this.vcount = 3; + + } + + Triangles.prototype = Object.create( Polygons.prototype ); + Triangles.prototype.constructor = Triangles; + + function Accessor() { + + this.source = ""; + this.count = 0; + this.stride = 0; + this.params = []; + + } + + Accessor.prototype.parse = function ( element ) { + + this.params = []; + this.source = element.getAttribute( 'source' ); + this.count = _attr_as_int( element, 'count', 0 ); + this.stride = _attr_as_int( element, 'stride', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'param' ) { + + var param = {}; + param[ 'name' ] = child.getAttribute( 'name' ); + param[ 'type' ] = child.getAttribute( 'type' ); + this.params.push( param ); + + } + + } + + return this; + + }; + + function Vertices() { + + this.input = {}; + + } + + Vertices.prototype.parse = function ( element ) { + + this.id = element.getAttribute('id'); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[i].nodeName === 'input' ) { + + var input = ( new Input() ).parse( element.childNodes[ i ] ); + this.input[ input.semantic ] = input; + + } + + } + + return this; + + }; + + function Input () { + + this.semantic = ""; + this.offset = 0; + this.source = ""; + this.set = 0; + + } + + Input.prototype.parse = function ( element ) { + + this.semantic = element.getAttribute('semantic'); + this.source = element.getAttribute('source').replace(/^#/, ''); + this.set = _attr_as_int(element, 'set', -1); + this.offset = _attr_as_int(element, 'offset', 0); + + if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { + + this.set = 0; + + } + + return this; + + }; + + function Source ( id ) { + + this.id = id; + this.type = null; + + } + + Source.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + + switch ( child.nodeName ) { + + case 'bool_array': + + this.data = _bools( child.textContent ); + this.type = child.nodeName; + break; + + case 'float_array': + + this.data = _floats( child.textContent ); + this.type = child.nodeName; + break; + + case 'int_array': + + this.data = _ints( child.textContent ); + this.type = child.nodeName; + break; + + case 'IDREF_array': + case 'Name_array': + + this.data = _strings( child.textContent ); + this.type = child.nodeName; + break; + + case 'technique_common': + + for ( var j = 0; j < child.childNodes.length; j ++ ) { + + if ( child.childNodes[ j ].nodeName === 'accessor' ) { + + this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); + break; + + } + } + break; + + default: + // console.log(child.nodeName); + break; + + } + + } + + return this; + + }; + + Source.prototype.read = function () { + + var result = []; + + //for (var i = 0; i < this.accessor.params.length; i++) { + + var param = this.accessor.params[ 0 ]; + + //console.log(param.name + " " + param.type); + + switch ( param.type ) { + + case 'IDREF': + case 'Name': case 'name': + case 'float': + + return this.data; + + case 'float4x4': + + for ( var j = 0; j < this.data.length; j += 16 ) { + + var s = this.data.slice( j, j + 16 ); + var m = getConvertedMat4( s ); + result.push( m ); + } + + break; + + default: + + console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); + break; + + } + + //} + + return result; + + }; + + function Material () { + + this.id = ""; + this.name = ""; + this.instance_effect = null; + + } + + Material.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { + + this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); + break; + + } + + } + + return this; + + }; + + function ColorOrTexture () { + + this.color = new THREE.Color(); + this.color.setRGB( Math.random(), Math.random(), Math.random() ); + this.color.a = 1.0; + + this.texture = null; + this.texcoord = null; + this.texOpts = null; + + } + + ColorOrTexture.prototype.isColor = function () { + + return ( this.texture === null ); + + }; + + ColorOrTexture.prototype.isTexture = function () { + + return ( this.texture != null ); + + }; + + ColorOrTexture.prototype.parse = function ( element ) { + + if (element.nodeName === 'transparent') { + + this.opaque = element.getAttribute('opaque'); + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(); + this.color.setRGB( rgba[0], rgba[1], rgba[2] ); + this.color.a = rgba[3]; + break; + + case 'texture': + + this.texture = child.getAttribute('texture'); + this.texcoord = child.getAttribute('texcoord'); + // Defaults from: + // https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension + this.texOpts = { + offsetU: 0, + offsetV: 0, + repeatU: 1, + repeatV: 1, + wrapU: 1, + wrapV: 1 + }; + this.parseTexture( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + ColorOrTexture.prototype.parseTexture = function ( element ) { + + if ( ! element.childNodes ) return this; + + // This should be supported by Maya, 3dsMax, and MotionBuilder + + if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { + + element = element.childNodes[1]; + + if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { + + element = element.childNodes[1]; + + } + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'offsetU': + case 'offsetV': + case 'repeatU': + case 'repeatV': + + this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); + + break; + + case 'wrapU': + case 'wrapV': + + // some dae have a value of true which becomes NaN via parseInt + + if ( child.textContent.toUpperCase() === 'TRUE' ) { + + this.texOpts[ child.nodeName ] = 1; + + } else { + + this.texOpts[ child.nodeName ] = parseInt( child.textContent ); + + } + break; + + default: + + this.texOpts[ child.nodeName ] = child.textContent; + + break; + + } + + } + + return this; + + }; + + function Shader ( type, effect ) { + + this.type = type; + this.effect = effect; + this.material = null; + + } + + Shader.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'emission': + case 'diffuse': + case 'specular': + case 'transparent': + + this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); + break; + + case 'bump': + + // If 'bumptype' is 'heightfield', create a 'bump' property + // Else if 'bumptype' is 'normalmap', create a 'normal' property + // (Default to 'bump') + var bumpType = child.getAttribute( 'bumptype' ); + if ( bumpType ) { + if ( bumpType.toLowerCase() === "heightfield" ) { + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } else if ( bumpType.toLowerCase() === "normalmap" ) { + this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); + } else { + console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } + } else { + console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } + + break; + + case 'shininess': + case 'reflectivity': + case 'index_of_refraction': + case 'transparency': + + var f = child.querySelectorAll('float'); + + if ( f.length > 0 ) + this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); + + break; + + default: + break; + + } + + } + + this.create(); + return this; + + }; + + Shader.prototype.create = function() { + + var props = {}; + + var transparent = false; + + if (this['transparency'] !== undefined && this['transparent'] !== undefined) { + // convert transparent color RBG to average value + var transparentColor = this['transparent']; + var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; + + if (transparencyLevel > 0) { + transparent = true; + props[ 'transparent' ] = true; + props[ 'opacity' ] = 1 - transparencyLevel; + + } + + } + + var keys = { + 'diffuse':'map', + 'ambient':'lightMap', + 'specular':'specularMap', + 'emission':'emissionMap', + 'bump':'bumpMap', + 'normal':'normalMap' + }; + + for ( var prop in this ) { + + switch ( prop ) { + + case 'ambient': + case 'emission': + case 'diffuse': + case 'specular': + case 'bump': + case 'normal': + + var cot = this[ prop ]; + + if ( cot instanceof ColorOrTexture ) { + + if ( cot.isTexture() ) { + + var samplerId = cot.texture; + var sampler = this.effect.sampler[samplerId]; + + if ( sampler !== undefined && sampler.source !== undefined ) { + + var surface = this.effect.surface[sampler.source]; + + if ( surface !== undefined ) { + + var image = images[ surface.init_from ]; + + if ( image ) { + + var url = baseUrl + image.init_from; + + var texture; + var loader = THREE.Loader.Handlers.get( url ); + + if ( loader !== null ) { + + texture = loader.load( url ); + + } else { + + texture = new THREE.Texture(); + + loadTextureImage( texture, url ); + + } + + if ( sampler.wrap_s === "MIRROR" ) { + + texture.wrapS = THREE.MirroredRepeatWrapping; + + } else if ( sampler.wrap_s === "WRAP" || cot.texOpts.wrapU ) { + + texture.wrapS = THREE.RepeatWrapping; + + } else { + + texture.wrapS = THREE.ClampToEdgeWrapping; + + } + + if ( sampler.wrap_t === "MIRROR" ) { + + texture.wrapT = THREE.MirroredRepeatWrapping; + + } else if ( sampler.wrap_t === "WRAP" || cot.texOpts.wrapV ) { + + texture.wrapT = THREE.RepeatWrapping; + + } else { + + texture.wrapT = THREE.ClampToEdgeWrapping; + + } + + texture.offset.x = cot.texOpts.offsetU; + texture.offset.y = cot.texOpts.offsetV; + texture.repeat.x = cot.texOpts.repeatU; + texture.repeat.y = cot.texOpts.repeatV; + props[keys[prop]] = texture; + + // Texture with baked lighting? + if (prop === 'emission') props['emissive'] = 0xffffff; + + } + + } + + } + + } else if ( prop === 'diffuse' || !transparent ) { + + if ( prop === 'emission' ) { + + props[ 'emissive' ] = cot.color.getHex(); + + } else { + + props[ prop ] = cot.color.getHex(); + + } + + } + + } + + break; + + case 'shininess': + + props[ prop ] = this[ prop ]; + break; + + case 'reflectivity': + + props[ prop ] = this[ prop ]; + if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; + props['combine'] = THREE.MixOperation; //mix regular shading with reflective component + break; + + case 'index_of_refraction': + + props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable + if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; + break; + + case 'transparency': + // gets figured out up top + break; + + default: + break; + + } + + } + + props[ 'shading' ] = preferredShading; + props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; + + if ( props.diffuse !== undefined ) { + + props.color = props.diffuse; + delete props.diffuse; + + } + + switch ( this.type ) { + + case 'constant': + + if (props.emissive != undefined) props.color = props.emissive; + this.material = new THREE.MeshBasicMaterial( props ); + break; + + case 'phong': + case 'blinn': + + this.material = new THREE.MeshPhongMaterial( props ); + break; + + case 'lambert': + default: + + this.material = new THREE.MeshLambertMaterial( props ); + break; + + } + + return this.material; + + }; + + function Surface ( effect ) { + + this.effect = effect; + this.init_from = null; + this.format = null; + + } + + Surface.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'init_from': + + this.init_from = child.textContent; + break; + + case 'format': + + this.format = child.textContent; + break; + + default: + + console.log( "unhandled Surface prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Sampler2D ( effect ) { + + this.effect = effect; + this.source = null; + this.wrap_s = null; + this.wrap_t = null; + this.minfilter = null; + this.magfilter = null; + this.mipfilter = null; + + } + + Sampler2D.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + this.source = child.textContent; + break; + + case 'minfilter': + + this.minfilter = child.textContent; + break; + + case 'magfilter': + + this.magfilter = child.textContent; + break; + + case 'mipfilter': + + this.mipfilter = child.textContent; + break; + + case 'wrap_s': + + this.wrap_s = child.textContent; + break; + + case 'wrap_t': + + this.wrap_t = child.textContent; + break; + + default: + + console.log( "unhandled Sampler2D prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Effect () { + + this.id = ""; + this.name = ""; + this.shader = null; + this.surface = {}; + this.sampler = {}; + + } + + Effect.prototype.create = function () { + + if ( this.shader === null ) { + + return null; + + } + + }; + + Effect.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + extractDoubleSided( this, element ); + + this.shader = null; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseTechnique( this.parseProfileCOMMON( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Effect.prototype.parseNewparam = function ( element ) { + + var sid = element.getAttribute( 'sid' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'surface': + + this.surface[sid] = ( new Surface( this ) ).parse( child ); + break; + + case 'sampler2D': + + this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); + break; + + case 'extra': + + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + }; + + Effect.prototype.parseProfileCOMMON = function ( element ) { + + var technique; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseProfileCOMMON( child ); + break; + + case 'technique': + + technique = child; + break; + + case 'newparam': + + this.parseNewparam( child ); + break; + + case 'image': + + var _image = ( new _Image() ).parse( child ); + images[ _image.id ] = _image; + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + return technique; + + }; + + Effect.prototype.parseTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'constant': + case 'lambert': + case 'blinn': + case 'phong': + + this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); + break; + case 'extra': + this.parseExtra(child); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtra = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique': + this.parseExtraTechnique( child ); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtraTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bump': + this.shader.parse( element ); + break; + default: + break; + + } + + } + + }; + + function InstanceEffect () { + + this.url = ""; + + } + + InstanceEffect.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + return this; + + }; + + function Animation() { + + this.id = ""; + this.name = ""; + this.source = {}; + this.sampler = []; + this.channel = []; + + } + + Animation.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.source = {}; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'animation': + + var anim = ( new Animation() ).parse( child ); + + for ( var src in anim.source ) { + + this.source[ src ] = anim.source[ src ]; + + } + + for ( var j = 0; j < anim.channel.length; j ++ ) { + + this.channel.push( anim.channel[ j ] ); + this.sampler.push( anim.sampler[ j ] ); + + } + + break; + + case 'source': + + var src = ( new Source() ).parse( child ); + this.source[ src.id ] = src; + break; + + case 'sampler': + + this.sampler.push( ( new Sampler( this ) ).parse( child ) ); + break; + + case 'channel': + + this.channel.push( ( new Channel( this ) ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Channel( animation ) { + + this.animation = animation; + this.source = ""; + this.target = ""; + this.fullSid = null; + this.sid = null; + this.dotSyntax = null; + this.arrSyntax = null; + this.arrIndices = null; + this.member = null; + + } + + Channel.prototype.parse = function ( element ) { + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.target = element.getAttribute( 'target' ); + + var parts = this.target.split( '/' ); + + var id = parts.shift(); + var sid = parts.shift(); + + var dotSyntax = ( sid.indexOf(".") >= 0 ); + var arrSyntax = ( sid.indexOf("(") >= 0 ); + + if ( dotSyntax ) { + + parts = sid.split("."); + this.sid = parts.shift(); + this.member = parts.shift(); + + } else if ( arrSyntax ) { + + var arrIndices = sid.split("("); + this.sid = arrIndices.shift(); + + for (var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); + + } + + this.arrIndices = arrIndices; + + } else { + + this.sid = sid; + + } + + this.fullSid = sid; + this.dotSyntax = dotSyntax; + this.arrSyntax = arrSyntax; + + return this; + + }; + + function Sampler ( animation ) { + + this.id = ""; + this.animation = animation; + this.inputs = []; + this.input = null; + this.output = null; + this.strideOut = null; + this.interpolation = null; + this.startTime = null; + this.endTime = null; + this.duration = 0; + + } + + Sampler.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( (new Input()).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Sampler.prototype.create = function () { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + var input = this.inputs[ i ]; + var source = this.animation.source[ input.source ]; + + switch ( input.semantic ) { + + case 'INPUT': + + this.input = source.read(); + break; + + case 'OUTPUT': + + this.output = source.read(); + this.strideOut = source.accessor.stride; + break; + + case 'INTERPOLATION': + + this.interpolation = source.read(); + break; + + case 'IN_TANGENT': + + break; + + case 'OUT_TANGENT': + + break; + + default: + + console.log(input.semantic); + break; + + } + + } + + this.startTime = 0; + this.endTime = 0; + this.duration = 0; + + if ( this.input.length ) { + + this.startTime = 100000000; + this.endTime = -100000000; + + for ( var i = 0; i < this.input.length; i ++ ) { + + this.startTime = Math.min( this.startTime, this.input[ i ] ); + this.endTime = Math.max( this.endTime, this.input[ i ] ); + + } + + this.duration = this.endTime - this.startTime; + + } + + }; + + Sampler.prototype.getData = function ( type, ndx, member ) { + + var data; + + if ( type === 'matrix' && this.strideOut === 16 ) { + + data = this.output[ ndx ]; + + } else if ( this.strideOut > 1 ) { + + data = []; + ndx *= this.strideOut; + + for ( var i = 0; i < this.strideOut; ++ i ) { + + data[ i ] = this.output[ ndx + i ]; + + } + + if ( this.strideOut === 3 ) { + + switch ( type ) { + + case 'rotate': + case 'translate': + + fixCoords( data, -1 ); + break; + + case 'scale': + + fixCoords( data, 1 ); + break; + + } + + } else if ( this.strideOut === 4 && type === 'matrix' ) { + + fixCoords( data, -1 ); + + } + + } else { + + data = this.output[ ndx ]; + + if ( member && type === 'translate' ) { + data = getConvertedTranslation( member, data ); + } + + } + + return data; + + }; + + function Key ( time ) { + + this.targets = []; + this.time = time; + + } + + Key.prototype.addTarget = function ( fullSid, transform, member, data ) { + + this.targets.push( { + sid: fullSid, + member: member, + transform: transform, + data: data + } ); + + }; + + Key.prototype.apply = function ( opt_sid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + var target = this.targets[ i ]; + + if ( !opt_sid || target.sid === opt_sid ) { + + target.transform.update( target.data, target.member ); + + } + + } + + }; + + Key.prototype.getTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return this.targets[ i ]; + + } + + } + + return null; + + }; + + Key.prototype.hasTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return true; + + } + + } + + return false; + + }; + + // TODO: Currently only doing linear interpolation. Should support full COLLADA spec. + Key.prototype.interpolate = function ( nextKey, time ) { + + for ( var i = 0, l = this.targets.length; i < l; i ++ ) { + + var target = this.targets[ i ], + nextTarget = nextKey.getTarget( target.sid ), + data; + + if ( target.transform.type !== 'matrix' && nextTarget ) { + + var scale = ( time - this.time ) / ( nextKey.time - this.time ), + nextData = nextTarget.data, + prevData = target.data; + + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; + + if ( prevData.length ) { + + data = []; + + for ( var j = 0; j < prevData.length; ++ j ) { + + data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + } else { + + data = target.data; + + } + + target.transform.update( data, target.member ); + + } + + }; + + // Camera + function Camera() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + } + + Camera.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'optics': + + this.parseOptics( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Camera.prototype.parseOptics = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'technique_common' ) { + + var technique = element.childNodes[ i ]; + + for ( var j = 0; j < technique.childNodes.length; j ++ ) { + + this.technique = technique.childNodes[ j ].nodeName; + + if ( this.technique === 'perspective' ) { + + var perspective = technique.childNodes[ j ]; + + for ( var k = 0; k < perspective.childNodes.length; k ++ ) { + + var param = perspective.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'yfov': + this.yfov = param.textContent; + break; + case 'xfov': + this.xfov = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } else if ( this.technique === 'orthographic' ) { + + var orthographic = technique.childNodes[ j ]; + + for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { + + var param = orthographic.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'xmag': + this.xmag = param.textContent; + break; + case 'ymag': + this.ymag = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } + + } + + } + + } + + return this; + + }; + + function InstanceCamera() { + + this.url = ""; + + } + + InstanceCamera.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + + return this; + + }; + + // Light + + function Light() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + } + + Light.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon( child ); + break; + + case 'technique': + + this.parseTechnique( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Light.prototype.parseCommon = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + switch ( element.childNodes[ i ].nodeName ) { + + case 'directional': + case 'point': + case 'spot': + case 'ambient': + + this.technique = element.childNodes[ i ].nodeName; + + var light = element.childNodes[ i ]; + + for ( var j = 0; j < light.childNodes.length; j ++ ) { + + var child = light.childNodes[j]; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(0); + this.color.setRGB( rgba[0], rgba[1], rgba[2] ); + this.color.a = rgba[3]; + break; + + case 'falloff_angle': + + this.falloff_angle = parseFloat( child.textContent ); + break; + + case 'quadratic_attenuation': + var f = parseFloat( child.textContent ); + this.distance = f ? Math.sqrt( 1 / f ) : 0; + } + + } + + } + + } + + return this; + + }; + + Light.prototype.parseTechnique = function ( element ) { + + this.profile = element.getAttribute( 'profile' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'intensity': + + this.intensity = parseFloat(child.textContent); + break; + + } + + } + + return this; + + }; + + function InstanceLight() { + + this.url = ""; + + } + + InstanceLight.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + + return this; + + }; + + function KinematicsModel( ) { + + this.id = ''; + this.name = ''; + this.joints = []; + this.links = []; + + } + + KinematicsModel.prototype.parse = function( element ) { + + this.id = element.getAttribute('id'); + this.name = element.getAttribute('name'); + this.joints = []; + this.links = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon(child); + break; + + default: + break; + + } + + } + + return this; + + }; + + KinematicsModel.prototype.parseCommon = function( element ) { + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( element.childNodes[ i ].nodeName ) { + + case 'joint': + this.joints.push( (new Joint()).parse(child) ); + break; + + case 'link': + this.links.push( (new Link()).parse(child) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Joint( ) { + + this.sid = ''; + this.name = ''; + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + } + + Joint.prototype.parse = function( element ) { + + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + var axisElement = element.querySelector('axis'); + var _axis = _floats(axisElement.textContent); + this.axis = getConvertedVec3(_axis, 0); + + var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; + var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; + + this.limits = { + min: min, + max: max + }; + + var jointTypes = [ 'prismatic', 'revolute' ]; + for (var i = 0; i < jointTypes.length; i ++ ) { + + var type = jointTypes[ i ]; + + var jointElement = element.querySelector(type); + + if ( jointElement ) { + + this.type = type; + + } + + } + + // if the min is equal to or somehow greater than the max, consider the joint static + if ( this.limits.min >= this.limits.max ) { + + this.static = true; + + } + + this.middlePosition = (this.limits.min + this.limits.max) / 2.0; + return this; + + }; + + function Link( ) { + + this.sid = ''; + this.name = ''; + this.transforms = []; + this.attachments = []; + + } + + Link.prototype.parse = function( element ) { + + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.transforms = []; + this.attachments = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'attachment_full': + this.attachments.push( (new Attachment()).parse(child) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( (new Transform()).parse(child) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function Attachment( ) { + + this.joint = ''; + this.transforms = []; + this.links = []; + + } + + Attachment.prototype.parse = function( element ) { + + this.joint = element.getAttribute('joint').split('/').pop(); + this.links = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'link': + this.links.push( (new Link()).parse(child) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( (new Transform()).parse(child) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function _source( element ) { + + var id = element.getAttribute( 'id' ); + + if ( sources[ id ] != undefined ) { + + return sources[ id ]; + + } + + sources[ id ] = ( new Source(id )).parse( element ); + return sources[ id ]; + + } + + function _nsResolver( nsPrefix ) { + + if ( nsPrefix === "dae" ) { + + return "http://www.collada.org/2005/11/COLLADASchema"; + + } + + return null; + + } + + function _bools( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); + + } + + return data; + + } + + function _floats( str ) { + + var raw = _strings(str); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseFloat( raw[ i ] ) ); + + } + + return data; + + } + + function _ints( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseInt( raw[ i ], 10 ) ); + + } + + return data; + + } + + function _strings( str ) { + + return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; + + } + + function _trimString( str ) { + + return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); + + } + + function _attr_as_float( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseFloat( element.getAttribute( name ) ); + + } else { + + return defaultValue; + + } + + } + + function _attr_as_int( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseInt( element.getAttribute( name ), 10) ; + + } else { + + return defaultValue; + + } + + } + + function _attr_as_string( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return element.getAttribute( name ); + + } else { + + return defaultValue; + + } + + } + + function _format_float( f, num ) { + + if ( f === undefined ) { + + var s = '0.'; + + while ( s.length < num + 2 ) { + + s += '0'; + + } + + return s; + + } + + num = num || 2; + + var parts = f.toString().split( '.' ); + parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; + + while ( parts[ 1 ].length < num ) { + + parts[ 1 ] += '0'; + + } + + return parts.join( '.' ); + + } + + function loadTextureImage ( texture, url ) { + + var loader = new THREE.ImageLoader(); + + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + } ); + + } + + function extractDoubleSided( obj, element ) { + + obj.doubleSided = false; + + var node = element.querySelectorAll('extra double_sided')[0]; + + if ( node ) { + + if ( node && parseInt( node.textContent, 10 ) === 1 ) { + + obj.doubleSided = true; + + } + + } + + } + + // Up axis conversion + + function setUpConversion() { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + upConversion = null; + + } else { + + switch ( colladaUp ) { + + case 'X': + + upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; + break; + + case 'Y': + + upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; + break; + + case 'Z': + + upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; + break; + + } + + } + + } + + function fixCoords( data, sign ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return; + + } + + switch ( upConversion ) { + + case 'XtoY': + + var tmp = data[ 0 ]; + data[ 0 ] = sign * data[ 1 ]; + data[ 1 ] = tmp; + break; + + case 'XtoZ': + + var tmp = data[ 2 ]; + data[ 2 ] = data[ 1 ]; + data[ 1 ] = data[ 0 ]; + data[ 0 ] = tmp; + break; + + case 'YtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = sign * tmp; + break; + + case 'YtoZ': + + var tmp = data[ 1 ]; + data[ 1 ] = sign * data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoY': + + var tmp = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = sign * tmp; + break; + + } + + } + + function getConvertedTranslation( axis, data ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return data; + + } + + switch ( axis ) { + case 'X': + data = upConversion === 'XtoY' ? data * -1 : data; + break; + case 'Y': + data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; + break; + case 'Z': + data = upConversion === 'ZtoY' ? data * -1 : data ; + break; + default: + break; + } + + return data; + } + + function getConvertedVec3( data, offset ) { + + var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; + fixCoords( arr, -1 ); + return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); + + } + + function getConvertedMat4( data ) { + + if ( options.convertUpAxis ) { + + // First fix rotation and scale + + // Columns first + var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; + fixCoords( arr, -1 ); + data[ 0 ] = arr[ 0 ]; + data[ 4 ] = arr[ 1 ]; + data[ 8 ] = arr[ 2 ]; + arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; + fixCoords( arr, -1 ); + data[ 1 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 9 ] = arr[ 2 ]; + arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; + fixCoords( arr, -1 ); + data[ 2 ] = arr[ 0 ]; + data[ 6 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + // Rows second + arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; + fixCoords( arr, -1 ); + data[ 0 ] = arr[ 0 ]; + data[ 1 ] = arr[ 1 ]; + data[ 2 ] = arr[ 2 ]; + arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; + fixCoords( arr, -1 ); + data[ 4 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 6 ] = arr[ 2 ]; + arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; + fixCoords( arr, -1 ); + data[ 8 ] = arr[ 0 ]; + data[ 9 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + + // Now fix translation + arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; + fixCoords( arr, -1 ); + data[ 3 ] = arr[ 0 ]; + data[ 7 ] = arr[ 1 ]; + data[ 11 ] = arr[ 2 ]; + + } + + return new THREE.Matrix4().set( + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], + data[12], data[13], data[14], data[15] + ); + + } + + function getConvertedIndex( index ) { + + if ( index > -1 && index < 3 ) { + + var members = [ 'X', 'Y', 'Z' ], + indices = { X: 0, Y: 1, Z: 2 }; + + index = getConvertedMember( members[ index ] ); + index = indices[ index ]; + + } + + return index; + + } + + function getConvertedMember( member ) { + + if ( options.convertUpAxis ) { + + switch ( member ) { + + case 'X': + + switch ( upConversion ) { + + case 'XtoY': + case 'XtoZ': + case 'YtoX': + + member = 'Y'; + break; + + case 'ZtoX': + + member = 'Z'; + break; + + } + + break; + + case 'Y': + + switch ( upConversion ) { + + case 'XtoY': + case 'YtoX': + case 'ZtoX': + + member = 'X'; + break; + + case 'XtoZ': + case 'YtoZ': + case 'ZtoY': + + member = 'Z'; + break; + + } + + break; + + case 'Z': + + switch ( upConversion ) { + + case 'XtoZ': + + member = 'X'; + break; + + case 'YtoZ': + case 'ZtoX': + case 'ZtoY': + + member = 'Y'; + break; + + } + + break; + + } + + } + + return member; + + } + + return { + + load: load, + parse: parse, + setPreferredShading: setPreferredShading, + applySkin: applySkin, + geometries : geometries, + options: options + + }; + +}; +// File: utils/GLTF2Loader.js +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + * @author Don McCurdy / https://www.donmccurdy.com + */ + +THREE.GLTF2Loader = ( function () { + + function GLTF2Loader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + GLTF2Loader.prototype = { + + constructor: GLTF2Loader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); + + var loader = new THREE.FileLoader( scope.manager ); + + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( data ) { + + scope.parse( data, onLoad, path ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setPath: function ( value ) { + + this.path = value; + + }, + + parse: function ( data, callback, path ) { + + var content; + var extensions = {}; + + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { + + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + + } else { + + content = convertUint8ArrayToString( new Uint8Array( data ) ); + + } + + var json = JSON.parse( content ); + + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + + } + + console.time( 'GLTF2Loader' ); + + var parser = new GLTFParser( json, extensions, { + + path: path || this.path, + crossOrigin: this.crossOrigin + + } ); + + parser.parse( function ( scene, scenes, cameras, animations ) { + + console.timeEnd( 'GLTF2Loader' ); + + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + + callback( glTF ); + + } ); + + } + + }; + + /* GLTFREGISTRY */ + + function GLTFRegistry() { + + var objects = {}; + + return { + + get: function ( key ) { + + return objects[ key ]; + + }, + + add: function ( key, object ) { + + objects[ key ] = object; + + }, + + remove: function ( key ) { + + delete objects[ key ]; + + }, + + removeAll: function () { + + objects = {}; + + }, + + update: function ( scene, camera ) { + + for ( var name in objects ) { + + var object = objects[ name ]; + + if ( object.update ) { + + object.update( scene, camera ); + + } + + } + + } + + }; + + } + + /* GLTFSHADER */ + + function GLTFShader( targetNode, allNodes ) { + + var boundUniforms = {}; + + // bind each uniform to its source node + + var uniforms = targetNode.material.uniforms; + + for ( var uniformId in uniforms ) { + + var uniform = uniforms[ uniformId ]; + + if ( uniform.semantic ) { + + var sourceNodeRef = uniform.node; + + var sourceNode = targetNode; + + if ( sourceNodeRef ) { + + sourceNode = allNodes[ sourceNodeRef ]; + + } + + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + + } + + } + + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + + } + + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + + var boundUniforms = this.boundUniforms; + + for ( var name in boundUniforms ) { + + var boundUniform = boundUniforms[ name ]; + + switch ( boundUniform.semantic ) { + + case "MODELVIEW": + + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + + case "MODELVIEWINVERSETRANSPOSE": + + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + + case "PROJECTION": + + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + + case "JOINTMATRIX": + + var m4v = boundUniform.uniform.value; + + for ( var mi = 0; mi < m4v.length; mi ++ ) { + + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + + } + + break; + + default : + + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + + } + + } + + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' + }; + + /* MATERIALS COMMON EXTENSION */ + + function GLTFMaterialsCommonExtension( json ) { + + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + + this.lights = {}; + + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + + for ( var lightId in lights ) { + + var light = lights[ lightId ]; + var lightNode; + + var lightParams = light[ light.type ]; + var color = new THREE.Color().fromArray( lightParams.color ); + + switch ( light.type ) { + + case "directional": + lightNode = new THREE.DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "point": + lightNode = new THREE.PointLight( color ); + break; + + case "spot": + lightNode = new THREE.SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "ambient": + lightNode = new THREE.AmbientLight( color ); + break; + + } + + if ( lightNode ) { + + this.lights[ lightId ] = lightNode; + + } + + } + + } + + /* BINARY EXTENSION */ + + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; + var BINARY_EXTENSION_HEADER_LENGTH = 12; + var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; + + function GLTFBinaryExtension( data ) { + + this.name = EXTENSIONS.KHR_BINARY_GLTF; + this.content = null; + this.body = null; + + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + + this.header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ) + }; + + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { + + throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); + + } else if ( this.header.version < 2.0 ) { + + throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); + + } + + var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); + var chunkIndex = 0; + + while ( chunkIndex < chunkView.byteLength ) { + + var chunkLength = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + var chunkType = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { + + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); + this.content = convertUint8ArrayToString( contentArray ); + + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { + + var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; + this.body = data.slice( byteOffset, byteOffset + chunkLength ); + + } + + // Clients must ignore chunks with unknown types. + + chunkIndex += chunkLength; + + } + + if ( this.content === null ) { + + throw new Error( 'GLTF2Loader: JSON content not found.' ); + + } + + } + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + + /* CONSTANTS */ + + var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 + }; + + var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: THREE.Matrix3, + 35676: THREE.Matrix4, + 35664: THREE.Vector2, + 35665: THREE.Vector3, + 35666: THREE.Vector4, + 35678: THREE.Texture + }; + + var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + + var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipMapNearestFilter, + 9985: THREE.LinearMipMapNearestFilter, + 9986: THREE.NearestMipMapLinearFilter, + 9987: THREE.LinearMipMapLinearFilter + }; + + var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping + }; + + var WEBGL_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + + function _each( object, callback, thisObj ) { + + if ( !object ) { + return Promise.resolve(); + } + + var results; + var fns = []; + + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + + results = []; + + var length = object.length; + + for ( var idx = 0; idx < length; idx ++ ) { + + var value = callback.call( thisObj || this, object[ idx ], idx ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function( key, value ) { + + results[ key ] = value; + + }.bind( this, idx )); + + } else { + + results[ idx ] = value; + + } + + } + + } + + } else { + + results = {}; + + for ( var key in object ) { + + if ( object.hasOwnProperty( key ) ) { + + var value = callback.call( thisObj || this, object[ key ], key ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function( key, value ) { + + results[ key ] = value; + + }.bind( this, key )); + + } else { + + results[ key ] = value; + + } + + } + + } + + } + + } + + return Promise.all( fns ).then( function() { + + return results; + + }); + + } + + function resolveURL( url, path ) { + + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + + return url; + + } + + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + + return url; + + } + + // Blob URL + if ( /^blob:.*$/i.test( url ) ) { + + return url; + + } + + // Relative URL + return ( path || '' ) + url; + + } + + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + function convertUint8ArrayToString( array ) { + + var s = ''; + + for ( var i = 0; i < array.length; i ++ ) { + + s += String.fromCharCode( array[ i ] ); + + } + + return s; + + } + + // Three.js seems too dependent on attribute names so globally + // replace those in the shader code + function replaceTHREEShaderAttributes( shaderText, technique ) { + + // Expected technique attributes + var attributes = {}; + + for ( var attributeId in technique.attributes ) { + + var pname = technique.attributes[ attributeId ]; + + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + + } + + // Figure out which attributes to change in technique + + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + + for ( var attributeId in attributes ) { + + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + + params[ attributeId ] = shaderParam; + + } + + } + + for ( var pname in params ) { + + var param = params[ pname ]; + var semantic = param.semantic; + + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + + switch ( semantic ) { + + case 'POSITION': + + shaderText = shaderText.replace( regEx, 'position' ); + break; + + case 'NORMAL': + + shaderText = shaderText.replace( regEx, 'normal' ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + shaderText = shaderText.replace( regEx, 'uv' ); + break; + + case 'TEXCOORD_1': + + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + shaderText = shaderText.replace( regEx, 'color' ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + + } + + } + + return shaderText; + + } + + function createDefaultMaterial() { + + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + + } + + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + + this.isDeferredShaderMaterial = true; + + this.params = params; + + } + + DeferredShaderMaterial.prototype.create = function () { + + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + + for ( var uniformId in this.params.uniforms ) { + + var originalUniform = this.params.uniforms[ uniformId ]; + + if ( originalUniform.value instanceof THREE.Texture ) { + + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + + } + + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + + } + + this.params.uniforms = uniforms; + + return new THREE.RawShaderMaterial( this.params ); + + }; + + /* GLTF PARSER */ + + function GLTFParser( json, extensions, options ) { + + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + + // loader object cache + this.cache = new GLTFRegistry(); + + } + + GLTFParser.prototype._withDependencies = function ( dependencies ) { + + var _dependencies = {}; + + for ( var i = 0; i < dependencies.length; i ++ ) { + + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + + var cached = this.cache.get( dependency ); + + if ( cached !== undefined ) { + + _dependencies[ dependency ] = cached; + + } else if ( this[ fnName ] ) { + + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + + _dependencies[ dependency ] = fn; + + } + + } + + return _each( _dependencies, function ( dependency ) { + + return dependency; + + } ); + + }; + + GLTFParser.prototype.parse = function ( callback ) { + + var json = this.json; + + // Clear the loader cache + this.cache.removeAll(); + + // Fire the callback on complete + this._withDependencies( [ + + "scenes", + "cameras", + "animations" + + ] ).then( function ( dependencies ) { + + var scenes = []; + + for ( var name in dependencies.scenes ) { + + scenes.push( dependencies.scenes[ name ] ); + + } + + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + + var cameras = []; + + for ( var name in dependencies.cameras ) { + + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + + } + + var animations = []; + + for ( var name in dependencies.animations ) { + + animations.push( dependencies.animations[ name ] ); + + } + + callback( scene, scenes, cameras, animations ); + + } ); + + }; + + GLTFParser.prototype.loadShaders = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.shaders, function ( shader ) { + + if ( shader.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ shader.bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + + resolve( shaderText ); + + } ); + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadBuffers = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return _each( json.buffers, function ( buffer, name ) { + + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + + // If present, GLB container is required to be the first buffer. + if ( buffer.uri === undefined && name === 0 ) { + + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + + resolve( buffer ); + + } ); + + } ); + + } else { + + console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); + + } + + } ); + + }; + + GLTFParser.prototype.loadBufferViews = function () { + + var json = this.json; + + return this._withDependencies( [ + + "buffers" + + ] ).then( function ( dependencies ) { + + return _each( json.bufferViews, function ( bufferView ) { + + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAccessors = function () { + + var json = this.json; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.accessors, function ( accessor ) { + + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + + var array; + + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + + // Use the full buffer if it's interleaved. + array = new TypedArray( arraybuffer ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + + } else { + + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + + return new THREE.BufferAttribute( array, itemSize ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadTextures = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.textures, function ( texture ) { + + if ( texture.source !== undefined ) { + + return new Promise( function ( resolve ) { + + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + + var urlCreator; + + if ( source.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ source.bufferView ]; + var blob = new Blob( [ bufferView ], { type: source.mimeType } ); + urlCreator = window.URL || window.webkitURL; + sourceUri = urlCreator.createObjectURL( blob ); + + } + + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + + if ( textureLoader === null ) { + + textureLoader = new THREE.TextureLoader(); + + } + + textureLoader.setCrossOrigin( options.crossOrigin ); + + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + + if ( urlCreator !== undefined ) { + + urlCreator.revokeObjectURL( sourceUri ); + + } + + _texture.flipY = false; + + if ( texture.name !== undefined ) _texture.name = texture.name; + + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + + console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + + } + + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + + if ( texture.sampler !== undefined ) { + + var sampler = json.samplers[ texture.sampler ]; + + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + + } + + resolve( _texture ); + + }, undefined, function () { + + resolve(); + + } ); + + } ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMaterials = function () { + + var json = this.json; + + return this._withDependencies( [ + + "shaders", + "textures" + + ] ).then( function ( dependencies ) { + + return _each( json.materials, function ( material ) { + + var materialType; + var materialValues = {}; + var materialParams = {}; + + var khr_material; + + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + + } + + if ( khr_material ) { + + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + + switch ( khr_material.technique ) { + + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + + } + + keys.forEach( function( v ) { + + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + + } ); + + if ( khr_material.doubleSided || materialValues.doubleSided ) { + + materialParams.side = THREE.DoubleSide; + + } + + if ( khr_material.transparent || materialValues.transparent ) { + + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + + } + + } else if ( material.technique === undefined ) { + + if ( material.pbrMetallicRoughness !== undefined ) { + + // specification + // https://github.com/sbtron/glTF/blob/30de0b365d1566b1bbd8b9c140f9e995d3203226/specification/2.0/README.md#metallic-roughness-material + + materialType = THREE.MeshStandardMaterial; + + if ( material.pbrMetallicRoughness !== undefined ) { + + var metallicRoughness = material.pbrMetallicRoughness; + + materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + + var array = metallicRoughness.baseColorFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( metallicRoughness.baseColorTexture !== undefined ) { + + materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; + + } + + if ( materialParams.opacity < 1.0 || + ( materialParams.map !== undefined && + ( materialParams.map.format === THREE.AlphaFormat || + materialParams.map.format === THREE.RGBAFormat || + materialParams.map.format === THREE.LuminanceAlphaFormat ) ) ) { + + materialParams.transparent = true; + + } + + materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; + materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; + + if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { + + var textureIndex = metallicRoughness.metallicRoughnessTexture.index; + + // Note that currently metalnessMap would be entirely ignored because + // Three.js and glTF specification use different texture channels for metalness + // (Blue: Three.js, Red: glTF). + // But glTF specification team is discussing if they can change. + // Let's keep an eye on it so far. + // + // https://github.com/KhronosGroup/glTF/issues/857 + materialParams.metalnessMap = dependencies.textures[ textureIndex ]; + materialParams.roughnessMap = dependencies.textures[ textureIndex ]; + + } + + } + + } else { + + materialType = THREE.MeshPhongMaterial; + + } + + if ( material.normalTexture !== undefined ) { + + materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; + + } + + if ( material.occlusionTexture !== undefined ) { + + materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; + + } + + if ( material.emissiveTexture !== undefined ) { + + materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; + + } + + materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); + + if ( material.emissiveFactor !== undefined ) { + + materialParams.emissive.fromArray( material.emissiveFactor ); + + } + + Object.assign( materialValues, material.values ); + + } else { + + materialType = DeferredShaderMaterial; + + var technique = json.techniques[ material.technique ]; + + materialParams.uniforms = {}; + + var program = json.programs[ technique.program ]; + + if ( program ) { + + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + + if ( ! materialParams.fragmentShader ) { + + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = THREE.MeshPhongMaterial; + + } + + var vertexShader = dependencies.shaders[ program.vertexShader ]; + + if ( ! vertexShader ) { + + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = THREE.MeshPhongMaterial; + + } + + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + + var uniforms = technique.uniforms; + + for ( var uniformId in uniforms ) { + + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + + var ptype = shaderParam.type; + + if ( WEBGL_TYPE[ ptype ] ) { + + var pcount = shaderParam.count; + var value; + + if ( material.values !== undefined ) value = material.values[ pname ]; + + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + + switch ( ptype ) { + + case WEBGL_CONSTANTS.FLOAT: + + uvalue = shaderParam.value; + + if ( pname == "transparency" ) { + + materialParams.transparent = true; + + } + + if ( value !== undefined ) { + + uvalue = value; + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + + if ( shaderParam && shaderParam.value ) { + + uvalue.fromArray( shaderParam.value ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_MAT2: + + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + + case WEBGL_CONSTANTS.FLOAT_MAT4: + + if ( pcount ) { + + uvalue = new Array( pcount ); + + for ( var mi = 0; mi < pcount; mi ++ ) { + + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + + } + + if ( shaderParam && shaderParam.value ) { + + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } else { + + if ( shaderParam && shaderParam.value ) { + + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } + + break; + + case WEBGL_CONSTANTS.SAMPLER_2D: + + if ( value !== undefined ) { + + uvalue = dependencies.textures[ value ]; + + } else if ( shaderParam.value !== undefined ) { + + uvalue = dependencies.textures[ shaderParam.value ]; + + } else { + + uvalue = null; + + } + + break; + + } + + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + + } else { + + throw new Error( "Unknown shader uniform param type: " + ptype ); + + } + + } + + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + + for ( var i = 0, il = enables.length; i < il; i ++ ) { + + var enable = enables[ i ]; + + switch ( STATES_ENABLES[ enable ] ) { + + case 'CULL_FACE': + + enableCullFace = true; + + break; + + case 'DEPTH_TEST': + + enableDepthTest = true; + + break; + + case 'BLEND': + + enableBlend = true; + + break; + + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + + break; + + default: + + throw new Error( "Unknown technique.states.enable: " + enable ); + + } + + } + + if ( enableCullFace ) { + + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + + } else { + + materialParams.side = THREE.DoubleSide; + + } + + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + + var blendEquationSeparate = functions.blendEquationSeparate; + + if ( blendEquationSeparate !== undefined ) { + + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + + } else { + + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + + } + + var blendFuncSeparate = functions.blendFuncSeparate; + + if ( blendFuncSeparate !== undefined ) { + + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + + } else { + + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + + } + + } + + } + + if ( Array.isArray( materialValues.diffuse ) ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); + + } else if ( typeof( materialValues.diffuse ) === 'string' ) { + + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + + } + + delete materialParams.diffuse; + + if ( typeof( materialValues.reflective ) === 'string' ) { + + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + + } + + if ( typeof( materialValues.bump ) === 'string' ) { + + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + + } + + if ( Array.isArray( materialValues.emission ) ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + + } else { + + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + + } + + } else if ( typeof( materialValues.emission ) === 'string' ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.map = dependencies.textures[ materialValues.emission ]; + + } else { + + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + + } + + } + + if ( Array.isArray( materialValues.specular ) ) { + + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + + } else if ( typeof( materialValues.specular ) === 'string' ) { + + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + + } + + if ( materialValues.shininess !== undefined ) { + + materialParams.shininess = materialValues.shininess; + + } + + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + + return _material; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMeshes = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "materials" + + ] ).then( function ( dependencies ) { + + return _each( json.meshes, function ( mesh ) { + + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + + if ( mesh.extras ) group.userData = mesh.extras; + + var primitives = mesh.primitives || []; + + for ( var name in primitives ) { + + var primitive = primitives[ name ]; + + var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + + var geometry; + + var meshNode; + + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( attributeEntry === undefined ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'NORMAL': + + geometry.addAttribute( 'normal', bufferAttribute ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + geometry.addAttribute( 'uv', bufferAttribute ); + break; + + case 'TEXCOORD_1': + + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + geometry.addAttribute( 'color', bufferAttribute ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + } + + meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( ! attributeEntry ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + meshNode = new THREE.LineSegments( geometry, material ); + + } else { + + meshNode = new THREE.Line( geometry, material ); + + } + + } else { + + throw new Error( "Only triangular and line primitives are supported" ); + + } + + if ( geometry.attributes.color !== undefined ) { + + material.vertexColors = THREE.VertexColors; + material.needsUpdate = true; + + } + + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } + + return group; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadCameras = function () { + + var json = this.json; + + return _each( json.cameras, function ( camera ) { + + if ( camera.type == "perspective" && camera.perspective ) { + + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } else if ( camera.type == "orthographic" && camera.orthographic ) { + + var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } + + } ); + + }; + + GLTFParser.prototype.loadSkins = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors" + + ] ).then( function ( dependencies ) { + + return _each( json.skins, function ( skin ) { + + var bindShapeMatrix = new THREE.Matrix4(); + + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + + return _skin; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAnimations = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.animations, function ( animation, animationId ) { + + var tracks = []; + + for ( var channelId in animation.channels ) { + + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + + if ( sampler ) { + + var target = channel.target; + var name = target.node || target.id; // NOTE: target.id is deprecated. + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + + var node = dependencies.nodes[ name ]; + + if ( node ) { + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + + } + + } + + } + + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + + return new THREE.AnimationClip( name, undefined, tracks ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadNodes = function () { + + var json = this.json; + var extensions = this.extensions; + var scope = this; + + return _each( json.nodes, function ( node ) { + + var matrix = new THREE.Matrix4(); + + var _node; + + if ( node.jointName ) { + + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + + } else { + + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + + } + + if ( node.extras ) _node.userData = node.extras; + + if ( node.matrix !== undefined ) { + + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + + } else { + + if ( node.translation !== undefined ) { + + _node.position.fromArray( node.translation ); + + } + + if ( node.rotation !== undefined ) { + + _node.quaternion.fromArray( node.rotation ); + + } + + if ( node.scale !== undefined ) { + + _node.scale.fromArray( node.scale ); + + } + + } + + return _node; + + } ).then( function ( __nodes ) { + + return scope._withDependencies( [ + + "meshes", + "skins", + "cameras" + + ] ).then( function ( dependencies ) { + + return _each( __nodes, function ( _node, nodeId ) { + + var node = json.nodes[ nodeId ]; + + var meshes; + + if ( node.mesh !== undefined) { + + meshes = [ node.mesh ]; + + } else if ( node.meshes !== undefined ) { + + console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); + + meshes = node.meshes; + + } + + if ( meshes !== undefined ) { + + for ( var meshId in meshes ) { + + var mesh = meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + + if ( group === undefined ) { + + console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); + continue; + + } + + for ( var childrenId in group.children ) { + + var child = group.children[ childrenId ]; + + // clone Mesh to add to _node + + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + + var material; + + if ( originalMaterial.isDeferredShaderMaterial ) { + + originalMaterial = material = originalMaterial.create(); + + } else { + + material = originalMaterial; + + } + + switch ( child.type ) { + + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + + default: + child = new THREE.Mesh( originalGeometry, material ); + + } + + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var skinEntry; + + if ( node.skin !== undefined ) { + + skinEntry = dependencies.skins[ node.skin ]; + + } + + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + + var getJointNode = function ( jointId ) { + + var keys = Object.keys( __nodes ); + + for ( var i = 0, il = keys.length; i < il; i ++ ) { + + var n = __nodes[ keys[ i ] ]; + + if ( n.jointName === jointId ) return n; + + } + + return null; + + }; + + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var bones = []; + var boneInverses = []; + + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + + if ( jointNode ) { + + bones.push( jointNode ); + + var m = skinEntry.inverseBindMatrices.array; + var mat = new THREE.Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + + } else { + + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + + } + + } + + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + + var buildBoneGraph = function ( parentJson, parentObject, property ) { + + var children = parentJson[ property ]; + + if ( children === undefined ) return; + + for ( var i = 0, il = children.length; i < il; i ++ ) { + + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + + } + + } + + }; + + buildBoneGraph( node, child, 'skeletons' ); + + } + + _node.add( child ); + + } + + } + + } + + if ( node.camera !== undefined ) { + + var camera = dependencies.cameras[ node.camera ]; + + _node.add( camera ); + + } + + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + + _node.add( light ); + + } + + return _node; + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadScenes = function () { + + var json = this.json; + + // scene node hierachy builder + + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + + var node = json.nodes[ nodeId ]; + + if ( node.children ) { + + var children = node.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + + } + + } + + } + + return this._withDependencies( [ + + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.scenes, function ( scene ) { + + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + + if ( scene.extras ) _scene.userData = scene.extras; + + var nodes = scene.nodes || []; + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + + } + + _scene.traverse( function ( child ) { + + // Register raw material meshes with GLTF2Loader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + + } + + } ); + + return _scene; + + } ); + + } ); + + }; + + return GLTF2Loader; + +} )(); +// File: utils/GLTFLoader.js +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + */ + +THREE.GLTFLoader = ( function () { + function GLTFLoader( manager ) { + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + } + GLTFLoader.prototype = { + constructor: GLTFLoader, + load: function ( url, onLoad, onProgress, onError ) { + var scope = this; + var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); + var loader = new THREE.FileLoader( scope.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( data ) { + scope.parse( data, onLoad, path ); + }, onProgress, onError ); + }, + setCrossOrigin: function ( value ) { + this.crossOrigin = value; + }, + setPath: function ( value ) { + this.path = value; + }, + parse: function ( data, callback, path ) { + var content; + var extensions = {}; + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + } else { + content = convertUint8ArrayToString( new Uint8Array( data ) ); + } + var json = JSON.parse( content ); + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + } + console.time( 'GLTFLoader' ); + var parser = new GLTFParser( json, extensions, { + path: path || this.path, + crossOrigin: this.crossOrigin + } ); + parser.parse( function ( scene, scenes, cameras, animations ) { + console.timeEnd( 'GLTFLoader' ); + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + callback( glTF ); + } ); + } + }; + + /* GLTFREGISTRY */ + function GLTFRegistry() { + var objects = {}; + return { + get: function ( key ) { + return objects[ key ]; + }, + add: function ( key, object ) { + objects[ key ] = object; + }, + remove: function ( key ) { + delete objects[ key ]; + }, + removeAll: function () { + objects = {}; + }, + update: function ( scene, camera ) { + for ( var name in objects ) { + var object = objects[ name ]; + if ( object.update ) { + object.update( scene, camera ); + } + } + } + }; + } + + /* GLTFSHADERS */ + GLTFLoader.Shaders = { + update: function () { + console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); + } + }; + + /* GLTFSHADER */ + function GLTFShader( targetNode, allNodes ) { + var boundUniforms = {}; + // bind each uniform to its source node + var uniforms = targetNode.material.uniforms; + for ( var uniformId in uniforms ) { + var uniform = uniforms[ uniformId ]; + if ( uniform.semantic ) { + var sourceNodeRef = uniform.node; + var sourceNode = targetNode; + if ( sourceNodeRef ) { + sourceNode = allNodes[ sourceNodeRef ]; + } + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + } + } + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + } + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + var boundUniforms = this.boundUniforms; + for ( var name in boundUniforms ) { + var boundUniform = boundUniforms[ name ]; + switch ( boundUniform.semantic ) { + case "MODELVIEW": + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + case "MODELVIEWINVERSETRANSPOSE": + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + case "PROJECTION": + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + case "JOINTMATRIX": + var m4v = boundUniform.uniform.value; + for ( var mi = 0; mi < m4v.length; mi ++ ) { + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + } + break; + default : + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + } + } + }; + + /* ANIMATION */ + GLTFLoader.Animations = { + update: function () { + console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); + } + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' + }; + + /* MATERIALS COMMON EXTENSION */ + function GLTFMaterialsCommonExtension( json ) { + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + this.lights = {}; + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + for ( var lightId in lights ) { + var light = lights[ lightId ]; + var lightNode; + var lightParams = light[ light.type ]; + var color = new THREE.Color().fromArray( lightParams.color ); + switch ( light.type ) { + case "directional": + lightNode = new THREE.DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + case "point": + lightNode = new THREE.PointLight( color ); + break; + case "spot": + lightNode = new THREE.SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + case "ambient": + lightNode = new THREE.AmbientLight( color ); + break; + } + if ( lightNode ) { + this.lights[ lightId ] = lightNode; + } + } + } + + /* BINARY EXTENSION */ + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; + var BINARY_EXTENSION_HEADER_LENGTH = 20; + function GLTFBinaryExtension( data ) { + this.name = EXTENSIONS.KHR_BINARY_GLTF; + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + var header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ), + contentLength: headerView.getUint32( 12, true ), + contentFormat: headerView.getUint32( 16, true ) + }; + for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { + var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; + if ( header[ key ] !== value ) { + throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); + } + } + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); + this.header = header; + this.content = convertUint8ArrayToString( contentArray ); + this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); + } + GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { + var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + }; + GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) { + var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; + var bufferView = bufferViews[ metadata.bufferView ]; + var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) ); + return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData ); + }; + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + /* CONSTANTS */ + var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 + }; + var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: THREE.Matrix3, + 35676: THREE.Matrix4, + 35664: THREE.Vector2, + 35665: THREE.Vector3, + 35666: THREE.Vector4, + 35678: THREE.Texture + }; + var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipMapNearestFilter, + 9985: THREE.LinearMipMapNearestFilter, + 9986: THREE.NearestMipMapLinearFilter, + 9987: THREE.LinearMipMapLinearFilter + }; + var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping + }; + var WEBGL_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + function _each( object, callback, thisObj ) { + if ( !object ) { + return Promise.resolve(); + } + var results; + var fns = []; + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + results = []; + var length = object.length; + for ( var idx = 0; idx < length; idx ++ ) { + var value = callback.call( thisObj || this, object[ idx ], idx ); + if ( value ) { + fns.push( value ); + if ( value instanceof Promise ) { + value.then( function( key, value ) { + results[ key ] = value; + }.bind( this, idx )); + } else { + results[ idx ] = value; + } + } + } + } else { + results = {}; + for ( var key in object ) { + if ( object.hasOwnProperty( key ) ) { + var value = callback.call( thisObj || this, object[ key ], key ); + if ( value ) { + fns.push( value ); + if ( value instanceof Promise ) { + value.then( function( key, value ) { + results[ key ] = value; + }.bind( this, key )); + } else { + results[ key ] = value; + } + } + } + } + } + return Promise.all( fns ).then( function() { + return results; + }); + } + function resolveURL( url, path ) { + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + return url; + } + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + return url; + } + // Relative URL + return ( path || '' ) + url; + } + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + function convertUint8ArrayToString( array ) { + var s = ''; + for ( var i = 0; i < array.length; i ++ ) { + s += String.fromCharCode( array[ i ] ); + } + return s; + } + // Three.js seems too dependent on attribute names so globally + // replace those in the shader code + function replaceTHREEShaderAttributes( shaderText, technique ) { + // Expected technique attributes + var attributes = {}; + for ( var attributeId in technique.attributes ) { + var pname = technique.attributes[ attributeId ]; + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + } + // Figure out which attributes to change in technique + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + for ( var attributeId in attributes ) { + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + params[ attributeId ] = shaderParam; + } + } + for ( var pname in params ) { + var param = params[ pname ]; + var semantic = param.semantic; + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + switch ( semantic ) { + case "POSITION": + shaderText = shaderText.replace( regEx, 'position' ); + break; + case "NORMAL": + shaderText = shaderText.replace( regEx, 'normal' ); + break; + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + shaderText = shaderText.replace( regEx, 'uv' ); + break; + case 'TEXCOORD_1': + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + shaderText = shaderText.replace( regEx, 'color' ); + break; + case "WEIGHT": + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + case "JOINT": + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + } + } + return shaderText; + } + function createDefaultMaterial() { + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + } + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + this.isDeferredShaderMaterial = true; + this.params = params; + } + DeferredShaderMaterial.prototype.create = function () { + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + for ( var uniformId in this.params.uniforms ) { + var originalUniform = this.params.uniforms[ uniformId ]; + if ( originalUniform.value instanceof THREE.Texture ) { + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + } + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + } + this.params.uniforms = uniforms; + return new THREE.RawShaderMaterial( this.params ); + }; + + /* GLTF PARSER */ + function GLTFParser( json, extensions, options ) { + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + // loader object cache + this.cache = new GLTFRegistry(); + } + GLTFParser.prototype._withDependencies = function ( dependencies ) { + var _dependencies = {}; + for ( var i = 0; i < dependencies.length; i ++ ) { + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + var cached = this.cache.get( dependency ); + if ( cached !== undefined ) { + _dependencies[ dependency ] = cached; + } else if ( this[ fnName ] ) { + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + _dependencies[ dependency ] = fn; + } + } + return _each( _dependencies, function ( dependency ) { + return dependency; + } ); + }; + GLTFParser.prototype.parse = function ( callback ) { + var json = this.json; + // Clear the loader cache + this.cache.removeAll(); + // Fire the callback on complete + this._withDependencies( [ + "scenes", + "cameras", + "animations" + ] ).then( function ( dependencies ) { + var scenes = []; + for ( var name in dependencies.scenes ) { + scenes.push( dependencies.scenes[ name ] ); + } + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + var cameras = []; + for ( var name in dependencies.cameras ) { + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + } + var animations = []; + for ( var name in dependencies.animations ) { + animations.push( dependencies.animations[ name ] ); + } + callback( scene, scenes, cameras, animations ); + } ); + }; + GLTFParser.prototype.loadShaders = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.shaders, function ( shader ) { + if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); + } + return new Promise( function ( resolve ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + resolve( shaderText ); + } ); + } ); + } ); + } ); + }; + GLTFParser.prototype.loadBuffers = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return _each( json.buffers, function ( buffer, name ) { + if ( name === BINARY_EXTENSION_BUFFER_NAME ) { + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + } + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + return new Promise( function ( resolve ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + resolve( buffer ); + } ); + } ); + } else { + console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); + } + } ); + }; + GLTFParser.prototype.loadBufferViews = function () { + var json = this.json; + return this._withDependencies( [ + "buffers" + ] ).then( function ( dependencies ) { + return _each( json.bufferViews, function ( bufferView ) { + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + } ); + } ); + }; + GLTFParser.prototype.loadAccessors = function () { + var json = this.json; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.accessors, function ( accessor ) { + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + // Use the full buffer if it's interleaved. + var array = new TypedArray( arraybuffer ); + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + } else { + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + return new THREE.BufferAttribute( array, itemSize ); + } + } ); + } ); + }; + GLTFParser.prototype.loadTextures = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.textures, function ( texture ) { + if ( texture.source ) { + return new Promise( function ( resolve ) { + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); + } + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + if ( textureLoader === null ) { + textureLoader = new THREE.TextureLoader(); + } + textureLoader.setCrossOrigin( options.crossOrigin ); + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + _texture.flipY = false; + if ( texture.name !== undefined ) _texture.name = texture.name; + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + } + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + if ( texture.sampler ) { + var sampler = json.samplers[ texture.sampler ]; + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + } + resolve( _texture ); + }, undefined, function () { + resolve(); + } ); + } ); + } + } ); + } ); + }; + GLTFParser.prototype.loadMaterials = function () { + var json = this.json; + return this._withDependencies( [ + "shaders", + "textures" + ] ).then( function ( dependencies ) { + return _each( json.materials, function ( material ) { + var materialType; + var materialValues = {}; + var materialParams = {}; + var khr_material; + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + } + if ( khr_material ) { + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + switch ( khr_material.technique ) { + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + } + keys.forEach( function( v ) { + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + } ); + if ( khr_material.doubleSided || materialValues.doubleSided ) { + materialParams.side = THREE.DoubleSide; + } + if ( khr_material.transparent || materialValues.transparent ) { + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + } + } else if ( material.technique === undefined ) { + materialType = THREE.MeshPhongMaterial; + Object.assign( materialValues, material.values ); + } else { + materialType = DeferredShaderMaterial; + var technique = json.techniques[ material.technique ]; + materialParams.uniforms = {}; + var program = json.programs[ technique.program ]; + if ( program ) { + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + if ( ! materialParams.fragmentShader ) { + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = THREE.MeshPhongMaterial; + } + var vertexShader = dependencies.shaders[ program.vertexShader ]; + if ( ! vertexShader ) { + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = THREE.MeshPhongMaterial; + } + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + var uniforms = technique.uniforms; + for ( var uniformId in uniforms ) { + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + var ptype = shaderParam.type; + if ( WEBGL_TYPE[ ptype ] ) { + var pcount = shaderParam.count; + var value; + if ( material.values !== undefined ) value = material.values[ pname ]; + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + switch ( ptype ) { + case WEBGL_CONSTANTS.FLOAT: + uvalue = shaderParam.value; + if ( pname == "transparency" ) { + materialParams.transparent = true; + } + if ( value !== undefined ) { + uvalue = value; + } + break; + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + if ( shaderParam && shaderParam.value ) { + uvalue.fromArray( shaderParam.value ); + } + if ( value ) { + uvalue.fromArray( value ); + } + break; + case WEBGL_CONSTANTS.FLOAT_MAT2: + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + case WEBGL_CONSTANTS.FLOAT_MAT4: + if ( pcount ) { + uvalue = new Array( pcount ); + for ( var mi = 0; mi < pcount; mi ++ ) { + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + } + if ( shaderParam && shaderParam.value ) { + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + } + if ( value ) { + uvalue.fromArray( value ); + } + } else { + if ( shaderParam && shaderParam.value ) { + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + } + if ( value ) { + uvalue.fromArray( value ); + } + } + break; + case WEBGL_CONSTANTS.SAMPLER_2D: + if ( value !== undefined ) { + uvalue = dependencies.textures[ value ]; + } else if ( shaderParam.value !== undefined ) { + uvalue = dependencies.textures[ shaderParam.value ]; + } else { + uvalue = null; + } + break; + } + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + } else { + throw new Error( "Unknown shader uniform param type: " + ptype ); + } + } + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + for ( var i = 0, il = enables.length; i < il; i ++ ) { + var enable = enables[ i ]; + switch ( STATES_ENABLES[ enable ] ) { + case 'CULL_FACE': + enableCullFace = true; + break; + case 'DEPTH_TEST': + enableDepthTest = true; + break; + case 'BLEND': + enableBlend = true; + break; + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + break; + default: + throw new Error( "Unknown technique.states.enable: " + enable ); + } + } + if ( enableCullFace ) { + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + } else { + materialParams.side = THREE.DoubleSide; + } + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + var blendEquationSeparate = functions.blendEquationSeparate; + if ( blendEquationSeparate !== undefined ) { + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + } else { + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + } + var blendFuncSeparate = functions.blendFuncSeparate; + if ( blendFuncSeparate !== undefined ) { + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + } else { + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + } + } + } + if ( Array.isArray( materialValues.diffuse ) ) { + materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); + } else if ( typeof( materialValues.diffuse ) === 'string' ) { + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + } + delete materialParams.diffuse; + if ( typeof( materialValues.reflective ) === 'string' ) { + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + } + if ( typeof( materialValues.bump ) === 'string' ) { + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + } + if ( Array.isArray( materialValues.emission ) ) { + if ( materialType === THREE.MeshBasicMaterial ) { + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + } else { + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + } + } else if ( typeof( materialValues.emission ) === 'string' ) { + if ( materialType === THREE.MeshBasicMaterial ) { + materialParams.map = dependencies.textures[ materialValues.emission ]; + } else { + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + } + } + if ( Array.isArray( materialValues.specular ) ) { + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + } else if ( typeof( materialValues.specular ) === 'string' ) { + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + } + if ( materialValues.shininess !== undefined ) { + materialParams.shininess = materialValues.shininess; + } + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + return _material; + } ); + } ); + }; + GLTFParser.prototype.loadMeshes = function () { + var json = this.json; + return this._withDependencies( [ + "accessors", + "materials" + ] ).then( function ( dependencies ) { + return _each( json.meshes, function ( mesh ) { + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + if ( mesh.extras ) group.userData = mesh.extras; + var primitives = mesh.primitives || []; + for ( var name in primitives ) { + var primitive = primitives[ name ]; + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + var geometry = new THREE.BufferGeometry(); + var attributes = primitive.attributes; + for ( var attributeId in attributes ) { + var attributeEntry = attributes[ attributeId ]; + if ( ! attributeEntry ) return; + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + switch ( attributeId ) { + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + case 'NORMAL': + geometry.addAttribute( 'normal', bufferAttribute ); + break; + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + geometry.addAttribute( 'uv', bufferAttribute ); + break; + case 'TEXCOORD_1': + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + case 'WEIGHT': + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + case 'JOINT': + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + } + } + if ( primitive.indices ) { + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + } + var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + var meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + meshNode.name = ( name === "0" ? group.name : group.name + name ); + if ( primitive.extras ) meshNode.userData = primitive.extras; + group.add( meshNode ); + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + var geometry = new THREE.BufferGeometry(); + var attributes = primitive.attributes; + for ( var attributeId in attributes ) { + var attributeEntry = attributes[ attributeId ]; + if ( ! attributeEntry ) return; + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + switch ( attributeId ) { + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + } + } + var material = dependencies.materials[ primitive.material ]; + var meshNode; + if ( primitive.indices ) { + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + meshNode = new THREE.LineSegments( geometry, material ); + } else { + meshNode = new THREE.Line( geometry, material ); + } + meshNode.name = ( name === "0" ? group.name : group.name + name ); + if ( primitive.extras ) meshNode.userData = primitive.extras; + group.add( meshNode ); + } else { + console.warn( "Only triangular and line primitives are supported" ); + } + } + return group; + } ); + } ); + }; + GLTFParser.prototype.loadCameras = function () { + var json = this.json; + return _each( json.cameras, function ( camera ) { + if ( camera.type == "perspective" && camera.perspective ) { + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + if ( camera.extras ) _camera.userData = camera.extras; + return _camera; + } else if ( camera.type == "orthographic" && camera.orthographic ) { + var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + if ( camera.extras ) _camera.userData = camera.extras; + return _camera; + } + } ); + }; + GLTFParser.prototype.loadSkins = function () { + var json = this.json; + return this._withDependencies( [ + "accessors" + ] ).then( function ( dependencies ) { + return _each( json.skins, function ( skin ) { + var bindShapeMatrix = new THREE.Matrix4(); + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + return _skin; + } ); + } ); + }; + GLTFParser.prototype.loadAnimations = function () { + var json = this.json; + return this._withDependencies( [ + "accessors", + "nodes" + ] ).then( function ( dependencies ) { + return _each( json.animations, function ( animation, animationId ) { + var tracks = []; + for ( var channelId in animation.channels ) { + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + if ( sampler ) { + var target = channel.target; + var name = target.id; + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + var node = dependencies.nodes[ name ]; + if ( node ) { + node.updateMatrix(); + node.matrixAutoUpdate = true; + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + } + } + } + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + return new THREE.AnimationClip( name, undefined, tracks ); + } ); + } ); + }; + GLTFParser.prototype.loadNodes = function () { + var json = this.json; + var extensions = this.extensions; + var scope = this; + return _each( json.nodes, function ( node ) { + var matrix = new THREE.Matrix4(); + var _node; + if ( node.jointName ) { + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + } else { + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + } + if ( node.extras ) _node.userData = node.extras; + if ( node.matrix !== undefined ) { + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + } else { + if ( node.translation !== undefined ) { + _node.position.fromArray( node.translation ); + } + if ( node.rotation !== undefined ) { + _node.quaternion.fromArray( node.rotation ); + } + if ( node.scale !== undefined ) { + _node.scale.fromArray( node.scale ); + } + } + return _node; + } ).then( function ( __nodes ) { + return scope._withDependencies( [ + "meshes", + "skins", + "cameras" + ] ).then( function ( dependencies ) { + return _each( __nodes, function ( _node, nodeId ) { + var node = json.nodes[ nodeId ]; + if ( node.meshes !== undefined ) { + for ( var meshId in node.meshes ) { + var mesh = node.meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + if ( group === undefined ) { + console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); + continue; + } + for ( var childrenId in group.children ) { + var child = group.children[ childrenId ]; + // clone Mesh to add to _node + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + var material; + if ( originalMaterial.isDeferredShaderMaterial ) { + originalMaterial = material = originalMaterial.create(); + } else { + material = originalMaterial; + } + switch ( child.type ) { + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + default: + child = new THREE.Mesh( originalGeometry, material ); + } + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + var skinEntry; + if ( node.skin ) { + skinEntry = dependencies.skins[ node.skin ]; + } + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + var getJointNode = function ( jointId ) { + var keys = Object.keys( __nodes ); + for ( var i = 0, il = keys.length; i < il; i ++ ) { + var n = __nodes[ keys[ i ] ]; + if ( n.jointName === jointId ) return n; + } + return null; + }; + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + var bones = []; + var boneInverses = []; + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + if ( jointNode ) { + bones.push( jointNode ); + var m = skinEntry.inverseBindMatrices.array; + var mat = new THREE.Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + } else { + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + } + } + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + var buildBoneGraph = function ( parentJson, parentObject, property ) { + var children = parentJson[ property ]; + if ( children === undefined ) return; + for ( var i = 0, il = children.length; i < il; i ++ ) { + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + } + } + }; + buildBoneGraph( node, child, 'skeletons' ); + } + _node.add( child ); + } + } + } + if ( node.camera !== undefined ) { + var camera = dependencies.cameras[ node.camera ]; + _node.add( camera ); + } + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + _node.add( light ); + } + return _node; + } ); + } ); + } ); + }; + GLTFParser.prototype.loadScenes = function () { + var json = this.json; + // scene node hierachy builder + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + var node = json.nodes[ nodeId ]; + if ( node.children ) { + var children = node.children; + for ( var i = 0, l = children.length; i < l; i ++ ) { + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + } + } + } + return this._withDependencies( [ + "nodes" + ] ).then( function ( dependencies ) { + return _each( json.scenes, function ( scene ) { + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + if ( scene.extras ) _scene.userData = scene.extras; + var nodes = scene.nodes || []; + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + } + _scene.traverse( function ( child ) { + // Register raw material meshes with GLTFLoader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + } + } ); + return _scene; + } ); + } ); + }; + return GLTFLoader; +} )(); +// File: utils/JSONParser.js +"use strict"; + +var JSONParser = function() +{ +} + +JSONParser.prototype.constructor = JSONParser; + + /** + * Load X3D JSON into an element. + * jsobj - the JavaScript object to convert to DOM. + */ +JSONParser.prototype.parseJavaScript = function(jsobj) { + var child = this.CreateElement('scene'); + this.ConvertToX3DOM(jsobj, "", child); + console.log(jsobj, child); + return child; + }; + + // 'http://www.web3d.org/specifications/x3d-namespace' + + // Load X3D JavaScript object into XML or DOM + + /** + * Yet another way to set an attribute on an element. does not allow you to + * set JSON schema or encoding. + */ +JSONParser.prototype.elementSetAttribute = function(element, key, value) { + if (key === 'SON schema') { + // JSON Schema + } else if (key === 'ncoding') { + // encoding, UTF-8, UTF-16 or UTF-32 + } else { + if (typeof element.setAttribute === 'function') { + element.setAttribute(key, value); + } + } + }; + + /** + * converts children of object to DOM. + */ +JSONParser.prototype.ConvertChildren = function(parentkey, object, element) { + var key; + + for (key in object) { + if (typeof object[key] === 'object') { + if (isNaN(parseInt(key))) { + this.ConvertObject(key, object, element, parentkey.substr(1)); + } else { + this.ConvertToX3DOM(object[key], key, element, parentkey.substr(1)); + } + } + } + }; + + /** + * a method to create and element with tagnam key to DOM in a namespace. If + * containerField is set, then the containerField is set in the elemetn. + */ +JSONParser.prototype.CreateElement = function(key, containerField) { + var child = document.createElement(key); + if (typeof containerField !== 'undefined') { + this.elementSetAttribute(child, 'containerField', containerField); + } + return child; + }; + + /** + * a way to create a CDATA function or script in HTML, by using a DOM parser. + */ +JSONParser.prototype.CDATACreateFunction = function(document, element, str) { + var y = str.replace(/\\"/g, "\\\"") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/&/g, "&"); + do { + str = y; + y = str.replace(/'([^'\r\n]*)\n([^']*)'/g, "'$1\\n$2'"); + if (str !== y) { + console.log("CDATA Replacing",str,"with",y); + } + } while (y != str); + var domParser = new DOMParser(); + var cdataStr = ''; // has to be wrapped into an element + var scriptDoc = domParser .parseFromString (cdataStr, 'application/xml'); + var cdata = scriptDoc .children[0] .childNodes[1]; // space after script is childNode[0] + element .appendChild(cdata); + }; + + /** + * convert the object at object[key] to DOM. + */ +JSONParser.prototype.ConvertObject = function(key, object, element, containerField) { + var child; + if (object !== null && typeof object[key] === 'object') { + if (key.substr(0,1) === '@') { + this.ConvertToX3DOM(object[key], key, element); + } else if (key.substr(0,1) === '-') { + this.ConvertChildren(key, object[key], element); + } else if (key === '#comment') { + for (var c in object[key]) { + child = document.createComment(this.CommentStringToXML(object[key][c])); + element.appendChild(child); + } + } else if (key === '#text') { + child = document.createTextNode(object[key].join("")); + element.appendChild(child); + } else if (key === '#sourceText') { + this.CDATACreateFunction(document, element, object[key].join("\r\n")+"\r\n"); + } else { + if (key === 'connect' || key === 'fieldValue' || key === 'field' || key === 'meta' || key === 'component') { + for (var childkey in object[key]) { // for each field + if (typeof object[key][childkey] === 'object') { + child = this.CreateElement(key, containerField); + this.ConvertToX3DOM(object[key][childkey], childkey, child); + element.appendChild(child); + element.appendChild(document.createTextNode("\n")); + } + } + } else { + child = this.CreateElement(key, containerField); + this.ConvertToX3DOM(object[key], key, child); + element.appendChild(child); + element.appendChild(document.createTextNode("\n")); + } + } + } + }; + + /** + * convert a comment string in JavaScript to XML. Pass the string + */ +JSONParser.prototype.CommentStringToXML = function(str) { + var y = str; + str = str.replace(/\\\\/g, '\\'); + if (y !== str) { + console.log("X3DJSONLD replacing", y, "with", str); + } + return str; + }; + + /** + * convert an SFString to XML. + */ +JSONParser.prototype.SFStringToXML = function(str) { + var y = str; + /* + str = (""+str).replace(/\\\\/g, '\\\\'); + str = str.replace(/\\\\\\\\/g, '\\\\'); + str = str.replace(/(\\+)"/g, '\\"'); + */ + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/"/g, '\\\"'); + if (y !== str) { + console.log("X3DJSONLD [] replacing", y, "with", str); + } + return str; + }; + + /** + * convert a JSON String to XML. + */ +JSONParser.prototype.JSONStringToXML = function(str) { + var y = str; + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/\n/g, '\\n'); + if (y !== str) { + console.log("X3DJSONLD replacing", y, "with", str); + } + return str; + }; + + /** + * main routine for converting a JavaScript object to DOM. + * object is the object to convert. + * parentkey is the key of the object in the parent. + * element is the parent element. + * containerField is a possible containerField. + */ +JSONParser.prototype.ConvertToX3DOM = function(object, parentkey, element, containerField) { + var key; + var localArray = []; + var isArray = false; + var arrayOfStrings = false; + for (key in object) { + if (isNaN(parseInt(key))) { + isArray = false; + } else { + isArray = true; + } + if (isArray) { + if (typeof object[key] === 'number') { + localArray.push(object[key]); + } else if (typeof object[key] === 'string') { + localArray.push(object[key]); + arrayOfStrings = true; + } else if (typeof object[key] === 'boolean') { + localArray.push(object[key]); + } else if (typeof object[key] === 'object') { + /* + if (object[key] != null && typeof object[key].join === 'function') { + localArray.push(object[key].join(" ")); + } + */ + this.ConvertToX3DOM(object[key], key, element); + } else if (typeof object[key] === 'undefined') { + } else { + console.error("Unknown type found in array "+typeof object[key]); + } + } else if (typeof object[key] === 'object') { + // This is where the whole thing starts + if (key === 'scene') { + this.ConvertToX3DOM(object[key], key, element); + } else { + this.ConvertObject(key, object, element); + } + } else if (typeof object[key] === 'number') { + this.elementSetAttribute(element, key.substr(1),object[key]); + } else if (typeof object[key] === 'string') { + if (key === '#comment') { + var child = document.createComment(this.CommentStringToXML(object[key])); + element.appendChild(child); + } else if (key === '#text') { + var child = document.createTextNode(object[key]); + element.appendChild(child); + } else { + // ordinary string attributes + this.elementSetAttribute(element, key.substr(1), this.JSONStringToXML(object[key])); + } + } else if (typeof object[key] === 'boolean') { + this.elementSetAttribute(element, key.substr(1),object[key]); + } else if (typeof object[key] === 'undefined') { + } else { + console.error("Unknown type found in object "+typeof object[key]); + console.error(object); + } + } + if (isArray) { + if (parentkey.substr(0,1) === '@') { + if (arrayOfStrings) { + arrayOfStrings = false; + for (var str in localArray) { + localArray[str] = this.SFStringToXML(localArray[str]); + } + this.elementSetAttribute(element, parentkey.substr(1),'"'+localArray.join('" "')+'"'); + } else { + // if non string array + this.elementSetAttribute(element, parentkey.substr(1),localArray.join(" ")); + } + } + isArray = false; + } + return element; + }; +// File: utils/LoadManager.js +/* + * For use with XSeen JavaScript Library + * http://tools.realism.com/... + * + * Licensed under MIT or GNU in the same manner as XSeen + * + * (C)2017 Daly Realiusm, Los Angeles + * + */ + +/* + * Manages all download requests. + * Requests are queued up and processed to the maximum limit (.MaxRequests) + * Use this for processing text (X3D, XML, JSON, HTML) files. + * Not really setup for binary files (.jpg, png, etc.) + * + * Requires jQuery -- should work on removing that... + * + */ + +function LoadManager () { + this.urlQueue = []; + this.urlNext = -1; + this.MaxRequests = 3; + this.totalRequests = 0; + this.totalResponses = 0; + this.requestCount = 0; + var lmThat = this; + + this.load = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadText = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'text', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadHtml = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'html', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadXml = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'xml', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadJson = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'json', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadImage = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'image', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.success = function (response, string, xhr) { + if (typeof(xhr._loadManager.success) !== undefined) { + xhr._loadManager.success (response, xhr._loadManager.userdata, xhr); + } + } + + this.failure = function (xhr, errorCode, errorText) { + if (typeof(xhr._loadManager.failure) !== undefined) { + xhr._loadManager.failure (xhr, xhr._loadManager.userdata, errorCode, errorText); + } + } + + this.requestComplete = function (event, xhr, settings) { + lmThat.requestCount --; + lmThat.totalResponses++; + lmThat.loadNextUrl(); + } + + this.loadNextUrl = function () { + if (this.requestCount >= this.MaxRequests) {return; } + if (this.urlNext >= this.urlQueue.length || this.urlNext < 0) { + this.urlNext = -1; + for (var i=0; i 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.CODE_SLASH: + slashes[ slashesPointer++ ] = i; + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.CODE_LF: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); + slashesPointer = 0; + bufferPointer = 0; + break; + + case Consts.CODE_CR: + break; + + default: + word += String.fromCharCode( code ); + break; + } + } + }; + + /** + * Parse the provided text + * @memberOf Parser + * + * @param {string} text OBJ data as string + */ + Parser.prototype.parseText = function ( text ) { + var length = text.length; + var buffer = new Array( 32 ); + var bufferPointer = 0; + var slashes = new Array( 32 ); + var slashesPointer = 0; + var reachedFaces = false; + var char; + var word = ''; + for ( var i = 0; i < length; i++ ) { + + char = text[ i ]; + switch ( char ) { + case Consts.STRING_SPACE: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.STRING_SLASH: + slashes[ slashesPointer++ ] = i; + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.STRING_LF: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); + slashesPointer = 0; + bufferPointer = 0; + break; + + case Consts.STRING_CR: + break; + + default: + word += char; + } + } + }; + + Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) { + if ( bufferPointer < 1 ) return reachedFaces; + + var bufferLength = bufferPointer - 1; + switch ( buffer[ 0 ] ) { + case Consts.LINE_V: + + // object complete instance required if reached faces already (= reached next block of v) + if ( reachedFaces ) { + + this.processCompletedObject( null, this.rawObject.groupName ); + reachedFaces = false; + + } + this.rawObject.pushVertex( buffer ); + break; + + case Consts.LINE_VT: + this.rawObject.pushUv( buffer ); + break; + + case Consts.LINE_VN: + this.rawObject.pushNormal( buffer ); + break; + + case Consts.LINE_F: + reachedFaces = true; + /* + * 0: "f vertex/uv/normal ..." + * 1: "f vertex/uv ..." + * 2: "f vertex//normal ..." + * 3: "f vertex ..." + */ + var haveQuad = bufferLength % 4 === 0; + if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVn( buffer ); + } else { + this.rawObject.buildFaceVVn( buffer ); + } + + } else if ( bufferLength === slashesPointer * 2 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVt( buffer ); + } else { + this.rawObject.buildFaceVVt( buffer ); + } + + } else if ( bufferLength * 2 === slashesPointer * 3 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVtVn( buffer ); + } else { + this.rawObject.buildFaceVVtVn( buffer ); + } + + } else { + + if ( haveQuad ) { + this.rawObject.buildQuadV( buffer ); + } else { + this.rawObject.buildFaceV( buffer ); + } + + } + break; + + case Consts.LINE_L: + if ( bufferLength === slashesPointer * 2 ) { + + this.rawObject.buildLineVvt( buffer ); + + } else { + + this.rawObject.buildLineV( buffer ); + + } + break; + + case Consts.LINE_S: + this.rawObject.pushSmoothingGroup( buffer[ 1 ] ); + break; + + case Consts.LINE_G: + this.processCompletedGroup( buffer[ 1 ] ); + break; + + case Consts.LINE_O: + if ( this.rawObject.vertices.length > 0 ) { + + this.processCompletedObject( buffer[ 1 ], null ); + reachedFaces = false; + + } else { + + this.rawObject.pushObject( buffer[ 1 ] ); + + } + break; + + case Consts.LINE_MTLLIB: + this.rawObject.pushMtllib( buffer[ 1 ] ); + break; + + case Consts.LINE_USEMTL: + this.rawObject.pushUsemtl( buffer[ 1 ] ); + break; + + default: + break; + } + return reachedFaces; + }; + + Parser.prototype.processCompletedObject = function ( objectName, groupName ) { + this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + this.inputObjectCount++; + this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName ); + }; + + Parser.prototype.processCompletedGroup = function ( groupName ) { + var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + if ( notEmpty ) { + + this.inputObjectCount ++; + this.rawObject = this.rawObject.newInstanceFromGroup( groupName ); + + } else { + + // if a group was set that did not lead to object creation in finalize, then the group name has to be updated + this.rawObject.pushGroup( groupName ); + + } + }; + + Parser.prototype.finalize = function () { + this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + this.inputObjectCount++; + }; + + return Parser; + })(); + + /** + * {@link RawObject} is only used by {@link Parser}. + * The user of OBJLoader2 does not need to care about this class. + * It is defined publicly for inclusion in web worker based OBJ loader ({@link THREE.OBJLoader2.WWOBJLoader2}) + */ + var RawObject = (function () { + + function RawObject( objectName, groupName, mtllibName ) { + this.globalVertexOffset = 1; + this.globalUvOffset = 1; + this.globalNormalOffset = 1; + + this.vertices = []; + this.normals = []; + this.uvs = []; + + // faces are stored according combined index of group, material and smoothingGroup (0 or not) + this.mtllibName = Validator.verifyInput( mtllibName, 'none' ); + this.objectName = Validator.verifyInput( objectName, 'none' ); + this.groupName = Validator.verifyInput( groupName, 'none' ); + this.activeMtlName = 'none'; + this.activeSmoothingGroup = 1; + + this.mtlCount = 0; + this.smoothingGroupCount = 0; + + this.rawObjectDescriptions = []; + // this default index is required as it is possible to define faces without 'g' or 'usemtl' + var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; + } + + RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) { + return materialName + '|' + smoothingGroup; + }; + + RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) { + var newRawObject = new RawObject( objectName, groupName, this.mtllibName ); + + // move indices forward + newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; + newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; + newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; + + return newRawObject; + }; + + RawObject.prototype.newInstanceFromGroup = function ( groupName ) { + var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName ); + + // keep current buffers and indices forward + newRawObject.vertices = this.vertices; + newRawObject.uvs = this.uvs; + newRawObject.normals = this.normals; + newRawObject.globalVertexOffset = this.globalVertexOffset; + newRawObject.globalUvOffset = this.globalUvOffset; + newRawObject.globalNormalOffset = this.globalNormalOffset; + + return newRawObject; + }; + + RawObject.prototype.pushVertex = function ( buffer ) { + this.vertices.push( parseFloat( buffer[ 1 ] ) ); + this.vertices.push( parseFloat( buffer[ 2 ] ) ); + this.vertices.push( parseFloat( buffer[ 3 ] ) ); + }; + + RawObject.prototype.pushUv = function ( buffer ) { + this.uvs.push( parseFloat( buffer[ 1 ] ) ); + this.uvs.push( parseFloat( buffer[ 2 ] ) ); + }; + + RawObject.prototype.pushNormal = function ( buffer ) { + this.normals.push( parseFloat( buffer[ 1 ] ) ); + this.normals.push( parseFloat( buffer[ 2 ] ) ); + this.normals.push( parseFloat( buffer[ 3 ] ) ); + }; + + RawObject.prototype.pushObject = function ( objectName ) { + this.objectName = objectName; + }; + + RawObject.prototype.pushMtllib = function ( mtllibName ) { + this.mtllibName = mtllibName; + }; + + RawObject.prototype.pushGroup = function ( groupName ) { + this.groupName = groupName; + this.verifyIndex(); + }; + + RawObject.prototype.pushUsemtl = function ( mtlName ) { + if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; + this.activeMtlName = mtlName; + this.mtlCount++; + + this.verifyIndex(); + }; + + RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) { + var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup; + if ( this.activeSmoothingGroup === normalized ) return; + this.activeSmoothingGroup = normalized; + this.smoothingGroupCount++; + + this.verifyIndex(); + }; + + RawObject.prototype.verifyIndex = function () { + var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 ); + this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ]; + if ( ! Validator.isValid( this.rawObjectDescriptionInUse ) ) { + + this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; + + } + }; + + RawObject.prototype.buildQuadVVtVn = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] ); + this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] ); + this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] ); + } + }; + + RawObject.prototype.buildQuadVVt = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); + this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); + } + }; + + RawObject.prototype.buildQuadVVn = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); + this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); + } + }; + + RawObject.prototype.buildQuadV = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] ); + } + }; + + RawObject.prototype.buildFaceVVtVn = function ( indexArray ) { + for ( var i = 1; i < 10; i += 3 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVt( indexArray[ i + 1 ] ); + this.attachFaceVn( indexArray[ i + 2 ] ); + } + }; + + RawObject.prototype.buildFaceVVt = function ( indexArray ) { + for ( var i = 1; i < 7; i += 2 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVt( indexArray[ i + 1 ] ); + } + }; + + RawObject.prototype.buildFaceVVn = function ( indexArray ) { + for ( var i = 1; i < 7; i += 2 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVn( indexArray[ i + 1 ] ); + } + }; + + RawObject.prototype.buildFaceV = function ( indexArray ) { + for ( var i = 1; i < 4; i ++ ) { + this.attachFaceV_( indexArray[ i ] ); + } + }; + + RawObject.prototype.attachFaceV_ = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalVertexOffset ) * 3; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.vertices.push( this.vertices[ index++ ] ); + rodiu.vertices.push( this.vertices[ index++ ] ); + rodiu.vertices.push( this.vertices[ index ] ); + }; + + RawObject.prototype.attachFaceVt = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalUvOffset ) * 2; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.uvs.push( this.uvs[ index++ ] ); + rodiu.uvs.push( this.uvs[ index ] ); + }; + + RawObject.prototype.attachFaceVn = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalNormalOffset ) * 3; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.normals.push( this.normals[ index++ ] ); + rodiu.normals.push( this.normals[ index++ ] ); + rodiu.normals.push( this.normals[ index ] ); + }; + + /* + * Support for lines with or without texture. irst element in indexArray is the line identification + * 0: "f vertex/uv vertex/uv ..." + * 1: "f vertex vertex ..." + */ + RawObject.prototype.buildLineVvt = function ( lineArray ) { + var length = lineArray.length; + for ( var i = 1; i < length; i ++ ) { + this.vertices.push( parseInt( lineArray[ i ] ) ); + this.uvs.push( parseInt( lineArray[ i ] ) ); + } + }; + + RawObject.prototype.buildLineV = function ( lineArray ) { + var length = lineArray.length; + for ( var i = 1; i < length; i++ ) { + this.vertices.push( parseInt( lineArray[ i ] ) ); + } + }; + + /** + * Clear any empty rawObjectDescription and calculate absolute vertex, normal and uv counts + */ + RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) { + var temp = this.rawObjectDescriptions; + this.rawObjectDescriptions = []; + var rawObjectDescription; + var index = 0; + var absoluteVertexCount = 0; + var absoluteNormalCount = 0; + var absoluteUvCount = 0; + + for ( var name in temp ) { + + rawObjectDescription = temp[ name ]; + if ( rawObjectDescription.vertices.length > 0 ) { + + if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName; + this.rawObjectDescriptions[ index++ ] = rawObjectDescription; + absoluteVertexCount += rawObjectDescription.vertices.length; + absoluteUvCount += rawObjectDescription.uvs.length; + absoluteNormalCount += rawObjectDescription.normals.length; + + } + } + + // don not continue if no result + var notEmpty = false; + if ( index > 0 ) { + + if ( debug ) this.createReport( inputObjectCount, true ); + meshCreator.buildMesh( + this.rawObjectDescriptions, + inputObjectCount, + absoluteVertexCount, + absoluteNormalCount, + absoluteUvCount + ); + notEmpty = true; + + } + return notEmpty; + }; + + RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) { + var report = { + name: this.objectName ? this.objectName : 'groups', + mtllibName: this.mtllibName, + vertexCount: this.vertices.length / 3, + normalCount: this.normals.length / 3, + uvCount: this.uvs.length / 2, + smoothingGroupCount: this.smoothingGroupCount, + mtlCount: this.mtlCount, + rawObjectDescriptions: this.rawObjectDescriptions.length + }; + + if ( printDirectly ) { + console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name ); + console.log( 'Mtllib name: ' + report.mtllibName ); + console.log( 'Vertex count: ' + report.vertexCount ); + console.log( 'Normal count: ' + report.normalCount ); + console.log( 'UV count: ' + report.uvCount ); + console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount ); + console.log( 'Material count: ' + report.mtlCount ); + console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions ); + console.log( '' ); + } + + return report; + }; + + return RawObject; + })(); + + /** + * Descriptive information and data (vertices, normals, uvs) to passed on to mesh building function. + * @class + * + * @param {string} objectName Name of the mesh + * @param {string} groupName Name of the group + * @param {string} materialName Name of the material + * @param {number} smoothingGroup Normalized smoothingGroup (0: THREE.FlatShading, 1: THREE.SmoothShading) + */ + var RawObjectDescription = (function () { + + function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) { + this.objectName = objectName; + this.groupName = groupName; + this.materialName = materialName; + this.smoothingGroup = smoothingGroup; + this.vertices = []; + this.uvs = []; + this.normals = []; + } + + return RawObjectDescription; + })(); + + /** + * MeshCreator is used to transform RawObjectDescriptions to THREE.Mesh + * + * @class + */ + var MeshCreator = (function () { + + function MeshCreator() { + this.sceneGraphBaseNode = null; + this.materials = null; + this.debug = false; + this.globalObjectCount = 1; + + this.validated = false; + } + + MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) { + this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, this.sceneGraphBaseNode ); + this.sceneGraphBaseNode = Validator.verifyInput( this.sceneGraphBaseNode, new THREE.Group() ); + }; + + MeshCreator.prototype.setMaterials = function ( materials ) { + this.materials = Validator.verifyInput( materials, this.materials ); + this.materials = Validator.verifyInput( this.materials, { materials: [] } ); + }; + + MeshCreator.prototype.setDebug = function ( debug ) { + if ( debug === true || debug === false ) this.debug = debug; + }; + + MeshCreator.prototype.validate = function () { + if ( this.validated ) return; + + this.setSceneGraphBaseNode( null ); + this.setMaterials( null ); + this.setDebug( null ); + this.globalObjectCount = 1; + }; + + MeshCreator.prototype.finalize = function () { + this.sceneGraphBaseNode = null; + this.materials = null; + this.validated = false; + }; + + /** + * This is an internal function, but due to its importance to Parser it is documented. + * RawObjectDescriptions are transformed to THREE.Mesh. + * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check). + * This method shall be overridden by the web worker implementation + * + * @param {RawObjectDescription[]} rawObjectDescriptions Array of descriptive information and data (vertices, normals, uvs) about the parsed object(s) + * @param {number} inputObjectCount Number of objects already retrieved from OBJ + * @param {number} absoluteVertexCount Sum of all vertices of all rawObjectDescriptions + * @param {number} absoluteNormalCount Sum of all normals of all rawObjectDescriptions + * @param {number} absoluteUvCount Sum of all uvs of all rawObjectDescriptions + */ + MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) { + + if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount ); + + var bufferGeometry = new THREE.BufferGeometry(); + var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 ); + bufferGeometry.addAttribute( 'position', vertexBA ); + + var normalBA; + if ( absoluteNormalCount > 0 ) { + + normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 ); + bufferGeometry.addAttribute( 'normal', normalBA ); + + } + var uvBA; + if ( absoluteUvCount > 0 ) { + + uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 ); + bufferGeometry.addAttribute( 'uv', uvBA ); + + } + + if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount ); + + var rawObjectDescription; + var material; + var materialName; + var createMultiMaterial = rawObjectDescriptions.length > 1; + var materials = []; + var materialIndex = 0; + var materialIndexMapping = []; + var selectedMaterialIndex; + + var vertexBAOffset = 0; + var vertexGroupOffset = 0; + var vertexLength; + var normalOffset = 0; + var uvOffset = 0; + + for ( var oodIndex in rawObjectDescriptions ) { + rawObjectDescription = rawObjectDescriptions[ oodIndex ]; + + materialName = rawObjectDescription.materialName; + material = this.materials[ materialName ]; + if ( ! material ) { + + material = this.materials[ 'defaultMaterial' ]; + if ( ! material ) { + + material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} ); + material.name = 'defaultMaterial'; + this.materials[ 'defaultMaterial' ] = material; + + } + console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' ); + + } + // clone material in case flat shading is needed due to smoothingGroup 0 + if ( rawObjectDescription.smoothingGroup === 0 ) { + + materialName = material.name + '_flat'; + var materialClone = this.materials[ materialName ]; + if ( ! materialClone ) { + + materialClone = material.clone(); + materialClone.name = materialName; + materialClone.shading = THREE.FlatShading; + this.materials[ materialName ] = name; + + } + + } + + vertexLength = rawObjectDescription.vertices.length; + if ( createMultiMaterial ) { + + // re-use material if already used before. Reduces materials array size and eliminates duplicates + selectedMaterialIndex = materialIndexMapping[ materialName ]; + if ( ! selectedMaterialIndex ) { + + selectedMaterialIndex = materialIndex; + materialIndexMapping[ materialName ] = materialIndex; + materials.push( material ); + materialIndex++; + + } + + bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex ); + vertexGroupOffset += vertexLength / 3; + } + + vertexBA.set( rawObjectDescription.vertices, vertexBAOffset ); + vertexBAOffset += vertexLength; + + if ( normalBA ) { + + normalBA.set( rawObjectDescription.normals, normalOffset ); + normalOffset += rawObjectDescription.normals.length; + + } + if ( uvBA ) { + + uvBA.set( rawObjectDescription.uvs, uvOffset ); + uvOffset += rawObjectDescription.uvs.length; + + } + if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex ); + + } + if ( ! normalBA ) bufferGeometry.computeVertexNormals(); + + if ( createMultiMaterial ) material = materials; + var mesh = new THREE.Mesh( bufferGeometry, material ); + this.sceneGraphBaseNode.add( mesh ); + + this.globalObjectCount++; + }; + + MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) { + console.log( + ' Output Object no.: ' + this.globalObjectCount + + '\n objectName: ' + rawObjectDescription.objectName + + '\n groupName: ' + rawObjectDescription.groupName + + '\n materialName: ' + rawObjectDescription.materialName + + '\n materialIndex: ' + selectedMaterialIndex + + '\n smoothingGroup: ' + rawObjectDescription.smoothingGroup + + '\n #vertices: ' + rawObjectDescription.vertices.length / 3 + + '\n #uvs: ' + rawObjectDescription.uvs.length / 2 + + '\n #normals: ' + rawObjectDescription.normals.length / 3 + ); + }; + + return MeshCreator; + })(); + + OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) { + var workerCode = ''; + workerCode += funcBuildObject( 'Consts', Consts ); + workerCode += funcBuildObject( 'Validator', Validator ); + workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); + workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject ); + workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription ); + return workerCode; + }; + + return OBJLoader2; +})(); +// File: init/Definitions.js +/* + * XSeen JavaScript Library + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + * + * Based on code originally provided by + * Philip Taylor: http://philip.html5.org + */ + +/** + * The Namespace container for x3dom objects. + * @namespace x3dom + * + * Removed THREE loaders + loaders: { + 'file' : new THREE.FileLoader(), + 'image' : 0, + }, + + * */ +var xseen = { + canvases : [], + sceneInfo : [], + nodeDefinitions : {}, + parseTable : {}, + node : {}, + utils : {}, + eventManager : {}, + Events : {}, + Navigation : {}, + + loadMgr : {}, + loader : { + 'Null' : '', + 'ColladaLoader' : '', + 'GltfLegacy' : '', + 'GltfLoader' : '', + 'ObjLoader' : '', + 'ImageLoader' : '', + 'X3dLoader' : '', + }, + loadProgress : function (xhr) { + if (xhr.total != 0) { + console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); + } + }, + loadError : function (xhr, userdata, code, message) { + console.error('An error happened on '+userdata.e.id+'\n'+code+'\n'+message); + }, + loadMime : { + '' : {name: 'Null', loader: 'Null'}, + 'dae' : {name: 'Collada', loader: 'ColladaLoader'}, + 'glb' : {name: 'glTF Binary', loader: 'GltfLoader'}, + 'glbl' : {name: 'glTF Binary', loader: 'GltfLegacy'}, + 'gltf' : {name: 'glTF JSON', loader: 'GltfLoader'}, + 'obj' : {name: 'OBJ', loader: 'ObjLoader'}, + 'png' : {name: 'PNG', loader: 'ImageLoader'}, + 'jpg' : {name: 'JPEG', loader: 'ImageLoader'}, + 'jpeg' : {name: 'JPEG', loader: 'ImageLoader'}, + 'gif' : {name: 'GIF', loader: 'ImageLoader'}, + 'x3d' : {name: 'X3D XML', loader: 'X3dLoader'}, + }, +// helper + array_to_object : function (a) { + var o = {}; + for(var i=0;i ' + obj.type + ' (' + obj.name + ')'); + for (var i=0; i + var validParams = xseen.array_to_object([ + 'showLog', + 'showStat', + 'showProgress', + 'PrimitiveQuality', + 'components', + 'loadpath', + 'disableDoubleClick', + 'backend', + 'altImg', + 'runtimeEnabled', + 'keysEnabled', + 'showTouchpoints', + 'disableTouch', + 'maxActiveDownloads' + ]); + var components, prefix; + var showLoggingConsole = false; + + // for each XSeens element + for (var i=0; i < xseens.length; i++) { + + // default parameters + settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'false'); + settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'true'); + settings.setProperty("showStat", xseens[i].getAttribute("showStat") || 'false'); + settings.setProperty("showProgress", xseens[i].getAttribute("showProgress") || 'true'); + settings.setProperty("PrimitiveQuality", xseens[i].getAttribute("PrimitiveQuality") || 'High'); + + // for each param element inside the X3D element + // add settings to properties object + params = xseens[i].getElementsByTagName('PARAM'); + for (var j=0; j < params.length; j++) { + if (params[j].getAttribute('name') in validParams) { + settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value')); + } else { + //xseen.debug.logError("Unknown parameter: " + params[j].getAttribute('name')); + } + } + + // enable log + if (settings.getProperty('showLog') === 'true') { + showLoggingConsole = true; + } + + if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) { + // load components from params or default to x3d attribute + components = settings.getProperty('components', xseens[i].getAttribute("components")); + if (components) { + prefix = settings.getProperty('loadpath', xseens[i].getAttribute("loadpath")); + components = components.trim().split(','); + for (j=0; j < components.length; j++) { + xseen.loadJS(components[j] + ".js", prefix); + } + } + + // src=foo.x3d adding inline node, not a good idea, but... + if (xseens[i].getAttribute("src")) { + var _scene = document.createElement("scene"); + var _inl = document.createElement("Inline"); + _inl.setAttribute("url", xseens[i].getAttribute("src")); + _scene.appendChild(_inl); + xseens[i].appendChild(_scene); + } + } + } + // }}} + + if (showLoggingConsole == true) { + xseen.debug.activate(true); + } else { + xseen.debug.activate(false); + } + + // Convert the collection into a simple array (is this necessary?) +/* Don't think so -- commented out + xseens = Array.map(xseens, function (n) { + n.hasRuntime = true; + return n; + }); + */ + + if (xseen.versionInfo !== undefined) { + xseen.debug.logInfo("XSeen version " + xseen.versionInfo.version + ", " + + "Date " + xseen.versionInfo.date); + xseen.debug.logInfo(xseen.versionInfo.splashText); + } + + //xseen.debug.logInfo("Found " + xseen.length + " XSeen nodes"); + + + // Create a HTML canvas for every XSeen scene and wrap it with + // an X3D canvas and load the content + var x_element; + var x_canvas; + var altDiv, altP, aLnk, altImg; + var t0, t1; + + for (var i=0; i < xseens.length; i++) + { + x_element = xseens[i]; // The XSeen DOM element + + x_canvas = new THREE.Scene(); // May need addtl info if multiple: xseen.X3DCanvas(x_element, xseen.canvases.length); + xseen.canvases.push(x_canvas); // TODO: Need to handle failure to initialize? + t0 = new Date().getTime(); + +/* + * Handle opening tag attributes + * divHeight + * divWidth + * turntable Indicates if view automatically rotates (independent of navigation) + */ + + var divWidth = x_element.getAttribute('width'); + var divHeight = x_element.getAttribute('height'); + if (divHeight + divWidth < 100) { + divHeight = 450; + divWidth = 800; + } else if (divHeight < 50) { + divHeight = Math.floor(divWidth/2) + 50; + } else if (divWidth < 50) { + divWidth = divHeight * 2 - 100; + } + var turntable = (x_element.getAttribute('turntable') || '').toLowerCase(); + if (turntable == 'on' || turntable == 'yes' || turntable == 'y' || turntable == '1') { + turntable = true; + } else { + turntable = false; + } + turntable = false; +/* + * Removed because camera is stored in the Scene node (x_element._xseen.renderer.camera + * Leave variable definition so other code works... + var x_camera = new THREE.PerspectiveCamera( 75, divWidth / divHeight, 0.1, 1000 ); + x_camera.position.x = 0; + x_camera.position.z = 10; + */ + var x_camera = {}; + var x_renderer = new THREE.WebGLRenderer(); + x_renderer.setSize (divWidth, divHeight); + //x_element.appendChild (x_renderer.domElement); + + // Stereo camera effect + // from http://charliegerard.github.io/blog/Virtual-Reality-ThreeJs/ + var x_effect = new THREE.StereoEffect(x_renderer); + x_renderer.controls = {'update' : function() {return;}}; + +/* + * Add event handler to XSeen tag (x_element) + * These handle all mouse/cursor/button controls when the cursor is + * in the XSeen region of the page + */ + + x_element.addEventListener ('dblclick', xseen.Events.canvasHandler, true); + x_element.addEventListener ('click', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mousedown', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mousemove', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mouseup', xseen.Events.canvasHandler, true); + x_element.addEventListener ('xseen', xseen.Events.XSeenHandler); // Last chance for XSeen handling of event + x_element.addEventListener ('change', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event +/* + x_element.addEventListener ('mousedown', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + x_element.addEventListener ('mouseup', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + x_element.addEventListener ('mousemove', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + */ + xseen.sceneInfo.push ({ + 'size' : {'width':divWidth, 'height':divHeight}, + 'scene' : x_canvas, + 'renderer' : x_renderer, + 'effect' : x_effect, + 'camera' : [x_camera], + 'turntable' : turntable, + 'mixers' : [], + 'clock' : new THREE.Clock(), + 'element' : x_element, + 'selectable': [], + 'stacks' : [], + 'tmp' : {activeViewpoint:false}, + 'xseen' : xseen, + }); + x_element._xseen = {}; + x_element._xseen.children = []; + x_element._xseen.sceneInfo = xseen.sceneInfo[xseen.sceneInfo.length-1]; + + t1 = new Date().getTime() - t0; + xseen.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms."); + } + + var ready = (function(eventType) { + var evt = null; + + if (document.createEvent) { + evt = document.createEvent("Events"); + evt.initEvent(eventType, true, true); + document.dispatchEvent(evt); + } else if (document.createEventObject) { + evt = document.createEventObject(); + // http://stackoverflow.com/questions/1874866/how-to-fire-onload-event-on-document-in-ie + document.body.fireEvent('on' + eventType, evt); + } + })('load'); + + // for each X-Scene tag, parse and load the contents + var t=[]; + for (var i=0; i root element that was added after document load. */ + xseen.reload = function() { + onload(); + }; + + /* FIX PROBLEM IN CHROME - HACK - searching for better solution !!! */ + if (navigator.userAgent.indexOf("Chrome") != -1) { + document.__getElementsByTagName = document.getElementsByTagName; + + document.getElementsByTagName = function(tag) { + var obj = []; + var elems = this.__getElementsByTagName("*"); + + if(tag =="*"){ + obj = elems; + } else { + tag = tag.toUpperCase(); + for (var i = 0; i < elems.length; i++) { + var tagName = elems[i].tagName.toUpperCase(); + if (tagName === tag) { + obj.push(elems[i]); + } + } + } + + return obj; + }; + + document.__getElementById = document.getElementById; + document.getElementById = function(id) { + var obj = this.__getElementById(id); + + if (!obj) { + var elems = this.__getElementsByTagName("*"); + for (var i=0; i + * MODE_NAVIGATION events are 'captured' (not bubbled) and drive the camera position + * + * In MODE_SELECT + * mousedown sets redispatch to TRUE + * click Activates + * dblclick ?? + * mouseup terminates select + * mousemove sets redispatch to FALSE + * In all cases, recreate event as type='xseen' and dispatch from geometry when + * redispatch is TRUE. + */ + canvasHandler: function (ev) + { + //console.log ('Primary canvas event handler for event type: ' + ev.type); + var sceneInfo = ev.currentTarget._xseen.sceneInfo; + var localXseen = sceneInfo.xseen; + var lEvents = localXseen.Events; + var type = ev.type; + if (type == 'mousedown') { + lEvents.redispatch = true; + lEvents.mode = lEvents.MODE_SELECT; + lEvents.mouse.x = (ev.clientX / 800) * 2 -1; // TODO: Use real XSeen display sizes + lEvents.mouse.y = (ev.clientY / 450) * 2 -1; + // + lEvents.raycaster.setFromCamera(lEvents.mouse, sceneInfo.element._xseen.renderer.activeCamera); + var hitGeometryList = lEvents.raycaster.intersectObjects (sceneInfo.selectable, true); + if (hitGeometryList.length != 0) { + lEvents.object = hitGeometryList[0]; + } else { + lEvents.object = {}; + lEvents.redispatch = false; + lEvents.mode = lEvents.MODE_NAVIGATION; + } + } + if ((lEvents.redispatch || type == 'click' || type == 'dblclick') && typeof(lEvents.object.object) !== 'undefined') { + // Generate an XSeen (Custom)Event of the same type and dispatch it + var newEv = lEvents.createEvent (ev, lEvents.object); + lEvents.object.object.userData.dispatchEvent(newEv); + ev.stopPropagation(); // No propagation beyond this tag + } else { + //console.log ('Navigation mode...'); + } + if (type == 'mouseup') { + lEvents.redispatch = false; + lEvents.mode = lEvents.MODE_NAVIGATION; + } + }, + + createEvent: function (ev, selectedObject) + { + var properties = { + 'detail': { // This object contains all of the XSeen data + 'type': ev.type, + 'originalType': ev.type, + 'originator': selectedObject.object.userData, + 'position': { + 'x': selectedObject.point.x, + 'y': selectedObject.point.y, + 'z': selectedObject.point.z, + }, + 'normal': { + 'x': 0, + 'y': 0, + 'z': 0, + }, + 'uv': { + 'x': selectedObject.uv.x, + 'y': selectedObject.uv.y, + }, + 'screenX': ev.screenX, + 'screenY': ev.screenY, + 'clientX': ev.clientX, + 'clientY': ev.clientY, + 'ctrlKey': ev.ctrlKey, + 'shiftKey': ev.shiftKey, + 'altKey': ev.altKey, + 'metaKey': ev.metaKey, + 'button': ev.button, + 'buttons': ev.buttons, + }, + 'bubbles': ev.bubbles, + 'cancelable': ev.cancelable, + 'composed': ev.composed, + }; + + var newEvent = new CustomEvent('xseen', properties); + return newEvent; + }, + // Uses method described in https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + // to change 'this' for the handler method. Want 'this' to refer to the target node. + addHandler: function (route, source, eventName, destination, field) + { + var handler = {}; + handler.route = route; // Route element + handler.source = source; // Source element + handler.type = eventName; // Event type + handler.destination = destination; // Destination element + handler.field = field; // Destination field structure + handler.handler = destination._xseen.handlers[field.handlerName]; + this.routes.push (handler); + if (typeof(source._xseen) === 'undefined') { // DOM event + source.addEventListener (eventName, function(ev) { + handler.handler(ev) + }); + } else { // XSeen event + source.addEventListener ('xseen', function(ev) { + //console.log ('New event of original type: |'+ev.detail.originalType+'|; Desired type: |'+handler.type+'|'); + if (ev.detail.originalType == handler.type) { + handler.handler(ev) + } + }); + } + }, + + // Generic notification handler for XSeen's canvas + XSeenHandler: function (ev) + { + //console.log ('XSeen DEBUG Event Bubble handler ('+ev.type+'/'+ev.eventPhase+').'); + }, + XSeenDebugHandler : function (ev) + { + console.log ('XSeen DEBUG Event Capture handler ('+ev.type+'/'+ev.eventPhase+').'); + }, + }; + + this.debug = { + INFO: "INFO", + WARNING: "WARNING", + ERROR: "ERROR", + EXCEPTION: "EXCEPTION", + + // determines whether debugging/logging is active. If set to "false" + // no debugging messages will be logged. + isActive: false, + + // stores if firebug is available + isFirebugAvailable: false, + + // stores if the xseen.debug object is initialized already + isSetup: false, + + // stores if xseen.debug object is append already (Need for IE integration) + isAppend: false, + + // stores the number of lines logged + numLinesLogged: 0, + + // the maximum number of lines to log in order to prevent + // the browser to slow down + maxLinesToLog: 10000, + + // the container div for the logging messages + logContainer: null, + + /** @brief Setup the xseen.debug object. + + Checks for firebug and creates the container div for the logging + messages. + */ + setup: function() { + // If debugging is already setup simply return + if (xseen.debug.isSetup) { return; } + + // Check for firebug console + try { + if (window.console.firebug !== undefined) { + xseen.debug.isFirebugAvailable = true; + } + } + catch (err) { + xseen.debug.isFirebugAvailable = false; + } + + xseen.debug.setupLogContainer(); + + // setup should be setup only once, thus store if we done that already + xseen.debug.isSetup = true; + }, + + /** @brief Activates the log + */ + activate: function(visible) { + xseen.debug.isActive = true; + + //var aDiv = document.createElement("div"); + //aDiv.style.clear = "both"; + //aDiv.appendChild(document.createTextNode("\r\n")); + //aDiv.style.display = (visible) ? "block" : "none"; + xseen.debug.logContainer.style.display = (visible) ? "block" : "none"; + + //Need this HACK for IE/Flash integration. IE don't have a document.body at this time when starting Flash-Backend + if(!xseen.debug.isAppend) { + if(navigator.appName == "Microsoft Internet Explorer") { + //document.documentElement.appendChild(aDiv); + xseen.debug.logContainer.style.marginLeft = "8px"; + document.documentElement.appendChild(xseen.debug.logContainer); + }else{ + //document.body.appendChild(aDiv); + document.body.appendChild(xseen.debug.logContainer); + } + xseen.debug.isAppend = true; + } + }, + + /** @brief Inserts a container div for the logging messages into the HTML page + */ + setupLogContainer: function() { + xseen.debug.logContainer = document.createElement("div"); + xseen.debug.logContainer.id = "xseen_logdiv"; + xseen.debug.logContainer.setAttribute("class", "xseen-logContainer"); + xseen.debug.logContainer.style.clear = "both"; + //document.body.appendChild(xseen.debug.logContainer); + }, + + /** @brief Generic logging function which does all the work. + + @param msg the log message + @param logType the type of the log message. One of INFO, WARNING, ERROR + or EXCEPTION. + */ + doLog: function(msg, logType) { + + // If logging is deactivated do nothing and simply return + if (!xseen.debug.isActive) { return; } + + // If we have reached the maximum number of logged lines output + // a warning message + if (xseen.debug.numLinesLogged === xseen.debug.maxLinesToLog) { + msg = "Maximum number of log lines (=" + xseen.debug.maxLinesToLog + ") reached. Deactivating logging..."; + } + + // If the maximum number of log lines is exceeded do not log anything + // but simply return + if (xseen.debug.numLinesLogged > xseen.debug.maxLinesToLog) { return; } + + // Output a log line to the HTML page + var node = document.createElement("p"); + node.style.margin = 0; + switch (logType) { + case xseen.debug.INFO: + node.style.color = "#009900"; + break; + case xseen.debug.WARNING: + node.style.color = "#cd853f"; + break; + case xseen.debug.ERROR: + node.style.color = "#ff4500"; + break; + case xseen.debug.EXCEPTION: + node.style.color = "#ffff00"; + break; + default: + node.style.color = "#009900"; + break; + } + + // not sure if try/catch solves problem http://sourceforge.net/apps/trac/x3dom/ticket/52 + // but due to no avail of ATI gfxcard can't test + try { + node.innerHTML = logType + ": " + msg; + xseen.debug.logContainer.insertBefore(node, xseen.debug.logContainer.firstChild); + } catch (err) { + if (window.console.firebug !== undefined) { + window.console.warn(msg); + } + } + + // Use firebug's console if available + if (xseen.debug.isFirebugAvailable) { + switch (logType) { + case xseen.debug.INFO: + window.console.info(msg); + break; + case xseen.debug.WARNING: + window.console.warn(msg); + break; + case xseen.debug.ERROR: + window.console.error(msg); + break; + case xseen.debug.EXCEPTION: + window.console.debug(msg); + break; + default: + break; + } + } + + xseen.debug.numLinesLogged++; + }, + + /** Log an info message. */ + logInfo: function(msg) { + xseen.debug.doLog(msg, xseen.debug.INFO); + }, + + /** Log a warning message. */ + logWarning: function(msg) { + xseen.debug.doLog(msg, xseen.debug.WARNING); + }, + + /** Log an error message. */ + logError: function(msg) { + xseen.debug.doLog(msg, xseen.debug.ERROR); + }, + + /** Log an exception message. */ + logException: function(msg) { + xseen.debug.doLog(msg, xseen.debug.EXCEPTION); + }, + + /** Log an assertion. */ + assert: function(c, msg) { + if (!c) { + xseen.debug.doLog("Assertion failed in " + xseen.debug.assert.caller.name + ': ' + msg, xseen.debug.ERROR); + } + }, + + /** + Checks the type of a given object. + + @param obj the object to check. + @returns one of; "boolean", "number", "string", "object", + "function", or "null". + */ + typeOf: function (obj) { + var type = typeof obj; + return type === "object" && !obj ? "null" : type; + }, + + /** + Checks if a property of a specified object has the given type. + + @param obj the object to check. + @param name the property name. + @param type the property type (optional, default is "function"). + @returns true if the property exists and has the specified type, + otherwise false. + */ + exists: function (obj, name, type) { + type = type || "function"; + return (obj ? this.typeOf(obj[name]) : "null") === type; + }, + + /** + Dumps all members of the given object. + */ + dumpFields: function (node) { + var str = ""; + for (var fName in node) { + str += (fName + ", "); + } + str += '\n'; + xseen.debug.logInfo(str); + return str; + } + }; +// Call the setup function to... umm, well, setup xseen.debug + this.debug.setup(); + + }; +// File: ./Nav-Viewpoint.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.Navigation.(label); + * Computes the new viewing location for the specific mode. + * + * Each Navigation method takes the following parameters: + * speed Floating point value indicating motion speed. + * Units are distance per milli-second for linear motion or + * revolutions (2*pi) per milli-second for angular motion + * deltaT Time since last update in milli-seconds + * TODO: This is not true for the Turntable class of camera motion -- which isn't really Navigation anyway + * scene The 'sceneInfo' object for this HTML instance + * camera The current (active) camera (aka scene.element._xseen.renderer.activeCamera) + * + * Navigation is the user-controlled process of moving in the 3D world. + * + */ + +xseen.Navigation = { + 'TwoPi' : 2 * Math.PI, + 'none' : function () {}, // Does not allow user-controlled navigation + + 'turntable' : function (speed, deltaT, scene, camera) + { + var T, radians, radius, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + radius = vp.fields._radius0; + camera.position.x = radius * Math.sin(radians) + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.position.z = radius * Math.cos(radians); + camera.lookAt(scene.ORIGIN); + }, + + 'tilt' : function (speed, deltaT, scene, camera) + { + var T, radians, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.lookAt(scene.ORIGIN); + }, + + 'setup' : { + 'none' : function () {return null;}, + + 'orbit' : function (camera, renderer) + { + var controls; + controls = new THREE.OrbitControls( camera, renderer.domElement ); + //controls.addEventListener( 'change', render ); // remove when using animation loop + // enable animation loop when using damping or autorotation + //controls.enableDamping = true; + //controls.dampingFactor = 0.25; + controls.enableZoom = false; + controls.enableZoom = true; + return controls; + }, + + 'trackball' : function (camera, renderer) + { + var controls; + controls = new THREE.TrackballControls(camera, renderer.domElement); + + // These are from the example code at https://github.com/mrdoob/three.js/blob/master/examples/misc_controls_trackball.html + controls.rotateSpeed = 1.0; + controls.zoomSpeed = 1.2; + controls.panSpeed = 0.8; + controls.noZoom = false; + controls.noPan = false; + controls.staticMoving = true; + controls.dynamicDampingFactor = 0.3; + controls.keys = [ 65, 83, 68 ]; + + // Render function is 'xseen.renderFrame' + // remove when using animation loop + //controls.addEventListener( 'change', xseen.renderFrame ); + return controls; + }, + }, +}; +// File: ./Navigation.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.Navigation.(label); + * Computes the new viewing location for the specific mode. + * + * Each Navigation method takes the following parameters: + * speed Floating point value indicating motion speed. + * Units are distance per milli-second for linear motion or + * revolutions (2*pi) per milli-second for angular motion + * deltaT Time since last update in milli-seconds + * TODO: This is not true for the Turntable class of camera motion -- which isn't really Navigation anyway + * scene The 'sceneInfo' object for this HTML instance + * camera The current (active) camera (aka scene.element._xseen.renderer.activeCamera) + * + * Navigation is the user-controlled process of moving in the 3D world. + * + */ + +xseen.Navigation = { + 'TwoPi' : 2 * Math.PI, + 'none' : function () {}, // Does not allow user-controlled navigation + + 'turntable' : function (speed, deltaT, scene, camera) + { + var T, radians, radius, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + radius = vp.fields._radius0; + camera.position.x = radius * Math.sin(radians) + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.position.z = radius * Math.cos(radians); + camera.lookAt(scene.ORIGIN); + }, + + 'tilt' : function (speed, deltaT, scene, camera) + { + var T, radians, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.lookAt(scene.ORIGIN); + }, + + 'setup' : { + 'none' : function () {return null;}, + + 'orbit' : function (camera, renderer) + { + var controls; + controls = new THREE.OrbitControls( camera, renderer.domElement ); + //controls.addEventListener( 'change', render ); // remove when using animation loop + // enable animation loop when using damping or autorotation + //controls.enableDamping = true; + //controls.dampingFactor = 0.25; + controls.enableZoom = false; + controls.enableZoom = true; + return controls; + }, + }, +}; +// File: ./NodeDefinitions.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.nodes. is the definition of + * All internal variables are stored in ._internal. All functions start with '_' + * + * This is a bare-bones setup. There is no error checking - missing arguments or + * methods that do not exist (e.g., .init) + * + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump (_dumpTable) would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + * Fields are added with the .addField method. It takes its values from the argument list + * or an object passed as the first argument. The properties of the argument are: + * name - the name of the field. This is converted to lowercase before use + * datatype - the datatype of the field. There must be a method in xseen.types by this name + * defaultValue - the default value of the field to be used if the field is not present or incorrectly defined. + * If this argument is an array, then it is the set of allowed values. The first element is the default. + * enumerated - the list of allowed values when the datatype only allows specific values for this field (optional) + * animatable - Flag (T/F) indicating if the field is animatable. Generally speaking, enumerated fieles are not animatable + */ + +xseen.nodes = { + '_defineNode' : function(nodeName, nodeComponent, nodeMethod) { + //methodBase = 'xseen.node.'; + var methodBase = ''; + node = { + 'tag' : nodeName, + 'taglc' : nodeName.toLowerCase(), + 'component' : nodeComponent, + 'method' : methodBase + nodeMethod, + 'fields' : [], + 'fieldIndex': [], + 'addField' : function (fieldObj, datatype, defaultValue) { + var fieldName, namelc, enumerated, animatable; + if (typeof(fieldObj) === 'object') { + fieldName = fieldObj.name; + datatype = fieldObj.datatype; + defaultValue = fieldObj.defaultValue; + enumerated = (typeof(fieldObj.enumerated) === 'undefined') ? [] : fieldObj.enumerated; + animatable = (typeof(fieldObj.animatable) === 'undefined') ? false : fieldObj.animatable; + } else { + fieldName = fieldObj; + animatable = false; + if (typeof(defaultValue) == 'array') { + enumerated = defaultValue; + defaultValue = enumerated[0]; + } else { + enumerated = []; + } + } + namelc = fieldName.toLowerCase(); + this.fields.push ({ + 'field' : fieldName, + 'fieldlc' : namelc, + 'type' : datatype, + 'default' : defaultValue, + 'enumeration' : enumerated, + 'animatable' : animatable, + 'clone' : this.cloneField, + 'setFieldName' : this.setFieldName, + }); + this.fieldIndex[namelc] = this.fields.length-1; + return this; + }, + 'addNode' : function () { + xseen.parseTable[this.taglc] = this; + }, + 'cloneField' : function () { + var newFieldObject = { + 'field' : this.field, + 'fieldlc' : this.fieldlc, + 'type' : this.type, + 'default' : 0, + 'enumeration' : [], + 'animatable' : this.animatable, + 'clone' : this.clone, + 'setFieldName' : this.setFieldName, + }; + for (var i=0; i xseen.node[xseen.nodeDefinitions[nodeName].method].endParse (element, parent); + } + //xseen.debug.logInfo(" reached bottom, heading back up from |" + nodeName + "|"); +} +// File: ./Properties.js +/* + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + * + * Based on code originally provided by + * Philip Taylor: http://philip.html5.org + */ + + +xseen.Properties = function() { + this.properties = {}; +}; + +xseen.Properties.prototype.setProperty = function(name, value) { + xseen.debug.logInfo("Properties: Setting property '"+ name + "' to value '" + value + "'"); + this.properties[name] = value; +}; + +xseen.Properties.prototype.getProperty = function(name, def) { + if (this.properties[name]) { + return this.properties[name] + } else { + return def; + } +}; + +xseen.Properties.prototype.merge = function(other) { + for (var attrname in other.properties) { + this.properties[attrname] = other.properties[attrname]; + } +}; + +xseen.Properties.prototype.toString = function() { + var str = ""; + for (var name in this.properties) { + str += "Name: " + name + " Value: " + this.properties[name] + "\n"; + } + return str; +}; +// File: ./StackHandler.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.utlis.StackHandler(label); + * Creates a new stack that is managed by this class + * + * Note that the Push-Down stack (aka FILO) is implemented as a reverse list + * so that the Array methods .push and .pop can be used. The end of the array + * is the "top-most" element in the stack. + */ + +xseen.utils.StackHandler = function (label) { + this._internals = {}; // Internal class storage + this._internals.label = label; // Unique user-supplied name for this stack + this._internals.stack = []; // Maintains the stack. Last entry on stack is active + this._internals.active = -1; // Index of currently active list element + this._internals.activeNode = {}; // Entry of currently active list element + this._internals.defaultNode = {}; // The default entry to be active if nothing else is + + this._setActiveNode = function() { // Sets the entry specified by nodeId as active + this._internals.active = this._internals.stack.length-1; + if (this._internals.active >= 0) { + this._internals.activeNode = this._internals.stack[this._internals.active]; + } else { + this._internals.activeNode = this._internals.defaultNode; + } + } + + this.init = function() { // Clears existing stack + this._internals.stack = []; + } + + this.pushDown = function(node) { // Push new node onto stack and make active + this._internals.stack.push (node); + this._setActiveNode(); + } + + this.popOff = function() { // Pop node off stack and make next one active + this._internals.stack.pop(); + this._setActiveNode(); + } + + this.getActive = function() { + return this._internals.activeNode; + } + + this.setDefault = function(node) { + this._internals.defaultNode = node; + if (Object.keys(this._internals.activeNode).length === 0) { + this._internals.activeNode = this._internals.defaultNode; + } + } +} +// File: ./Types.js +/* + * xseen.types contains the datatype and conversion utilities. These convert one format to another. + * Any method ending in 'toX' where 'X' is some datatype is a conversion to that type + * Other methods convert from string with space-spearated values + */ +xseen.types = { + 'Deg2Rad' : Math.PI / 180, + + 'SFFloat' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return value; + }, + + 'SFInt' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return Math.round(value); + }, + + 'SFBool' : function (value, def) + { + if (value === null) {return def;} + if (value) {return true;} + if (!value) {return false;} + return def; + }, + + 'SFTime' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return value; + }, + + 'SFVec3f' : function (value, def) + { + if (value === null) {return def;} + var v3 = value.split(' '); + if (v3.length < 3 || Number.isNaN(v3[0]) || Number.isNaN(v3[1]) || Number.isNaN(v3[2])) { + return def; + } + return [v3[0]-0, v3[1]-0, v3[2]-0]; + }, + + 'SFVec2f' : function (value, def) + { + if (value === null) {return def;} + var v2 = value.split(' '); + if (v2.length != 2 || Number.isNaN(v2[0]) || Number.isNaN(v2[1])) { + return def; + } + return [v2[0]-0, v2[1]-0]; + }, + + 'SFRotation' : function (value, def) + { + if (value === null) {return def;} + var v4 = value.split(' '); + if (v4.length != 4 || Number.isNaN(v4[0]) || Number.isNaN(v4[1]) || Number.isNaN(v4[2]) || Number.isNaN(v4[3])) { + return def; + } + var result = { + 'vector' : [v4[0], v4[1], v4[2], v4[3]], + 'axis_angle' : [{'x': v4[0], 'y': v4[1], 'z': v4[2]}, v4[3]], + }; + return result; + }, + + 'SFColor' : function (value, defaultString) + { + var v3 = this.SFVec3f(value, defaultString); + v3[0] = Math.min(Math.max(v3[0], 0.0), 1.0); + v3[1] = Math.min(Math.max(v3[1], 0.0), 1.0); + v3[2] = Math.min(Math.max(v3[2], 0.0), 1.0); + return v3; + }, + + 'SFString' : function (value, def) + { + if (value === null) {value = def;} + return value; + }, + +// For MF* types, a default of '' means to return an empty array on parsing error + 'MFFloat' : function (value, def) + { + var defReturn = (def == '') ? [] : def; + if (value === null) {return defReturn;} + var mi = value.split(' '); + var rv = []; + for (var i=0; iDocumentation."]; +/* + * All X3D and A-Frame pre-defined solids, fixed camera, directional light, Material texture only, glTF model loader with animations, Assets and reuse, Viewpoint, Background, Lighting, Image Texture, [Indexed]TriangleSet, IndexedFaceSet, [Indexed]QuadSet
\nNext work
  • Event Model/Animation
  • Extrusion
  • Navigation
", + * + * All of the following are ALPHA releases for V0.4.x + * V0.4.0+13 Feature -- events (from HTML to XSeen) + * V0.4.1+14 Fix - minor text correction in xseen.node.geometry__TriangulateFix (nodes-x3d_Geometry.js) + * V0.4.1+15 Modified build.pl to increase compression by removing block comments + * V0.4.1+16 Feature -- XSeen events (from XSeen to HTML) + * V0.4.2+17 Feature -- XSeen internals events (from XSeen to XSeen) with changes to fix previous event handling + * V0.4.2+18 Feature -- Split screen VR display + * V0.4.3+19 Rebuild and fix loading caused by new Stereo library + * V0.4.3+20 Feature -- Navigation (orbit), including Stack update for Viewpoint and restructuring the rendering loop + * V0.4.3+21 Feature -- Changed handling of Viewpoint to include camera motion + * V0.4.4+22 Fix -- Internal event handling in passing on events of the proper type + * V0.4.5+23 Feature -- Navigation (trackball) + * V0.4.5+24 Fix -- when there is no navigation + * V0.4.6+25 Update -- JSON Parser (contributed by John Carlson) + * + * In progress + */ + var version = { + major : Major, + minor : Minor, + patch : Patch, + preRelease : PreRelease, + release : Release, + version : '', + date : RDate, + splashText : SplashText + }; +// Using the scheme at http://semver.org/ + version.version = version.major + '.' + version.minor + '.' + version.patch; + version.version += (version.preRelease != '') ? '-' + version.preRelease : ''; + version.version += (version.release != '') ? '+' + version.release : ''; + return version; +} +// File: nodes/nodes-af.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code for A-Frame nodes + + +xseen.node.core_NOOP = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.parsing = function (s, e) { + xseen.debug.logInfo ('Parsing init details stub for ' + s); +} + +xseen.node.af_Entity = { + 'init' : function (e,p) + { + xseen.node.parsing('A-Frame Entity'); + }, + 'fin' : function (e,p) {} +}; +xseen.node.af_Assets = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.af_AssetItem = { + 'init' : function (e,p) // Only field is SRC. + { + }, + 'fin' : function (e,p) {} +}; +xseen.node.af_Mixin = { + 'init' : function (e,p) // Lots of fields -- all nebelous until used + { + }, + 'fin' : function (e,p) {} +}; + + + +xseen.node.af_Appearance = function (e) { + var parameters = { + 'aoMap' : e._xseen.fields['ambient-occlusion-map'], + 'aoMapIntensity' : e._xseen.fields['ambient-occlusion-map-intensity'], + 'color' : e._xseen.fields['color'], + 'displacementMap' : e._xseen.fields['displacement-map'], + 'displacementScale' : e._xseen.fields['displacement-scale'], + 'displacementBias' : e._xseen.fields['displacement-bias'], + 'envMap' : e._xseen.fields['env-map'], + 'normalMap' : e._xseen.fields['normal-map'], + 'normalScale' : e._xseen.fields['normal-scale'], + 'wireframe' : e._xseen.fields['wireframe'], + 'wireframeLinewidth' : e._xseen.fields['wireframe-linewidth'], + }; + var material = new THREE.MeshPhongMaterial(parameters); + return material; +/* + * === All Entries === +.aoMap +.aoMapIntensity +.color + .combine +.displacementMap +.displacementScale +.displacementBias + .emissive + .emissiveMap + .emissiveIntensity +.envMap + .lightMap + .lightMapIntensity + .map + .morphNormals + .morphTargets +.normalMap +.normalScale + .reflectivity + .refractionRatio + .shininess + .skinning + .specular + .specularMap +.wireframe + .wireframeLinecap + .wireframeLinejoin +.wireframeLinewidth +/////////////////////////////////////////////////////////////////////////////// +e._xseen.fields['ambient-occlusion-map'] +e._xseen.fields['ambient-occlusion-map-intensity'] + e._xseen.fields['ambient-occlusion-texture-offset'] + e._xseen.fields['ambient-occlusion-texture-repeat'] +e._xseen.fields['color'] +e._xseen.fields['displacement-bias'] +e._xseen.fields['displacement-map'] +e._xseen.fields['displacement-scale'] + e._xseen.fields['displacement-texture-offset'] + e._xseen.fields['displacement-texture-repeat'] +e._xseen.fields['env-map'] + e._xseen.fields['fog'] + e._xseen.fields['metalness'] +e._xseen.fields['normal-map'] +e._xseen.fields['normal-scale'] + e._xseen.fields['normal-texture-offset'] + e._xseen.fields['normal-texture-repeat'] + e._xseen.fields['repeat'] + e._xseen.fields['roughness'] + e._xseen.fields['spherical-env-map'] + e._xseen.fields['src'] +e._xseen.fields['wireframe'] +e._xseen.fields['wireframe-linewidth'] + + * === Unused Entries === + .combine + .emissive + .emissiveMap + .emissiveIntensity + .lightMap + .lightMapIntensity + .map + .morphNormals + .morphTargets + .reflectivity + .refractionRatio + .shininess + .skinning + .specular + .specularMap + .wireframeLinecap + .wireframeLinejoin +/////////////////////////////////////////////////////////////////////////////// + e._xseen.fields['ambient-occlusion-texture-offset'] + e._xseen.fields['ambient-occlusion-texture-repeat'] + e._xseen.fields['displacement-texture-offset'] + e._xseen.fields['displacement-texture-repeat'] + e._xseen.fields['fog'] + e._xseen.fields['metalness'] + e._xseen.fields['normal-texture-offset'] + e._xseen.fields['normal-texture-repeat'] + e._xseen.fields['repeat'] + e._xseen.fields['roughness'] + e._xseen.fields['spherical-env-map'] + e._xseen.fields['src'] + */ +} + +xseen.node.af_Box = { + 'init' : function (e,p) + { + var geometry = new THREE.BoxGeometry( + e._xseen.fields.width, + e._xseen.fields.height, + e._xseen.fields.depth, + e._xseen.fields['segments-width'], + e._xseen.fields['segments-height'], + e._xseen.fields['segments-depth'] + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Cone = { + 'init' : function (e,p) + { + var geometry = new THREE.ConeGeometry( + e._xseen.fields.radius, + e._xseen.fields.height, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-height'], + e._xseen.fields['open-ended'], + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Cylinder = { + 'init' : function (e,p) + { + var geometry = new THREE.CylinderGeometry( + e._xseen.fields['radius-top'], + e._xseen.fields['radius-bottom'], + e._xseen.fields.height, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-height'], + e._xseen.fields['open-ended'], + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Dodecahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.DodecahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Icosahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.IcosahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Octahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.OctahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Sphere = { + 'init' : function (e,p) + { + var geometry = new THREE.SphereGeometry( + e._xseen.fields.radius, + e._xseen.fields['segments-width'], + e._xseen.fields['segments-height'], + e._xseen.fields['phi-start'] * xseen.types.Deg2Rad, + e._xseen.fields['phi-length'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Tetrahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.TetrahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Torus = { + 'init' : function (e,p) + { + var geometry = new THREE.TorusGeometry( + e._xseen.fields.radius, + e._xseen.fields.tube, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-tubular'], + e._xseen.fields.arc * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; +// File: nodes/nodes-Viewing.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Control Node definitions + + +xseen.node.unk_Viewpoint = { + 'init' : function (e,p) + { // This should really go in a separate push-down list for Viewpoints + e._xseen.fields._radius0 = Math.sqrt( e._xseen.fields.position[0]*e._xseen.fields.position[0] + + e._xseen.fields.position[1]*e._xseen.fields.position[1] + + e._xseen.fields.position[2]*e._xseen.fields.position[2]); + e._xseen.domNode = e; // Back-link to node if needed later on + e._xseen.position = new THREE.Vector3(e._xseen.fields.position[0], e._xseen.fields.position[1], e._xseen.fields.position[2]); + e._xseen.type = e._xseen.fields.cameratype; + e._xseen.motion = e._xseen.fields.motion; + e._xseen.motionspeed = e._xseen.fields.motionspeed * 1000; + if (e._xseen.motion == 'turntable' || e._xseen.motion == 'tilt') {e._xseen.motionspeed = 1.0/e._xseen.motionspeed;} + + if (!e._xseen.sceneInfo.tmp.activeViewpoint) { + e._xseen.sceneInfo.stacks.Viewpoints.pushDown(e._xseen); + e._xseen.sceneInfo.tmp.activeViewpoint = true; + } + + e._xseen.handlers = {}; + e._xseen.handlers.setactive = this.setactive; + }, + 'fin' : function (e,p) {}, + + 'setactive' : function (ev) + { + var xseenNode = this.destination._xseen; + xseenNode.sceneInfo.stacks.Viewpoints.pushDown(xseenNode); // TODO: This is probably not the right way to change VP in the stack + xseenNode.sceneInfo.element._xseen.renderer.activeCamera = + xseenNode.sceneInfo.element._xseen.renderer.cameras[xseenNode.fields.type]; + xseenNode.sceneInfo.element._xseen.renderer.activeRender = + xseenNode.sceneInfo.element._xseen.renderer.renderEffects[xseenNode.fields.type]; + if (xseenNode.fields.type != 'stereo') { + xseenNode.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, xseenNode.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); + } + }, +}; + +xseen.node.controls_Navigation = { + 'init' : function (e,p) + { // This should really go in a separate push-down list for Viewpoints + + e._xseen.domNode = e; // Back-link to node if needed later on + e._xseen.speed = e._xseen.fields.speed; + if (e._xseen.setup == 'examine') {e._xseen.setup == 'trackball';} + //e._xseen.type = e._xseen.fields.type; + e._xseen.type = 'none'; + e._xseen.setup = e._xseen.fields.type; + if (!(e._xseen.setup == 'orbit' || e._xseen.setup == 'trackball')) {e._xseen.setup = 'none';} + + if (!e._xseen.sceneInfo.tmp.activeNavigation) { + e._xseen.sceneInfo.stacks.Navigation.pushDown(e._xseen); + e._xseen.sceneInfo.tmp.activeNavigation = true; + } + + e._xseen.handlers = {}; + e._xseen.handlers.setactive = this.setactive; + }, + 'fin' : function (e,p) {}, + + 'setactive' : function (ev) + { +/* + this.destination._xseen.sceneInfo.stacks.Viewpoints.pushDown(this.destination); // TODO: This is probably not the right way to change VP in the stack + this.destination._xseen.sceneInfo.element._xseen.renderer.activeCamera = + this.destination._xseen.sceneInfo.element._xseen.renderer.cameras[this.destination._xseen.fields.type]; + this.destination._xseen.sceneInfo.element._xseen.renderer.activeRender = + this.destination._xseen.sceneInfo.element._xseen.renderer.renderEffects[this.destination._xseen.fields.type]; + if (this.destination._xseen.fields.type != 'stereo') { + this.destination._xseen.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, this.destination._xseen.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); + } + */ + }, +}; + +xseen.node.lighting_Light = { + 'init' : function (e,p) + { + var color = xseen.types.Color3toInt (e._xseen.fields.color); + var intensity = e._xseen.fields.intensity - 0; + var lamp, type=e._xseen.fields.type.toLowerCase(); +/* + if (typeof(p._xseen.children) == 'undefined') { + console.log('Parent of Light does not have children...'); + p._xseen.children = []; + } + */ + + if (type == 'point') { + // Ignored field -- e._xseen.fields.location + lamp = new THREE.PointLight (color, intensity); + lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); + lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); + + } else if (type == 'spot') { + lamp = new THREE.SpotLight (color, intensity); + lamp.position.set(0-e._xseen.fields.direction[0], 0-e._xseen.fields.direction[1], 0-e._xseen.fields.direction[2]); + lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); + lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); + lamp.angle = Math.max(0.0, Math.min(1.5707963267948966192313216916398, e._xseen.fields.cutoffangle)); + lamp.penumbra = 1 - Math.max(0.0, Math.min(lamp.angle, e._xseen.fields.beamwidth)) / lamp.angle; + + } else { // DirectionalLight (by default) + lamp = new THREE.DirectionalLight (color, intensity); + lamp.position.x = 0-e._xseen.fields.direction[0]; + lamp.position.y = 0-e._xseen.fields.direction[1]; + lamp.position.z = 0-e._xseen.fields.direction[2]; + } + p._xseen.children.push(lamp); + lamp = null; + } + , + 'fin' : function (e,p) + { + } +}; +// File: nodes/nodes-x3d_Appearance.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.appearance_Material = { + 'init' : function (e,p) + { + var transparency = e._xseen.fields.transparency - 0; + var shininess = e._xseen.fields.shininess - 0; + var colorDiffuse = xseen.types.Color3toInt (e._xseen.fields.diffusecolor); + var colorEmissive = xseen.types.Color3toInt (e._xseen.fields.emissivecolor); + var colorSpecular = xseen.types.Color3toInt (e._xseen.fields.specularcolor); + p._xseen.material = new THREE.MeshPhongMaterial( { +// p._xseen.material = new THREE.MeshBasicMaterial( { + 'color' : colorDiffuse, + 'emissive' : colorEmissive, + 'specular' : colorSpecular, + 'shininess' : shininess, + 'opacity' : 1.0-transparency, + 'transparent' : (transparency > 0.0) ? true : false + } ); + e._xseen.animate['diffusecolor'] = p._xseen.material.color; + e._xseen.animate['emissivecolor'] = p._xseen.material.emissive; + e._xseen.animate['specularcolor'] = p._xseen.material.specular; + e._xseen.animate['transparency'] = p._xseen.material.opacity; + e._xseen.animate['shininess'] = p._xseen.material.shininess; + }, + 'fin' : function (e,p) {} +}; + +xseen.node.appearance_ImageTexture = { + 'init' : function (e,p) + { + p._xseen.texture = xseen.loader.ImageLoader.load(e._xseen.fields.url); + p._xseen.texture.wrapS = (e._xseen.fields.repeats) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + p._xseen.texture.wrapT = (e._xseen.fields.repeatt) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + }, + 'fin' : function (e,p) {} +}; + +xseen.node.appearance_Appearance = { + 'init' : function (e,p) {}, + + 'fin' : function (e,p) + { + if (typeof(e._xseen.texture) !== 'undefined' && e._xseen.texture !== null) { + e._xseen.material.map = e._xseen.texture; + } + p._xseen.appearance = e._xseen.material; + } +}; +// File: nodes/nodes-x3d_Geometry.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.geometry_Coordinate = { + 'init' : function (e,p) + { + var vertices = []; + for (var i=0; i= 0 where each triple defines a triangle face. It is assumed that the + * indices define the face in counter-clockwise order when looking at the face. + * vertices - an array of THREE.Vector3 points + */ +xseen.node.geometry__Indexed3 = function (indices, vertices, normals=[], color=[]) { + var i, face, normal=[], faceCount=0, n; + var useNormals = (normals.length > 0) ? true : false; + var useColor = (color.length > 0) ? true : false; + var maxIndex = Math.max.apply(null, indices); + var minIndex = Math.min.apply(null, indices); + if (maxIndex >= vertices.length) { + console.log ('Maximum index ('+maxIndex+') exceeds vertex count ('+vertices.length+'). No geometry is created'); + return; + } + if (useNormals && maxIndex >= normals.length) { + console.log ('Maximum index ('+maxIndex+') exceeds normal count ('+normals.length+'). No geometry is created'); + return; + } + if (useColor && maxIndex >= color.length) { + console.log ('Maximum index ('+maxIndex+') exceeds color count ('+color.length+'). No geometry is created'); + return; + } + if (minIndex < 0) { + console.log ('Minimum index ('+minIndex+') less than zero. No geometry is created'); + return; + } + if (indices.length % 3 != 0) { + console.log ('Number of indices ('+indices.length+') not divisible by 3. No geometry is created'); + return; + } + + var geometry = new THREE.Geometry(); + var normal_pz = new THREE.Vector3 (0, 0, 1); + var normal_mz = new THREE.Vector3 (0, 0, -1); + for (var i=0; i 0) {clip.duration = e._xseen.fields.duration;} + e._xseen.mixer.clipAction( clip ).play(); + } ); + } else { // Play a specific animation + var clip = THREE.AnimationClip.findByName(response.animations, e._xseen.fields.playonload); + var action = e._xseen.mixer.clipAction (clip); + action.play(); + } + } + } + } +}; + + +xseen.node.x_Route = { + 'init' : function (e,p) + { + var dest = e._xseen.fields.destination; + var hand = e._xseen.fields.handler; + var externalHandler = false; + + // Make sure sufficient data is provided + if (e._xseen.fields.source == '' || + typeof(window[hand]) !== 'function' && + (dest == '' || e._xseen.fields.event == '' || e._xseen.fields.field == '')) { + xseen.debug.logError ('Route node missing field. No route setup. Source: '+e._xseen.fields.source+'.'+e._xseen.fields.event+'; Destination: '+dest+'.'+e._xseen.fields.field+'; Handler: '+hand); + return; + } else if (typeof(window[hand]) === 'function') { + externalHandler = true; + } + + // For toNode routing, check existence of source and destination elements + var eSource = document.getElementById (e._xseen.fields.source); + if (! externalHandler) { + var eDestination = document.getElementById (dest); + if (typeof(eSource) === 'undefined' || typeof(eDestination) === 'undefined') { + xseen.debug.logError ('Source or Destination node does not exist. No route setup'); + return; + } + // Get field information -- perhaps there is some use in the Animate node? + var fField = xseen.nodes._getFieldInfo (eDestination.nodeName, e._xseen.fields.field); + if (typeof(fField) === 'undefined' || !fField.good) { + xseen.debug.logError ('Destination field does not exist or incorrectly specified. No route setup'); + return; + } + // Set up listener on source node for specified event. The listener code is the 'set' method for the + // node. It is passed the DOM 'event' data structure. Since there may be more than one node of the type + // specified by 'destination', the event handler is attached to the node in e._xseen.handlers. This is done + // when the node is parsed + xseen.Events.addHandler (e, eSource, e._xseen.fields.event, eDestination, fField); + +/* + * External (to XSeen) event handler + * TODO: limit the events to those requested if e._xseen.fields.event != 'xseen' + * This probably requires an intermediatiary event handler + */ + } else { + var handler = window[hand]; + eSource.addEventListener ('xseen', handler); + } + }, + + 'fin' : function (e,p) + { + }, + 'evHandler' : function (u) + { + var de = u.e; + var df = u.f; + return de._xseen.handlers[df.handlerName]; + }, +}; +// File: nodes/nodes.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.core_NOOP = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.core_WorldInfo = { + 'init' : function (e,p) {parsing('WorldInfo', e)}, + 'fin' : function (e,p) {} +}; + +function parsing (s, e) { + xseen.debug.logInfo ('Parsing init details stub for ' + s); +} + + +xseen.node.unk_Shape = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) + { +// if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + if (typeof(e._xseen.materialProperty) !== 'undefined') { + e._xseen.appearance.vertexColors = THREE.VertexColors; + //e._xseen.appearance.vertexColors = THREE.FaceColors; + e._xseen.appearance._needsUpdate = true; + e._xseen.appearance.needsUpdate = true; + } + var mesh = new THREE.Mesh (e._xseen.geometry, e._xseen.appearance); + mesh.userData = e; + p._xseen.children.push(mesh); + p._xseen.sceneInfo.selectable.push(mesh); + mesh = null; + } +}; + +xseen.node.grouping_Transform = { + 'init' : function (e,p) + { + var group = new THREE.Group(); + if (e.nodeName == "TRANSFORM") { + var rotation = xseen.types.Rotation2Quat(e._xseen.fields.rotation); + group.name = 'Transform children [' + e.id + ']'; + group.position.x = e._xseen.fields.translation[0]; + group.position.y = e._xseen.fields.translation[1]; + group.position.z = e._xseen.fields.translation[2]; + group.scale.x = e._xseen.fields.scale[0]; + group.scale.y = e._xseen.fields.scale[1]; + group.scale.z = e._xseen.fields.scale[2]; + group.quaternion.x = rotation.x; + group.quaternion.y = rotation.y; + group.quaternion.z = rotation.z; + group.quaternion.w = rotation.w; + + e._xseen.animate['translation'] = group.position; + e._xseen.animate['rotation'] = group.quaternion; + e._xseen.animate['scale'] = group.scale; + } + e._xseen.sceneNode = group; + }, + 'fin' : function (e,p) + { + // Apply transform to all objects in e._xseen.children + e._xseen.children.forEach (function (child, ndx, wholeThing) + { + e._xseen.sceneNode.add(child); + }); + p._xseen.children.push(e._xseen.sceneNode); + } +}; + +xseen.node.networking_Inline = { + 'init' : function (e,p) + { + if (typeof(e._xseen.processedUrl) === 'undefined' || !e._xseen.requestedUrl) { + var uri = xseen.parseUrl (e._xseen.fields.url); + var type = uri.extension; + e._xseen.loadGroup = new THREE.Group(); + e._xseen.loadGroup.name = 'Inline content [' + e.id + ']'; + console.log ('Created Inline Group with UUID ' + e._xseen.loadGroup.uuid); + var userdata = {'requestType':'x3d', 'e':e, 'p':p} + if (type.toLowerCase() == 'json') { + userdata.requestType = 'json'; + xseen.loadMgr.loadJson (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); + } else { + xseen.loadMgr.loadXml (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); + } + e._xseen.requestedUrl = true; + } + //if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(e._xseen.loadGroup); + console.log ('Using Inline Group with UUID ' + e._xseen.loadGroup.uuid); + }, + 'fin' : function (e,p) + { + }, + + 'loadSuccess' : + function (response, userdata, xhr) { + userdata.e._xseen.processedUrl = true; + userdata.e._xseen.loadResponse = response; + console.log("download successful for "+userdata.e.id); + if (userdata.requestType == 'json') { + var tmp = {'scene': response}; + response = null; + response = (new JSONParser()).parseJavaScript(tmp); + } + var start = {'_xseen':0}; + var findSceneTag = function (fragment) { + if (typeof(fragment._xseen) === 'undefined') {fragment._xseen = {'childCount': -1};} + if (fragment.nodeName.toLowerCase() == 'scene') { + start = fragment; + return; + } else if (fragment.children.length > 0) { + for (fragment._xseen.childCount=0; fragment._xseen.childCount 0) { + userdata.e.appendChild(start.children[0]); + } + xseen.Parse(userdata.e, userdata.p, userdata.p._xseen.sceneInfo); + userdata.e._xseen.children.forEach (function (child, ndx, wholeThing) + { + userdata.e._xseen.loadGroup.add(child); +console.log ('...Adding ' + child.type + ' (' + child.name + ') to Inline Group? with UUID ' + userdata.e._xseen.loadGroup.uuid + ' (' + userdata.e._xseen.loadGroup.name + ')'); + }); + userdata.p._xseen.sceneInfo.scene.updateMatrixWorld(); + //xseen.debug.logInfo("Complete work on Inline..."); + } else { + console.log("Found illegal X3D file -- no 'scene' tag"); + } + // Parse (start, userdata.p)... + } +}; + +/* + * Most of this stuff is only done once per XSeen element. Loading of Inline contents should not + * repeat the definitions and canvas creation + */ +xseen.node.core_Scene = { + 'DEFAULT' : { + 'Viewpoint' : { + 'Position' : new THREE.Vector3 (0, 0, 10), + 'Orientation' : '0 1 0 0', // TODO: fix (and below) when handling orientation + 'Type' : 'perpsective', + 'Motion' : 'none', + 'MotionSpeed' : 1.0, + }, + 'Navigation' : { + 'Speed' : 1.0, // 16 spr (1 revolution per 16 seconds), in mseconds. + 'Type' : 'none', + 'Setup' : 'none', + } + }, + 'init' : function (e,p) + { + // Create default Viewpoint and Navigation + xseen.sceneInfo[0].stacks.Viewpoints.setDefault( + { + 'position' : this.DEFAULT.Viewpoint.Position, + 'type' : this.DEFAULT.Viewpoint.Type, + 'motion' : this.DEFAULT.Viewpoint.Motion, + 'motionspeed': this.DEFAULT.Viewpoint.MotionSpeed / 1000, + 'domNode' : e, + 'fields' : {}, + } + ); + xseen.sceneInfo[0].stacks.Navigation.setDefault( + { + 'speed' : this.DEFAULT.Navigation.Speed / 1000, + 'type' : this.DEFAULT.Navigation.Type, + 'setup' : this.DEFAULT.Navigation.Setup, + 'domNode' : e, + 'fields' : {}, + } + ); + + var width = e._xseen.sceneInfo.size.width; + var height = e._xseen.sceneInfo.size.height; + var x_renderer = new THREE.WebGLRenderer(); + x_renderer.setSize (width, height); + var perspectiveCamera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 ); + var orthoCamera = new THREE.OrthographicCamera( 75, width / height, 0.1, 1000 ); + //perspectiveCamera.translateX(this.DEFAULT.Viewpoint.Position.x).translateY(this.DEFAULT.Viewpoint.Position.y).translateZ(this.DEFAULT.Viewpoint.Position.z); // Default position + //orthoCamera.translateX(this.DEFAULT.Viewpoint.Position.x).translateY(this.DEFAULT.Viewpoint.Position.y).translateZ(this.DEFAULT.Viewpoint.Position.z); // Default position + perspectiveCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position + perspectiveCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position + perspectiveCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position + orthoCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position + orthoCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position + orthoCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position + + // Stereo viewing effect + // from http://charliegerard.github.io/blog/Virtual-Reality-ThreeJs/ + var x_effect = new THREE.StereoEffect(x_renderer); + + e.appendChild (x_renderer.domElement); + e._xseen.renderer = { + 'canvas' : e._xseen.sceneInfo.scene, + 'width' : width, + 'height' : height, + 'cameras' : { + 'perspective' : perspectiveCamera, + 'ortho' : orthoCamera, + 'stereo' : perspectiveCamera, + }, // Removed .sceneInfo camera because this node defines the camera + 'effects' : x_effect, + 'renderEffects' : { + 'normal' : x_renderer, + 'perspective' : x_renderer, + 'ortho' : x_renderer, + 'stereo' : x_effect, + }, + 'activeRender' : {}, + 'activeCamera' : {}, + 'controls' : {}, // Used for navigation + }; + e._xseen.renderer.activeRender = e._xseen.renderer.renderEffects.normal; + e._xseen.renderer.activeCamera = e._xseen.renderer.cameras.perspective; + }, + +/* + * This appears now to be working!!! + * + * Late loading content is not getting inserted into the scene graph for rendering. Need to read + * THREE docs about how to do that. + * Camera will need to be redone. Existing camera is treated as a special child. A separate camera + * should be established and Viewpoint nodes define "photostops" rather than a camera. The camera is + * in effect, parented to the "photostop". This probably needs to list of Viewpoints discussed in the + * X3D specification. + */ + 'fin' : function (e,p) + { + // Render all Children + //xseen.renderNewChildren (e._xseen.children, e._xseen.renderer.canvas); + e._xseen.children.forEach (function (child, ndx, wholeThing) + { + console.log('Adding child of type ' + child.type + ' (' + child.name + ')'); + e._xseen.renderer.canvas.add(child); + }); + xseen.dumpSceneGraph (); +// e._xseen.renderer.renderer.render( e._xseen.renderer.canvas, e._xseen.renderer.camera ); +// xseen.debug.logInfo("Rendered all elements -- Starting animation"); +/* + * TODO: Need to get current top-of-stack for all stack-bound nodes and set them as active. + * This only happens the initial time for each XSeen tag in the main HTML file + * + * At this time, only Viewpoint is stack-bound. Probably need to stack just the ._xseen object. + * Also, .fields.position is the initial specified location; not the navigated/animated one + */ + var vp = xseen.sceneInfo[0].stacks.Viewpoints.getActive(); + var nav = xseen.sceneInfo[0].stacks.Navigation.getActive(); + var currentCamera = e._xseen.renderer.activeCamera; + var currentRenderer = e._xseen.renderer.activeRender; + currentCamera.position.x = vp.position.x; + currentCamera.position.y = vp.position.y; + currentCamera.position.z = vp.position.z; + e._xseen.renderer.controls = xseen.Navigation.setup[nav.setup] (currentCamera, currentRenderer); + xseen.debug.logInfo("Ready to kick off rendering loop"); + xseen.renderFrame(); + }, + +}; + +xseen.node.env_Background = { + 'init' : function (e,p) + { + var color = new THREE.Color(e._xseen.fields.skycolor[0], e._xseen.fields.skycolor[1], e._xseen.fields.skycolor[2]); + var textureCube = new THREE.CubeTextureLoader() + .load ([e._xseen.fields.srcright, + e._xseen.fields.srcleft, + e._xseen.fields.srctop, + e._xseen.fields.srcbottom, + e._xseen.fields.srcfront, + e._xseen.fields.srcback], + this.loadSuccess({'e':e, 'p':p}) + ); + e._xseen.sceneInfo.scene.background = color; +/* + var material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } ); + var size = 1; + //var geometry = new THREE.BoxGeometry(200, 200, 2); + var geometry = new THREE.Geometry(); + geometry.vertices.push ( + new THREE.Vector3(-size, -size, size), + new THREE.Vector3( size, -size, size), + new THREE.Vector3( size, -size, -size), + new THREE.Vector3(-size, -size, -size), + new THREE.Vector3(-size, size, size), + new THREE.Vector3( size, size, size), + new THREE.Vector3( size, size, -size), + new THREE.Vector3(-size, size, -size) + ); + + geometry.faces.push ( // external facing geometry + new THREE.Face3(0, 1, 5), + new THREE.Face3(0, 5, 4), + new THREE.Face3(1, 2, 6), + new THREE.Face3(1, 6, 5), + new THREE.Face3(2, 3, 7), + new THREE.Face3(2, 7, 6), + new THREE.Face3(3, 0, 4), + new THREE.Face3(3, 4, 7), + new THREE.Face3(4, 5, 6), + new THREE.Face3(4, 6, 7), + new THREE.Face3(0, 2, 1), + new THREE.Face3(0, 3, 2), + ); + geometry.computeBoundingSphere(); + var mesh = new THREE.Mesh (geometry, material); + e._xseen.sceneInfo.element._xseen.renderer.canvas.add(mesh); +*/ + }, + + 'fin' : function (e,p) + { + p._xseen.appearance = e._xseen.material; + }, + + 'loadSuccess' : function (userdata) + { + var e = userdata.e; + var p = userdata.p; + return function (textureCube) + { + e._xseen.processedUrl = true; + e._xseen.loadTexture = textureCube; + e._xseen.sceneInfo.scene.background = textureCube; + } + }, + +}; +// File: nodes/nodes_Animate.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.x_Animate = { + 'init' : function (e,p) + { + var delay = e._xseen.fields.delay * 1000; // Convert to milliseconds + var duration = e._xseen.fields.duration * 1000; // Convert to milliseconds + var repeat = (e._xseen.fields.repeat < 0) ? Infinity : e._xseen.fields.repeat; + var interpolator = e._xseen.fields.interpolator; + var easing = e._xseen.fields.easing; + + var fields = xseen.parseTable[p.localName.toLowerCase()].fields; + var fieldIndex = xseen.parseTable[p.localName.toLowerCase()].fieldIndex; + var toField = e._xseen.fields.field; + var toFieldIndex = fieldIndex[toField]; + if (typeof(fields[toFieldIndex]) === 'undefined') { + xseen.debug.logInfo("Field '" + toField + "' not found in parent (" + p.localName.toLowerCase() + "). No animation performed."); + return; + } + var fieldObject = fields[toFieldIndex].clone().setFieldName('to'); // Parse table entry for 'toField' + var to = xseen.nodes._parseField(fieldObject, e); // Parsed data -- need to convert to THREE format + +// Convert 'to' to the datatype of 'field' and set interpolation type. + var interpolation; + if (fieldObject.type == 'SFVec3f') { + interpolation = TWEEN.Interpolation.Linear; + to = xseen.types.Vector3(to); + xseen.debug.logInfo("Interpolating field '" + toField + "' as 3-space."); + + } else if (fieldObject.type == 'SFColor') { + interpolation = this.Interpolator.color; + to = new THREE.Color (xseen.types.Color3toInt(to)); + xseen.debug.logInfo("Interpolation field '" + toField + "' as color."); + + } else if (fieldObject.type == 'SFRotation') { + interpolation = this.Interpolator.slerp; + to = xseen.types.Rotation2Quat(to); + xseen.debug.logInfo("Interpolation field '" + toField + "' as rotation."); + + } else { + xseen.debug.logInfo("Field '" + toField + "' not converted to THREE format. No animation performed."); + return; + } + var fieldTHREE = p._xseen.animate[toField]; // THREE field for animation + + var tween = new TWEEN.Tween(fieldTHREE) + .to(to, duration) + .delay(delay) + .repeat(repeat) + .interpolation(interpolation); + var easingType = e._xseen.fields.easingtype; + easingType = easingType.charAt(0).toUpperCase() + easingType.slice(1); + easing = (easingType != 'Linear' && easing == '') ? 'inout' : easing; + if (easing != '') { + easing = easing.replace('in', 'In').replace('out', 'Out'); + easingType = (easingType == 'Linear') ? 'Quadratic' : easingType; + e._xseen.fields.easing = easing; + e._xseen.fields.easingtype = easingType; + tween.easing(TWEEN.Easing[easingType][easing]); + } + +/* + * Put animation-specific data in node (e._xseen) so it can be accessed on events (through 'this') + * This includes initial value and field + * All handlers (goes into .handlers) + * TWEEN object + */ + e._xseen.initialValue = fieldTHREE.clone(); + e._xseen.animatingField = fieldTHREE; + e._xseen.handlers = {}; + e._xseen.handlers.setstart = this.setstart; + e._xseen.handlers.setstop = this.setstop; + e._xseen.handlers.setpause = this.setpause; + e._xseen.handlers.setresetstart = this.setresetstart; + e._xseen.animating = tween; + p._xseen.animation.push (tween); + tween.start(); + }, + 'fin' : function (e,p) {}, + 'setstart' : function (ev) + { + console.log ('Starting animation'); + this.destination._xseen.animating.start(); + }, + 'setstop' : function (ev) + { + console.log ('Stopping animation'); + this.destination._xseen.animating.stop(); + }, +/* + * TODO: Update TWEEN to support real pause & resume. + * Pause needs to hold current position + * Resume needs to restart the timer to current time so there is no "jump" + */ + 'setpause' : function (ev) + { + console.log ('Pausing (really stopping) animation'); + this.destination._xseen.animating.stop(); + }, + 'setresetstart' : function (ev) // TODO: Create seperate 'reset' method + { + console.log ('Reset and start animation'); + this.destination._xseen.animatingField = this.destination._xseen.initialValue; + this.destination._xseen.animating.start(); + }, + +/* + * Various interpolator functions for use with different data types + * All are designed to be used within TWEEN and take two arguments + * v A vector of way points (key values) that define the interpolated path + * k The interpolating factor that defines how far along the path for the current result + * + * Functions + * slerp - Linear in quaterian space (though not yet) + * color - Linear in color space (currently HSL as used by THREE) + * + */ + 'Interpolator' : { + 'slerp' : function (v,k) + { + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + + if (k < 0) { + return v[0].slerp(v[1], f); + } + + if (k > 1) { + return v[m].slerp(v[m-1], m-f); + } + + return v[i].slerp (v[i + 1 > m ? m : i + 1], f-i); + }, + 'color' : function (v,k) + { + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + var fn = this.slerpCompute; + + if (k < 0) { + return v[0].lerp(v[1], f); + } + if (k > 1) { + return v[m].lerp(v[m-1], m-f); + } + return v[i].lerp (v[i + 1 > m ? m : i + 1], f - i); + }, + }, +}; +// File: nodes/_Definitions-aframe.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen._addAframeAppearance = function (node) { + node + .addField('ambient-occlusion-map', 'SFString', '') + .addField('ambient-occlusion-map-intensity', 'SFFloat', 1) + .addField('ambient-occlusion-texture-offset', 'SFVec2f', '0 0') + .addField('ambient-occlusion-texture-repeat', 'SFVec2f', '1 1') + .addField('color', 'Color', '#FFF') + .addField('displacement-bias', 'SFFloat', 0.5) + .addField('displacement-map', 'SFString', '') + .addField('displacement-scale', 'SFFloat', 1) + .addField('displacement-texture-offset', 'SFVec2f', '0 0') + .addField('displacement-texture-repeat', 'SFVec2f', '1 1') + .addField('env-map', 'SFString', '') + .addField('fog', 'SFBool', true) + .addField('metalness', 'SFFloat', 0) + .addField('normal-map', 'SFString', '') + .addField('normal-scale', 'SFVec2f', '1 1') + .addField('normal-texture-offset', 'SFVec2f', '0 0') + .addField('normal-texture-repeat', 'SFVec2f', '1 1') + .addField('repeat', 'SFVec2f', '1 1') + .addField('roughness', 'SFFloat', 0.5) + .addField('spherical-env-map', 'SFString', '') + .addField('src', 'SFString', '') + .addField('wireframe', 'SFBool', false) + .addField('wireframe-linewidth', 'SFInt', 2) + .addNode(); +} + +xseen.nodes._defineNode('a-entity', 'A-Frame', 'af_Entity') + .addField('geometry', 'SFString', '') + .addField('material', 'SFString', '') + .addField('light', 'SFString', '') + .addNode(); + +var node; +node = xseen.nodes._defineNode('a-box', 'A-Frame', 'af_Box') + .addField('depth', 'SFFloat', 1) + .addField('height', 'SFFloat', 1) + .addField('width', 'SFFloat', 512) + .addField('segments-depth', 'SFInt', 1) + .addField('segments-height', 'SFInt', 1) + .addField('segments-width', 'SFInt', 1); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-cone', 'A-Frame', 'af_Cone') + .addField('height', 'SFFloat', 1) + .addField('radius', 'SFFloat', 1) + .addField('open-ended', 'SFBool', false) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 1) + .addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-cylinder', 'A-Frame', 'af_Cylinder') + .addField('height', 'SFFloat', 1) + .addField('open-ended', 'SFBool', false) + .addField('radius-bottom', 'SFFloat', 1) + .addField('radius-top', 'SFFloat', 1) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 1) + .addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-dodecahedron', 'A-Frame', 'af_Dodecahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-icosahedron', 'A-Frame', 'af_Icosahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-octahedron', 'A-Frame', 'af_Octahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-sphere', 'A-Frame', 'af_Sphere') + .addField('radius', 'SFFloat', 1) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 180) + .addField('phi-start', 'SFFloat', 0) + .addField('phi-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 18) + .addField('segments-width', 'SFInt', 36); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-tetrahedron', 'A-Frame', 'af_Tetrahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-torus', 'A-Frame', 'af_Torus') + .addField('radius', 'SFFloat', 2) + .addField('tube', 'SFFloat', 1) + .addField('arc', 'SFFloat', 360) + .addField('segments-radial', 'SFInt', 8) + .addField('segments-tubular', 'SFInt', 6); +xseen._addAframeAppearance (node); + +/* + * Asset management system nodes + */ +xseen.nodes._defineNode('a-assets', 'A-Frame', 'af_Assets') + .addNode(); +xseen.nodes._defineNode('a-asset-item', 'A-Frame', 'af_AssetItem') + .addField('src', 'SFString', '') + .addNode(); +xseen.nodes._defineNode('a-mixin', 'A-Frame', 'af_Mixin') + .addField('*', 'SFString', '') + .addNode(); +// File: nodes/_Definitions-x3d.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen.nodes._defineNode('Cone', 'Geometry3D', 'geometry3D_Cone') + .addField('bottomRadius', 'SFFloat', 1) + .addField('height', 'SFFloat', 2) + .addField('bottom', 'SFBool', true) + .addField('side', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode('Box', 'Geometry3D', 'geometry3D_Box') + .addField('size', 'SFVec3f', [1,1,1]) + .addNode(); + +xseen.nodes._defineNode('Sphere', 'Geometry3D', 'geometry3D_Sphere') + .addField('radius', 'SFFloat', '1') + .addNode(); + +xseen.nodes._defineNode('Cylinder', 'Geometry3D', 'geometry3D_Cylinder') + .addField('radius', 'SFFloat', 1) + .addField('height', 'SFFloat', 2) + .addField('bottom', 'SFBool', true) + .addField('side', 'SFBool', true) + .addField('top', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode ('Material', 'Appearance', 'appearance_Material') + .addField({name:'diffuseColor', datatype:'SFColor', defaultValue:[.8,.8,.8], animatable:true}) + .addField({name:'emissiveColor',datatype: 'SFColor', defaultValue:[0,0,0], animatable:true}) + .addField({name:'specularColor', datatype:'SFColor', defaultValue:[0,0,0], animatable:true}) + .addField({name:'transparency', datatype:'SFFloat', defaultValue:'0', animatable:true}) + .addField({name:'shininess', datatype:'SFFloat', defaultValue:'0', animatable:true}) + .addNode(); + +xseen.nodes._defineNode ('Transform', 'Grouping', 'grouping_Transform') + .addField({name:'translation', datatype:'SFVec3f', defaultValue:[0,0,0], animatable:true}) + .addField({name:'scale', datatype:'SFVec3f', defaultValue:[1,1,1], animatable:true}) + .addField({name:'rotation', datatype:'SFRotation', defaultValue:xseen.types.SFRotation('0 1 0 0',''), animatable:true}) + .addNode(); +xseen.nodes._defineNode ('Group', 'Grouping', 'grouping_Transform') + .addNode(); + +xseen.nodes._defineNode ('Light', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) // DirectionalLight + .addField('location', 'SFVec3f', [0,0,0]) // PointLight & SpotLight + .addField('radius', 'SFFloat', '100') // PointLight & SpotLight + .addField('attenuation', 'SFVec3f', [1,0,0]) // PointLight & SpotLight + .addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // SpotLight + .addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // SpotLight + .addField('color', 'SFColor', [1,1,1]) // General + .addField('intensity', 'SFFloat', '1') // General + .addField({name:'type', datatype:'EnumerateString', defaultValue:'Directional', enumerated:['Directional', 'Spot', 'Point'], animatable:true}) + .addNode(); +xseen.nodes._defineNode ('DirectionalLight', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Directional') + .addNode(); +xseen.nodes._defineNode ('PointLight', 'Lighting', 'lighting_Light') + .addField('location', 'SFVec3f', [0,0,0]) + .addField('radius', 'SFFloat', '100') + .addField('attenuation', 'SFVec3f', [1,0,0]) + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Point') + .addNode(); +xseen.nodes._defineNode ('SpotLight', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) + .addField('radius', 'SFFloat', '100') + .addField('attenuation', 'SFVec3f', [1,0,0]) + .addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // pi/4 + .addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // pi/2 + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Spot') + .addNode(); + +xseen.nodes._defineNode ('Viewpoint', 'Controls', 'unk_Viewpoint') + .addField('position', 'SFVec3f', '0 0 10') + .addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) + .addField('description', 'SFString', '') + .addField({name:'cameratype', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) + .addField({name:'type', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) + .addField({name:'motion', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'turntable', 'tilt'], animatable:false}) + .addField('motionspeed', 'SFFloat', 16) + .addField('active', 'SFBool', true) // incoming event + .addNode(); +xseen.nodes._defineNode ('NavigationMode', 'Controls', 'controls_Navigation') + .addField('speed', 'SFFloat', 1.) + .addField({name:'type', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'orbit', 'fly', 'examine', 'trackball'], animatable:false}) + .addNode(); +xseen.nodes._defineNode ('Camera', 'Controls', 'unk_Viewpoint') + .addField('position', 'SFVec3f', [0,0,10]) + .addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) + .addNode(); + +xseen.nodes._defineNode ('Inline', 'Networking', 'networking_Inline') + .addField('url', 'SFString', '') + .addNode(); + +xseen.nodes._defineNode ('scene', 'Core', 'core_Scene') + .addNode(); +xseen.nodes._defineNode ('canvas', 'Core', 'core_NOOP') + .addNode(); +xseen.nodes._defineNode ('WorldInfo', 'Core', 'core_WorldInfo') + .addNode(); +xseen.nodes._defineNode ('Appearance', 'Appearance', 'appearance_Appearance') + .addNode(); +xseen.nodes._defineNode ('ImageTexture', 'Appearance', 'appearance_ImageTexture') + .addField('url', 'SFString', '') + .addField('repeatS', 'SFBool', true) + .addField('repeatT', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode ('Shape', 'Shape', 'unk_Shape') + .addNode(); +xseen.nodes._defineNode('Background', 'Environmental', 'env_Background') + .addField('skyColor', 'SFColor', [0,0,0]) + .addField('srcFront', 'SFString', '') + .addField('srcBack', 'SFString', '') + .addField('srcTop', 'SFString', '') + .addField('srcBottom', 'SFString', '') + .addField('srcLeft', 'SFString', '') + .addField('srcRight', 'SFString', '') + .addField('backgroundIsCube', 'SFBool', 'true') + .addNode(); + +xseen.nodes._defineNode('TriangleSet', 'Geometry', 'geometry_TriangleSet') + .addField('ccw', 'SFBool', 'true') + .addField('colorPerVertex', 'SFBool', 'true') + .addField('solid', 'SFBool', 'true') + .addNode(); +xseen.nodes._defineNode('IndexedTriangleSet', 'Geometry', 'geometry_IndexedTriangleSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('index', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('Coordinate', 'Geometry', 'geometry_Coordinate') + .addField('point', 'MFVec3f', []) + .addNode(); +xseen.nodes._defineNode('Normal', 'Geometry', 'geometry_Normal') + .addField('vector', 'MFVec3f', []) + .addNode(); +xseen.nodes._defineNode('Color', 'Geometry', 'geometry_Color') + .addField('color', 'MFColor', []) + .addNode(); +xseen.nodes._defineNode('IndexedFaceSet', 'Geometry', 'geometry_IndexedFaceSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('coordIndex', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('IndexedQuadSet', 'Geometry', 'geometry_IndexedQuadSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('index', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('QuadSet', 'Geometry', 'geometry_QuadSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addNode(); + +//xseen.nodes._dumpTable(); +// File: nodes/_Definitions-xseen.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen.nodes._defineNode('model', 'XSeen', 'x_Model') + .addField('src', 'SFString', '') + .addField('playonload', 'SFString', '') + .addField('duration', 'SFFloat', '-1') + .addNode(); + +xseen.nodes._defineNode('animate', 'XSeen', 'x_Animate') + .addField('field', 'SFString', '') + .addField('to', 'MFFloat', '') // Needs to be 'field' datatype. That is not known until node-parse. For now insist on numeric array + .addField('delay', 'SFTime', 0) + .addField('duration', 'SFTime', 0) + .addField('repeat', 'SFInt', 0) + .addField({name:'interpolator', datatype:'EnumerateString', defaultValue:'position', enumerated:['position', 'rotation', 'color'], animatable:false}) + .addField({name:'Easing', datatype:'EnumerateString', defaultValue:'', enumerated:['', 'in', 'out', 'inout'], animatable:false}) + .addField({name:'EasingType', datatype:'EnumerateString', defaultValue:'linear', enumerated:['linear', 'quadratic', 'sinusoidal', 'exponential', 'elastic', 'bounce'], animatable:false}) + .addField('start', 'SFBool', true) // incoming event, need to set timer trigger + .addField('stop', 'SFBool', true) // incoming event, need to set timer trigger + .addField('resetstart', 'SFBool', true) // incoming event, need to set timer trigger + .addField('pause', 'SFBool', true) // incoming event, need to set timer trigger + .addNode(); + +xseen.nodes._defineNode('route', 'XSeen', 'x_Route') + .addField('source', 'SFString', '') + .addField('event', 'SFString', '') + .addField('destination', 'SFString', '') + .addField('field', 'SFString', '') + .addField('handler', 'SFString', '') + .addNode(); + + +// Dump parse table +//xseen.nodes._dumpTable(); \ No newline at end of file diff --git a/Release/XSeen.0.4.6-rc1+25_0e9dbd1.min.js b/Release/XSeen.0.4.6-rc1+25_0e9dbd1.min.js new file mode 100644 index 0000000..d6670e1 --- /dev/null +++ b/Release/XSeen.0.4.6-rc1+25_0e9dbd1.min.js @@ -0,0 +1,9290 @@ +/* + * XSeen V0.4.6-rc1+25_0e9dbd1 + * Built Tue Jul 11 08:12:44 2017 + * + +Dual licensed under the MIT and GPL licenses. + +==[MIT]==================================================================== +Copyright (c) 2017, Daly Realism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +==[GPL]==================================================================== + +XSeen - Declarative 3D for HTML + +Copyright (C) 2017, Daly Realism + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +=== COPYRIGHT +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (C) 2017, Daly Realism for XSeen +Copyright, Fraunhofer for X3DOM +Copyright, Mozilla for A-Frame +Copyright, THREE and Khronos for various parts of THREE.js +Copyright (C) 2017, John Carlson for JSON->XML converter (JSONParser.js) + +=== +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + */ +THREE.ColladaLoader = function () { +var COLLADA = null; +var scene = null; +var visualScene; +var kinematicsModel; +var readyCallbackFunc = null; +var sources = {}; +var images = {}; +var animations = {}; +var controllers = {}; +var geometries = {}; +var materials = {}; +var effects = {}; +var cameras = {}; +var lights = {}; +var animData; +var kinematics; +var visualScenes; +var kinematicsModels; +var baseUrl; +var morphs; +var skins; +var flip_uv = true; +var preferredShading = THREE.SmoothShading; +var options = { +centerGeometry: false, +convertUpAxis: false, +subdivideFaces: true, +upAxis: 'Y', +defaultEnvMap: null +}; +var colladaUnit = 1.0; +var colladaUp = 'Y'; +var upConversion = null; +function load ( url, readyCallback, progressCallback, failCallback ) { +var length = 0; +if ( document.implementation && document.implementation.createDocument ) { +var request = new XMLHttpRequest(); +request.onreadystatechange = function() { +if ( request.readyState === 4 ) { +if ( request.status === 0 || request.status === 200 ) { +if ( request.response ) { +readyCallbackFunc = readyCallback; +parse( request.response, undefined, url ); +} else { +if ( failCallback ) { +failCallback( { type: 'error', url: url } ); +} else { +console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); +} +} +}else{ +if( failCallback ){ +failCallback( { type: 'error', url: url } ); +}else{ +console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' ); +} +} +} else if ( request.readyState === 3 ) { +if ( progressCallback ) { +if ( length === 0 ) { +length = request.getResponseHeader( "Content-Length" ); +} +progressCallback( { total: length, loaded: request.responseText.length } ); +} +} +}; +request.open( "GET", url, true ); +request.send( null ); +} else { +alert( "Don't know how to parse XML!" ); +} +} +function parse( text, callBack, url ) { +COLLADA = new DOMParser().parseFromString( text, 'text/xml' ); +callBack = callBack || readyCallbackFunc; +if ( url !== undefined ) { +var parts = url.split( '/' ); +parts.pop(); +baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; +} +parseAsset(); +setUpConversion(); +images = parseLib( "library_images image", _Image, "image" ); +materials = parseLib( "library_materials material", Material, "material" ); +effects = parseLib( "library_effects effect", Effect, "effect" ); +geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); +cameras = parseLib( "library_cameras camera", Camera, "camera" ); +lights = parseLib( "library_lights light", Light, "light" ); +controllers = parseLib( "library_controllers controller", Controller, "controller" ); +animations = parseLib( "library_animations animation", Animation, "animation" ); +visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); +kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); +morphs = []; +skins = []; +visualScene = parseScene(); +scene = new THREE.Group(); +for ( var i = 0; i < visualScene.nodes.length; i ++ ) { +scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); +} +scene.scale.multiplyScalar( colladaUnit ); +createAnimations(); +kinematicsModel = parseKinematicsModel(); +createKinematics(); +var result = { +scene: scene, +morphs: morphs, +skins: skins, +animations: animData, +kinematics: kinematics, +dae: { +images: images, +materials: materials, +cameras: cameras, +lights: lights, +effects: effects, +geometries: geometries, +controllers: controllers, +animations: animations, +visualScenes: visualScenes, +visualScene: visualScene, +scene: visualScene, +kinematicsModels: kinematicsModels, +kinematicsModel: kinematicsModel +} +}; +if ( callBack ) { +callBack( result ); +} +return result; +} +function setPreferredShading ( shading ) { +preferredShading = shading; +} +function parseAsset () { +var elements = COLLADA.querySelectorAll('asset'); +var element = elements[0]; +if ( element && element.childNodes ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'unit': +var meter = child.getAttribute( 'meter' ); +if ( meter ) { +colladaUnit = parseFloat( meter ); +} +break; +case 'up_axis': +colladaUp = child.textContent.charAt(0); +break; +} +} +} +} +function parseLib ( q, classSpec, prefix ) { +var elements = COLLADA.querySelectorAll(q); +var lib = {}; +var i = 0; +var elementsLength = elements.length; +for ( var j = 0; j < elementsLength; j ++ ) { +var element = elements[j]; +var daeElement = ( new classSpec() ).parse( element ); +if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); +lib[ daeElement.id ] = daeElement; +} +return lib; +} +function parseScene() { +var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; +if ( sceneElement ) { +var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); +return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; +} else { +return null; +} +} +function parseKinematicsModel() { +var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; +if ( kinematicsModelElement ) { +var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); +return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; +} else { +return null; +} +} +function createAnimations() { +animData = []; +recurseHierarchy( scene ); +} +function recurseHierarchy( node ) { +var n = visualScene.getChildById( node.colladaId, true ), +newData = null; +if ( n && n.keys ) { +newData = { +fps: 60, +hierarchy: [ { +node: n, +keys: n.keys, +sids: n.sids +} ], +node: node, +name: 'animation_' + node.name, +length: 0 +}; +animData.push(newData); +for ( var i = 0, il = n.keys.length; i < il; i ++ ) { +newData.length = Math.max( newData.length, n.keys[i].time ); +} +} else { +newData = { +hierarchy: [ { +keys: [], +sids: [] +} ] +} +} +for ( var i = 0, il = node.children.length; i < il; i ++ ) { +var d = recurseHierarchy( node.children[i] ); +for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { +newData.hierarchy.push( { +keys: [], +sids: [] +} ); +} +} +return newData; +} +function calcAnimationBounds () { +var start = 1000000; +var end = -start; +var frames = 0; +var ID; +for ( var id in animations ) { +var animation = animations[ id ]; +ID = ID || animation.id; +for ( var i = 0; i < animation.sampler.length; i ++ ) { +var sampler = animation.sampler[ i ]; +sampler.create(); +start = Math.min( start, sampler.startTime ); +end = Math.max( end, sampler.endTime ); +frames = Math.max( frames, sampler.input.length ); +} +} +return { start:start, end:end, frames:frames,ID:ID }; +} +function createMorph ( geometry, ctrl ) { +var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; +if ( !morphCtrl || !morphCtrl.morph ) { +console.log("could not find morph controller!"); +return; +} +var morph = morphCtrl.morph; +for ( var i = 0; i < morph.targets.length; i ++ ) { +var target_id = morph.targets[ i ]; +var daeGeometry = geometries[ target_id ]; +if ( !daeGeometry.mesh || +!daeGeometry.mesh.primitives || +!daeGeometry.mesh.primitives.length ) { +continue; +} +var target = daeGeometry.mesh.primitives[ 0 ].geometry; +if ( target.vertices.length === geometry.vertices.length ) { +geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); +} +} +geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); +} +function createSkin ( geometry, ctrl, applyBindShape ) { +var skinCtrl = controllers[ ctrl.url ]; +if ( !skinCtrl || !skinCtrl.skin ) { +console.log( "could not find skin controller!" ); +return; +} +if ( !ctrl.skeleton || !ctrl.skeleton.length ) { +console.log( "could not find the skeleton for the skin!" ); +return; +} +var skin = skinCtrl.skin; +var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); +var hierarchy = []; +applyBindShape = applyBindShape !== undefined ? applyBindShape : true; +var bones = []; +geometry.skinWeights = []; +geometry.skinIndices = []; +if ( applyBindShape ) { +for ( var i = 0; i < geometry.vertices.length; i ++ ) { +geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); +} +} +} +function setupSkeleton ( node, bones, frame, parent ) { +node.world = node.world || new THREE.Matrix4(); +node.localworld = node.localworld || new THREE.Matrix4(); +node.world.copy( node.matrix ); +node.localworld.copy( node.matrix ); +if ( node.channels && node.channels.length ) { +var channel = node.channels[ 0 ]; +var m = channel.sampler.output[ frame ]; +if ( m instanceof THREE.Matrix4 ) { +node.world.copy( m ); +node.localworld.copy(m); +if (frame === 0) +node.matrix.copy(m); +} +} +if ( parent ) { +node.world.multiplyMatrices( parent, node.world ); +} +bones.push( node ); +for ( var i = 0; i < node.nodes.length; i ++ ) { +setupSkeleton( node.nodes[ i ], bones, frame, node.world ); +} +} +function setupSkinningMatrices ( bones, skin ) { +for ( var i = 0; i < bones.length; i ++ ) { +var bone = bones[ i ]; +var found = -1; +if ( bone.type != 'JOINT' ) continue; +for ( var j = 0; j < skin.joints.length; j ++ ) { +if ( bone.sid === skin.joints[ j ] ) { +found = j; +break; +} +} +if ( found >= 0 ) { +var inv = skin.invBindMatrices[ found ]; +bone.invBindMatrix = inv; +bone.skinningMatrix = new THREE.Matrix4(); +bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) +bone.animatrix = new THREE.Matrix4(); +bone.animatrix.copy(bone.localworld); +bone.weights = []; +for ( var j = 0; j < skin.weights.length; j ++ ) { +for (var k = 0; k < skin.weights[ j ].length; k ++ ) { +var w = skin.weights[ j ][ k ]; +if ( w.joint === found ) { +bone.weights.push( w ); +} +} +} +} else { +console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); +bone.skinningMatrix = new THREE.Matrix4(); +bone.weights = []; +} +} +} +function flattenSkeleton(skeleton) { +var list = []; +var walk = function(parentid, node, list) { +var bone = {}; +bone.name = node.sid; +bone.parent = parentid; +bone.matrix = node.matrix; +var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; +bone.matrix.decompose(data[0], data[1], data[2]); +bone.pos = [ data[0].x,data[0].y,data[0].z ]; +bone.scl = [ data[2].x,data[2].y,data[2].z ]; +bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; +list.push(bone); +for (var i in node.nodes) { +walk(node.sid, node.nodes[i], list); +} +}; +walk(-1, skeleton, list); +return list; +} +function skinToBindPose(geometry,skeleton,skinController) { +var bones = []; +setupSkeleton( skeleton, bones, -1 ); +setupSkinningMatrices( bones, skinController.skin ); +var v = new THREE.Vector3(); +var skinned = []; +for (var i = 0; i < geometry.vertices.length; i ++) { +skinned.push(new THREE.Vector3()); +} +for ( i = 0; i < bones.length; i ++ ) { +if ( bones[ i ].type != 'JOINT' ) continue; +for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { +var w = bones[ i ].weights[ j ]; +var vidx = w.index; +var weight = w.weight; +var o = geometry.vertices[vidx]; +var s = skinned[vidx]; +v.x = o.x; +v.y = o.y; +v.z = o.z; +v.applyMatrix4( bones[i].skinningMatrix ); +s.x += (v.x * weight); +s.y += (v.y * weight); +s.z += (v.z * weight); +} +} +for (var i = 0; i < geometry.vertices.length; i ++) { +geometry.vertices[i] = skinned[i]; +} +} +function applySkin ( geometry, instanceCtrl, frame ) { +var skinController = controllers[ instanceCtrl.url ]; +frame = frame !== undefined ? frame : 40; +if ( !skinController || !skinController.skin ) { +console.log( 'ColladaLoader: Could not find skin controller.' ); +return; +} +if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { +console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); +return; +} +var animationBounds = calcAnimationBounds(); +var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); +var bonelist = flattenSkeleton(skeleton); +var joints = skinController.skin.joints; +var sortedbones = []; +for (var i = 0; i < joints.length; i ++) { +for (var j = 0; j < bonelist.length; j ++) { +if (bonelist[j].name === joints[i]) { +sortedbones[i] = bonelist[j]; +} +} +} +for (var i = 0; i < sortedbones.length; i ++) { +for (var j = 0; j < sortedbones.length; j ++) { +if (sortedbones[i].parent === sortedbones[j].name) { +sortedbones[i].parent = j; +} +} +} +var i, j, w, vidx, weight; +var v = new THREE.Vector3(), o, s; +for ( i = 0; i < geometry.vertices.length; i ++ ) { +geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); +} +var skinIndices = []; +var skinWeights = []; +var weights = skinController.skin.weights; +for ( var i =0; i < weights.length; i ++ ) { +var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); +var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); +skinIndices.push(indicies); +skinWeights.push(weight); +} +geometry.skinIndices = skinIndices; +geometry.skinWeights = skinWeights; +geometry.bones = sortedbones; +var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; +for (var j = 0; j < sortedbones.length; j ++) { +animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); +} +console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); +skinToBindPose(geometry, skeleton, skinController); +for ( frame = 0; frame < animationBounds.frames; frame ++ ) { +var bones = []; +var skinned = []; +setupSkeleton( skeleton, bones, frame ); +setupSkinningMatrices( bones, skinController.skin ); +for (var i = 0; i < bones.length; i ++) { +for (var j = 0; j < animationdata.hierarchy.length; j ++) { +if (animationdata.hierarchy[j].name === bones[i].sid) { +var key = {}; +key.time = (frame / 30); +key.matrix = bones[i].animatrix; +if (frame === 0) +bones[i].matrix = key.matrix; +var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; +key.matrix.decompose(data[0], data[1], data[2]); +key.pos = [ data[0].x,data[0].y,data[0].z ]; +key.scl = [ data[2].x,data[2].y,data[2].z ]; +key.rot = data[1]; +animationdata.hierarchy[j].keys.push(key); +} +} +} +geometry.animation = animationdata; +} +} +function createKinematics() { +if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { +kinematics = undefined; +return; +} +var jointMap = {}; +var _addToMap = function( jointIndex, parentVisualElement ) { +var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); +var colladaNode = visualScene.getChildById( parentVisualElementId, true ); +var joint = kinematicsModel.joints[ jointIndex ]; +scene.traverse(function( node ) { +if ( node.colladaId == parentVisualElementId ) { +jointMap[ jointIndex ] = { +node: node, +transforms: colladaNode.transforms, +joint: joint, +position: joint.zeroPosition +}; +} +}); +}; +kinematics = { +joints: kinematicsModel && kinematicsModel.joints, +getJointValue: function( jointIndex ) { +var jointData = jointMap[ jointIndex ]; +if ( jointData ) { +return jointData.position; +} else { +console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); +} +}, +setJointValue: function( jointIndex, value ) { +var jointData = jointMap[ jointIndex ]; +if ( jointData ) { +var joint = jointData.joint; +if ( value > joint.limits.max || value < joint.limits.min ) { +console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); +} else if ( joint.static ) { +console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); +} else { +var threejsNode = jointData.node; +var axis = joint.axis; +var transforms = jointData.transforms; +var matrix = new THREE.Matrix4(); +var m1 = new THREE.Matrix4(); +for (i = 0; i < transforms.length; i ++ ) { +var transform = transforms[ i ]; +if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { +switch ( joint.type ) { +case 'revolute': +matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); +break; +case 'prismatic': +matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); +break; +default: +console.warn( 'setJointValue: unknown joint type: ' + joint.type ); +break; +} +} else { +switch ( transform.type ) { +case 'matrix': +matrix.multiply( transform.obj ); +break; +case 'translate': +matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); +break; +case 'rotate': +matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); +break; +} +} +} +var elementsFloat32Arr = matrix.elements; +var elements = Array.prototype.slice.call( elementsFloat32Arr ); +var elementsRowMajor = [ +elements[ 0 ], +elements[ 4 ], +elements[ 8 ], +elements[ 12 ], +elements[ 1 ], +elements[ 5 ], +elements[ 9 ], +elements[ 13 ], +elements[ 2 ], +elements[ 6 ], +elements[ 10 ], +elements[ 14 ], +elements[ 3 ], +elements[ 7 ], +elements[ 11 ], +elements[ 15 ] +]; +threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); +threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); +jointMap[ jointIndex ].position = value; +} +} else { +console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); +} +} +}; +var element = COLLADA.querySelector('scene instance_kinematics_scene'); +if ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bind_joint_axis': +var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); +var axis = child.querySelector('axis param').textContent; +var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); +var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); +if ( visualTargetElement ) { +var parentVisualElement = visualTargetElement.parentElement; +_addToMap(jointIndex, parentVisualElement); +} +break; +default: +break; +} +} +} +} +function createSceneGraph ( node, parent ) { +var obj = new THREE.Object3D(); +var skinned = false; +var skinController; +var morphController; +var i, j; +for ( i = 0; i < node.controllers.length; i ++ ) { +var controller = controllers[ node.controllers[ i ].url ]; +switch ( controller.type ) { +case 'skin': +if ( geometries[ controller.skin.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = controller.skin.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +skinned = true; +skinController = node.controllers[ i ]; +} else if ( controllers[ controller.skin.source ] ) { +var second = controllers[ controller.skin.source ]; +morphController = second; +if ( second.morph && geometries[ second.morph.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = second.morph.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +} +} +break; +case 'morph': +if ( geometries[ controller.morph.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = controller.morph.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +morphController = node.controllers[ i ]; +} +console.log( 'ColladaLoader: Morph-controller partially supported.' ); +default: +break; +} +} +var double_sided_materials = {}; +for ( i = 0; i < node.geometries.length; i ++ ) { +var instance_geometry = node.geometries[i]; +var instance_materials = instance_geometry.instance_material; +var geometry = geometries[ instance_geometry.url ]; +var used_materials = {}; +var used_materials_array = []; +var num_materials = 0; +var first_material; +if ( geometry ) { +if ( !geometry.mesh || !geometry.mesh.primitives ) +continue; +if ( obj.name.length === 0 ) { +obj.name = geometry.id; +} +if ( instance_materials ) { +for ( j = 0; j < instance_materials.length; j ++ ) { +var instance_material = instance_materials[ j ]; +var mat = materials[ instance_material.target ]; +var effect_id = mat.instance_effect.url; +var shader = effects[ effect_id ].shader; +var material3js = shader.material; +if ( geometry.doubleSided ) { +if ( !( instance_material.symbol in double_sided_materials ) ) { +var _copied_material = material3js.clone(); +_copied_material.side = THREE.DoubleSide; +double_sided_materials[ instance_material.symbol ] = _copied_material; +} +material3js = double_sided_materials[ instance_material.symbol ]; +} +material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; +used_materials[ instance_material.symbol ] = num_materials; +used_materials_array.push( material3js ); +first_material = material3js; +first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; +num_materials ++; +} +} +var mesh; +var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); +var geom = geometry.mesh.geometry3js; +if ( num_materials > 1 ) { +material = new THREE.MultiMaterial( used_materials_array ); +for ( j = 0; j < geom.faces.length; j ++ ) { +var face = geom.faces[ j ]; +face.materialIndex = used_materials[ face.daeMaterial ] +} +} +if ( skinController !== undefined ) { +applySkin( geom, skinController ); +if ( geom.morphTargets.length > 0 ) { +material.morphTargets = true; +material.skinning = false; +} else { +material.morphTargets = false; +material.skinning = true; +} +mesh = new THREE.SkinnedMesh( geom, material, false ); +mesh.name = 'skin_' + skins.length; +skins.push( mesh ); +} else if ( morphController !== undefined ) { +createMorph( geom, morphController ); +material.morphTargets = true; +mesh = new THREE.Mesh( geom, material ); +mesh.name = 'morph_' + morphs.length; +morphs.push( mesh ); +} else { +if ( geom.isLineStrip === true ) { +mesh = new THREE.Line( geom ); +} else { +mesh = new THREE.Mesh( geom, material ); +} +} +obj.add(mesh); +} +} +for ( i = 0; i < node.cameras.length; i ++ ) { +var instance_camera = node.cameras[i]; +var cparams = cameras[instance_camera.url]; +var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), +parseFloat(cparams.znear), parseFloat(cparams.zfar)); +obj.add(cam); +} +for ( i = 0; i < node.lights.length; i ++ ) { +var light = null; +var instance_light = node.lights[i]; +var lparams = lights[instance_light.url]; +if ( lparams && lparams.technique ) { +var color = lparams.color.getHex(); +var intensity = lparams.intensity; +var distance = lparams.distance; +var angle = lparams.falloff_angle; +switch ( lparams.technique ) { +case 'directional': +light = new THREE.DirectionalLight( color, intensity, distance ); +light.position.set(0, 0, 1); +break; +case 'point': +light = new THREE.PointLight( color, intensity, distance ); +break; +case 'spot': +light = new THREE.SpotLight( color, intensity, distance, angle ); +light.position.set(0, 0, 1); +break; +case 'ambient': +light = new THREE.AmbientLight( color ); +break; +} +} +if (light) { +obj.add(light); +} +} +obj.name = node.name || node.id || ""; +obj.colladaId = node.id || ""; +obj.layer = node.layer || ""; +obj.matrix = node.matrix; +obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); +if ( options.centerGeometry && obj.geometry ) { +var delta = obj.geometry.center(); +delta.multiply( obj.scale ); +delta.applyQuaternion( obj.quaternion ); +obj.position.sub( delta ); +} +for ( i = 0; i < node.nodes.length; i ++ ) { +obj.add( createSceneGraph( node.nodes[i], node ) ); +} +return obj; +} +function getJointId( skin, id ) { +for ( var i = 0; i < skin.joints.length; i ++ ) { +if ( skin.joints[ i ] === id ) { +return i; +} +} +} +function getLibraryNode( id ) { +var nodes = COLLADA.querySelectorAll('library_nodes node'); +for ( var i = 0; i < nodes.length; i++ ) { +var attObj = nodes[i].attributes.getNamedItem('id'); +if ( attObj && attObj.value === id ) { +return nodes[i]; +} +} +return undefined; +} +function getChannelsForNode ( node ) { +var channels = []; +var startTime = 1000000; +var endTime = -1000000; +for ( var id in animations ) { +var animation = animations[id]; +for ( var i = 0; i < animation.channel.length; i ++ ) { +var channel = animation.channel[i]; +var sampler = animation.sampler[i]; +var id = channel.target.split('/')[0]; +if ( id == node.id ) { +sampler.create(); +channel.sampler = sampler; +startTime = Math.min(startTime, sampler.startTime); +endTime = Math.max(endTime, sampler.endTime); +channels.push(channel); +} +} +} +if ( channels.length ) { +node.startTime = startTime; +node.endTime = endTime; +} +return channels; +} +function calcFrameDuration( node ) { +var minT = 10000000; +for ( var i = 0; i < node.channels.length; i ++ ) { +var sampler = node.channels[i].sampler; +for ( var j = 0; j < sampler.input.length - 1; j ++ ) { +var t0 = sampler.input[ j ]; +var t1 = sampler.input[ j + 1 ]; +minT = Math.min( minT, t1 - t0 ); +} +} +return minT; +} +function calcMatrixAt( node, t ) { +var animated = {}; +var i, j; +for ( i = 0; i < node.channels.length; i ++ ) { +var channel = node.channels[ i ]; +animated[ channel.sid ] = channel; +} +var matrix = new THREE.Matrix4(); +for ( i = 0; i < node.transforms.length; i ++ ) { +var transform = node.transforms[ i ]; +var channel = animated[ transform.sid ]; +if ( channel !== undefined ) { +var sampler = channel.sampler; +var value; +for ( j = 0; j < sampler.input.length - 1; j ++ ) { +if ( sampler.input[ j + 1 ] > t ) { +value = sampler.output[ j ]; +break; +} +} +if ( value !== undefined ) { +if ( value instanceof THREE.Matrix4 ) { +matrix.multiplyMatrices( matrix, value ); +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} +return matrix; +} +function bakeAnimations ( node ) { +if ( node.channels && node.channels.length ) { +var keys = [], +sids = []; +for ( var i = 0, il = node.channels.length; i < il; i ++ ) { +var channel = node.channels[i], +fullSid = channel.fullSid, +sampler = channel.sampler, +input = sampler.input, +transform = node.getTransformBySid( channel.sid ), +member; +if ( channel.arrIndices ) { +member = []; +for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { +member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); +} +} else { +member = getConvertedMember( channel.member ); +} +if ( transform ) { +if ( sids.indexOf( fullSid ) === -1 ) { +sids.push( fullSid ); +} +for ( var j = 0, jl = input.length; j < jl; j ++ ) { +var time = input[j], +data = sampler.getData( transform.type, j, member ), +key = findKey( keys, time ); +if ( !key ) { +key = new Key( time ); +var timeNdx = findTimeNdx( keys, time ); +keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); +} +key.addTarget( fullSid, transform, member, data ); +} +} else { +console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); +} +} +for ( var i = 0; i < sids.length; i ++ ) { +var sid = sids[ i ]; +for ( var j = 0; j < keys.length; j ++ ) { +var key = keys[ j ]; +if ( !key.hasTarget( sid ) ) { +interpolateKeys( keys, key, j, sid ); +} +} +} +node.keys = keys; +node.sids = sids; +} +} +function findKey ( keys, time) { +var retVal = null; +for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { +var key = keys[i]; +if ( key.time === time ) { +retVal = key; +} else if ( key.time > time ) { +break; +} +} +return retVal; +} +function findTimeNdx ( keys, time) { +var ndx = -1; +for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { +var key = keys[i]; +if ( key.time >= time ) { +ndx = i; +} +} +return ndx; +} +function interpolateKeys ( keys, key, ndx, fullSid ) { +var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), +nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); +if ( prevKey && nextKey ) { +var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), +prevTarget = prevKey.getTarget( fullSid ), +nextData = nextKey.getTarget( fullSid ).data, +prevData = prevTarget.data, +data; +if ( prevTarget.type === 'matrix' ) { +data = prevData; +} else if ( prevData.length ) { +data = []; +for ( var i = 0; i < prevData.length; ++ i ) { +data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; +} +} else { +data = prevData + ( nextData - prevData ) * scale; +} +key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); +} +} +function getNextKeyWith( keys, fullSid, ndx ) { +for ( ; ndx < keys.length; ndx ++ ) { +var key = keys[ ndx ]; +if ( key.hasTarget( fullSid ) ) { +return key; +} +} +return null; +} +function getPrevKeyWith( keys, fullSid, ndx ) { +ndx = ndx >= 0 ? ndx : ndx + keys.length; +for ( ; ndx >= 0; ndx -- ) { +var key = keys[ ndx ]; +if ( key.hasTarget( fullSid ) ) { +return key; +} +} +return null; +} +function _Image() { +this.id = ""; +this.init_from = ""; +} +_Image.prototype.parse = function(element) { +this.id = element.getAttribute('id'); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeName === 'init_from' ) { +this.init_from = child.textContent; +} +} +return this; +}; +function Controller() { +this.id = ""; +this.name = ""; +this.type = ""; +this.skin = null; +this.morph = null; +} +Controller.prototype.parse = function( element ) { +this.id = element.getAttribute('id'); +this.name = element.getAttribute('name'); +this.type = "none"; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'skin': +this.skin = (new Skin()).parse(child); +this.type = child.nodeName; +break; +case 'morph': +this.morph = (new Morph()).parse(child); +this.type = child.nodeName; +break; +default: +break; +} +} +return this; +}; +function Morph() { +this.method = null; +this.source = null; +this.targets = null; +this.weights = null; +} +Morph.prototype.parse = function( element ) { +var sources = {}; +var inputs = []; +var i; +this.method = element.getAttribute( 'method' ); +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +for ( i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'source': +var source = ( new Source() ).parse( child ); +sources[ source.id ] = source; +break; +case 'targets': +inputs = this.parseInputs( child ); +break; +default: +console.log( child.nodeName ); +break; +} +} +for ( i = 0; i < inputs.length; i ++ ) { +var input = inputs[ i ]; +var source = sources[ input.source ]; +switch ( input.semantic ) { +case 'MORPH_TARGET': +this.targets = source.read(); +break; +case 'MORPH_WEIGHT': +this.weights = source.read(); +break; +default: +break; +} +} +return this; +}; +Morph.prototype.parseInputs = function(element) { +var inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1) continue; +switch ( child.nodeName ) { +case 'input': +inputs.push( (new Input()).parse(child) ); +break; +default: +break; +} +} +return inputs; +}; +function Skin() { +this.source = ""; +this.bindShapeMatrix = null; +this.invBindMatrices = []; +this.joints = []; +this.weights = []; +} +Skin.prototype.parse = function( element ) { +var sources = {}; +var joints, weights; +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +this.invBindMatrices = []; +this.joints = []; +this.weights = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bind_shape_matrix': +var f = _floats(child.textContent); +this.bindShapeMatrix = getConvertedMat4( f ); +break; +case 'source': +var src = new Source().parse(child); +sources[ src.id ] = src; +break; +case 'joints': +joints = child; +break; +case 'vertex_weights': +weights = child; +break; +default: +console.log( child.nodeName ); +break; +} +} +this.parseJoints( joints, sources ); +this.parseWeights( weights, sources ); +return this; +}; +Skin.prototype.parseJoints = function ( element, sources ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +var input = ( new Input() ).parse( child ); +var source = sources[ input.source ]; +if ( input.semantic === 'JOINT' ) { +this.joints = source.read(); +} else if ( input.semantic === 'INV_BIND_MATRIX' ) { +this.invBindMatrices = source.read(); +} +break; +default: +break; +} +} +}; +Skin.prototype.parseWeights = function ( element, sources ) { +var v, vcount, inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +inputs.push( ( new Input() ).parse( child ) ); +break; +case 'v': +v = _ints( child.textContent ); +break; +case 'vcount': +vcount = _ints( child.textContent ); +break; +default: +break; +} +} +var index = 0; +for ( var i = 0; i < vcount.length; i ++ ) { +var numBones = vcount[i]; +var vertex_weights = []; +for ( var j = 0; j < numBones; j ++ ) { +var influence = {}; +for ( var k = 0; k < inputs.length; k ++ ) { +var input = inputs[ k ]; +var value = v[ index + input.offset ]; +switch ( input.semantic ) { +case 'JOINT': +influence.joint = value;//this.joints[value]; +break; +case 'WEIGHT': +influence.weight = sources[ input.source ].data[ value ]; +break; +default: +break; +} +} +vertex_weights.push( influence ); +index += inputs.length; +} +for ( var j = 0; j < vertex_weights.length; j ++ ) { +vertex_weights[ j ].index = i; +} +this.weights.push( vertex_weights ); +} +}; +function VisualScene () { +this.id = ""; +this.name = ""; +this.nodes = []; +this.scene = new THREE.Group(); +} +VisualScene.prototype.getChildById = function( id, recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var node = this.nodes[ i ].getChildById( id, recursive ); +if ( node ) { +return node; +} +} +return null; +}; +VisualScene.prototype.getChildBySid = function( sid, recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var node = this.nodes[ i ].getChildBySid( sid, recursive ); +if ( node ) { +return node; +} +} +return null; +}; +VisualScene.prototype.parse = function( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +this.nodes = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'node': +this.nodes.push( ( new Node() ).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +function Node() { +this.id = ""; +this.name = ""; +this.sid = ""; +this.nodes = []; +this.controllers = []; +this.transforms = []; +this.geometries = []; +this.channels = []; +this.matrix = new THREE.Matrix4(); +} +Node.prototype.getChannelForTransform = function( transformSid ) { +for ( var i = 0; i < this.channels.length; i ++ ) { +var channel = this.channels[i]; +var parts = channel.target.split('/'); +var id = parts.shift(); +var sid = parts.shift(); +var dotSyntax = (sid.indexOf(".") >= 0); +var arrSyntax = (sid.indexOf("(") >= 0); +var arrIndices; +var member; +if ( dotSyntax ) { +parts = sid.split("."); +sid = parts.shift(); +member = parts.shift(); +} else if ( arrSyntax ) { +arrIndices = sid.split("("); +sid = arrIndices.shift(); +for ( var j = 0; j < arrIndices.length; j ++ ) { +arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); +} +} +if ( sid === transformSid ) { +channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; +return channel; +} +} +return null; +}; +Node.prototype.getChildById = function ( id, recursive ) { +if ( this.id === id ) { +return this; +} +if ( recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var n = this.nodes[ i ].getChildById( id, recursive ); +if ( n ) { +return n; +} +} +} +return null; +}; +Node.prototype.getChildBySid = function ( sid, recursive ) { +if ( this.sid === sid ) { +return this; +} +if ( recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var n = this.nodes[ i ].getChildBySid( sid, recursive ); +if ( n ) { +return n; +} +} +} +return null; +}; +Node.prototype.getTransformBySid = function ( sid ) { +for ( var i = 0; i < this.transforms.length; i ++ ) { +if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; +} +return null; +}; +Node.prototype.parse = function( element ) { +var url; +this.id = element.getAttribute('id'); +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.type = element.getAttribute('type'); +this.layer = element.getAttribute('layer'); +this.type = this.type === 'JOINT' ? this.type : 'NODE'; +this.nodes = []; +this.transforms = []; +this.geometries = []; +this.cameras = []; +this.lights = []; +this.controllers = []; +this.matrix = new THREE.Matrix4(); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'node': +this.nodes.push( ( new Node() ).parse( child ) ); +break; +case 'instance_camera': +this.cameras.push( ( new InstanceCamera() ).parse( child ) ); +break; +case 'instance_controller': +this.controllers.push( ( new InstanceController() ).parse( child ) ); +break; +case 'instance_geometry': +this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); +break; +case 'instance_light': +this.lights.push( ( new InstanceLight() ).parse( child ) ); +break; +case 'instance_node': +url = child.getAttribute( 'url' ).replace( /^#/, '' ); +var iNode = getLibraryNode( url ); +if ( iNode ) { +this.nodes.push( ( new Node() ).parse( iNode )) ; +} +break; +case 'rotate': +case 'translate': +case 'scale': +case 'matrix': +case 'lookat': +case 'skew': +this.transforms.push( ( new Transform() ).parse( child ) ); +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +this.channels = getChannelsForNode( this ); +bakeAnimations( this ); +this.updateMatrix(); +return this; +}; +Node.prototype.updateMatrix = function () { +this.matrix.identity(); +for ( var i = 0; i < this.transforms.length; i ++ ) { +this.transforms[ i ].apply( this.matrix ); +} +}; +function Transform () { +this.sid = ""; +this.type = ""; +this.data = []; +this.obj = null; +} +Transform.prototype.parse = function ( element ) { +this.sid = element.getAttribute( 'sid' ); +this.type = element.nodeName; +this.data = _floats( element.textContent ); +this.convert(); +return this; +}; +Transform.prototype.convert = function () { +switch ( this.type ) { +case 'matrix': +this.obj = getConvertedMat4( this.data ); +break; +case 'rotate': +this.angle = THREE.Math.degToRad( this.data[3] ); +case 'translate': +fixCoords( this.data, -1 ); +this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); +break; +case 'scale': +fixCoords( this.data, 1 ); +this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); +break; +default: +console.log( 'Can not convert Transform of type ' + this.type ); +break; +} +}; +Transform.prototype.apply = function () { +var m1 = new THREE.Matrix4(); +return function ( matrix ) { +switch ( this.type ) { +case 'matrix': +matrix.multiply( this.obj ); +break; +case 'translate': +matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); +break; +case 'rotate': +matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); +break; +case 'scale': +matrix.scale( this.obj ); +break; +} +}; +}(); +Transform.prototype.update = function ( data, member ) { +var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; +switch ( this.type ) { +case 'matrix': +if ( ! member ) { +this.obj.copy( data ); +} else if ( member.length === 1 ) { +switch ( member[ 0 ] ) { +case 0: +this.obj.n11 = data[ 0 ]; +this.obj.n21 = data[ 1 ]; +this.obj.n31 = data[ 2 ]; +this.obj.n41 = data[ 3 ]; +break; +case 1: +this.obj.n12 = data[ 0 ]; +this.obj.n22 = data[ 1 ]; +this.obj.n32 = data[ 2 ]; +this.obj.n42 = data[ 3 ]; +break; +case 2: +this.obj.n13 = data[ 0 ]; +this.obj.n23 = data[ 1 ]; +this.obj.n33 = data[ 2 ]; +this.obj.n43 = data[ 3 ]; +break; +case 3: +this.obj.n14 = data[ 0 ]; +this.obj.n24 = data[ 1 ]; +this.obj.n34 = data[ 2 ]; +this.obj.n44 = data[ 3 ]; +break; +} +} else if ( member.length === 2 ) { +var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); +this.obj[ propName ] = data; +} else { +console.log('Incorrect addressing of matrix in transform.'); +} +break; +case 'translate': +case 'scale': +if ( Object.prototype.toString.call( member ) === '[object Array]' ) { +member = members[ member[ 0 ] ]; +} +switch ( member ) { +case 'X': +this.obj.x = data; +break; +case 'Y': +this.obj.y = data; +break; +case 'Z': +this.obj.z = data; +break; +default: +this.obj.x = data[ 0 ]; +this.obj.y = data[ 1 ]; +this.obj.z = data[ 2 ]; +break; +} +break; +case 'rotate': +if ( Object.prototype.toString.call( member ) === '[object Array]' ) { +member = members[ member[ 0 ] ]; +} +switch ( member ) { +case 'X': +this.obj.x = data; +break; +case 'Y': +this.obj.y = data; +break; +case 'Z': +this.obj.z = data; +break; +case 'ANGLE': +this.angle = THREE.Math.degToRad( data ); +break; +default: +this.obj.x = data[ 0 ]; +this.obj.y = data[ 1 ]; +this.obj.z = data[ 2 ]; +this.angle = THREE.Math.degToRad( data[ 3 ] ); +break; +} +break; +} +}; +function InstanceController() { +this.url = ""; +this.skeleton = []; +this.instance_material = []; +} +InstanceController.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +this.skeleton = []; +this.instance_material = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType !== 1 ) continue; +switch ( child.nodeName ) { +case 'skeleton': +this.skeleton.push( child.textContent.replace(/^#/, '') ); +break; +case 'bind_material': +var instances = child.querySelectorAll('instance_material'); +for ( var j = 0; j < instances.length; j ++ ) { +var instance = instances[j]; +this.instance_material.push( (new InstanceMaterial()).parse(instance) ); +} +break; +case 'extra': +break; +default: +break; +} +} +return this; +}; +function InstanceMaterial () { +this.symbol = ""; +this.target = ""; +} +InstanceMaterial.prototype.parse = function ( element ) { +this.symbol = element.getAttribute('symbol'); +this.target = element.getAttribute('target').replace(/^#/, ''); +return this; +}; +function InstanceGeometry() { +this.url = ""; +this.instance_material = []; +} +InstanceGeometry.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +this.instance_material = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +if ( child.nodeName === 'bind_material' ) { +var instances = child.querySelectorAll('instance_material'); +for ( var j = 0; j < instances.length; j ++ ) { +var instance = instances[j]; +this.instance_material.push( (new InstanceMaterial()).parse(instance) ); +} +break; +} +} +return this; +}; +function Geometry() { +this.id = ""; +this.mesh = null; +} +Geometry.prototype.parse = function ( element ) { +this.id = element.getAttribute('id'); +extractDoubleSided( this, element ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +switch ( child.nodeName ) { +case 'mesh': +this.mesh = (new Mesh(this)).parse(child); +break; +case 'extra': +break; +default: +break; +} +} +return this; +}; +function Mesh( geometry ) { +this.geometry = geometry.id; +this.primitives = []; +this.vertices = null; +this.geometry3js = null; +} +Mesh.prototype.parse = function ( element ) { +this.primitives = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'source': +_source( child ); +break; +case 'vertices': +this.vertices = ( new Vertices() ).parse( child ); +break; +case 'linestrips': +this.primitives.push( ( new LineStrips().parse( child ) ) ); +break; +case 'triangles': +this.primitives.push( ( new Triangles().parse( child ) ) ); +break; +case 'polygons': +this.primitives.push( ( new Polygons().parse( child ) ) ); +break; +case 'polylist': +this.primitives.push( ( new Polylist().parse( child ) ) ); +break; +default: +break; +} +} +this.geometry3js = new THREE.Geometry(); +if ( this.vertices === null ) { +return this; +} +var vertexData = sources[ this.vertices.input['POSITION'].source ].data; +for ( var i = 0; i < vertexData.length; i += 3 ) { +this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); +} +for ( var i = 0; i < this.primitives.length; i ++ ) { +var primitive = this.primitives[ i ]; +primitive.setVertices( this.vertices ); +this.handlePrimitive( primitive, this.geometry3js ); +} +if ( this.geometry3js.calcNormals ) { +this.geometry3js.computeVertexNormals(); +delete this.geometry3js.calcNormals; +} +return this; +}; +Mesh.prototype.handlePrimitive = function ( primitive, geom ) { +if ( primitive instanceof LineStrips ) { +geom.isLineStrip = true; +return; +} +var j, k, pList = primitive.p, inputs = primitive.inputs; +var input, index, idx32; +var source, numParams; +var vcIndex = 0, vcount = 3, maxOffset = 0; +var texture_sets = []; +for ( j = 0; j < inputs.length; j ++ ) { +input = inputs[ j ]; +var offset = input.offset + 1; +maxOffset = (maxOffset < offset) ? offset : maxOffset; +switch ( input.semantic ) { +case 'TEXCOORD': +texture_sets.push( input.set ); +break; +} +} +for ( var pCount = 0; pCount < pList.length; ++ pCount ) { +var p = pList[ pCount ], i = 0; +while ( i < p.length ) { +var vs = []; +var ns = []; +var ts = null; +var cs = []; +if ( primitive.vcount ) { +vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; +} else { +vcount = p.length / maxOffset; +} +for ( j = 0; j < vcount; j ++ ) { +for ( k = 0; k < inputs.length; k ++ ) { +input = inputs[ k ]; +source = sources[ input.source ]; +index = p[ i + ( j * maxOffset ) + input.offset ]; +numParams = source.accessor.params.length; +idx32 = index * numParams; +switch ( input.semantic ) { +case 'VERTEX': +vs.push( index ); +break; +case 'NORMAL': +ns.push( getConvertedVec3( source.data, idx32 ) ); +break; +case 'TEXCOORD': +ts = ts || { }; +if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; +ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); +break; +case 'COLOR': +cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); +break; +default: +break; +} +} +} +if ( ns.length === 0 ) { +input = this.vertices.input.NORMAL; +if ( input ) { +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); +} +} else { +geom.calcNormals = true; +} +} +if ( !ts ) { +ts = { }; +input = this.vertices.input.TEXCOORD; +if ( input ) { +texture_sets.push( input.set ); +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +idx32 = vs[ ndx ] * numParams; +if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; +ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); +} +} +} +if ( cs.length === 0 ) { +input = this.vertices.input.COLOR; +if ( input ) { +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +idx32 = vs[ ndx ] * numParams; +cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); +} +} +} +var face = null, faces = [], uv, uvArr; +if ( vcount === 3 ) { +faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); +} else if ( vcount === 4 ) { +faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); +faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); +} else if ( vcount > 4 && options.subdivideFaces ) { +var clr = cs.length ? cs : new THREE.Color(), +vec1, vec2, vec3, v1, v2, norm; +for ( k = 1; k < vcount - 1; ) { +faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) ); +} +} +if ( faces.length ) { +for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { +face = faces[ndx]; +face.daeMaterial = primitive.material; +geom.faces.push( face ); +for ( k = 0; k < texture_sets.length; k ++ ) { +uv = ts[ texture_sets[k] ]; +if ( vcount > 4 ) { +uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; +} else if ( vcount === 4 ) { +if ( ndx === 0 ) { +uvArr = [ uv[0], uv[1], uv[3] ]; +} else { +uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; +} +} else { +uvArr = [ uv[0], uv[1], uv[2] ]; +} +if ( geom.faceVertexUvs[k] === undefined ) { +geom.faceVertexUvs[k] = []; +} +geom.faceVertexUvs[k].push( uvArr ); +} +} +} else { +console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); +} +i += maxOffset * vcount; +} +} +}; +function Polygons () { +this.material = ""; +this.count = 0; +this.inputs = []; +this.vcount = null; +this.p = []; +this.geometry = new THREE.Geometry(); +} +Polygons.prototype.setVertices = function ( vertices ) { +for ( var i = 0; i < this.inputs.length; i ++ ) { +if ( this.inputs[ i ].source === vertices.id ) { +this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; +} +} +}; +Polygons.prototype.parse = function ( element ) { +this.material = element.getAttribute( 'material' ); +this.count = _attr_as_int( element, 'count', 0 ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'input': +this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); +break; +case 'vcount': +this.vcount = _ints( child.textContent ); +break; +case 'p': +this.p.push( _ints( child.textContent ) ); +break; +case 'ph': +console.warn( 'polygon holes not yet supported!' ); +break; +default: +break; +} +} +return this; +}; +function Polylist () { +Polygons.call( this ); +this.vcount = []; +} +Polylist.prototype = Object.create( Polygons.prototype ); +Polylist.prototype.constructor = Polylist; +function LineStrips() { +Polygons.call( this ); +this.vcount = 1; +} +LineStrips.prototype = Object.create( Polygons.prototype ); +LineStrips.prototype.constructor = LineStrips; +function Triangles () { +Polygons.call( this ); +this.vcount = 3; +} +Triangles.prototype = Object.create( Polygons.prototype ); +Triangles.prototype.constructor = Triangles; +function Accessor() { +this.source = ""; +this.count = 0; +this.stride = 0; +this.params = []; +} +Accessor.prototype.parse = function ( element ) { +this.params = []; +this.source = element.getAttribute( 'source' ); +this.count = _attr_as_int( element, 'count', 0 ); +this.stride = _attr_as_int( element, 'stride', 0 ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeName === 'param' ) { +var param = {}; +param[ 'name' ] = child.getAttribute( 'name' ); +param[ 'type' ] = child.getAttribute( 'type' ); +this.params.push( param ); +} +} +return this; +}; +function Vertices() { +this.input = {}; +} +Vertices.prototype.parse = function ( element ) { +this.id = element.getAttribute('id'); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[i].nodeName === 'input' ) { +var input = ( new Input() ).parse( element.childNodes[ i ] ); +this.input[ input.semantic ] = input; +} +} +return this; +}; +function Input () { +this.semantic = ""; +this.offset = 0; +this.source = ""; +this.set = 0; +} +Input.prototype.parse = function ( element ) { +this.semantic = element.getAttribute('semantic'); +this.source = element.getAttribute('source').replace(/^#/, ''); +this.set = _attr_as_int(element, 'set', -1); +this.offset = _attr_as_int(element, 'offset', 0); +if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { +this.set = 0; +} +return this; +}; +function Source ( id ) { +this.id = id; +this.type = null; +} +Source.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +switch ( child.nodeName ) { +case 'bool_array': +this.data = _bools( child.textContent ); +this.type = child.nodeName; +break; +case 'float_array': +this.data = _floats( child.textContent ); +this.type = child.nodeName; +break; +case 'int_array': +this.data = _ints( child.textContent ); +this.type = child.nodeName; +break; +case 'IDREF_array': +case 'Name_array': +this.data = _strings( child.textContent ); +this.type = child.nodeName; +break; +case 'technique_common': +for ( var j = 0; j < child.childNodes.length; j ++ ) { +if ( child.childNodes[ j ].nodeName === 'accessor' ) { +this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); +break; +} +} +break; +default: +break; +} +} +return this; +}; +Source.prototype.read = function () { +var result = []; +var param = this.accessor.params[ 0 ]; +switch ( param.type ) { +case 'IDREF': +case 'Name': case 'name': +case 'float': +return this.data; +case 'float4x4': +for ( var j = 0; j < this.data.length; j += 16 ) { +var s = this.data.slice( j, j + 16 ); +var m = getConvertedMat4( s ); +result.push( m ); +} +break; +default: +console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); +break; +} +return result; +}; +function Material () { +this.id = ""; +this.name = ""; +this.instance_effect = null; +} +Material.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { +this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); +break; +} +} +return this; +}; +function ColorOrTexture () { +this.color = new THREE.Color(); +this.color.setRGB( Math.random(), Math.random(), Math.random() ); +this.color.a = 1.0; +this.texture = null; +this.texcoord = null; +this.texOpts = null; +} +ColorOrTexture.prototype.isColor = function () { +return ( this.texture === null ); +}; +ColorOrTexture.prototype.isTexture = function () { +return ( this.texture != null ); +}; +ColorOrTexture.prototype.parse = function ( element ) { +if (element.nodeName === 'transparent') { +this.opaque = element.getAttribute('opaque'); +} +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'color': +var rgba = _floats( child.textContent ); +this.color = new THREE.Color(); +this.color.setRGB( rgba[0], rgba[1], rgba[2] ); +this.color.a = rgba[3]; +break; +case 'texture': +this.texture = child.getAttribute('texture'); +this.texcoord = child.getAttribute('texcoord'); +this.texOpts = { +offsetU: 0, +offsetV: 0, +repeatU: 1, +repeatV: 1, +wrapU: 1, +wrapV: 1 +}; +this.parseTexture( child ); +break; +default: +break; +} +} +return this; +}; +ColorOrTexture.prototype.parseTexture = function ( element ) { +if ( ! element.childNodes ) return this; +if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { +element = element.childNodes[1]; +if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { +element = element.childNodes[1]; +} +} +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'offsetU': +case 'offsetV': +case 'repeatU': +case 'repeatV': +this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); +break; +case 'wrapU': +case 'wrapV': +if ( child.textContent.toUpperCase() === 'TRUE' ) { +this.texOpts[ child.nodeName ] = 1; +} else { +this.texOpts[ child.nodeName ] = parseInt( child.textContent ); +} +break; +default: +this.texOpts[ child.nodeName ] = child.textContent; +break; +} +} +return this; +}; +function Shader ( type, effect ) { +this.type = type; +this.effect = effect; +this.material = null; +} +Shader.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'emission': +case 'diffuse': +case 'specular': +case 'transparent': +this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); +break; +case 'bump': +var bumpType = child.getAttribute( 'bumptype' ); +if ( bumpType ) { +if ( bumpType.toLowerCase() === "heightfield" ) { +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} else if ( bumpType.toLowerCase() === "normalmap" ) { +this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); +} else { +console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} +} else { +console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} +break; +case 'shininess': +case 'reflectivity': +case 'index_of_refraction': +case 'transparency': +var f = child.querySelectorAll('float'); +if ( f.length > 0 ) +this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); +break; +default: +break; +} +} +this.create(); +return this; +}; +Shader.prototype.create = function() { +var props = {}; +var transparent = false; +if (this['transparency'] !== undefined && this['transparent'] !== undefined) { +var transparentColor = this['transparent']; +var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; +if (transparencyLevel > 0) { +transparent = true; +props[ 'transparent' ] = true; +props[ 'opacity' ] = 1 - transparencyLevel; +} +} +var keys = { +'diffuse':'map', +'ambient':'lightMap', +'specular':'specularMap', +'emission':'emissionMap', +'bump':'bumpMap', +'normal':'normalMap' +}; +for ( var prop in this ) { +switch ( prop ) { +case 'ambient': +case 'emission': +case 'diffuse': +case 'specular': +case 'bump': +case 'normal': +var cot = this[ prop ]; +if ( cot instanceof ColorOrTexture ) { +if ( cot.isTexture() ) { +var samplerId = cot.texture; +var sampler = this.effect.sampler[samplerId]; +if ( sampler !== undefined && sampler.source !== undefined ) { +var surface = this.effect.surface[sampler.source]; +if ( surface !== undefined ) { +var image = images[ surface.init_from ]; +if ( image ) { +var url = baseUrl + image.init_from; +var texture; +var loader = THREE.Loader.Handlers.get( url ); +if ( loader !== null ) { +texture = loader.load( url ); +} else { +texture = new THREE.Texture(); +loadTextureImage( texture, url ); +} +if ( sampler.wrap_s === "MIRROR" ) { +texture.wrapS = THREE.MirroredRepeatWrapping; +} else if ( sampler.wrap_s === "WRAP" || cot.texOpts.wrapU ) { +texture.wrapS = THREE.RepeatWrapping; +} else { +texture.wrapS = THREE.ClampToEdgeWrapping; +} +if ( sampler.wrap_t === "MIRROR" ) { +texture.wrapT = THREE.MirroredRepeatWrapping; +} else if ( sampler.wrap_t === "WRAP" || cot.texOpts.wrapV ) { +texture.wrapT = THREE.RepeatWrapping; +} else { +texture.wrapT = THREE.ClampToEdgeWrapping; +} +texture.offset.x = cot.texOpts.offsetU; +texture.offset.y = cot.texOpts.offsetV; +texture.repeat.x = cot.texOpts.repeatU; +texture.repeat.y = cot.texOpts.repeatV; +props[keys[prop]] = texture; +if (prop === 'emission') props['emissive'] = 0xffffff; +} +} +} +} else if ( prop === 'diffuse' || !transparent ) { +if ( prop === 'emission' ) { +props[ 'emissive' ] = cot.color.getHex(); +} else { +props[ prop ] = cot.color.getHex(); +} +} +} +break; +case 'shininess': +props[ prop ] = this[ prop ]; +break; +case 'reflectivity': +props[ prop ] = this[ prop ]; +if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; +props['combine'] = THREE.MixOperation; //mix regular shading with reflective component +break; +case 'index_of_refraction': +props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable +if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; +break; +case 'transparency': +break; +default: +break; +} +} +props[ 'shading' ] = preferredShading; +props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; +if ( props.diffuse !== undefined ) { +props.color = props.diffuse; +delete props.diffuse; +} +switch ( this.type ) { +case 'constant': +if (props.emissive != undefined) props.color = props.emissive; +this.material = new THREE.MeshBasicMaterial( props ); +break; +case 'phong': +case 'blinn': +this.material = new THREE.MeshPhongMaterial( props ); +break; +case 'lambert': +default: +this.material = new THREE.MeshLambertMaterial( props ); +break; +} +return this.material; +}; +function Surface ( effect ) { +this.effect = effect; +this.init_from = null; +this.format = null; +} +Surface.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'init_from': +this.init_from = child.textContent; +break; +case 'format': +this.format = child.textContent; +break; +default: +console.log( "unhandled Surface prop: " + child.nodeName ); +break; +} +} +return this; +}; +function Sampler2D ( effect ) { +this.effect = effect; +this.source = null; +this.wrap_s = null; +this.wrap_t = null; +this.minfilter = null; +this.magfilter = null; +this.mipfilter = null; +} +Sampler2D.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'source': +this.source = child.textContent; +break; +case 'minfilter': +this.minfilter = child.textContent; +break; +case 'magfilter': +this.magfilter = child.textContent; +break; +case 'mipfilter': +this.mipfilter = child.textContent; +break; +case 'wrap_s': +this.wrap_s = child.textContent; +break; +case 'wrap_t': +this.wrap_t = child.textContent; +break; +default: +console.log( "unhandled Sampler2D prop: " + child.nodeName ); +break; +} +} +return this; +}; +function Effect () { +this.id = ""; +this.name = ""; +this.shader = null; +this.surface = {}; +this.sampler = {}; +} +Effect.prototype.create = function () { +if ( this.shader === null ) { +return null; +} +}; +Effect.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +extractDoubleSided( this, element ); +this.shader = null; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'profile_COMMON': +this.parseTechnique( this.parseProfileCOMMON( child ) ); +break; +default: +break; +} +} +return this; +}; +Effect.prototype.parseNewparam = function ( element ) { +var sid = element.getAttribute( 'sid' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'surface': +this.surface[sid] = ( new Surface( this ) ).parse( child ); +break; +case 'sampler2D': +this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +}; +Effect.prototype.parseProfileCOMMON = function ( element ) { +var technique; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'profile_COMMON': +this.parseProfileCOMMON( child ); +break; +case 'technique': +technique = child; +break; +case 'newparam': +this.parseNewparam( child ); +break; +case 'image': +var _image = ( new _Image() ).parse( child ); +images[ _image.id ] = _image; +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +return technique; +}; +Effect.prototype.parseTechnique = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'constant': +case 'lambert': +case 'blinn': +case 'phong': +this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); +break; +case 'extra': +this.parseExtra(child); +break; +default: +break; +} +} +}; +Effect.prototype.parseExtra = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique': +this.parseExtraTechnique( child ); +break; +default: +break; +} +} +}; +Effect.prototype.parseExtraTechnique = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bump': +this.shader.parse( element ); +break; +default: +break; +} +} +}; +function InstanceEffect () { +this.url = ""; +} +InstanceEffect.prototype.parse = function ( element ) { +this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); +return this; +}; +function Animation() { +this.id = ""; +this.name = ""; +this.source = {}; +this.sampler = []; +this.channel = []; +} +Animation.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +this.source = {}; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'animation': +var anim = ( new Animation() ).parse( child ); +for ( var src in anim.source ) { +this.source[ src ] = anim.source[ src ]; +} +for ( var j = 0; j < anim.channel.length; j ++ ) { +this.channel.push( anim.channel[ j ] ); +this.sampler.push( anim.sampler[ j ] ); +} +break; +case 'source': +var src = ( new Source() ).parse( child ); +this.source[ src.id ] = src; +break; +case 'sampler': +this.sampler.push( ( new Sampler( this ) ).parse( child ) ); +break; +case 'channel': +this.channel.push( ( new Channel( this ) ).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +function Channel( animation ) { +this.animation = animation; +this.source = ""; +this.target = ""; +this.fullSid = null; +this.sid = null; +this.dotSyntax = null; +this.arrSyntax = null; +this.arrIndices = null; +this.member = null; +} +Channel.prototype.parse = function ( element ) { +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +this.target = element.getAttribute( 'target' ); +var parts = this.target.split( '/' ); +var id = parts.shift(); +var sid = parts.shift(); +var dotSyntax = ( sid.indexOf(".") >= 0 ); +var arrSyntax = ( sid.indexOf("(") >= 0 ); +if ( dotSyntax ) { +parts = sid.split("."); +this.sid = parts.shift(); +this.member = parts.shift(); +} else if ( arrSyntax ) { +var arrIndices = sid.split("("); +this.sid = arrIndices.shift(); +for (var j = 0; j < arrIndices.length; j ++ ) { +arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); +} +this.arrIndices = arrIndices; +} else { +this.sid = sid; +} +this.fullSid = sid; +this.dotSyntax = dotSyntax; +this.arrSyntax = arrSyntax; +return this; +}; +function Sampler ( animation ) { +this.id = ""; +this.animation = animation; +this.inputs = []; +this.input = null; +this.output = null; +this.strideOut = null; +this.interpolation = null; +this.startTime = null; +this.endTime = null; +this.duration = 0; +} +Sampler.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +this.inputs.push( (new Input()).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +Sampler.prototype.create = function () { +for ( var i = 0; i < this.inputs.length; i ++ ) { +var input = this.inputs[ i ]; +var source = this.animation.source[ input.source ]; +switch ( input.semantic ) { +case 'INPUT': +this.input = source.read(); +break; +case 'OUTPUT': +this.output = source.read(); +this.strideOut = source.accessor.stride; +break; +case 'INTERPOLATION': +this.interpolation = source.read(); +break; +case 'IN_TANGENT': +break; +case 'OUT_TANGENT': +break; +default: +console.log(input.semantic); +break; +} +} +this.startTime = 0; +this.endTime = 0; +this.duration = 0; +if ( this.input.length ) { +this.startTime = 100000000; +this.endTime = -100000000; +for ( var i = 0; i < this.input.length; i ++ ) { +this.startTime = Math.min( this.startTime, this.input[ i ] ); +this.endTime = Math.max( this.endTime, this.input[ i ] ); +} +this.duration = this.endTime - this.startTime; +} +}; +Sampler.prototype.getData = function ( type, ndx, member ) { +var data; +if ( type === 'matrix' && this.strideOut === 16 ) { +data = this.output[ ndx ]; +} else if ( this.strideOut > 1 ) { +data = []; +ndx *= this.strideOut; +for ( var i = 0; i < this.strideOut; ++ i ) { +data[ i ] = this.output[ ndx + i ]; +} +if ( this.strideOut === 3 ) { +switch ( type ) { +case 'rotate': +case 'translate': +fixCoords( data, -1 ); +break; +case 'scale': +fixCoords( data, 1 ); +break; +} +} else if ( this.strideOut === 4 && type === 'matrix' ) { +fixCoords( data, -1 ); +} +} else { +data = this.output[ ndx ]; +if ( member && type === 'translate' ) { +data = getConvertedTranslation( member, data ); +} +} +return data; +}; +function Key ( time ) { +this.targets = []; +this.time = time; +} +Key.prototype.addTarget = function ( fullSid, transform, member, data ) { +this.targets.push( { +sid: fullSid, +member: member, +transform: transform, +data: data +} ); +}; +Key.prototype.apply = function ( opt_sid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +var target = this.targets[ i ]; +if ( !opt_sid || target.sid === opt_sid ) { +target.transform.update( target.data, target.member ); +} +} +}; +Key.prototype.getTarget = function ( fullSid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +if ( this.targets[ i ].sid === fullSid ) { +return this.targets[ i ]; +} +} +return null; +}; +Key.prototype.hasTarget = function ( fullSid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +if ( this.targets[ i ].sid === fullSid ) { +return true; +} +} +return false; +}; +Key.prototype.interpolate = function ( nextKey, time ) { +for ( var i = 0, l = this.targets.length; i < l; i ++ ) { +var target = this.targets[ i ], +nextTarget = nextKey.getTarget( target.sid ), +data; +if ( target.transform.type !== 'matrix' && nextTarget ) { +var scale = ( time - this.time ) / ( nextKey.time - this.time ), +nextData = nextTarget.data, +prevData = target.data; +if ( scale < 0 ) scale = 0; +if ( scale > 1 ) scale = 1; +if ( prevData.length ) { +data = []; +for ( var j = 0; j < prevData.length; ++ j ) { +data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; +} +} else { +data = prevData + ( nextData - prevData ) * scale; +} +} else { +data = target.data; +} +target.transform.update( data, target.member ); +} +}; +function Camera() { +this.id = ""; +this.name = ""; +this.technique = ""; +} +Camera.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'optics': +this.parseOptics( child ); +break; +default: +break; +} +} +return this; +}; +Camera.prototype.parseOptics = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[ i ].nodeName === 'technique_common' ) { +var technique = element.childNodes[ i ]; +for ( var j = 0; j < technique.childNodes.length; j ++ ) { +this.technique = technique.childNodes[ j ].nodeName; +if ( this.technique === 'perspective' ) { +var perspective = technique.childNodes[ j ]; +for ( var k = 0; k < perspective.childNodes.length; k ++ ) { +var param = perspective.childNodes[ k ]; +switch ( param.nodeName ) { +case 'yfov': +this.yfov = param.textContent; +break; +case 'xfov': +this.xfov = param.textContent; +break; +case 'znear': +this.znear = param.textContent; +break; +case 'zfar': +this.zfar = param.textContent; +break; +case 'aspect_ratio': +this.aspect_ratio = param.textContent; +break; +} +} +} else if ( this.technique === 'orthographic' ) { +var orthographic = technique.childNodes[ j ]; +for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { +var param = orthographic.childNodes[ k ]; +switch ( param.nodeName ) { +case 'xmag': +this.xmag = param.textContent; +break; +case 'ymag': +this.ymag = param.textContent; +break; +case 'znear': +this.znear = param.textContent; +break; +case 'zfar': +this.zfar = param.textContent; +break; +case 'aspect_ratio': +this.aspect_ratio = param.textContent; +break; +} +} +} +} +} +} +return this; +}; +function InstanceCamera() { +this.url = ""; +} +InstanceCamera.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +return this; +}; +function Light() { +this.id = ""; +this.name = ""; +this.technique = ""; +} +Light.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique_common': +this.parseCommon( child ); +break; +case 'technique': +this.parseTechnique( child ); +break; +default: +break; +} +} +return this; +}; +Light.prototype.parseCommon = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +switch ( element.childNodes[ i ].nodeName ) { +case 'directional': +case 'point': +case 'spot': +case 'ambient': +this.technique = element.childNodes[ i ].nodeName; +var light = element.childNodes[ i ]; +for ( var j = 0; j < light.childNodes.length; j ++ ) { +var child = light.childNodes[j]; +switch ( child.nodeName ) { +case 'color': +var rgba = _floats( child.textContent ); +this.color = new THREE.Color(0); +this.color.setRGB( rgba[0], rgba[1], rgba[2] ); +this.color.a = rgba[3]; +break; +case 'falloff_angle': +this.falloff_angle = parseFloat( child.textContent ); +break; +case 'quadratic_attenuation': +var f = parseFloat( child.textContent ); +this.distance = f ? Math.sqrt( 1 / f ) : 0; +} +} +} +} +return this; +}; +Light.prototype.parseTechnique = function ( element ) { +this.profile = element.getAttribute( 'profile' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'intensity': +this.intensity = parseFloat(child.textContent); +break; +} +} +return this; +}; +function InstanceLight() { +this.url = ""; +} +InstanceLight.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +return this; +}; +function KinematicsModel( ) { +this.id = ''; +this.name = ''; +this.joints = []; +this.links = []; +} +KinematicsModel.prototype.parse = function( element ) { +this.id = element.getAttribute('id'); +this.name = element.getAttribute('name'); +this.joints = []; +this.links = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique_common': +this.parseCommon(child); +break; +default: +break; +} +} +return this; +}; +KinematicsModel.prototype.parseCommon = function( element ) { +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( element.childNodes[ i ].nodeName ) { +case 'joint': +this.joints.push( (new Joint()).parse(child) ); +break; +case 'link': +this.links.push( (new Link()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function Joint( ) { +this.sid = ''; +this.name = ''; +this.axis = new THREE.Vector3(); +this.limits = { +min: 0, +max: 0 +}; +this.type = ''; +this.static = false; +this.zeroPosition = 0.0; +this.middlePosition = 0.0; +} +Joint.prototype.parse = function( element ) { +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.axis = new THREE.Vector3(); +this.limits = { +min: 0, +max: 0 +}; +this.type = ''; +this.static = false; +this.zeroPosition = 0.0; +this.middlePosition = 0.0; +var axisElement = element.querySelector('axis'); +var _axis = _floats(axisElement.textContent); +this.axis = getConvertedVec3(_axis, 0); +var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; +var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; +this.limits = { +min: min, +max: max +}; +var jointTypes = [ 'prismatic', 'revolute' ]; +for (var i = 0; i < jointTypes.length; i ++ ) { +var type = jointTypes[ i ]; +var jointElement = element.querySelector(type); +if ( jointElement ) { +this.type = type; +} +} +if ( this.limits.min >= this.limits.max ) { +this.static = true; +} +this.middlePosition = (this.limits.min + this.limits.max) / 2.0; +return this; +}; +function Link( ) { +this.sid = ''; +this.name = ''; +this.transforms = []; +this.attachments = []; +} +Link.prototype.parse = function( element ) { +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.transforms = []; +this.attachments = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'attachment_full': +this.attachments.push( (new Attachment()).parse(child) ); +break; +case 'rotate': +case 'translate': +case 'matrix': +this.transforms.push( (new Transform()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function Attachment( ) { +this.joint = ''; +this.transforms = []; +this.links = []; +} +Attachment.prototype.parse = function( element ) { +this.joint = element.getAttribute('joint').split('/').pop(); +this.links = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'link': +this.links.push( (new Link()).parse(child) ); +break; +case 'rotate': +case 'translate': +case 'matrix': +this.transforms.push( (new Transform()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function _source( element ) { +var id = element.getAttribute( 'id' ); +if ( sources[ id ] != undefined ) { +return sources[ id ]; +} +sources[ id ] = ( new Source(id )).parse( element ); +return sources[ id ]; +} +function _nsResolver( nsPrefix ) { +if ( nsPrefix === "dae" ) { +return "http://www.collada.org/2005/11/COLLADASchema"; +} +return null; +} +function _bools( str ) { +var raw = _strings( str ); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); +} +return data; +} +function _floats( str ) { +var raw = _strings(str); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( parseFloat( raw[ i ] ) ); +} +return data; +} +function _ints( str ) { +var raw = _strings( str ); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( parseInt( raw[ i ], 10 ) ); +} +return data; +} +function _strings( str ) { +return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; +} +function _trimString( str ) { +return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); +} +function _attr_as_float( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return parseFloat( element.getAttribute( name ) ); +} else { +return defaultValue; +} +} +function _attr_as_int( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return parseInt( element.getAttribute( name ), 10) ; +} else { +return defaultValue; +} +} +function _attr_as_string( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return element.getAttribute( name ); +} else { +return defaultValue; +} +} +function _format_float( f, num ) { +if ( f === undefined ) { +var s = '0.'; +while ( s.length < num + 2 ) { +s += '0'; +} +return s; +} +num = num || 2; +var parts = f.toString().split( '.' ); +parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; +while ( parts[ 1 ].length < num ) { +parts[ 1 ] += '0'; +} +return parts.join( '.' ); +} +function loadTextureImage ( texture, url ) { +var loader = new THREE.ImageLoader(); +loader.load( url, function ( image ) { +texture.image = image; +texture.needsUpdate = true; +} ); +} +function extractDoubleSided( obj, element ) { +obj.doubleSided = false; +var node = element.querySelectorAll('extra double_sided')[0]; +if ( node ) { +if ( node && parseInt( node.textContent, 10 ) === 1 ) { +obj.doubleSided = true; +} +} +} +function setUpConversion() { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +upConversion = null; +} else { +switch ( colladaUp ) { +case 'X': +upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; +break; +case 'Y': +upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; +break; +case 'Z': +upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; +break; +} +} +} +function fixCoords( data, sign ) { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +return; +} +switch ( upConversion ) { +case 'XtoY': +var tmp = data[ 0 ]; +data[ 0 ] = sign * data[ 1 ]; +data[ 1 ] = tmp; +break; +case 'XtoZ': +var tmp = data[ 2 ]; +data[ 2 ] = data[ 1 ]; +data[ 1 ] = data[ 0 ]; +data[ 0 ] = tmp; +break; +case 'YtoX': +var tmp = data[ 0 ]; +data[ 0 ] = data[ 1 ]; +data[ 1 ] = sign * tmp; +break; +case 'YtoZ': +var tmp = data[ 1 ]; +data[ 1 ] = sign * data[ 2 ]; +data[ 2 ] = tmp; +break; +case 'ZtoX': +var tmp = data[ 0 ]; +data[ 0 ] = data[ 1 ]; +data[ 1 ] = data[ 2 ]; +data[ 2 ] = tmp; +break; +case 'ZtoY': +var tmp = data[ 1 ]; +data[ 1 ] = data[ 2 ]; +data[ 2 ] = sign * tmp; +break; +} +} +function getConvertedTranslation( axis, data ) { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +return data; +} +switch ( axis ) { +case 'X': +data = upConversion === 'XtoY' ? data * -1 : data; +break; +case 'Y': +data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; +break; +case 'Z': +data = upConversion === 'ZtoY' ? data * -1 : data ; +break; +default: +break; +} +return data; +} +function getConvertedVec3( data, offset ) { +var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; +fixCoords( arr, -1 ); +return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); +} +function getConvertedMat4( data ) { +if ( options.convertUpAxis ) { +var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; +fixCoords( arr, -1 ); +data[ 0 ] = arr[ 0 ]; +data[ 4 ] = arr[ 1 ]; +data[ 8 ] = arr[ 2 ]; +arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; +fixCoords( arr, -1 ); +data[ 1 ] = arr[ 0 ]; +data[ 5 ] = arr[ 1 ]; +data[ 9 ] = arr[ 2 ]; +arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; +fixCoords( arr, -1 ); +data[ 2 ] = arr[ 0 ]; +data[ 6 ] = arr[ 1 ]; +data[ 10 ] = arr[ 2 ]; +arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; +fixCoords( arr, -1 ); +data[ 0 ] = arr[ 0 ]; +data[ 1 ] = arr[ 1 ]; +data[ 2 ] = arr[ 2 ]; +arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; +fixCoords( arr, -1 ); +data[ 4 ] = arr[ 0 ]; +data[ 5 ] = arr[ 1 ]; +data[ 6 ] = arr[ 2 ]; +arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; +fixCoords( arr, -1 ); +data[ 8 ] = arr[ 0 ]; +data[ 9 ] = arr[ 1 ]; +data[ 10 ] = arr[ 2 ]; +arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; +fixCoords( arr, -1 ); +data[ 3 ] = arr[ 0 ]; +data[ 7 ] = arr[ 1 ]; +data[ 11 ] = arr[ 2 ]; +} +return new THREE.Matrix4().set( +data[0], data[1], data[2], data[3], +data[4], data[5], data[6], data[7], +data[8], data[9], data[10], data[11], +data[12], data[13], data[14], data[15] +); +} +function getConvertedIndex( index ) { +if ( index > -1 && index < 3 ) { +var members = [ 'X', 'Y', 'Z' ], +indices = { X: 0, Y: 1, Z: 2 }; +index = getConvertedMember( members[ index ] ); +index = indices[ index ]; +} +return index; +} +function getConvertedMember( member ) { +if ( options.convertUpAxis ) { +switch ( member ) { +case 'X': +switch ( upConversion ) { +case 'XtoY': +case 'XtoZ': +case 'YtoX': +member = 'Y'; +break; +case 'ZtoX': +member = 'Z'; +break; +} +break; +case 'Y': +switch ( upConversion ) { +case 'XtoY': +case 'YtoX': +case 'ZtoX': +member = 'X'; +break; +case 'XtoZ': +case 'YtoZ': +case 'ZtoY': +member = 'Z'; +break; +} +break; +case 'Z': +switch ( upConversion ) { +case 'XtoZ': +member = 'X'; +break; +case 'YtoZ': +case 'ZtoX': +case 'ZtoY': +member = 'Y'; +break; +} +break; +} +} +return member; +} +return { +load: load, +parse: parse, +setPreferredShading: setPreferredShading, +applySkin: applySkin, +geometries : geometries, +options: options +}; +}; +THREE.GLTF2Loader = ( function () { +function GLTF2Loader( manager ) { +this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +} +GLTF2Loader.prototype = { +constructor: GLTF2Loader, +load: function ( url, onLoad, onProgress, onError ) { +var scope = this; +var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); +var loader = new THREE.FileLoader( scope.manager ); +loader.setResponseType( 'arraybuffer' ); +loader.load( url, function ( data ) { +scope.parse( data, onLoad, path ); +}, onProgress, onError ); +}, +setCrossOrigin: function ( value ) { +this.crossOrigin = value; +}, +setPath: function ( value ) { +this.path = value; +}, +parse: function ( data, callback, path ) { +var content; +var extensions = {}; +var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); +if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { +extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); +content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; +} else { +content = convertUint8ArrayToString( new Uint8Array( data ) ); +} +var json = JSON.parse( content ); +if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { +extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); +} +console.time( 'GLTF2Loader' ); +var parser = new GLTFParser( json, extensions, { +path: path || this.path, +crossOrigin: this.crossOrigin +} ); +parser.parse( function ( scene, scenes, cameras, animations ) { +console.timeEnd( 'GLTF2Loader' ); +var glTF = { +"scene": scene, +"scenes": scenes, +"cameras": cameras, +"animations": animations +}; +callback( glTF ); +} ); +} +}; +function GLTFRegistry() { +var objects = {}; +return { +get: function ( key ) { +return objects[ key ]; +}, +add: function ( key, object ) { +objects[ key ] = object; +}, +remove: function ( key ) { +delete objects[ key ]; +}, +removeAll: function () { +objects = {}; +}, +update: function ( scene, camera ) { +for ( var name in objects ) { +var object = objects[ name ]; +if ( object.update ) { +object.update( scene, camera ); +} +} +} +}; +} +function GLTFShader( targetNode, allNodes ) { +var boundUniforms = {}; +var uniforms = targetNode.material.uniforms; +for ( var uniformId in uniforms ) { +var uniform = uniforms[ uniformId ]; +if ( uniform.semantic ) { +var sourceNodeRef = uniform.node; +var sourceNode = targetNode; +if ( sourceNodeRef ) { +sourceNode = allNodes[ sourceNodeRef ]; +} +boundUniforms[ uniformId ] = { +semantic: uniform.semantic, +sourceNode: sourceNode, +targetNode: targetNode, +uniform: uniform +}; +} +} +this.boundUniforms = boundUniforms; +this._m4 = new THREE.Matrix4(); +} +GLTFShader.prototype.update = function ( scene, camera ) { +var boundUniforms = this.boundUniforms; +for ( var name in boundUniforms ) { +var boundUniform = boundUniforms[ name ]; +switch ( boundUniform.semantic ) { +case "MODELVIEW": +var m4 = boundUniform.uniform.value; +m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +break; +case "MODELVIEWINVERSETRANSPOSE": +var m3 = boundUniform.uniform.value; +this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +m3.getNormalMatrix( this._m4 ); +break; +case "PROJECTION": +var m4 = boundUniform.uniform.value; +m4.copy( camera.projectionMatrix ); +break; +case "JOINTMATRIX": +var m4v = boundUniform.uniform.value; +for ( var mi = 0; mi < m4v.length; mi ++ ) { +m4v[ mi ] +.getInverse( boundUniform.sourceNode.matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) +.multiply( boundUniform.targetNode.bindMatrix ); +} +break; +default : +console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); +break; +} +} +}; +var EXTENSIONS = { +KHR_BINARY_GLTF: 'KHR_binary_glTF', +KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; +function GLTFMaterialsCommonExtension( json ) { +this.name = EXTENSIONS.KHR_MATERIALS_COMMON; +this.lights = {}; +var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; +var lights = extension.lights || {}; +for ( var lightId in lights ) { +var light = lights[ lightId ]; +var lightNode; +var lightParams = light[ light.type ]; +var color = new THREE.Color().fromArray( lightParams.color ); +switch ( light.type ) { +case "directional": +lightNode = new THREE.DirectionalLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "point": +lightNode = new THREE.PointLight( color ); +break; +case "spot": +lightNode = new THREE.SpotLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "ambient": +lightNode = new THREE.AmbientLight( color ); +break; +} +if ( lightNode ) { +this.lights[ lightId ] = lightNode; +} +} +} +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; +var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; +var BINARY_EXTENSION_HEADER_LENGTH = 12; +var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; +function GLTFBinaryExtension( data ) { +this.name = EXTENSIONS.KHR_BINARY_GLTF; +this.content = null; +this.body = null; +var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); +this.header = { +magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), +version: headerView.getUint32( 4, true ), +length: headerView.getUint32( 8, true ) +}; +if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { +throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); +} else if ( this.header.version < 2.0 ) { +throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); +} +var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); +var chunkIndex = 0; +while ( chunkIndex < chunkView.byteLength ) { +var chunkLength = chunkView.getUint32( chunkIndex, true ); +chunkIndex += 4; +var chunkType = chunkView.getUint32( chunkIndex, true ); +chunkIndex += 4; +if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { +var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); +this.content = convertUint8ArrayToString( contentArray ); +} else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { +var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; +this.body = data.slice( byteOffset, byteOffset + chunkLength ); +} +chunkIndex += chunkLength; +} +if ( this.content === null ) { +throw new Error( 'GLTF2Loader: JSON content not found.' ); +} +} +var WEBGL_CONSTANTS = { +FLOAT: 5126, +FLOAT_MAT3: 35675, +FLOAT_MAT4: 35676, +FLOAT_VEC2: 35664, +FLOAT_VEC3: 35665, +FLOAT_VEC4: 35666, +LINEAR: 9729, +REPEAT: 10497, +SAMPLER_2D: 35678, +TRIANGLES: 4, +LINES: 1, +UNSIGNED_BYTE: 5121, +UNSIGNED_SHORT: 5123, +VERTEX_SHADER: 35633, +FRAGMENT_SHADER: 35632 +}; +var WEBGL_TYPE = { +5126: Number, +35675: THREE.Matrix3, +35676: THREE.Matrix4, +35664: THREE.Vector2, +35665: THREE.Vector3, +35666: THREE.Vector4, +35678: THREE.Texture +}; +var WEBGL_COMPONENT_TYPES = { +5120: Int8Array, +5121: Uint8Array, +5122: Int16Array, +5123: Uint16Array, +5125: Uint32Array, +5126: Float32Array +}; +var WEBGL_FILTERS = { +9728: THREE.NearestFilter, +9729: THREE.LinearFilter, +9984: THREE.NearestMipMapNearestFilter, +9985: THREE.LinearMipMapNearestFilter, +9986: THREE.NearestMipMapLinearFilter, +9987: THREE.LinearMipMapLinearFilter +}; +var WEBGL_WRAPPINGS = { +33071: THREE.ClampToEdgeWrapping, +33648: THREE.MirroredRepeatWrapping, +10497: THREE.RepeatWrapping +}; +var WEBGL_TEXTURE_FORMATS = { +6406: THREE.AlphaFormat, +6407: THREE.RGBFormat, +6408: THREE.RGBAFormat, +6409: THREE.LuminanceFormat, +6410: THREE.LuminanceAlphaFormat +}; +var WEBGL_TEXTURE_DATATYPES = { +5121: THREE.UnsignedByteType, +32819: THREE.UnsignedShort4444Type, +32820: THREE.UnsignedShort5551Type, +33635: THREE.UnsignedShort565Type +}; +var WEBGL_SIDES = { +1028: THREE.BackSide, // Culling front +1029: THREE.FrontSide // Culling back +}; +var WEBGL_DEPTH_FUNCS = { +512: THREE.NeverDepth, +513: THREE.LessDepth, +514: THREE.EqualDepth, +515: THREE.LessEqualDepth, +516: THREE.GreaterEqualDepth, +517: THREE.NotEqualDepth, +518: THREE.GreaterEqualDepth, +519: THREE.AlwaysDepth +}; +var WEBGL_BLEND_EQUATIONS = { +32774: THREE.AddEquation, +32778: THREE.SubtractEquation, +32779: THREE.ReverseSubtractEquation +}; +var WEBGL_BLEND_FUNCS = { +0: THREE.ZeroFactor, +1: THREE.OneFactor, +768: THREE.SrcColorFactor, +769: THREE.OneMinusSrcColorFactor, +770: THREE.SrcAlphaFactor, +771: THREE.OneMinusSrcAlphaFactor, +772: THREE.DstAlphaFactor, +773: THREE.OneMinusDstAlphaFactor, +774: THREE.DstColorFactor, +775: THREE.OneMinusDstColorFactor, +776: THREE.SrcAlphaSaturateFactor +}; +var WEBGL_TYPE_SIZES = { +'SCALAR': 1, +'VEC2': 2, +'VEC3': 3, +'VEC4': 4, +'MAT2': 4, +'MAT3': 9, +'MAT4': 16 +}; +var PATH_PROPERTIES = { +scale: 'scale', +translation: 'position', +rotation: 'quaternion' +}; +var INTERPOLATION = { +LINEAR: THREE.InterpolateLinear, +STEP: THREE.InterpolateDiscrete +}; +var STATES_ENABLES = { +2884: 'CULL_FACE', +2929: 'DEPTH_TEST', +3042: 'BLEND', +3089: 'SCISSOR_TEST', +32823: 'POLYGON_OFFSET_FILL', +32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; +function _each( object, callback, thisObj ) { +if ( !object ) { +return Promise.resolve(); +} +var results; +var fns = []; +if ( Object.prototype.toString.call( object ) === '[object Array]' ) { +results = []; +var length = object.length; +for ( var idx = 0; idx < length; idx ++ ) { +var value = callback.call( thisObj || this, object[ idx ], idx ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, idx )); +} else { +results[ idx ] = value; +} +} +} +} else { +results = {}; +for ( var key in object ) { +if ( object.hasOwnProperty( key ) ) { +var value = callback.call( thisObj || this, object[ key ], key ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, key )); +} else { +results[ key ] = value; +} +} +} +} +} +return Promise.all( fns ).then( function() { +return results; +}); +} +function resolveURL( url, path ) { +if ( typeof url !== 'string' || url === '' ) +return ''; +if ( /^(https?:)?\/\//i.test( url ) ) { +return url; +} +if ( /^data:.*,.*$/i.test( url ) ) { +return url; +} +if ( /^blob:.*$/i.test( url ) ) { +return url; +} +return ( path || '' ) + url; +} +function convertUint8ArrayToString( array ) { +var s = ''; +for ( var i = 0; i < array.length; i ++ ) { +s += String.fromCharCode( array[ i ] ); +} +return s; +} +function replaceTHREEShaderAttributes( shaderText, technique ) { +var attributes = {}; +for ( var attributeId in technique.attributes ) { +var pname = technique.attributes[ attributeId ]; +var param = technique.parameters[ pname ]; +var atype = param.type; +var semantic = param.semantic; +attributes[ attributeId ] = { +type: atype, +semantic: semantic +}; +} +var shaderParams = technique.parameters; +var shaderAttributes = technique.attributes; +var params = {}; +for ( var attributeId in attributes ) { +var pname = shaderAttributes[ attributeId ]; +var shaderParam = shaderParams[ pname ]; +var semantic = shaderParam.semantic; +if ( semantic ) { +params[ attributeId ] = shaderParam; +} +} +for ( var pname in params ) { +var param = params[ pname ]; +var semantic = param.semantic; +var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); +switch ( semantic ) { +case 'POSITION': +shaderText = shaderText.replace( regEx, 'position' ); +break; +case 'NORMAL': +shaderText = shaderText.replace( regEx, 'normal' ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +shaderText = shaderText.replace( regEx, 'uv' ); +break; +case 'TEXCOORD_1': +shaderText = shaderText.replace( regEx, 'uv2' ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +shaderText = shaderText.replace( regEx, 'color' ); +break; +case 'WEIGHTS_0': +case 'WEIGHT': // WEIGHT semantic deprecated. +shaderText = shaderText.replace( regEx, 'skinWeight' ); +break; +case 'JOINTS_0': +case 'JOINT': // JOINT semantic deprecated. +shaderText = shaderText.replace( regEx, 'skinIndex' ); +break; +} +} +return shaderText; +} +function createDefaultMaterial() { +return new THREE.MeshPhongMaterial( { +color: 0x00000, +emissive: 0x888888, +specular: 0x000000, +shininess: 0, +transparent: false, +depthTest: true, +side: THREE.FrontSide +} ); +} +function DeferredShaderMaterial( params ) { +this.isDeferredShaderMaterial = true; +this.params = params; +} +DeferredShaderMaterial.prototype.create = function () { +var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); +for ( var uniformId in this.params.uniforms ) { +var originalUniform = this.params.uniforms[ uniformId ]; +if ( originalUniform.value instanceof THREE.Texture ) { +uniforms[ uniformId ].value = originalUniform.value; +uniforms[ uniformId ].value.needsUpdate = true; +} +uniforms[ uniformId ].semantic = originalUniform.semantic; +uniforms[ uniformId ].node = originalUniform.node; +} +this.params.uniforms = uniforms; +return new THREE.RawShaderMaterial( this.params ); +}; +function GLTFParser( json, extensions, options ) { +this.json = json || {}; +this.extensions = extensions || {}; +this.options = options || {}; +this.cache = new GLTFRegistry(); +} +GLTFParser.prototype._withDependencies = function ( dependencies ) { +var _dependencies = {}; +for ( var i = 0; i < dependencies.length; i ++ ) { +var dependency = dependencies[ i ]; +var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); +var cached = this.cache.get( dependency ); +if ( cached !== undefined ) { +_dependencies[ dependency ] = cached; +} else if ( this[ fnName ] ) { +var fn = this[ fnName ](); +this.cache.add( dependency, fn ); +_dependencies[ dependency ] = fn; +} +} +return _each( _dependencies, function ( dependency ) { +return dependency; +} ); +}; +GLTFParser.prototype.parse = function ( callback ) { +var json = this.json; +this.cache.removeAll(); +this._withDependencies( [ +"scenes", +"cameras", +"animations" +] ).then( function ( dependencies ) { +var scenes = []; +for ( var name in dependencies.scenes ) { +scenes.push( dependencies.scenes[ name ] ); +} +var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; +var cameras = []; +for ( var name in dependencies.cameras ) { +var camera = dependencies.cameras[ name ]; +cameras.push( camera ); +} +var animations = []; +for ( var name in dependencies.animations ) { +animations.push( dependencies.animations[ name ] ); +} +callback( scene, scenes, cameras, animations ); +} ); +}; +GLTFParser.prototype.loadShaders = function () { +var json = this.json; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.shaders, function ( shader ) { +if ( shader.bufferView !== undefined ) { +var bufferView = dependencies.bufferViews[ shader.bufferView ]; +var array = new Uint8Array( bufferView ); +return convertUint8ArrayToString( array ); +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'text' ); +loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { +resolve( shaderText ); +} ); +} ); +} ); +} ); +}; +GLTFParser.prototype.loadBuffers = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return _each( json.buffers, function ( buffer, name ) { +if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { +if ( buffer.uri === undefined && name === 0 ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'arraybuffer' ); +loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { +resolve( buffer ); +} ); +} ); +} else { +console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); +} +} ); +}; +GLTFParser.prototype.loadBufferViews = function () { +var json = this.json; +return this._withDependencies( [ +"buffers" +] ).then( function ( dependencies ) { +return _each( json.bufferViews, function ( bufferView ) { +var arraybuffer = dependencies.buffers[ bufferView.buffer ]; +var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; +return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); +} ); +} ); +}; +GLTFParser.prototype.loadAccessors = function () { +var json = this.json; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.accessors, function ( accessor ) { +var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; +var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; +var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; +var elementBytes = TypedArray.BYTES_PER_ELEMENT; +var itemBytes = elementBytes * itemSize; +var array; +if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { +array = new TypedArray( arraybuffer ); +var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); +return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); +} else { +array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); +return new THREE.BufferAttribute( array, itemSize ); +} +} ); +} ); +}; +GLTFParser.prototype.loadTextures = function () { +var json = this.json; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.textures, function ( texture ) { +if ( texture.source !== undefined ) { +return new Promise( function ( resolve ) { +var source = json.images[ texture.source ]; +var sourceUri = source.uri; +var urlCreator; +if ( source.bufferView !== undefined ) { +var bufferView = dependencies.bufferViews[ source.bufferView ]; +var blob = new Blob( [ bufferView ], { type: source.mimeType } ); +urlCreator = window.URL || window.webkitURL; +sourceUri = urlCreator.createObjectURL( blob ); +} +var textureLoader = THREE.Loader.Handlers.get( sourceUri ); +if ( textureLoader === null ) { +textureLoader = new THREE.TextureLoader(); +} +textureLoader.setCrossOrigin( options.crossOrigin ); +textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { +if ( urlCreator !== undefined ) { +urlCreator.revokeObjectURL( sourceUri ); +} +_texture.flipY = false; +if ( texture.name !== undefined ) _texture.name = texture.name; +_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; +if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { +console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + +'internalFormat will be forced to be the same value as format.' ); +} +_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; +if ( texture.sampler !== undefined ) { +var sampler = json.samplers[ texture.sampler ]; +_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; +_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; +_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; +_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; +} +resolve( _texture ); +}, undefined, function () { +resolve(); +} ); +} ); +} +} ); +} ); +}; +GLTFParser.prototype.loadMaterials = function () { +var json = this.json; +return this._withDependencies( [ +"shaders", +"textures" +] ).then( function ( dependencies ) { +return _each( json.materials, function ( material ) { +var materialType; +var materialValues = {}; +var materialParams = {}; +var khr_material; +if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { +khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; +} +if ( khr_material ) { +var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; +switch ( khr_material.technique ) { +case 'BLINN' : +case 'PHONG' : +materialType = THREE.MeshPhongMaterial; +keys.push( 'diffuse', 'specular', 'shininess' ); +break; +case 'LAMBERT' : +materialType = THREE.MeshLambertMaterial; +keys.push( 'diffuse' ); +break; +case 'CONSTANT' : +default : +materialType = THREE.MeshBasicMaterial; +break; +} +keys.forEach( function( v ) { +if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; +} ); +if ( khr_material.doubleSided || materialValues.doubleSided ) { +materialParams.side = THREE.DoubleSide; +} +if ( khr_material.transparent || materialValues.transparent ) { +materialParams.transparent = true; +materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; +} +} else if ( material.technique === undefined ) { +if ( material.pbrMetallicRoughness !== undefined ) { +materialType = THREE.MeshStandardMaterial; +if ( material.pbrMetallicRoughness !== undefined ) { +var metallicRoughness = material.pbrMetallicRoughness; +materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); +materialParams.opacity = 1.0; +if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { +var array = metallicRoughness.baseColorFactor; +materialParams.color.fromArray( array ); +materialParams.opacity = array[ 3 ]; +} +if ( metallicRoughness.baseColorTexture !== undefined ) { +materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; +} +if ( materialParams.opacity < 1.0 || +( materialParams.map !== undefined && +( materialParams.map.format === THREE.AlphaFormat || +materialParams.map.format === THREE.RGBAFormat || +materialParams.map.format === THREE.LuminanceAlphaFormat ) ) ) { +materialParams.transparent = true; +} +materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; +materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; +if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { +var textureIndex = metallicRoughness.metallicRoughnessTexture.index; +materialParams.metalnessMap = dependencies.textures[ textureIndex ]; +materialParams.roughnessMap = dependencies.textures[ textureIndex ]; +} +} +} else { +materialType = THREE.MeshPhongMaterial; +} +if ( material.normalTexture !== undefined ) { +materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; +} +if ( material.occlusionTexture !== undefined ) { +materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; +} +if ( material.emissiveTexture !== undefined ) { +materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; +} +materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); +if ( material.emissiveFactor !== undefined ) { +materialParams.emissive.fromArray( material.emissiveFactor ); +} +Object.assign( materialValues, material.values ); +} else { +materialType = DeferredShaderMaterial; +var technique = json.techniques[ material.technique ]; +materialParams.uniforms = {}; +var program = json.programs[ technique.program ]; +if ( program ) { +materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; +if ( ! materialParams.fragmentShader ) { +console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); +materialType = THREE.MeshPhongMaterial; +} +var vertexShader = dependencies.shaders[ program.vertexShader ]; +if ( ! vertexShader ) { +console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); +materialType = THREE.MeshPhongMaterial; +} +materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); +var uniforms = technique.uniforms; +for ( var uniformId in uniforms ) { +var pname = uniforms[ uniformId ]; +var shaderParam = technique.parameters[ pname ]; +var ptype = shaderParam.type; +if ( WEBGL_TYPE[ ptype ] ) { +var pcount = shaderParam.count; +var value; +if ( material.values !== undefined ) value = material.values[ pname ]; +var uvalue = new WEBGL_TYPE[ ptype ](); +var usemantic = shaderParam.semantic; +var unode = shaderParam.node; +switch ( ptype ) { +case WEBGL_CONSTANTS.FLOAT: +uvalue = shaderParam.value; +if ( pname == "transparency" ) { +materialParams.transparent = true; +} +if ( value !== undefined ) { +uvalue = value; +} +break; +case WEBGL_CONSTANTS.FLOAT_VEC2: +case WEBGL_CONSTANTS.FLOAT_VEC3: +case WEBGL_CONSTANTS.FLOAT_VEC4: +case WEBGL_CONSTANTS.FLOAT_MAT3: +if ( shaderParam && shaderParam.value ) { +uvalue.fromArray( shaderParam.value ); +} +if ( value ) { +uvalue.fromArray( value ); +} +break; +case WEBGL_CONSTANTS.FLOAT_MAT2: +console.warn( "FLOAT_MAT2 is not a supported uniform type" ); +break; +case WEBGL_CONSTANTS.FLOAT_MAT4: +if ( pcount ) { +uvalue = new Array( pcount ); +for ( var mi = 0; mi < pcount; mi ++ ) { +uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); +} +if ( shaderParam && shaderParam.value ) { +var m4v = shaderParam.value; +uvalue.fromArray( m4v ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} else { +if ( shaderParam && shaderParam.value ) { +var m4 = shaderParam.value; +uvalue.fromArray( m4 ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} +break; +case WEBGL_CONSTANTS.SAMPLER_2D: +if ( value !== undefined ) { +uvalue = dependencies.textures[ value ]; +} else if ( shaderParam.value !== undefined ) { +uvalue = dependencies.textures[ shaderParam.value ]; +} else { +uvalue = null; +} +break; +} +materialParams.uniforms[ uniformId ] = { +value: uvalue, +semantic: usemantic, +node: unode +}; +} else { +throw new Error( "Unknown shader uniform param type: " + ptype ); +} +} +var states = technique.states || {}; +var enables = states.enable || []; +var functions = states.functions || {}; +var enableCullFace = false; +var enableDepthTest = false; +var enableBlend = false; +for ( var i = 0, il = enables.length; i < il; i ++ ) { +var enable = enables[ i ]; +switch ( STATES_ENABLES[ enable ] ) { +case 'CULL_FACE': +enableCullFace = true; +break; +case 'DEPTH_TEST': +enableDepthTest = true; +break; +case 'BLEND': +enableBlend = true; +break; +case 'SCISSOR_TEST': +case 'POLYGON_OFFSET_FILL': +case 'SAMPLE_ALPHA_TO_COVERAGE': +break; +default: +throw new Error( "Unknown technique.states.enable: " + enable ); +} +} +if ( enableCullFace ) { +materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; +} else { +materialParams.side = THREE.DoubleSide; +} +materialParams.depthTest = enableDepthTest; +materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; +materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; +materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; +materialParams.transparent = enableBlend; +var blendEquationSeparate = functions.blendEquationSeparate; +if ( blendEquationSeparate !== undefined ) { +materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; +materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; +} else { +materialParams.blendEquation = THREE.AddEquation; +materialParams.blendEquationAlpha = THREE.AddEquation; +} +var blendFuncSeparate = functions.blendFuncSeparate; +if ( blendFuncSeparate !== undefined ) { +materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; +materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; +materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; +materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; +} else { +materialParams.blendSrc = THREE.OneFactor; +materialParams.blendDst = THREE.ZeroFactor; +materialParams.blendSrcAlpha = THREE.OneFactor; +materialParams.blendDstAlpha = THREE.ZeroFactor; +} +} +} +if ( Array.isArray( materialValues.diffuse ) ) { +materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); +} else if ( typeof( materialValues.diffuse ) === 'string' ) { +materialParams.map = dependencies.textures[ materialValues.diffuse ]; +} +delete materialParams.diffuse; +if ( typeof( materialValues.reflective ) === 'string' ) { +materialParams.envMap = dependencies.textures[ materialValues.reflective ]; +} +if ( typeof( materialValues.bump ) === 'string' ) { +materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; +} +if ( Array.isArray( materialValues.emission ) ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.color = new THREE.Color().fromArray( materialValues.emission ); +} else { +materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); +} +} else if ( typeof( materialValues.emission ) === 'string' ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.map = dependencies.textures[ materialValues.emission ]; +} else { +materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; +} +} +if ( Array.isArray( materialValues.specular ) ) { +materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); +} else if ( typeof( materialValues.specular ) === 'string' ) { +materialParams.specularMap = dependencies.textures[ materialValues.specular ]; +} +if ( materialValues.shininess !== undefined ) { +materialParams.shininess = materialValues.shininess; +} +var _material = new materialType( materialParams ); +if ( material.name !== undefined ) _material.name = material.name; +return _material; +} ); +} ); +}; +GLTFParser.prototype.loadMeshes = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"materials" +] ).then( function ( dependencies ) { +return _each( json.meshes, function ( mesh ) { +var group = new THREE.Group(); +if ( mesh.name !== undefined ) group.name = mesh.name; +if ( mesh.extras ) group.userData = mesh.extras; +var primitives = mesh.primitives || []; +for ( var name in primitives ) { +var primitive = primitives[ name ]; +var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); +var geometry; +var meshNode; +if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { +geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( attributeEntry === undefined ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'NORMAL': +geometry.addAttribute( 'normal', bufferAttribute ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +geometry.addAttribute( 'uv', bufferAttribute ); +break; +case 'TEXCOORD_1': +geometry.addAttribute( 'uv2', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +case 'WEIGHTS_0': +case 'WEIGHT': // WEIGHT semantic deprecated. +geometry.addAttribute( 'skinWeight', bufferAttribute ); +break; +case 'JOINTS_0': +case 'JOINT': // JOINT semantic deprecated. +geometry.addAttribute( 'skinIndex', bufferAttribute ); +break; +} +} +if ( primitive.indices !== undefined ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +} +meshNode = new THREE.Mesh( geometry, material ); +meshNode.castShadow = true; +} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { +geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +} +} +if ( primitive.indices !== undefined ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +meshNode = new THREE.LineSegments( geometry, material ); +} else { +meshNode = new THREE.Line( geometry, material ); +} +} else { +throw new Error( "Only triangular and line primitives are supported" ); +} +if ( geometry.attributes.color !== undefined ) { +material.vertexColors = THREE.VertexColors; +material.needsUpdate = true; +} +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} +return group; +} ); +} ); +}; +GLTFParser.prototype.loadCameras = function () { +var json = this.json; +return _each( json.cameras, function ( camera ) { +if ( camera.type == "perspective" && camera.perspective ) { +var yfov = camera.perspective.yfov; +var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; +var xfov = yfov * aspectRatio; +var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} else if ( camera.type == "orthographic" && camera.orthographic ) { +var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} +} ); +}; +GLTFParser.prototype.loadSkins = function () { +var json = this.json; +return this._withDependencies( [ +"accessors" +] ).then( function ( dependencies ) { +return _each( json.skins, function ( skin ) { +var bindShapeMatrix = new THREE.Matrix4(); +if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); +var _skin = { +bindShapeMatrix: bindShapeMatrix, +jointNames: skin.jointNames, +inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] +}; +return _skin; +} ); +} ); +}; +GLTFParser.prototype.loadAnimations = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"nodes" +] ).then( function ( dependencies ) { +return _each( json.animations, function ( animation, animationId ) { +var tracks = []; +for ( var channelId in animation.channels ) { +var channel = animation.channels[ channelId ]; +var sampler = animation.samplers[ channel.sampler ]; +if ( sampler ) { +var target = channel.target; +var name = target.node || target.id; // NOTE: target.id is deprecated. +var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; +var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; +var inputAccessor = dependencies.accessors[ input ]; +var outputAccessor = dependencies.accessors[ output ]; +var node = dependencies.nodes[ name ]; +if ( node ) { +node.updateMatrix(); +node.matrixAutoUpdate = true; +var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation +? THREE.QuaternionKeyframeTrack +: THREE.VectorKeyframeTrack; +var targetName = node.name ? node.name : node.uuid; +var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; +tracks.push( new TypedKeyframeTrack( +targetName + '.' + PATH_PROPERTIES[ target.path ], +THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), +THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), +interpolation +) ); +} +} +} +var name = animation.name !== undefined ? animation.name : "animation_" + animationId; +return new THREE.AnimationClip( name, undefined, tracks ); +} ); +} ); +}; +GLTFParser.prototype.loadNodes = function () { +var json = this.json; +var extensions = this.extensions; +var scope = this; +return _each( json.nodes, function ( node ) { +var matrix = new THREE.Matrix4(); +var _node; +if ( node.jointName ) { +_node = new THREE.Bone(); +_node.name = node.name !== undefined ? node.name : node.jointName; +_node.jointName = node.jointName; +} else { +_node = new THREE.Object3D(); +if ( node.name !== undefined ) _node.name = node.name; +} +if ( node.extras ) _node.userData = node.extras; +if ( node.matrix !== undefined ) { +matrix.fromArray( node.matrix ); +_node.applyMatrix( matrix ); +} else { +if ( node.translation !== undefined ) { +_node.position.fromArray( node.translation ); +} +if ( node.rotation !== undefined ) { +_node.quaternion.fromArray( node.rotation ); +} +if ( node.scale !== undefined ) { +_node.scale.fromArray( node.scale ); +} +} +return _node; +} ).then( function ( __nodes ) { +return scope._withDependencies( [ +"meshes", +"skins", +"cameras" +] ).then( function ( dependencies ) { +return _each( __nodes, function ( _node, nodeId ) { +var node = json.nodes[ nodeId ]; +var meshes; +if ( node.mesh !== undefined) { +meshes = [ node.mesh ]; +} else if ( node.meshes !== undefined ) { +console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); +meshes = node.meshes; +} +if ( meshes !== undefined ) { +for ( var meshId in meshes ) { +var mesh = meshes[ meshId ]; +var group = dependencies.meshes[ mesh ]; +if ( group === undefined ) { +console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); +continue; +} +for ( var childrenId in group.children ) { +var child = group.children[ childrenId ]; +var originalMaterial = child.material; +var originalGeometry = child.geometry; +var originalUserData = child.userData; +var originalName = child.name; +var material; +if ( originalMaterial.isDeferredShaderMaterial ) { +originalMaterial = material = originalMaterial.create(); +} else { +material = originalMaterial; +} +switch ( child.type ) { +case 'LineSegments': +child = new THREE.LineSegments( originalGeometry, material ); +break; +case 'LineLoop': +child = new THREE.LineLoop( originalGeometry, material ); +break; +case 'Line': +child = new THREE.Line( originalGeometry, material ); +break; +default: +child = new THREE.Mesh( originalGeometry, material ); +} +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var skinEntry; +if ( node.skin !== undefined ) { +skinEntry = dependencies.skins[ node.skin ]; +} +if ( skinEntry ) { +var getJointNode = function ( jointId ) { +var keys = Object.keys( __nodes ); +for ( var i = 0, il = keys.length; i < il; i ++ ) { +var n = __nodes[ keys[ i ] ]; +if ( n.jointName === jointId ) return n; +} +return null; +}; +var geometry = originalGeometry; +var material = originalMaterial; +material.skinning = true; +child = new THREE.SkinnedMesh( geometry, material, false ); +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var bones = []; +var boneInverses = []; +for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { +var jointId = skinEntry.jointNames[ i ]; +var jointNode = getJointNode( jointId ); +if ( jointNode ) { +bones.push( jointNode ); +var m = skinEntry.inverseBindMatrices.array; +var mat = new THREE.Matrix4().fromArray( m, i * 16 ); +boneInverses.push( mat ); +} else { +console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); +} +} +child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); +var buildBoneGraph = function ( parentJson, parentObject, property ) { +var children = parentJson[ property ]; +if ( children === undefined ) return; +for ( var i = 0, il = children.length; i < il; i ++ ) { +var nodeId = children[ i ]; +var bone = __nodes[ nodeId ]; +var boneJson = json.nodes[ nodeId ]; +if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { +parentObject.add( bone ); +buildBoneGraph( boneJson, bone, 'children' ); +} +} +}; +buildBoneGraph( node, child, 'skeletons' ); +} +_node.add( child ); +} +} +} +if ( node.camera !== undefined ) { +var camera = dependencies.cameras[ node.camera ]; +_node.add( camera ); +} +if ( node.extensions +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { +var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; +var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; +_node.add( light ); +} +return _node; +} ); +} ); +} ); +}; +GLTFParser.prototype.loadScenes = function () { +var json = this.json; +function buildNodeHierachy( nodeId, parentObject, allNodes ) { +var _node = allNodes[ nodeId ]; +parentObject.add( _node ); +var node = json.nodes[ nodeId ]; +if ( node.children ) { +var children = node.children; +for ( var i = 0, l = children.length; i < l; i ++ ) { +var child = children[ i ]; +buildNodeHierachy( child, _node, allNodes ); +} +} +} +return this._withDependencies( [ +"nodes" +] ).then( function ( dependencies ) { +return _each( json.scenes, function ( scene ) { +var _scene = new THREE.Scene(); +if ( scene.name !== undefined ) _scene.name = scene.name; +if ( scene.extras ) _scene.userData = scene.extras; +var nodes = scene.nodes || []; +for ( var i = 0, l = nodes.length; i < l; i ++ ) { +var nodeId = nodes[ i ]; +buildNodeHierachy( nodeId, _scene, dependencies.nodes ); +} +_scene.traverse( function ( child ) { +if ( child.material && child.material.isRawShaderMaterial ) { +child.gltfShader = new GLTFShader( child, dependencies.nodes ); +child.onBeforeRender = function(renderer, scene, camera){ +this.gltfShader.update(scene, camera); +}; +} +} ); +return _scene; +} ); +} ); +}; +return GLTF2Loader; +} )(); +THREE.GLTFLoader = ( function () { +function GLTFLoader( manager ) { +this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +} +GLTFLoader.prototype = { +constructor: GLTFLoader, +load: function ( url, onLoad, onProgress, onError ) { +var scope = this; +var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); +var loader = new THREE.FileLoader( scope.manager ); +loader.setResponseType( 'arraybuffer' ); +loader.load( url, function ( data ) { +scope.parse( data, onLoad, path ); +}, onProgress, onError ); +}, +setCrossOrigin: function ( value ) { +this.crossOrigin = value; +}, +setPath: function ( value ) { +this.path = value; +}, +parse: function ( data, callback, path ) { +var content; +var extensions = {}; +var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); +if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { +extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); +content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; +} else { +content = convertUint8ArrayToString( new Uint8Array( data ) ); +} +var json = JSON.parse( content ); +if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { +extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); +} +console.time( 'GLTFLoader' ); +var parser = new GLTFParser( json, extensions, { +path: path || this.path, +crossOrigin: this.crossOrigin +} ); +parser.parse( function ( scene, scenes, cameras, animations ) { +console.timeEnd( 'GLTFLoader' ); +var glTF = { +"scene": scene, +"scenes": scenes, +"cameras": cameras, +"animations": animations +}; +callback( glTF ); +} ); +} +}; +function GLTFRegistry() { +var objects = {}; +return { +get: function ( key ) { +return objects[ key ]; +}, +add: function ( key, object ) { +objects[ key ] = object; +}, +remove: function ( key ) { +delete objects[ key ]; +}, +removeAll: function () { +objects = {}; +}, +update: function ( scene, camera ) { +for ( var name in objects ) { +var object = objects[ name ]; +if ( object.update ) { +object.update( scene, camera ); +} +} +} +}; +} +GLTFLoader.Shaders = { +update: function () { +console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); +} +}; +function GLTFShader( targetNode, allNodes ) { +var boundUniforms = {}; +var uniforms = targetNode.material.uniforms; +for ( var uniformId in uniforms ) { +var uniform = uniforms[ uniformId ]; +if ( uniform.semantic ) { +var sourceNodeRef = uniform.node; +var sourceNode = targetNode; +if ( sourceNodeRef ) { +sourceNode = allNodes[ sourceNodeRef ]; +} +boundUniforms[ uniformId ] = { +semantic: uniform.semantic, +sourceNode: sourceNode, +targetNode: targetNode, +uniform: uniform +}; +} +} +this.boundUniforms = boundUniforms; +this._m4 = new THREE.Matrix4(); +} +GLTFShader.prototype.update = function ( scene, camera ) { +var boundUniforms = this.boundUniforms; +for ( var name in boundUniforms ) { +var boundUniform = boundUniforms[ name ]; +switch ( boundUniform.semantic ) { +case "MODELVIEW": +var m4 = boundUniform.uniform.value; +m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +break; +case "MODELVIEWINVERSETRANSPOSE": +var m3 = boundUniform.uniform.value; +this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +m3.getNormalMatrix( this._m4 ); +break; +case "PROJECTION": +var m4 = boundUniform.uniform.value; +m4.copy( camera.projectionMatrix ); +break; +case "JOINTMATRIX": +var m4v = boundUniform.uniform.value; +for ( var mi = 0; mi < m4v.length; mi ++ ) { +m4v[ mi ] +.getInverse( boundUniform.sourceNode.matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) +.multiply( boundUniform.targetNode.bindMatrix ); +} +break; +default : +console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); +break; +} +} +}; +GLTFLoader.Animations = { +update: function () { +console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); +} +}; +var EXTENSIONS = { +KHR_BINARY_GLTF: 'KHR_binary_glTF', +KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; +function GLTFMaterialsCommonExtension( json ) { +this.name = EXTENSIONS.KHR_MATERIALS_COMMON; +this.lights = {}; +var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; +var lights = extension.lights || {}; +for ( var lightId in lights ) { +var light = lights[ lightId ]; +var lightNode; +var lightParams = light[ light.type ]; +var color = new THREE.Color().fromArray( lightParams.color ); +switch ( light.type ) { +case "directional": +lightNode = new THREE.DirectionalLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "point": +lightNode = new THREE.PointLight( color ); +break; +case "spot": +lightNode = new THREE.SpotLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "ambient": +lightNode = new THREE.AmbientLight( color ); +break; +} +if ( lightNode ) { +this.lights[ lightId ] = lightNode; +} +} +} +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; +var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; +var BINARY_EXTENSION_HEADER_LENGTH = 20; +function GLTFBinaryExtension( data ) { +this.name = EXTENSIONS.KHR_BINARY_GLTF; +var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); +var header = { +magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), +version: headerView.getUint32( 4, true ), +length: headerView.getUint32( 8, true ), +contentLength: headerView.getUint32( 12, true ), +contentFormat: headerView.getUint32( 16, true ) +}; +for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { +var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; +if ( header[ key ] !== value ) { +throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); +} +} +var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); +this.header = header; +this.content = convertUint8ArrayToString( contentArray ); +this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); +} +GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { +var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; +var array = new Uint8Array( bufferView ); +return convertUint8ArrayToString( array ); +}; +GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) { +var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; +var bufferView = bufferViews[ metadata.bufferView ]; +var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) ); +return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData ); +}; +var WEBGL_CONSTANTS = { +FLOAT: 5126, +FLOAT_MAT3: 35675, +FLOAT_MAT4: 35676, +FLOAT_VEC2: 35664, +FLOAT_VEC3: 35665, +FLOAT_VEC4: 35666, +LINEAR: 9729, +REPEAT: 10497, +SAMPLER_2D: 35678, +TRIANGLES: 4, +LINES: 1, +UNSIGNED_BYTE: 5121, +UNSIGNED_SHORT: 5123, +VERTEX_SHADER: 35633, +FRAGMENT_SHADER: 35632 +}; +var WEBGL_TYPE = { +5126: Number, +35675: THREE.Matrix3, +35676: THREE.Matrix4, +35664: THREE.Vector2, +35665: THREE.Vector3, +35666: THREE.Vector4, +35678: THREE.Texture +}; +var WEBGL_COMPONENT_TYPES = { +5120: Int8Array, +5121: Uint8Array, +5122: Int16Array, +5123: Uint16Array, +5125: Uint32Array, +5126: Float32Array +}; +var WEBGL_FILTERS = { +9728: THREE.NearestFilter, +9729: THREE.LinearFilter, +9984: THREE.NearestMipMapNearestFilter, +9985: THREE.LinearMipMapNearestFilter, +9986: THREE.NearestMipMapLinearFilter, +9987: THREE.LinearMipMapLinearFilter +}; +var WEBGL_WRAPPINGS = { +33071: THREE.ClampToEdgeWrapping, +33648: THREE.MirroredRepeatWrapping, +10497: THREE.RepeatWrapping +}; +var WEBGL_TEXTURE_FORMATS = { +6406: THREE.AlphaFormat, +6407: THREE.RGBFormat, +6408: THREE.RGBAFormat, +6409: THREE.LuminanceFormat, +6410: THREE.LuminanceAlphaFormat +}; +var WEBGL_TEXTURE_DATATYPES = { +5121: THREE.UnsignedByteType, +32819: THREE.UnsignedShort4444Type, +32820: THREE.UnsignedShort5551Type, +33635: THREE.UnsignedShort565Type +}; +var WEBGL_SIDES = { +1028: THREE.BackSide, // Culling front +1029: THREE.FrontSide // Culling back +}; +var WEBGL_DEPTH_FUNCS = { +512: THREE.NeverDepth, +513: THREE.LessDepth, +514: THREE.EqualDepth, +515: THREE.LessEqualDepth, +516: THREE.GreaterEqualDepth, +517: THREE.NotEqualDepth, +518: THREE.GreaterEqualDepth, +519: THREE.AlwaysDepth +}; +var WEBGL_BLEND_EQUATIONS = { +32774: THREE.AddEquation, +32778: THREE.SubtractEquation, +32779: THREE.ReverseSubtractEquation +}; +var WEBGL_BLEND_FUNCS = { +0: THREE.ZeroFactor, +1: THREE.OneFactor, +768: THREE.SrcColorFactor, +769: THREE.OneMinusSrcColorFactor, +770: THREE.SrcAlphaFactor, +771: THREE.OneMinusSrcAlphaFactor, +772: THREE.DstAlphaFactor, +773: THREE.OneMinusDstAlphaFactor, +774: THREE.DstColorFactor, +775: THREE.OneMinusDstColorFactor, +776: THREE.SrcAlphaSaturateFactor +}; +var WEBGL_TYPE_SIZES = { +'SCALAR': 1, +'VEC2': 2, +'VEC3': 3, +'VEC4': 4, +'MAT2': 4, +'MAT3': 9, +'MAT4': 16 +}; +var PATH_PROPERTIES = { +scale: 'scale', +translation: 'position', +rotation: 'quaternion' +}; +var INTERPOLATION = { +LINEAR: THREE.InterpolateLinear, +STEP: THREE.InterpolateDiscrete +}; +var STATES_ENABLES = { +2884: 'CULL_FACE', +2929: 'DEPTH_TEST', +3042: 'BLEND', +3089: 'SCISSOR_TEST', +32823: 'POLYGON_OFFSET_FILL', +32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; +function _each( object, callback, thisObj ) { +if ( !object ) { +return Promise.resolve(); +} +var results; +var fns = []; +if ( Object.prototype.toString.call( object ) === '[object Array]' ) { +results = []; +var length = object.length; +for ( var idx = 0; idx < length; idx ++ ) { +var value = callback.call( thisObj || this, object[ idx ], idx ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, idx )); +} else { +results[ idx ] = value; +} +} +} +} else { +results = {}; +for ( var key in object ) { +if ( object.hasOwnProperty( key ) ) { +var value = callback.call( thisObj || this, object[ key ], key ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, key )); +} else { +results[ key ] = value; +} +} +} +} +} +return Promise.all( fns ).then( function() { +return results; +}); +} +function resolveURL( url, path ) { +if ( typeof url !== 'string' || url === '' ) +return ''; +if ( /^(https?:)?\/\//i.test( url ) ) { +return url; +} +if ( /^data:.*,.*$/i.test( url ) ) { +return url; +} +return ( path || '' ) + url; +} +function convertUint8ArrayToString( array ) { +var s = ''; +for ( var i = 0; i < array.length; i ++ ) { +s += String.fromCharCode( array[ i ] ); +} +return s; +} +function replaceTHREEShaderAttributes( shaderText, technique ) { +var attributes = {}; +for ( var attributeId in technique.attributes ) { +var pname = technique.attributes[ attributeId ]; +var param = technique.parameters[ pname ]; +var atype = param.type; +var semantic = param.semantic; +attributes[ attributeId ] = { +type: atype, +semantic: semantic +}; +} +var shaderParams = technique.parameters; +var shaderAttributes = technique.attributes; +var params = {}; +for ( var attributeId in attributes ) { +var pname = shaderAttributes[ attributeId ]; +var shaderParam = shaderParams[ pname ]; +var semantic = shaderParam.semantic; +if ( semantic ) { +params[ attributeId ] = shaderParam; +} +} +for ( var pname in params ) { +var param = params[ pname ]; +var semantic = param.semantic; +var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); +switch ( semantic ) { +case "POSITION": +shaderText = shaderText.replace( regEx, 'position' ); +break; +case "NORMAL": +shaderText = shaderText.replace( regEx, 'normal' ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +shaderText = shaderText.replace( regEx, 'uv' ); +break; +case 'TEXCOORD_1': +shaderText = shaderText.replace( regEx, 'uv2' ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +shaderText = shaderText.replace( regEx, 'color' ); +break; +case "WEIGHT": +shaderText = shaderText.replace( regEx, 'skinWeight' ); +break; +case "JOINT": +shaderText = shaderText.replace( regEx, 'skinIndex' ); +break; +} +} +return shaderText; +} +function createDefaultMaterial() { +return new THREE.MeshPhongMaterial( { +color: 0x00000, +emissive: 0x888888, +specular: 0x000000, +shininess: 0, +transparent: false, +depthTest: true, +side: THREE.FrontSide +} ); +} +function DeferredShaderMaterial( params ) { +this.isDeferredShaderMaterial = true; +this.params = params; +} +DeferredShaderMaterial.prototype.create = function () { +var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); +for ( var uniformId in this.params.uniforms ) { +var originalUniform = this.params.uniforms[ uniformId ]; +if ( originalUniform.value instanceof THREE.Texture ) { +uniforms[ uniformId ].value = originalUniform.value; +uniforms[ uniformId ].value.needsUpdate = true; +} +uniforms[ uniformId ].semantic = originalUniform.semantic; +uniforms[ uniformId ].node = originalUniform.node; +} +this.params.uniforms = uniforms; +return new THREE.RawShaderMaterial( this.params ); +}; +function GLTFParser( json, extensions, options ) { +this.json = json || {}; +this.extensions = extensions || {}; +this.options = options || {}; +this.cache = new GLTFRegistry(); +} +GLTFParser.prototype._withDependencies = function ( dependencies ) { +var _dependencies = {}; +for ( var i = 0; i < dependencies.length; i ++ ) { +var dependency = dependencies[ i ]; +var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); +var cached = this.cache.get( dependency ); +if ( cached !== undefined ) { +_dependencies[ dependency ] = cached; +} else if ( this[ fnName ] ) { +var fn = this[ fnName ](); +this.cache.add( dependency, fn ); +_dependencies[ dependency ] = fn; +} +} +return _each( _dependencies, function ( dependency ) { +return dependency; +} ); +}; +GLTFParser.prototype.parse = function ( callback ) { +var json = this.json; +this.cache.removeAll(); +this._withDependencies( [ +"scenes", +"cameras", +"animations" +] ).then( function ( dependencies ) { +var scenes = []; +for ( var name in dependencies.scenes ) { +scenes.push( dependencies.scenes[ name ] ); +} +var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; +var cameras = []; +for ( var name in dependencies.cameras ) { +var camera = dependencies.cameras[ name ]; +cameras.push( camera ); +} +var animations = []; +for ( var name in dependencies.animations ) { +animations.push( dependencies.animations[ name ] ); +} +callback( scene, scenes, cameras, animations ); +} ); +}; +GLTFParser.prototype.loadShaders = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.shaders, function ( shader ) { +if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'text' ); +loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { +resolve( shaderText ); +} ); +} ); +} ); +} ); +}; +GLTFParser.prototype.loadBuffers = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return _each( json.buffers, function ( buffer, name ) { +if ( name === BINARY_EXTENSION_BUFFER_NAME ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; +} +if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'arraybuffer' ); +loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { +resolve( buffer ); +} ); +} ); +} else { +console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); +} +} ); +}; +GLTFParser.prototype.loadBufferViews = function () { +var json = this.json; +return this._withDependencies( [ +"buffers" +] ).then( function ( dependencies ) { +return _each( json.bufferViews, function ( bufferView ) { +var arraybuffer = dependencies.buffers[ bufferView.buffer ]; +var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; +return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); +} ); +} ); +}; +GLTFParser.prototype.loadAccessors = function () { +var json = this.json; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.accessors, function ( accessor ) { +var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; +var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; +var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; +var elementBytes = TypedArray.BYTES_PER_ELEMENT; +var itemBytes = elementBytes * itemSize; +if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { +var array = new TypedArray( arraybuffer ); +var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); +return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); +} else { +array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); +return new THREE.BufferAttribute( array, itemSize ); +} +} ); +} ); +}; +GLTFParser.prototype.loadTextures = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.textures, function ( texture ) { +if ( texture.source ) { +return new Promise( function ( resolve ) { +var source = json.images[ texture.source ]; +var sourceUri = source.uri; +if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { +sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); +} +var textureLoader = THREE.Loader.Handlers.get( sourceUri ); +if ( textureLoader === null ) { +textureLoader = new THREE.TextureLoader(); +} +textureLoader.setCrossOrigin( options.crossOrigin ); +textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { +_texture.flipY = false; +if ( texture.name !== undefined ) _texture.name = texture.name; +_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; +if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { +console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + +'internalFormat will be forced to be the same value as format.' ); +} +_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; +if ( texture.sampler ) { +var sampler = json.samplers[ texture.sampler ]; +_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; +_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; +_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; +_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; +} +resolve( _texture ); +}, undefined, function () { +resolve(); +} ); +} ); +} +} ); +} ); +}; +GLTFParser.prototype.loadMaterials = function () { +var json = this.json; +return this._withDependencies( [ +"shaders", +"textures" +] ).then( function ( dependencies ) { +return _each( json.materials, function ( material ) { +var materialType; +var materialValues = {}; +var materialParams = {}; +var khr_material; +if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { +khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; +} +if ( khr_material ) { +var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; +switch ( khr_material.technique ) { +case 'BLINN' : +case 'PHONG' : +materialType = THREE.MeshPhongMaterial; +keys.push( 'diffuse', 'specular', 'shininess' ); +break; +case 'LAMBERT' : +materialType = THREE.MeshLambertMaterial; +keys.push( 'diffuse' ); +break; +case 'CONSTANT' : +default : +materialType = THREE.MeshBasicMaterial; +break; +} +keys.forEach( function( v ) { +if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; +} ); +if ( khr_material.doubleSided || materialValues.doubleSided ) { +materialParams.side = THREE.DoubleSide; +} +if ( khr_material.transparent || materialValues.transparent ) { +materialParams.transparent = true; +materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; +} +} else if ( material.technique === undefined ) { +materialType = THREE.MeshPhongMaterial; +Object.assign( materialValues, material.values ); +} else { +materialType = DeferredShaderMaterial; +var technique = json.techniques[ material.technique ]; +materialParams.uniforms = {}; +var program = json.programs[ technique.program ]; +if ( program ) { +materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; +if ( ! materialParams.fragmentShader ) { +console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); +materialType = THREE.MeshPhongMaterial; +} +var vertexShader = dependencies.shaders[ program.vertexShader ]; +if ( ! vertexShader ) { +console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); +materialType = THREE.MeshPhongMaterial; +} +materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); +var uniforms = technique.uniforms; +for ( var uniformId in uniforms ) { +var pname = uniforms[ uniformId ]; +var shaderParam = technique.parameters[ pname ]; +var ptype = shaderParam.type; +if ( WEBGL_TYPE[ ptype ] ) { +var pcount = shaderParam.count; +var value; +if ( material.values !== undefined ) value = material.values[ pname ]; +var uvalue = new WEBGL_TYPE[ ptype ](); +var usemantic = shaderParam.semantic; +var unode = shaderParam.node; +switch ( ptype ) { +case WEBGL_CONSTANTS.FLOAT: +uvalue = shaderParam.value; +if ( pname == "transparency" ) { +materialParams.transparent = true; +} +if ( value !== undefined ) { +uvalue = value; +} +break; +case WEBGL_CONSTANTS.FLOAT_VEC2: +case WEBGL_CONSTANTS.FLOAT_VEC3: +case WEBGL_CONSTANTS.FLOAT_VEC4: +case WEBGL_CONSTANTS.FLOAT_MAT3: +if ( shaderParam && shaderParam.value ) { +uvalue.fromArray( shaderParam.value ); +} +if ( value ) { +uvalue.fromArray( value ); +} +break; +case WEBGL_CONSTANTS.FLOAT_MAT2: +console.warn( "FLOAT_MAT2 is not a supported uniform type" ); +break; +case WEBGL_CONSTANTS.FLOAT_MAT4: +if ( pcount ) { +uvalue = new Array( pcount ); +for ( var mi = 0; mi < pcount; mi ++ ) { +uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); +} +if ( shaderParam && shaderParam.value ) { +var m4v = shaderParam.value; +uvalue.fromArray( m4v ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} else { +if ( shaderParam && shaderParam.value ) { +var m4 = shaderParam.value; +uvalue.fromArray( m4 ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} +break; +case WEBGL_CONSTANTS.SAMPLER_2D: +if ( value !== undefined ) { +uvalue = dependencies.textures[ value ]; +} else if ( shaderParam.value !== undefined ) { +uvalue = dependencies.textures[ shaderParam.value ]; +} else { +uvalue = null; +} +break; +} +materialParams.uniforms[ uniformId ] = { +value: uvalue, +semantic: usemantic, +node: unode +}; +} else { +throw new Error( "Unknown shader uniform param type: " + ptype ); +} +} +var states = technique.states || {}; +var enables = states.enable || []; +var functions = states.functions || {}; +var enableCullFace = false; +var enableDepthTest = false; +var enableBlend = false; +for ( var i = 0, il = enables.length; i < il; i ++ ) { +var enable = enables[ i ]; +switch ( STATES_ENABLES[ enable ] ) { +case 'CULL_FACE': +enableCullFace = true; +break; +case 'DEPTH_TEST': +enableDepthTest = true; +break; +case 'BLEND': +enableBlend = true; +break; +case 'SCISSOR_TEST': +case 'POLYGON_OFFSET_FILL': +case 'SAMPLE_ALPHA_TO_COVERAGE': +break; +default: +throw new Error( "Unknown technique.states.enable: " + enable ); +} +} +if ( enableCullFace ) { +materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; +} else { +materialParams.side = THREE.DoubleSide; +} +materialParams.depthTest = enableDepthTest; +materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; +materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; +materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; +materialParams.transparent = enableBlend; +var blendEquationSeparate = functions.blendEquationSeparate; +if ( blendEquationSeparate !== undefined ) { +materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; +materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; +} else { +materialParams.blendEquation = THREE.AddEquation; +materialParams.blendEquationAlpha = THREE.AddEquation; +} +var blendFuncSeparate = functions.blendFuncSeparate; +if ( blendFuncSeparate !== undefined ) { +materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; +materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; +materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; +materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; +} else { +materialParams.blendSrc = THREE.OneFactor; +materialParams.blendDst = THREE.ZeroFactor; +materialParams.blendSrcAlpha = THREE.OneFactor; +materialParams.blendDstAlpha = THREE.ZeroFactor; +} +} +} +if ( Array.isArray( materialValues.diffuse ) ) { +materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); +} else if ( typeof( materialValues.diffuse ) === 'string' ) { +materialParams.map = dependencies.textures[ materialValues.diffuse ]; +} +delete materialParams.diffuse; +if ( typeof( materialValues.reflective ) === 'string' ) { +materialParams.envMap = dependencies.textures[ materialValues.reflective ]; +} +if ( typeof( materialValues.bump ) === 'string' ) { +materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; +} +if ( Array.isArray( materialValues.emission ) ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.color = new THREE.Color().fromArray( materialValues.emission ); +} else { +materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); +} +} else if ( typeof( materialValues.emission ) === 'string' ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.map = dependencies.textures[ materialValues.emission ]; +} else { +materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; +} +} +if ( Array.isArray( materialValues.specular ) ) { +materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); +} else if ( typeof( materialValues.specular ) === 'string' ) { +materialParams.specularMap = dependencies.textures[ materialValues.specular ]; +} +if ( materialValues.shininess !== undefined ) { +materialParams.shininess = materialValues.shininess; +} +var _material = new materialType( materialParams ); +if ( material.name !== undefined ) _material.name = material.name; +return _material; +} ); +} ); +}; +GLTFParser.prototype.loadMeshes = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"materials" +] ).then( function ( dependencies ) { +return _each( json.meshes, function ( mesh ) { +var group = new THREE.Group(); +if ( mesh.name !== undefined ) group.name = mesh.name; +if ( mesh.extras ) group.userData = mesh.extras; +var primitives = mesh.primitives || []; +for ( var name in primitives ) { +var primitive = primitives[ name ]; +if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { +var geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'NORMAL': +geometry.addAttribute( 'normal', bufferAttribute ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +geometry.addAttribute( 'uv', bufferAttribute ); +break; +case 'TEXCOORD_1': +geometry.addAttribute( 'uv2', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +case 'WEIGHT': +geometry.addAttribute( 'skinWeight', bufferAttribute ); +break; +case 'JOINT': +geometry.addAttribute( 'skinIndex', bufferAttribute ); +break; +} +} +if ( primitive.indices ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +} +var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); +var meshNode = new THREE.Mesh( geometry, material ); +meshNode.castShadow = true; +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { +var geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +} +} +var material = dependencies.materials[ primitive.material ]; +var meshNode; +if ( primitive.indices ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +meshNode = new THREE.LineSegments( geometry, material ); +} else { +meshNode = new THREE.Line( geometry, material ); +} +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} else { +console.warn( "Only triangular and line primitives are supported" ); +} +} +return group; +} ); +} ); +}; +GLTFParser.prototype.loadCameras = function () { +var json = this.json; +return _each( json.cameras, function ( camera ) { +if ( camera.type == "perspective" && camera.perspective ) { +var yfov = camera.perspective.yfov; +var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; +var xfov = yfov * aspectRatio; +var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} else if ( camera.type == "orthographic" && camera.orthographic ) { +var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} +} ); +}; +GLTFParser.prototype.loadSkins = function () { +var json = this.json; +return this._withDependencies( [ +"accessors" +] ).then( function ( dependencies ) { +return _each( json.skins, function ( skin ) { +var bindShapeMatrix = new THREE.Matrix4(); +if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); +var _skin = { +bindShapeMatrix: bindShapeMatrix, +jointNames: skin.jointNames, +inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] +}; +return _skin; +} ); +} ); +}; +GLTFParser.prototype.loadAnimations = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"nodes" +] ).then( function ( dependencies ) { +return _each( json.animations, function ( animation, animationId ) { +var tracks = []; +for ( var channelId in animation.channels ) { +var channel = animation.channels[ channelId ]; +var sampler = animation.samplers[ channel.sampler ]; +if ( sampler ) { +var target = channel.target; +var name = target.id; +var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; +var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; +var inputAccessor = dependencies.accessors[ input ]; +var outputAccessor = dependencies.accessors[ output ]; +var node = dependencies.nodes[ name ]; +if ( node ) { +node.updateMatrix(); +node.matrixAutoUpdate = true; +var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation +? THREE.QuaternionKeyframeTrack +: THREE.VectorKeyframeTrack; +var targetName = node.name ? node.name : node.uuid; +var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; +tracks.push( new TypedKeyframeTrack( +targetName + '.' + PATH_PROPERTIES[ target.path ], +THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), +THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), +interpolation +) ); +} +} +} +var name = animation.name !== undefined ? animation.name : "animation_" + animationId; +return new THREE.AnimationClip( name, undefined, tracks ); +} ); +} ); +}; +GLTFParser.prototype.loadNodes = function () { +var json = this.json; +var extensions = this.extensions; +var scope = this; +return _each( json.nodes, function ( node ) { +var matrix = new THREE.Matrix4(); +var _node; +if ( node.jointName ) { +_node = new THREE.Bone(); +_node.name = node.name !== undefined ? node.name : node.jointName; +_node.jointName = node.jointName; +} else { +_node = new THREE.Object3D(); +if ( node.name !== undefined ) _node.name = node.name; +} +if ( node.extras ) _node.userData = node.extras; +if ( node.matrix !== undefined ) { +matrix.fromArray( node.matrix ); +_node.applyMatrix( matrix ); +} else { +if ( node.translation !== undefined ) { +_node.position.fromArray( node.translation ); +} +if ( node.rotation !== undefined ) { +_node.quaternion.fromArray( node.rotation ); +} +if ( node.scale !== undefined ) { +_node.scale.fromArray( node.scale ); +} +} +return _node; +} ).then( function ( __nodes ) { +return scope._withDependencies( [ +"meshes", +"skins", +"cameras" +] ).then( function ( dependencies ) { +return _each( __nodes, function ( _node, nodeId ) { +var node = json.nodes[ nodeId ]; +if ( node.meshes !== undefined ) { +for ( var meshId in node.meshes ) { +var mesh = node.meshes[ meshId ]; +var group = dependencies.meshes[ mesh ]; +if ( group === undefined ) { +console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); +continue; +} +for ( var childrenId in group.children ) { +var child = group.children[ childrenId ]; +var originalMaterial = child.material; +var originalGeometry = child.geometry; +var originalUserData = child.userData; +var originalName = child.name; +var material; +if ( originalMaterial.isDeferredShaderMaterial ) { +originalMaterial = material = originalMaterial.create(); +} else { +material = originalMaterial; +} +switch ( child.type ) { +case 'LineSegments': +child = new THREE.LineSegments( originalGeometry, material ); +break; +case 'LineLoop': +child = new THREE.LineLoop( originalGeometry, material ); +break; +case 'Line': +child = new THREE.Line( originalGeometry, material ); +break; +default: +child = new THREE.Mesh( originalGeometry, material ); +} +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var skinEntry; +if ( node.skin ) { +skinEntry = dependencies.skins[ node.skin ]; +} +if ( skinEntry ) { +var getJointNode = function ( jointId ) { +var keys = Object.keys( __nodes ); +for ( var i = 0, il = keys.length; i < il; i ++ ) { +var n = __nodes[ keys[ i ] ]; +if ( n.jointName === jointId ) return n; +} +return null; +}; +var geometry = originalGeometry; +var material = originalMaterial; +material.skinning = true; +child = new THREE.SkinnedMesh( geometry, material, false ); +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var bones = []; +var boneInverses = []; +for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { +var jointId = skinEntry.jointNames[ i ]; +var jointNode = getJointNode( jointId ); +if ( jointNode ) { +bones.push( jointNode ); +var m = skinEntry.inverseBindMatrices.array; +var mat = new THREE.Matrix4().fromArray( m, i * 16 ); +boneInverses.push( mat ); +} else { +console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); +} +} +child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); +var buildBoneGraph = function ( parentJson, parentObject, property ) { +var children = parentJson[ property ]; +if ( children === undefined ) return; +for ( var i = 0, il = children.length; i < il; i ++ ) { +var nodeId = children[ i ]; +var bone = __nodes[ nodeId ]; +var boneJson = json.nodes[ nodeId ]; +if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { +parentObject.add( bone ); +buildBoneGraph( boneJson, bone, 'children' ); +} +} +}; +buildBoneGraph( node, child, 'skeletons' ); +} +_node.add( child ); +} +} +} +if ( node.camera !== undefined ) { +var camera = dependencies.cameras[ node.camera ]; +_node.add( camera ); +} +if ( node.extensions +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { +var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; +var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; +_node.add( light ); +} +return _node; +} ); +} ); +} ); +}; +GLTFParser.prototype.loadScenes = function () { +var json = this.json; +function buildNodeHierachy( nodeId, parentObject, allNodes ) { +var _node = allNodes[ nodeId ]; +parentObject.add( _node ); +var node = json.nodes[ nodeId ]; +if ( node.children ) { +var children = node.children; +for ( var i = 0, l = children.length; i < l; i ++ ) { +var child = children[ i ]; +buildNodeHierachy( child, _node, allNodes ); +} +} +} +return this._withDependencies( [ +"nodes" +] ).then( function ( dependencies ) { +return _each( json.scenes, function ( scene ) { +var _scene = new THREE.Scene(); +if ( scene.name !== undefined ) _scene.name = scene.name; +if ( scene.extras ) _scene.userData = scene.extras; +var nodes = scene.nodes || []; +for ( var i = 0, l = nodes.length; i < l; i ++ ) { +var nodeId = nodes[ i ]; +buildNodeHierachy( nodeId, _scene, dependencies.nodes ); +} +_scene.traverse( function ( child ) { +if ( child.material && child.material.isRawShaderMaterial ) { +child.gltfShader = new GLTFShader( child, dependencies.nodes ); +child.onBeforeRender = function(renderer, scene, camera){ +this.gltfShader.update(scene, camera); +}; +} +} ); +return _scene; +} ); +} ); +}; +return GLTFLoader; +} )(); +"use strict"; +var JSONParser = function() +{ +} +JSONParser.prototype.constructor = JSONParser; +JSONParser.prototype.parseJavaScript = function(jsobj) { +var child = this.CreateElement('scene'); +this.ConvertToX3DOM(jsobj, "", child); +console.log(jsobj, child); +return child; +}; +JSONParser.prototype.elementSetAttribute = function(element, key, value) { +if (key === 'SON schema') { +} else if (key === 'ncoding') { +} else { +if (typeof element.setAttribute === 'function') { +element.setAttribute(key, value); +} +} +}; +JSONParser.prototype.ConvertChildren = function(parentkey, object, element) { +var key; +for (key in object) { +if (typeof object[key] === 'object') { +if (isNaN(parseInt(key))) { +this.ConvertObject(key, object, element, parentkey.substr(1)); +} else { +this.ConvertToX3DOM(object[key], key, element, parentkey.substr(1)); +} +} +} +}; +JSONParser.prototype.CreateElement = function(key, containerField) { +var child = document.createElement(key); +if (typeof containerField !== 'undefined') { +this.elementSetAttribute(child, 'containerField', containerField); +} +return child; +}; +JSONParser.prototype.CDATACreateFunction = function(document, element, str) { +var y = str.replace(/\\"/g, "\\\"") +.replace(/</g, "<") +.replace(/>/g, ">") +.replace(/&/g, "&"); +do { +str = y; +y = str.replace(/'([^'\r\n]*)\n([^']*)'/g, "'$1\\n$2'"); +if (str !== y) { +console.log("CDATA Replacing",str,"with",y); +} +} while (y != str); +var domParser = new DOMParser(); +var cdataStr = ''; // has to be wrapped into an element +var scriptDoc = domParser .parseFromString (cdataStr, 'application/xml'); +var cdata = scriptDoc .children[0] .childNodes[1]; // space after script is childNode[0] +element .appendChild(cdata); +}; +JSONParser.prototype.ConvertObject = function(key, object, element, containerField) { +var child; +if (object !== null && typeof object[key] === 'object') { +if (key.substr(0,1) === '@') { +this.ConvertToX3DOM(object[key], key, element); +} else if (key.substr(0,1) === '-') { +this.ConvertChildren(key, object[key], element); +} else if (key === '#comment') { +for (var c in object[key]) { +child = document.createComment(this.CommentStringToXML(object[key][c])); +element.appendChild(child); +} +} else if (key === '#text') { +child = document.createTextNode(object[key].join("")); +element.appendChild(child); +} else if (key === '#sourceText') { +this.CDATACreateFunction(document, element, object[key].join("\r\n")+"\r\n"); +} else { +if (key === 'connect' || key === 'fieldValue' || key === 'field' || key === 'meta' || key === 'component') { +for (var childkey in object[key]) { // for each field +if (typeof object[key][childkey] === 'object') { +child = this.CreateElement(key, containerField); +this.ConvertToX3DOM(object[key][childkey], childkey, child); +element.appendChild(child); +element.appendChild(document.createTextNode("\n")); +} +} +} else { +child = this.CreateElement(key, containerField); +this.ConvertToX3DOM(object[key], key, child); +element.appendChild(child); +element.appendChild(document.createTextNode("\n")); +} +} +} +}; +JSONParser.prototype.CommentStringToXML = function(str) { +var y = str; +str = str.replace(/\\\\/g, '\\'); +if (y !== str) { +console.log("X3DJSONLD replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.SFStringToXML = function(str) { +var y = str; +str = str.replace(/\\/g, '\\\\'); +str = str.replace(/"/g, '\\\"'); +if (y !== str) { +console.log("X3DJSONLD [] replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.JSONStringToXML = function(str) { +var y = str; +str = str.replace(/\\/g, '\\\\'); +str = str.replace(/\n/g, '\\n'); +if (y !== str) { +console.log("X3DJSONLD replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.ConvertToX3DOM = function(object, parentkey, element, containerField) { +var key; +var localArray = []; +var isArray = false; +var arrayOfStrings = false; +for (key in object) { +if (isNaN(parseInt(key))) { +isArray = false; +} else { +isArray = true; +} +if (isArray) { +if (typeof object[key] === 'number') { +localArray.push(object[key]); +} else if (typeof object[key] === 'string') { +localArray.push(object[key]); +arrayOfStrings = true; +} else if (typeof object[key] === 'boolean') { +localArray.push(object[key]); +} else if (typeof object[key] === 'object') { +this.ConvertToX3DOM(object[key], key, element); +} else if (typeof object[key] === 'undefined') { +} else { +console.error("Unknown type found in array "+typeof object[key]); +} +} else if (typeof object[key] === 'object') { +if (key === 'scene') { +this.ConvertToX3DOM(object[key], key, element); +} else { +this.ConvertObject(key, object, element); +} +} else if (typeof object[key] === 'number') { +this.elementSetAttribute(element, key.substr(1),object[key]); +} else if (typeof object[key] === 'string') { +if (key === '#comment') { +var child = document.createComment(this.CommentStringToXML(object[key])); +element.appendChild(child); +} else if (key === '#text') { +var child = document.createTextNode(object[key]); +element.appendChild(child); +} else { +this.elementSetAttribute(element, key.substr(1), this.JSONStringToXML(object[key])); +} +} else if (typeof object[key] === 'boolean') { +this.elementSetAttribute(element, key.substr(1),object[key]); +} else if (typeof object[key] === 'undefined') { +} else { +console.error("Unknown type found in object "+typeof object[key]); +console.error(object); +} +} +if (isArray) { +if (parentkey.substr(0,1) === '@') { +if (arrayOfStrings) { +arrayOfStrings = false; +for (var str in localArray) { +localArray[str] = this.SFStringToXML(localArray[str]); +} +this.elementSetAttribute(element, parentkey.substr(1),'"'+localArray.join('" "')+'"'); +} else { +this.elementSetAttribute(element, parentkey.substr(1),localArray.join(" ")); +} +} +isArray = false; +} +return element; +}; +function LoadManager () { +this.urlQueue = []; +this.urlNext = -1; +this.MaxRequests = 3; +this.totalRequests = 0; +this.totalResponses = 0; +this.requestCount = 0; +var lmThat = this; +this.load = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadText = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'text', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadHtml = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'html', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadXml = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'xml', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadJson = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'json', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadImage = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'image', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.success = function (response, string, xhr) { +if (typeof(xhr._loadManager.success) !== undefined) { +xhr._loadManager.success (response, xhr._loadManager.userdata, xhr); +} +} +this.failure = function (xhr, errorCode, errorText) { +if (typeof(xhr._loadManager.failure) !== undefined) { +xhr._loadManager.failure (xhr, xhr._loadManager.userdata, errorCode, errorText); +} +} +this.requestComplete = function (event, xhr, settings) { +lmThat.requestCount --; +lmThat.totalResponses++; +lmThat.loadNextUrl(); +} +this.loadNextUrl = function () { +if (this.requestCount >= this.MaxRequests) {return; } +if (this.urlNext >= this.urlQueue.length || this.urlNext < 0) { +this.urlNext = -1; +for (var i=0; i 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.CODE_SLASH: +slashes[ slashesPointer++ ] = i; +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.CODE_LF: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); +slashesPointer = 0; +bufferPointer = 0; +break; +case Consts.CODE_CR: +break; +default: +word += String.fromCharCode( code ); +break; +} +} +}; +Parser.prototype.parseText = function ( text ) { +var length = text.length; +var buffer = new Array( 32 ); +var bufferPointer = 0; +var slashes = new Array( 32 ); +var slashesPointer = 0; +var reachedFaces = false; +var char; +var word = ''; +for ( var i = 0; i < length; i++ ) { +char = text[ i ]; +switch ( char ) { +case Consts.STRING_SPACE: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.STRING_SLASH: +slashes[ slashesPointer++ ] = i; +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.STRING_LF: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); +slashesPointer = 0; +bufferPointer = 0; +break; +case Consts.STRING_CR: +break; +default: +word += char; +} +} +}; +Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) { +if ( bufferPointer < 1 ) return reachedFaces; +var bufferLength = bufferPointer - 1; +switch ( buffer[ 0 ] ) { +case Consts.LINE_V: +if ( reachedFaces ) { +this.processCompletedObject( null, this.rawObject.groupName ); +reachedFaces = false; +} +this.rawObject.pushVertex( buffer ); +break; +case Consts.LINE_VT: +this.rawObject.pushUv( buffer ); +break; +case Consts.LINE_VN: +this.rawObject.pushNormal( buffer ); +break; +case Consts.LINE_F: +reachedFaces = true; +var haveQuad = bufferLength % 4 === 0; +if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVn( buffer ); +} else { +this.rawObject.buildFaceVVn( buffer ); +} +} else if ( bufferLength === slashesPointer * 2 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVt( buffer ); +} else { +this.rawObject.buildFaceVVt( buffer ); +} +} else if ( bufferLength * 2 === slashesPointer * 3 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVtVn( buffer ); +} else { +this.rawObject.buildFaceVVtVn( buffer ); +} +} else { +if ( haveQuad ) { +this.rawObject.buildQuadV( buffer ); +} else { +this.rawObject.buildFaceV( buffer ); +} +} +break; +case Consts.LINE_L: +if ( bufferLength === slashesPointer * 2 ) { +this.rawObject.buildLineVvt( buffer ); +} else { +this.rawObject.buildLineV( buffer ); +} +break; +case Consts.LINE_S: +this.rawObject.pushSmoothingGroup( buffer[ 1 ] ); +break; +case Consts.LINE_G: +this.processCompletedGroup( buffer[ 1 ] ); +break; +case Consts.LINE_O: +if ( this.rawObject.vertices.length > 0 ) { +this.processCompletedObject( buffer[ 1 ], null ); +reachedFaces = false; +} else { +this.rawObject.pushObject( buffer[ 1 ] ); +} +break; +case Consts.LINE_MTLLIB: +this.rawObject.pushMtllib( buffer[ 1 ] ); +break; +case Consts.LINE_USEMTL: +this.rawObject.pushUsemtl( buffer[ 1 ] ); +break; +default: +break; +} +return reachedFaces; +}; +Parser.prototype.processCompletedObject = function ( objectName, groupName ) { +this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +this.inputObjectCount++; +this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName ); +}; +Parser.prototype.processCompletedGroup = function ( groupName ) { +var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +if ( notEmpty ) { +this.inputObjectCount ++; +this.rawObject = this.rawObject.newInstanceFromGroup( groupName ); +} else { +this.rawObject.pushGroup( groupName ); +} +}; +Parser.prototype.finalize = function () { +this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +this.inputObjectCount++; +}; +return Parser; +})(); +var RawObject = (function () { +function RawObject( objectName, groupName, mtllibName ) { +this.globalVertexOffset = 1; +this.globalUvOffset = 1; +this.globalNormalOffset = 1; +this.vertices = []; +this.normals = []; +this.uvs = []; +this.mtllibName = Validator.verifyInput( mtllibName, 'none' ); +this.objectName = Validator.verifyInput( objectName, 'none' ); +this.groupName = Validator.verifyInput( groupName, 'none' ); +this.activeMtlName = 'none'; +this.activeSmoothingGroup = 1; +this.mtlCount = 0; +this.smoothingGroupCount = 0; +this.rawObjectDescriptions = []; +var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; +} +RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) { +return materialName + '|' + smoothingGroup; +}; +RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) { +var newRawObject = new RawObject( objectName, groupName, this.mtllibName ); +newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; +newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; +newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; +return newRawObject; +}; +RawObject.prototype.newInstanceFromGroup = function ( groupName ) { +var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName ); +newRawObject.vertices = this.vertices; +newRawObject.uvs = this.uvs; +newRawObject.normals = this.normals; +newRawObject.globalVertexOffset = this.globalVertexOffset; +newRawObject.globalUvOffset = this.globalUvOffset; +newRawObject.globalNormalOffset = this.globalNormalOffset; +return newRawObject; +}; +RawObject.prototype.pushVertex = function ( buffer ) { +this.vertices.push( parseFloat( buffer[ 1 ] ) ); +this.vertices.push( parseFloat( buffer[ 2 ] ) ); +this.vertices.push( parseFloat( buffer[ 3 ] ) ); +}; +RawObject.prototype.pushUv = function ( buffer ) { +this.uvs.push( parseFloat( buffer[ 1 ] ) ); +this.uvs.push( parseFloat( buffer[ 2 ] ) ); +}; +RawObject.prototype.pushNormal = function ( buffer ) { +this.normals.push( parseFloat( buffer[ 1 ] ) ); +this.normals.push( parseFloat( buffer[ 2 ] ) ); +this.normals.push( parseFloat( buffer[ 3 ] ) ); +}; +RawObject.prototype.pushObject = function ( objectName ) { +this.objectName = objectName; +}; +RawObject.prototype.pushMtllib = function ( mtllibName ) { +this.mtllibName = mtllibName; +}; +RawObject.prototype.pushGroup = function ( groupName ) { +this.groupName = groupName; +this.verifyIndex(); +}; +RawObject.prototype.pushUsemtl = function ( mtlName ) { +if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; +this.activeMtlName = mtlName; +this.mtlCount++; +this.verifyIndex(); +}; +RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) { +var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup; +if ( this.activeSmoothingGroup === normalized ) return; +this.activeSmoothingGroup = normalized; +this.smoothingGroupCount++; +this.verifyIndex(); +}; +RawObject.prototype.verifyIndex = function () { +var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 ); +this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ]; +if ( ! Validator.isValid( this.rawObjectDescriptionInUse ) ) { +this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; +} +}; +RawObject.prototype.buildQuadVVtVn = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] ); +this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] ); +this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] ); +} +}; +RawObject.prototype.buildQuadVVt = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); +this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); +} +}; +RawObject.prototype.buildQuadVVn = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); +this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); +} +}; +RawObject.prototype.buildQuadV = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] ); +} +}; +RawObject.prototype.buildFaceVVtVn = function ( indexArray ) { +for ( var i = 1; i < 10; i += 3 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVt( indexArray[ i + 1 ] ); +this.attachFaceVn( indexArray[ i + 2 ] ); +} +}; +RawObject.prototype.buildFaceVVt = function ( indexArray ) { +for ( var i = 1; i < 7; i += 2 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVt( indexArray[ i + 1 ] ); +} +}; +RawObject.prototype.buildFaceVVn = function ( indexArray ) { +for ( var i = 1; i < 7; i += 2 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVn( indexArray[ i + 1 ] ); +} +}; +RawObject.prototype.buildFaceV = function ( indexArray ) { +for ( var i = 1; i < 4; i ++ ) { +this.attachFaceV_( indexArray[ i ] ); +} +}; +RawObject.prototype.attachFaceV_ = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalVertexOffset ) * 3; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.vertices.push( this.vertices[ index++ ] ); +rodiu.vertices.push( this.vertices[ index++ ] ); +rodiu.vertices.push( this.vertices[ index ] ); +}; +RawObject.prototype.attachFaceVt = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalUvOffset ) * 2; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.uvs.push( this.uvs[ index++ ] ); +rodiu.uvs.push( this.uvs[ index ] ); +}; +RawObject.prototype.attachFaceVn = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalNormalOffset ) * 3; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.normals.push( this.normals[ index++ ] ); +rodiu.normals.push( this.normals[ index++ ] ); +rodiu.normals.push( this.normals[ index ] ); +}; +RawObject.prototype.buildLineVvt = function ( lineArray ) { +var length = lineArray.length; +for ( var i = 1; i < length; i ++ ) { +this.vertices.push( parseInt( lineArray[ i ] ) ); +this.uvs.push( parseInt( lineArray[ i ] ) ); +} +}; +RawObject.prototype.buildLineV = function ( lineArray ) { +var length = lineArray.length; +for ( var i = 1; i < length; i++ ) { +this.vertices.push( parseInt( lineArray[ i ] ) ); +} +}; +RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) { +var temp = this.rawObjectDescriptions; +this.rawObjectDescriptions = []; +var rawObjectDescription; +var index = 0; +var absoluteVertexCount = 0; +var absoluteNormalCount = 0; +var absoluteUvCount = 0; +for ( var name in temp ) { +rawObjectDescription = temp[ name ]; +if ( rawObjectDescription.vertices.length > 0 ) { +if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName; +this.rawObjectDescriptions[ index++ ] = rawObjectDescription; +absoluteVertexCount += rawObjectDescription.vertices.length; +absoluteUvCount += rawObjectDescription.uvs.length; +absoluteNormalCount += rawObjectDescription.normals.length; +} +} +var notEmpty = false; +if ( index > 0 ) { +if ( debug ) this.createReport( inputObjectCount, true ); +meshCreator.buildMesh( +this.rawObjectDescriptions, +inputObjectCount, +absoluteVertexCount, +absoluteNormalCount, +absoluteUvCount +); +notEmpty = true; +} +return notEmpty; +}; +RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) { +var report = { +name: this.objectName ? this.objectName : 'groups', +mtllibName: this.mtllibName, +vertexCount: this.vertices.length / 3, +normalCount: this.normals.length / 3, +uvCount: this.uvs.length / 2, +smoothingGroupCount: this.smoothingGroupCount, +mtlCount: this.mtlCount, +rawObjectDescriptions: this.rawObjectDescriptions.length +}; +if ( printDirectly ) { +console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name ); +console.log( 'Mtllib name: ' + report.mtllibName ); +console.log( 'Vertex count: ' + report.vertexCount ); +console.log( 'Normal count: ' + report.normalCount ); +console.log( 'UV count: ' + report.uvCount ); +console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount ); +console.log( 'Material count: ' + report.mtlCount ); +console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions ); +console.log( '' ); +} +return report; +}; +return RawObject; +})(); +var RawObjectDescription = (function () { +function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) { +this.objectName = objectName; +this.groupName = groupName; +this.materialName = materialName; +this.smoothingGroup = smoothingGroup; +this.vertices = []; +this.uvs = []; +this.normals = []; +} +return RawObjectDescription; +})(); +var MeshCreator = (function () { +function MeshCreator() { +this.sceneGraphBaseNode = null; +this.materials = null; +this.debug = false; +this.globalObjectCount = 1; +this.validated = false; +} +MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) { +this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, this.sceneGraphBaseNode ); +this.sceneGraphBaseNode = Validator.verifyInput( this.sceneGraphBaseNode, new THREE.Group() ); +}; +MeshCreator.prototype.setMaterials = function ( materials ) { +this.materials = Validator.verifyInput( materials, this.materials ); +this.materials = Validator.verifyInput( this.materials, { materials: [] } ); +}; +MeshCreator.prototype.setDebug = function ( debug ) { +if ( debug === true || debug === false ) this.debug = debug; +}; +MeshCreator.prototype.validate = function () { +if ( this.validated ) return; +this.setSceneGraphBaseNode( null ); +this.setMaterials( null ); +this.setDebug( null ); +this.globalObjectCount = 1; +}; +MeshCreator.prototype.finalize = function () { +this.sceneGraphBaseNode = null; +this.materials = null; +this.validated = false; +}; +MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) { +if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount ); +var bufferGeometry = new THREE.BufferGeometry(); +var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 ); +bufferGeometry.addAttribute( 'position', vertexBA ); +var normalBA; +if ( absoluteNormalCount > 0 ) { +normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 ); +bufferGeometry.addAttribute( 'normal', normalBA ); +} +var uvBA; +if ( absoluteUvCount > 0 ) { +uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 ); +bufferGeometry.addAttribute( 'uv', uvBA ); +} +if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount ); +var rawObjectDescription; +var material; +var materialName; +var createMultiMaterial = rawObjectDescriptions.length > 1; +var materials = []; +var materialIndex = 0; +var materialIndexMapping = []; +var selectedMaterialIndex; +var vertexBAOffset = 0; +var vertexGroupOffset = 0; +var vertexLength; +var normalOffset = 0; +var uvOffset = 0; +for ( var oodIndex in rawObjectDescriptions ) { +rawObjectDescription = rawObjectDescriptions[ oodIndex ]; +materialName = rawObjectDescription.materialName; +material = this.materials[ materialName ]; +if ( ! material ) { +material = this.materials[ 'defaultMaterial' ]; +if ( ! material ) { +material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} ); +material.name = 'defaultMaterial'; +this.materials[ 'defaultMaterial' ] = material; +} +console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' ); +} +if ( rawObjectDescription.smoothingGroup === 0 ) { +materialName = material.name + '_flat'; +var materialClone = this.materials[ materialName ]; +if ( ! materialClone ) { +materialClone = material.clone(); +materialClone.name = materialName; +materialClone.shading = THREE.FlatShading; +this.materials[ materialName ] = name; +} +} +vertexLength = rawObjectDescription.vertices.length; +if ( createMultiMaterial ) { +selectedMaterialIndex = materialIndexMapping[ materialName ]; +if ( ! selectedMaterialIndex ) { +selectedMaterialIndex = materialIndex; +materialIndexMapping[ materialName ] = materialIndex; +materials.push( material ); +materialIndex++; +} +bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex ); +vertexGroupOffset += vertexLength / 3; +} +vertexBA.set( rawObjectDescription.vertices, vertexBAOffset ); +vertexBAOffset += vertexLength; +if ( normalBA ) { +normalBA.set( rawObjectDescription.normals, normalOffset ); +normalOffset += rawObjectDescription.normals.length; +} +if ( uvBA ) { +uvBA.set( rawObjectDescription.uvs, uvOffset ); +uvOffset += rawObjectDescription.uvs.length; +} +if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex ); +} +if ( ! normalBA ) bufferGeometry.computeVertexNormals(); +if ( createMultiMaterial ) material = materials; +var mesh = new THREE.Mesh( bufferGeometry, material ); +this.sceneGraphBaseNode.add( mesh ); +this.globalObjectCount++; +}; +MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) { +console.log( +' Output Object no.: ' + this.globalObjectCount + +'\n objectName: ' + rawObjectDescription.objectName + +'\n groupName: ' + rawObjectDescription.groupName + +'\n materialName: ' + rawObjectDescription.materialName + +'\n materialIndex: ' + selectedMaterialIndex + +'\n smoothingGroup: ' + rawObjectDescription.smoothingGroup + +'\n #vertices: ' + rawObjectDescription.vertices.length / 3 + +'\n #uvs: ' + rawObjectDescription.uvs.length / 2 + +'\n #normals: ' + rawObjectDescription.normals.length / 3 +); +}; +return MeshCreator; +})(); +OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) { +var workerCode = ''; +workerCode += funcBuildObject( 'Consts', Consts ); +workerCode += funcBuildObject( 'Validator', Validator ); +workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); +workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject ); +workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription ); +return workerCode; +}; +return OBJLoader2; +})(); +var xseen = { +canvases : [], +sceneInfo : [], +nodeDefinitions : {}, +parseTable : {}, +node : {}, +utils : {}, +eventManager : {}, +Events : {}, +Navigation : {}, +loadMgr : {}, +loader : { +'Null' : '', +'ColladaLoader' : '', +'GltfLegacy' : '', +'GltfLoader' : '', +'ObjLoader' : '', +'ImageLoader' : '', +'X3dLoader' : '', +}, +loadProgress : function (xhr) { +if (xhr.total != 0) { +console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); +} +}, +loadError : function (xhr, userdata, code, message) { +console.error('An error happened on '+userdata.e.id+'\n'+code+'\n'+message); +}, +loadMime : { +'' : {name: 'Null', loader: 'Null'}, +'dae' : {name: 'Collada', loader: 'ColladaLoader'}, +'glb' : {name: 'glTF Binary', loader: 'GltfLoader'}, +'glbl' : {name: 'glTF Binary', loader: 'GltfLegacy'}, +'gltf' : {name: 'glTF JSON', loader: 'GltfLoader'}, +'obj' : {name: 'OBJ', loader: 'ObjLoader'}, +'png' : {name: 'PNG', loader: 'ImageLoader'}, +'jpg' : {name: 'JPEG', loader: 'ImageLoader'}, +'jpeg' : {name: 'JPEG', loader: 'ImageLoader'}, +'gif' : {name: 'GIF', loader: 'ImageLoader'}, +'x3d' : {name: 'X3D XML', loader: 'X3dLoader'}, +}, +array_to_object : function (a) { +var o = {}; +for(var i=0;i ' + obj.type + ' (' + obj.name + ')'); +for (var i=0; i +var validParams = xseen.array_to_object([ +'showLog', +'showStat', +'showProgress', +'PrimitiveQuality', +'components', +'loadpath', +'disableDoubleClick', +'backend', +'altImg', +'runtimeEnabled', +'keysEnabled', +'showTouchpoints', +'disableTouch', +'maxActiveDownloads' +]); +var components, prefix; +var showLoggingConsole = false; +for (var i=0; i < xseens.length; i++) { +settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'false'); +settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'true'); +settings.setProperty("showStat", xseens[i].getAttribute("showStat") || 'false'); +settings.setProperty("showProgress", xseens[i].getAttribute("showProgress") || 'true'); +settings.setProperty("PrimitiveQuality", xseens[i].getAttribute("PrimitiveQuality") || 'High'); +params = xseens[i].getElementsByTagName('PARAM'); +for (var j=0; j < params.length; j++) { +if (params[j].getAttribute('name') in validParams) { +settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value')); +} else { +} +} +if (settings.getProperty('showLog') === 'true') { +showLoggingConsole = true; +} +if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) { +components = settings.getProperty('components', xseens[i].getAttribute("components")); +if (components) { +prefix = settings.getProperty('loadpath', xseens[i].getAttribute("loadpath")); +components = components.trim().split(','); +for (j=0; j < components.length; j++) { +xseen.loadJS(components[j] + ".js", prefix); +} +} +if (xseens[i].getAttribute("src")) { +var _scene = document.createElement("scene"); +var _inl = document.createElement("Inline"); +_inl.setAttribute("url", xseens[i].getAttribute("src")); +_scene.appendChild(_inl); +xseens[i].appendChild(_scene); +} +} +} +if (showLoggingConsole == true) { +xseen.debug.activate(true); +} else { +xseen.debug.activate(false); +} +if (xseen.versionInfo !== undefined) { +xseen.debug.logInfo("XSeen version " + xseen.versionInfo.version + ", " + +"Date " + xseen.versionInfo.date); +xseen.debug.logInfo(xseen.versionInfo.splashText); +} +var x_element; +var x_canvas; +var altDiv, altP, aLnk, altImg; +var t0, t1; +for (var i=0; i < xseens.length; i++) +{ +x_element = xseens[i]; // The XSeen DOM element +x_canvas = new THREE.Scene(); // May need addtl info if multiple: xseen.X3DCanvas(x_element, xseen.canvases.length); +xseen.canvases.push(x_canvas); // TODO: Need to handle failure to initialize? +t0 = new Date().getTime(); +var divWidth = x_element.getAttribute('width'); +var divHeight = x_element.getAttribute('height'); +if (divHeight + divWidth < 100) { +divHeight = 450; +divWidth = 800; +} else if (divHeight < 50) { +divHeight = Math.floor(divWidth/2) + 50; +} else if (divWidth < 50) { +divWidth = divHeight * 2 - 100; +} +var turntable = (x_element.getAttribute('turntable') || '').toLowerCase(); +if (turntable == 'on' || turntable == 'yes' || turntable == 'y' || turntable == '1') { +turntable = true; +} else { +turntable = false; +} +turntable = false; +var x_camera = {}; +var x_renderer = new THREE.WebGLRenderer(); +x_renderer.setSize (divWidth, divHeight); +var x_effect = new THREE.StereoEffect(x_renderer); +x_renderer.controls = {'update' : function() {return;}}; +x_element.addEventListener ('dblclick', xseen.Events.canvasHandler, true); +x_element.addEventListener ('click', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mousedown', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mousemove', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mouseup', xseen.Events.canvasHandler, true); +x_element.addEventListener ('xseen', xseen.Events.XSeenHandler); // Last chance for XSeen handling of event +x_element.addEventListener ('change', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event +xseen.sceneInfo.push ({ +'size' : {'width':divWidth, 'height':divHeight}, +'scene' : x_canvas, +'renderer' : x_renderer, +'effect' : x_effect, +'camera' : [x_camera], +'turntable' : turntable, +'mixers' : [], +'clock' : new THREE.Clock(), +'element' : x_element, +'selectable': [], +'stacks' : [], +'tmp' : {activeViewpoint:false}, +'xseen' : xseen, +}); +x_element._xseen = {}; +x_element._xseen.children = []; +x_element._xseen.sceneInfo = xseen.sceneInfo[xseen.sceneInfo.length-1]; +t1 = new Date().getTime() - t0; +xseen.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms."); +} +var ready = (function(eventType) { +var evt = null; +if (document.createEvent) { +evt = document.createEvent("Events"); +evt.initEvent(eventType, true, true); +document.dispatchEvent(evt); +} else if (document.createEventObject) { +evt = document.createEventObject(); +document.body.fireEvent('on' + eventType, evt); +} +})('load'); +var t=[]; +for (var i=0; i xseen.debug.maxLinesToLog) { return; } +var node = document.createElement("p"); +node.style.margin = 0; +switch (logType) { +case xseen.debug.INFO: +node.style.color = "#009900"; +break; +case xseen.debug.WARNING: +node.style.color = "#cd853f"; +break; +case xseen.debug.ERROR: +node.style.color = "#ff4500"; +break; +case xseen.debug.EXCEPTION: +node.style.color = "#ffff00"; +break; +default: +node.style.color = "#009900"; +break; +} +try { +node.innerHTML = logType + ": " + msg; +xseen.debug.logContainer.insertBefore(node, xseen.debug.logContainer.firstChild); +} catch (err) { +if (window.console.firebug !== undefined) { +window.console.warn(msg); +} +} +if (xseen.debug.isFirebugAvailable) { +switch (logType) { +case xseen.debug.INFO: +window.console.info(msg); +break; +case xseen.debug.WARNING: +window.console.warn(msg); +break; +case xseen.debug.ERROR: +window.console.error(msg); +break; +case xseen.debug.EXCEPTION: +window.console.debug(msg); +break; +default: +break; +} +} +xseen.debug.numLinesLogged++; +}, +logInfo: function(msg) { +xseen.debug.doLog(msg, xseen.debug.INFO); +}, +logWarning: function(msg) { +xseen.debug.doLog(msg, xseen.debug.WARNING); +}, +logError: function(msg) { +xseen.debug.doLog(msg, xseen.debug.ERROR); +}, +logException: function(msg) { +xseen.debug.doLog(msg, xseen.debug.EXCEPTION); +}, +assert: function(c, msg) { +if (!c) { +xseen.debug.doLog("Assertion failed in " + xseen.debug.assert.caller.name + ': ' + msg, xseen.debug.ERROR); +} +}, +typeOf: function (obj) { +var type = typeof obj; +return type === "object" && !obj ? "null" : type; +}, +exists: function (obj, name, type) { +type = type || "function"; +return (obj ? this.typeOf(obj[name]) : "null") === type; +}, +dumpFields: function (node) { +var str = ""; +for (var fName in node) { +str += (fName + ", "); +} +str += '\n'; +xseen.debug.logInfo(str); +return str; +} +}; +this.debug.setup(); +}; +xseen.Navigation = { +'TwoPi' : 2 * Math.PI, +'none' : function () {}, // Does not allow user-controlled navigation +'turntable' : function (speed, deltaT, scene, camera) +{ +var T, radians, radius, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +radius = vp.fields._radius0; +camera.position.x = radius * Math.sin(radians) +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.position.z = radius * Math.cos(radians); +camera.lookAt(scene.ORIGIN); +}, +'tilt' : function (speed, deltaT, scene, camera) +{ +var T, radians, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.lookAt(scene.ORIGIN); +}, +'setup' : { +'none' : function () {return null;}, +'orbit' : function (camera, renderer) +{ +var controls; +controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.enableZoom = false; +controls.enableZoom = true; +return controls; +}, +'trackball' : function (camera, renderer) +{ +var controls; +controls = new THREE.TrackballControls(camera, renderer.domElement); +controls.rotateSpeed = 1.0; +controls.zoomSpeed = 1.2; +controls.panSpeed = 0.8; +controls.noZoom = false; +controls.noPan = false; +controls.staticMoving = true; +controls.dynamicDampingFactor = 0.3; +controls.keys = [ 65, 83, 68 ]; +return controls; +}, +}, +}; +xseen.Navigation = { +'TwoPi' : 2 * Math.PI, +'none' : function () {}, // Does not allow user-controlled navigation +'turntable' : function (speed, deltaT, scene, camera) +{ +var T, radians, radius, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +radius = vp.fields._radius0; +camera.position.x = radius * Math.sin(radians) +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.position.z = radius * Math.cos(radians); +camera.lookAt(scene.ORIGIN); +}, +'tilt' : function (speed, deltaT, scene, camera) +{ +var T, radians, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.lookAt(scene.ORIGIN); +}, +'setup' : { +'none' : function () {return null;}, +'orbit' : function (camera, renderer) +{ +var controls; +controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.enableZoom = false; +controls.enableZoom = true; +return controls; +}, +}, +}; +xseen.nodes = { +'_defineNode' : function(nodeName, nodeComponent, nodeMethod) { +var methodBase = ''; +node = { +'tag' : nodeName, +'taglc' : nodeName.toLowerCase(), +'component' : nodeComponent, +'method' : methodBase + nodeMethod, +'fields' : [], +'fieldIndex': [], +'addField' : function (fieldObj, datatype, defaultValue) { +var fieldName, namelc, enumerated, animatable; +if (typeof(fieldObj) === 'object') { +fieldName = fieldObj.name; +datatype = fieldObj.datatype; +defaultValue = fieldObj.defaultValue; +enumerated = (typeof(fieldObj.enumerated) === 'undefined') ? [] : fieldObj.enumerated; +animatable = (typeof(fieldObj.animatable) === 'undefined') ? false : fieldObj.animatable; +} else { +fieldName = fieldObj; +animatable = false; +if (typeof(defaultValue) == 'array') { +enumerated = defaultValue; +defaultValue = enumerated[0]; +} else { +enumerated = []; +} +} +namelc = fieldName.toLowerCase(); +this.fields.push ({ +'field' : fieldName, +'fieldlc' : namelc, +'type' : datatype, +'default' : defaultValue, +'enumeration' : enumerated, +'animatable' : animatable, +'clone' : this.cloneField, +'setFieldName' : this.setFieldName, +}); +this.fieldIndex[namelc] = this.fields.length-1; +return this; +}, +'addNode' : function () { +xseen.parseTable[this.taglc] = this; +}, +'cloneField' : function () { +var newFieldObject = { +'field' : this.field, +'fieldlc' : this.fieldlc, +'type' : this.type, +'default' : 0, +'enumeration' : [], +'animatable' : this.animatable, +'clone' : this.clone, +'setFieldName' : this.setFieldName, +}; +for (var i=0; i= 0) { +this._internals.activeNode = this._internals.stack[this._internals.active]; +} else { +this._internals.activeNode = this._internals.defaultNode; +} +} +this.init = function() { // Clears existing stack +this._internals.stack = []; +} +this.pushDown = function(node) { // Push new node onto stack and make active +this._internals.stack.push (node); +this._setActiveNode(); +} +this.popOff = function() { // Pop node off stack and make next one active +this._internals.stack.pop(); +this._setActiveNode(); +} +this.getActive = function() { +return this._internals.activeNode; +} +this.setDefault = function(node) { +this._internals.defaultNode = node; +if (Object.keys(this._internals.activeNode).length === 0) { +this._internals.activeNode = this._internals.defaultNode; +} +} +} +xseen.types = { +'Deg2Rad' : Math.PI / 180, +'SFFloat' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return value; +}, +'SFInt' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return Math.round(value); +}, +'SFBool' : function (value, def) +{ +if (value === null) {return def;} +if (value) {return true;} +if (!value) {return false;} +return def; +}, +'SFTime' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return value; +}, +'SFVec3f' : function (value, def) +{ +if (value === null) {return def;} +var v3 = value.split(' '); +if (v3.length < 3 || Number.isNaN(v3[0]) || Number.isNaN(v3[1]) || Number.isNaN(v3[2])) { +return def; +} +return [v3[0]-0, v3[1]-0, v3[2]-0]; +}, +'SFVec2f' : function (value, def) +{ +if (value === null) {return def;} +var v2 = value.split(' '); +if (v2.length != 2 || Number.isNaN(v2[0]) || Number.isNaN(v2[1])) { +return def; +} +return [v2[0]-0, v2[1]-0]; +}, +'SFRotation' : function (value, def) +{ +if (value === null) {return def;} +var v4 = value.split(' '); +if (v4.length != 4 || Number.isNaN(v4[0]) || Number.isNaN(v4[1]) || Number.isNaN(v4[2]) || Number.isNaN(v4[3])) { +return def; +} +var result = { +'vector' : [v4[0], v4[1], v4[2], v4[3]], +'axis_angle' : [{'x': v4[0], 'y': v4[1], 'z': v4[2]}, v4[3]], +}; +return result; +}, +'SFColor' : function (value, defaultString) +{ +var v3 = this.SFVec3f(value, defaultString); +v3[0] = Math.min(Math.max(v3[0], 0.0), 1.0); +v3[1] = Math.min(Math.max(v3[1], 0.0), 1.0); +v3[2] = Math.min(Math.max(v3[2], 0.0), 1.0); +return v3; +}, +'SFString' : function (value, def) +{ +if (value === null) {value = def;} +return value; +}, +'MFFloat' : function (value, def) +{ +var defReturn = (def == '') ? [] : def; +if (value === null) {return defReturn;} +var mi = value.split(' '); +var rv = []; +for (var i=0; iDocumentation."]; +var version = { +major : Major, +minor : Minor, +patch : Patch, +preRelease : PreRelease, +release : Release, +version : '', +date : RDate, +splashText : SplashText +}; +version.version = version.major + '.' + version.minor + '.' + version.patch; +version.version += (version.preRelease != '') ? '-' + version.preRelease : ''; +version.version += (version.release != '') ? '+' + version.release : ''; +return version; +} +xseen.node.core_NOOP = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.parsing = function (s, e) { +xseen.debug.logInfo ('Parsing init details stub for ' + s); +} +xseen.node.af_Entity = { +'init' : function (e,p) +{ +xseen.node.parsing('A-Frame Entity'); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Assets = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.af_AssetItem = { +'init' : function (e,p) // Only field is SRC. +{ +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Mixin = { +'init' : function (e,p) // Lots of fields -- all nebelous until used +{ +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Appearance = function (e) { +var parameters = { +'aoMap' : e._xseen.fields['ambient-occlusion-map'], +'aoMapIntensity' : e._xseen.fields['ambient-occlusion-map-intensity'], +'color' : e._xseen.fields['color'], +'displacementMap' : e._xseen.fields['displacement-map'], +'displacementScale' : e._xseen.fields['displacement-scale'], +'displacementBias' : e._xseen.fields['displacement-bias'], +'envMap' : e._xseen.fields['env-map'], +'normalMap' : e._xseen.fields['normal-map'], +'normalScale' : e._xseen.fields['normal-scale'], +'wireframe' : e._xseen.fields['wireframe'], +'wireframeLinewidth' : e._xseen.fields['wireframe-linewidth'], +}; +var material = new THREE.MeshPhongMaterial(parameters); +return material; +} +xseen.node.af_Box = { +'init' : function (e,p) +{ +var geometry = new THREE.BoxGeometry( +e._xseen.fields.width, +e._xseen.fields.height, +e._xseen.fields.depth, +e._xseen.fields['segments-width'], +e._xseen.fields['segments-height'], +e._xseen.fields['segments-depth'] +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Cone = { +'init' : function (e,p) +{ +var geometry = new THREE.ConeGeometry( +e._xseen.fields.radius, +e._xseen.fields.height, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-height'], +e._xseen.fields['open-ended'], +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Cylinder = { +'init' : function (e,p) +{ +var geometry = new THREE.CylinderGeometry( +e._xseen.fields['radius-top'], +e._xseen.fields['radius-bottom'], +e._xseen.fields.height, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-height'], +e._xseen.fields['open-ended'], +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Dodecahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.DodecahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Icosahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.IcosahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Octahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.OctahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Sphere = { +'init' : function (e,p) +{ +var geometry = new THREE.SphereGeometry( +e._xseen.fields.radius, +e._xseen.fields['segments-width'], +e._xseen.fields['segments-height'], +e._xseen.fields['phi-start'] * xseen.types.Deg2Rad, +e._xseen.fields['phi-length'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Tetrahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.TetrahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Torus = { +'init' : function (e,p) +{ +var geometry = new THREE.TorusGeometry( +e._xseen.fields.radius, +e._xseen.fields.tube, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-tubular'], +e._xseen.fields.arc * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.unk_Viewpoint = { +'init' : function (e,p) +{ // This should really go in a separate push-down list for Viewpoints +e._xseen.fields._radius0 = Math.sqrt( e._xseen.fields.position[0]*e._xseen.fields.position[0] + +e._xseen.fields.position[1]*e._xseen.fields.position[1] + +e._xseen.fields.position[2]*e._xseen.fields.position[2]); +e._xseen.domNode = e; // Back-link to node if needed later on +e._xseen.position = new THREE.Vector3(e._xseen.fields.position[0], e._xseen.fields.position[1], e._xseen.fields.position[2]); +e._xseen.type = e._xseen.fields.cameratype; +e._xseen.motion = e._xseen.fields.motion; +e._xseen.motionspeed = e._xseen.fields.motionspeed * 1000; +if (e._xseen.motion == 'turntable' || e._xseen.motion == 'tilt') {e._xseen.motionspeed = 1.0/e._xseen.motionspeed;} +if (!e._xseen.sceneInfo.tmp.activeViewpoint) { +e._xseen.sceneInfo.stacks.Viewpoints.pushDown(e._xseen); +e._xseen.sceneInfo.tmp.activeViewpoint = true; +} +e._xseen.handlers = {}; +e._xseen.handlers.setactive = this.setactive; +}, +'fin' : function (e,p) {}, +'setactive' : function (ev) +{ +var xseenNode = this.destination._xseen; +xseenNode.sceneInfo.stacks.Viewpoints.pushDown(xseenNode); // TODO: This is probably not the right way to change VP in the stack +xseenNode.sceneInfo.element._xseen.renderer.activeCamera = +xseenNode.sceneInfo.element._xseen.renderer.cameras[xseenNode.fields.type]; +xseenNode.sceneInfo.element._xseen.renderer.activeRender = +xseenNode.sceneInfo.element._xseen.renderer.renderEffects[xseenNode.fields.type]; +if (xseenNode.fields.type != 'stereo') { +xseenNode.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, xseenNode.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); +} +}, +}; +xseen.node.controls_Navigation = { +'init' : function (e,p) +{ // This should really go in a separate push-down list for Viewpoints +e._xseen.domNode = e; // Back-link to node if needed later on +e._xseen.speed = e._xseen.fields.speed; +if (e._xseen.setup == 'examine') {e._xseen.setup == 'trackball';} +e._xseen.type = 'none'; +e._xseen.setup = e._xseen.fields.type; +if (!(e._xseen.setup == 'orbit' || e._xseen.setup == 'trackball')) {e._xseen.setup = 'none';} +if (!e._xseen.sceneInfo.tmp.activeNavigation) { +e._xseen.sceneInfo.stacks.Navigation.pushDown(e._xseen); +e._xseen.sceneInfo.tmp.activeNavigation = true; +} +e._xseen.handlers = {}; +e._xseen.handlers.setactive = this.setactive; +}, +'fin' : function (e,p) {}, +'setactive' : function (ev) +{ +}, +}; +xseen.node.lighting_Light = { +'init' : function (e,p) +{ +var color = xseen.types.Color3toInt (e._xseen.fields.color); +var intensity = e._xseen.fields.intensity - 0; +var lamp, type=e._xseen.fields.type.toLowerCase(); +if (type == 'point') { +lamp = new THREE.PointLight (color, intensity); +lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); +lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); +} else if (type == 'spot') { +lamp = new THREE.SpotLight (color, intensity); +lamp.position.set(0-e._xseen.fields.direction[0], 0-e._xseen.fields.direction[1], 0-e._xseen.fields.direction[2]); +lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); +lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); +lamp.angle = Math.max(0.0, Math.min(1.5707963267948966192313216916398, e._xseen.fields.cutoffangle)); +lamp.penumbra = 1 - Math.max(0.0, Math.min(lamp.angle, e._xseen.fields.beamwidth)) / lamp.angle; +} else { // DirectionalLight (by default) +lamp = new THREE.DirectionalLight (color, intensity); +lamp.position.x = 0-e._xseen.fields.direction[0]; +lamp.position.y = 0-e._xseen.fields.direction[1]; +lamp.position.z = 0-e._xseen.fields.direction[2]; +} +p._xseen.children.push(lamp); +lamp = null; +} +, +'fin' : function (e,p) +{ +} +}; +xseen.node.appearance_Material = { +'init' : function (e,p) +{ +var transparency = e._xseen.fields.transparency - 0; +var shininess = e._xseen.fields.shininess - 0; +var colorDiffuse = xseen.types.Color3toInt (e._xseen.fields.diffusecolor); +var colorEmissive = xseen.types.Color3toInt (e._xseen.fields.emissivecolor); +var colorSpecular = xseen.types.Color3toInt (e._xseen.fields.specularcolor); +p._xseen.material = new THREE.MeshPhongMaterial( { +'color' : colorDiffuse, +'emissive' : colorEmissive, +'specular' : colorSpecular, +'shininess' : shininess, +'opacity' : 1.0-transparency, +'transparent' : (transparency > 0.0) ? true : false +} ); +e._xseen.animate['diffusecolor'] = p._xseen.material.color; +e._xseen.animate['emissivecolor'] = p._xseen.material.emissive; +e._xseen.animate['specularcolor'] = p._xseen.material.specular; +e._xseen.animate['transparency'] = p._xseen.material.opacity; +e._xseen.animate['shininess'] = p._xseen.material.shininess; +}, +'fin' : function (e,p) {} +}; +xseen.node.appearance_ImageTexture = { +'init' : function (e,p) +{ +p._xseen.texture = xseen.loader.ImageLoader.load(e._xseen.fields.url); +p._xseen.texture.wrapS = (e._xseen.fields.repeats) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; +p._xseen.texture.wrapT = (e._xseen.fields.repeatt) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; +}, +'fin' : function (e,p) {} +}; +xseen.node.appearance_Appearance = { +'init' : function (e,p) {}, +'fin' : function (e,p) +{ +if (typeof(e._xseen.texture) !== 'undefined' && e._xseen.texture !== null) { +e._xseen.material.map = e._xseen.texture; +} +p._xseen.appearance = e._xseen.material; +} +}; +xseen.node.geometry_Coordinate = { +'init' : function (e,p) +{ +var vertices = []; +for (var i=0; i 0) ? true : false; +var useColor = (color.length > 0) ? true : false; +var maxIndex = Math.max.apply(null, indices); +var minIndex = Math.min.apply(null, indices); +if (maxIndex >= vertices.length) { +console.log ('Maximum index ('+maxIndex+') exceeds vertex count ('+vertices.length+'). No geometry is created'); +return; +} +if (useNormals && maxIndex >= normals.length) { +console.log ('Maximum index ('+maxIndex+') exceeds normal count ('+normals.length+'). No geometry is created'); +return; +} +if (useColor && maxIndex >= color.length) { +console.log ('Maximum index ('+maxIndex+') exceeds color count ('+color.length+'). No geometry is created'); +return; +} +if (minIndex < 0) { +console.log ('Minimum index ('+minIndex+') less than zero. No geometry is created'); +return; +} +if (indices.length % 3 != 0) { +console.log ('Number of indices ('+indices.length+') not divisible by 3. No geometry is created'); +return; +} +var geometry = new THREE.Geometry(); +var normal_pz = new THREE.Vector3 (0, 0, 1); +var normal_mz = new THREE.Vector3 (0, 0, -1); +for (var i=0; i 0) {clip.duration = e._xseen.fields.duration;} +e._xseen.mixer.clipAction( clip ).play(); +} ); +} else { // Play a specific animation +var clip = THREE.AnimationClip.findByName(response.animations, e._xseen.fields.playonload); +var action = e._xseen.mixer.clipAction (clip); +action.play(); +} +} +} +} +}; +xseen.node.x_Route = { +'init' : function (e,p) +{ +var dest = e._xseen.fields.destination; +var hand = e._xseen.fields.handler; +var externalHandler = false; +if (e._xseen.fields.source == '' || +typeof(window[hand]) !== 'function' && +(dest == '' || e._xseen.fields.event == '' || e._xseen.fields.field == '')) { +xseen.debug.logError ('Route node missing field. No route setup. Source: '+e._xseen.fields.source+'.'+e._xseen.fields.event+'; Destination: '+dest+'.'+e._xseen.fields.field+'; Handler: '+hand); +return; +} else if (typeof(window[hand]) === 'function') { +externalHandler = true; +} +var eSource = document.getElementById (e._xseen.fields.source); +if (! externalHandler) { +var eDestination = document.getElementById (dest); +if (typeof(eSource) === 'undefined' || typeof(eDestination) === 'undefined') { +xseen.debug.logError ('Source or Destination node does not exist. No route setup'); +return; +} +var fField = xseen.nodes._getFieldInfo (eDestination.nodeName, e._xseen.fields.field); +if (typeof(fField) === 'undefined' || !fField.good) { +xseen.debug.logError ('Destination field does not exist or incorrectly specified. No route setup'); +return; +} +xseen.Events.addHandler (e, eSource, e._xseen.fields.event, eDestination, fField); +} else { +var handler = window[hand]; +eSource.addEventListener ('xseen', handler); +} +}, +'fin' : function (e,p) +{ +}, +'evHandler' : function (u) +{ +var de = u.e; +var df = u.f; +return de._xseen.handlers[df.handlerName]; +}, +}; +xseen.node.core_NOOP = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.core_WorldInfo = { +'init' : function (e,p) {parsing('WorldInfo', e)}, +'fin' : function (e,p) {} +}; +function parsing (s, e) { +xseen.debug.logInfo ('Parsing init details stub for ' + s); +} +xseen.node.unk_Shape = { +'init' : function (e,p) {}, +'fin' : function (e,p) +{ +if (typeof(e._xseen.materialProperty) !== 'undefined') { +e._xseen.appearance.vertexColors = THREE.VertexColors; +e._xseen.appearance._needsUpdate = true; +e._xseen.appearance.needsUpdate = true; +} +var mesh = new THREE.Mesh (e._xseen.geometry, e._xseen.appearance); +mesh.userData = e; +p._xseen.children.push(mesh); +p._xseen.sceneInfo.selectable.push(mesh); +mesh = null; +} +}; +xseen.node.grouping_Transform = { +'init' : function (e,p) +{ +var group = new THREE.Group(); +if (e.nodeName == "TRANSFORM") { +var rotation = xseen.types.Rotation2Quat(e._xseen.fields.rotation); +group.name = 'Transform children [' + e.id + ']'; +group.position.x = e._xseen.fields.translation[0]; +group.position.y = e._xseen.fields.translation[1]; +group.position.z = e._xseen.fields.translation[2]; +group.scale.x = e._xseen.fields.scale[0]; +group.scale.y = e._xseen.fields.scale[1]; +group.scale.z = e._xseen.fields.scale[2]; +group.quaternion.x = rotation.x; +group.quaternion.y = rotation.y; +group.quaternion.z = rotation.z; +group.quaternion.w = rotation.w; +e._xseen.animate['translation'] = group.position; +e._xseen.animate['rotation'] = group.quaternion; +e._xseen.animate['scale'] = group.scale; +} +e._xseen.sceneNode = group; +}, +'fin' : function (e,p) +{ +e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +e._xseen.sceneNode.add(child); +}); +p._xseen.children.push(e._xseen.sceneNode); +} +}; +xseen.node.networking_Inline = { +'init' : function (e,p) +{ +if (typeof(e._xseen.processedUrl) === 'undefined' || !e._xseen.requestedUrl) { +var uri = xseen.parseUrl (e._xseen.fields.url); +var type = uri.extension; +e._xseen.loadGroup = new THREE.Group(); +e._xseen.loadGroup.name = 'Inline content [' + e.id + ']'; +console.log ('Created Inline Group with UUID ' + e._xseen.loadGroup.uuid); +var userdata = {'requestType':'x3d', 'e':e, 'p':p} +if (type.toLowerCase() == 'json') { +userdata.requestType = 'json'; +xseen.loadMgr.loadJson (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); +} else { +xseen.loadMgr.loadXml (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); +} +e._xseen.requestedUrl = true; +} +p._xseen.children.push(e._xseen.loadGroup); +console.log ('Using Inline Group with UUID ' + e._xseen.loadGroup.uuid); +}, +'fin' : function (e,p) +{ +}, +'loadSuccess' : +function (response, userdata, xhr) { +userdata.e._xseen.processedUrl = true; +userdata.e._xseen.loadResponse = response; +console.log("download successful for "+userdata.e.id); +if (userdata.requestType == 'json') { +var tmp = {'scene': response}; +response = null; +response = (new JSONParser()).parseJavaScript(tmp); +} +var start = {'_xseen':0}; +var findSceneTag = function (fragment) { +if (typeof(fragment._xseen) === 'undefined') {fragment._xseen = {'childCount': -1};} +if (fragment.nodeName.toLowerCase() == 'scene') { +start = fragment; +return; +} else if (fragment.children.length > 0) { +for (fragment._xseen.childCount=0; fragment._xseen.childCount 0) { +userdata.e.appendChild(start.children[0]); +} +xseen.Parse(userdata.e, userdata.p, userdata.p._xseen.sceneInfo); +userdata.e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +userdata.e._xseen.loadGroup.add(child); +console.log ('...Adding ' + child.type + ' (' + child.name + ') to Inline Group? with UUID ' + userdata.e._xseen.loadGroup.uuid + ' (' + userdata.e._xseen.loadGroup.name + ')'); +}); +userdata.p._xseen.sceneInfo.scene.updateMatrixWorld(); +} else { +console.log("Found illegal X3D file -- no 'scene' tag"); +} +} +}; +xseen.node.core_Scene = { +'DEFAULT' : { +'Viewpoint' : { +'Position' : new THREE.Vector3 (0, 0, 10), +'Orientation' : '0 1 0 0', // TODO: fix (and below) when handling orientation +'Type' : 'perpsective', +'Motion' : 'none', +'MotionSpeed' : 1.0, +}, +'Navigation' : { +'Speed' : 1.0, // 16 spr (1 revolution per 16 seconds), in mseconds. +'Type' : 'none', +'Setup' : 'none', +} +}, +'init' : function (e,p) +{ +xseen.sceneInfo[0].stacks.Viewpoints.setDefault( +{ +'position' : this.DEFAULT.Viewpoint.Position, +'type' : this.DEFAULT.Viewpoint.Type, +'motion' : this.DEFAULT.Viewpoint.Motion, +'motionspeed': this.DEFAULT.Viewpoint.MotionSpeed / 1000, +'domNode' : e, +'fields' : {}, +} +); +xseen.sceneInfo[0].stacks.Navigation.setDefault( +{ +'speed' : this.DEFAULT.Navigation.Speed / 1000, +'type' : this.DEFAULT.Navigation.Type, +'setup' : this.DEFAULT.Navigation.Setup, +'domNode' : e, +'fields' : {}, +} +); +var width = e._xseen.sceneInfo.size.width; +var height = e._xseen.sceneInfo.size.height; +var x_renderer = new THREE.WebGLRenderer(); +x_renderer.setSize (width, height); +var perspectiveCamera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 ); +var orthoCamera = new THREE.OrthographicCamera( 75, width / height, 0.1, 1000 ); +perspectiveCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position +perspectiveCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position +perspectiveCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position +orthoCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position +orthoCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position +orthoCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position +var x_effect = new THREE.StereoEffect(x_renderer); +e.appendChild (x_renderer.domElement); +e._xseen.renderer = { +'canvas' : e._xseen.sceneInfo.scene, +'width' : width, +'height' : height, +'cameras' : { +'perspective' : perspectiveCamera, +'ortho' : orthoCamera, +'stereo' : perspectiveCamera, +}, // Removed .sceneInfo camera because this node defines the camera +'effects' : x_effect, +'renderEffects' : { +'normal' : x_renderer, +'perspective' : x_renderer, +'ortho' : x_renderer, +'stereo' : x_effect, +}, +'activeRender' : {}, +'activeCamera' : {}, +'controls' : {}, // Used for navigation +}; +e._xseen.renderer.activeRender = e._xseen.renderer.renderEffects.normal; +e._xseen.renderer.activeCamera = e._xseen.renderer.cameras.perspective; +}, +'fin' : function (e,p) +{ +e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +console.log('Adding child of type ' + child.type + ' (' + child.name + ')'); +e._xseen.renderer.canvas.add(child); +}); +xseen.dumpSceneGraph (); +var vp = xseen.sceneInfo[0].stacks.Viewpoints.getActive(); +var nav = xseen.sceneInfo[0].stacks.Navigation.getActive(); +var currentCamera = e._xseen.renderer.activeCamera; +var currentRenderer = e._xseen.renderer.activeRender; +currentCamera.position.x = vp.position.x; +currentCamera.position.y = vp.position.y; +currentCamera.position.z = vp.position.z; +e._xseen.renderer.controls = xseen.Navigation.setup[nav.setup] (currentCamera, currentRenderer); +xseen.debug.logInfo("Ready to kick off rendering loop"); +xseen.renderFrame(); +}, +}; +xseen.node.env_Background = { +'init' : function (e,p) +{ +var color = new THREE.Color(e._xseen.fields.skycolor[0], e._xseen.fields.skycolor[1], e._xseen.fields.skycolor[2]); +var textureCube = new THREE.CubeTextureLoader() +.load ([e._xseen.fields.srcright, +e._xseen.fields.srcleft, +e._xseen.fields.srctop, +e._xseen.fields.srcbottom, +e._xseen.fields.srcfront, +e._xseen.fields.srcback], +this.loadSuccess({'e':e, 'p':p}) +); +e._xseen.sceneInfo.scene.background = color; +}, +'fin' : function (e,p) +{ +p._xseen.appearance = e._xseen.material; +}, +'loadSuccess' : function (userdata) +{ +var e = userdata.e; +var p = userdata.p; +return function (textureCube) +{ +e._xseen.processedUrl = true; +e._xseen.loadTexture = textureCube; +e._xseen.sceneInfo.scene.background = textureCube; +} +}, +}; +xseen.node.x_Animate = { +'init' : function (e,p) +{ +var delay = e._xseen.fields.delay * 1000; // Convert to milliseconds +var duration = e._xseen.fields.duration * 1000; // Convert to milliseconds +var repeat = (e._xseen.fields.repeat < 0) ? Infinity : e._xseen.fields.repeat; +var interpolator = e._xseen.fields.interpolator; +var easing = e._xseen.fields.easing; +var fields = xseen.parseTable[p.localName.toLowerCase()].fields; +var fieldIndex = xseen.parseTable[p.localName.toLowerCase()].fieldIndex; +var toField = e._xseen.fields.field; +var toFieldIndex = fieldIndex[toField]; +if (typeof(fields[toFieldIndex]) === 'undefined') { +xseen.debug.logInfo("Field '" + toField + "' not found in parent (" + p.localName.toLowerCase() + "). No animation performed."); +return; +} +var fieldObject = fields[toFieldIndex].clone().setFieldName('to'); // Parse table entry for 'toField' +var to = xseen.nodes._parseField(fieldObject, e); // Parsed data -- need to convert to THREE format +var interpolation; +if (fieldObject.type == 'SFVec3f') { +interpolation = TWEEN.Interpolation.Linear; +to = xseen.types.Vector3(to); +xseen.debug.logInfo("Interpolating field '" + toField + "' as 3-space."); +} else if (fieldObject.type == 'SFColor') { +interpolation = this.Interpolator.color; +to = new THREE.Color (xseen.types.Color3toInt(to)); +xseen.debug.logInfo("Interpolation field '" + toField + "' as color."); +} else if (fieldObject.type == 'SFRotation') { +interpolation = this.Interpolator.slerp; +to = xseen.types.Rotation2Quat(to); +xseen.debug.logInfo("Interpolation field '" + toField + "' as rotation."); +} else { +xseen.debug.logInfo("Field '" + toField + "' not converted to THREE format. No animation performed."); +return; +} +var fieldTHREE = p._xseen.animate[toField]; // THREE field for animation +var tween = new TWEEN.Tween(fieldTHREE) +.to(to, duration) +.delay(delay) +.repeat(repeat) +.interpolation(interpolation); +var easingType = e._xseen.fields.easingtype; +easingType = easingType.charAt(0).toUpperCase() + easingType.slice(1); +easing = (easingType != 'Linear' && easing == '') ? 'inout' : easing; +if (easing != '') { +easing = easing.replace('in', 'In').replace('out', 'Out'); +easingType = (easingType == 'Linear') ? 'Quadratic' : easingType; +e._xseen.fields.easing = easing; +e._xseen.fields.easingtype = easingType; +tween.easing(TWEEN.Easing[easingType][easing]); +} +e._xseen.initialValue = fieldTHREE.clone(); +e._xseen.animatingField = fieldTHREE; +e._xseen.handlers = {}; +e._xseen.handlers.setstart = this.setstart; +e._xseen.handlers.setstop = this.setstop; +e._xseen.handlers.setpause = this.setpause; +e._xseen.handlers.setresetstart = this.setresetstart; +e._xseen.animating = tween; +p._xseen.animation.push (tween); +tween.start(); +}, +'fin' : function (e,p) {}, +'setstart' : function (ev) +{ +console.log ('Starting animation'); +this.destination._xseen.animating.start(); +}, +'setstop' : function (ev) +{ +console.log ('Stopping animation'); +this.destination._xseen.animating.stop(); +}, +'setpause' : function (ev) +{ +console.log ('Pausing (really stopping) animation'); +this.destination._xseen.animating.stop(); +}, +'setresetstart' : function (ev) // TODO: Create seperate 'reset' method +{ +console.log ('Reset and start animation'); +this.destination._xseen.animatingField = this.destination._xseen.initialValue; +this.destination._xseen.animating.start(); +}, +'Interpolator' : { +'slerp' : function (v,k) +{ +var m = v.length - 1; +var f = m * k; +var i = Math.floor(f); +if (k < 0) { +return v[0].slerp(v[1], f); +} +if (k > 1) { +return v[m].slerp(v[m-1], m-f); +} +return v[i].slerp (v[i + 1 > m ? m : i + 1], f-i); +}, +'color' : function (v,k) +{ +var m = v.length - 1; +var f = m * k; +var i = Math.floor(f); +var fn = this.slerpCompute; +if (k < 0) { +return v[0].lerp(v[1], f); +} +if (k > 1) { +return v[m].lerp(v[m-1], m-f); +} +return v[i].lerp (v[i + 1 > m ? m : i + 1], f - i); +}, +}, +}; +xseen._addAframeAppearance = function (node) { +node +.addField('ambient-occlusion-map', 'SFString', '') +.addField('ambient-occlusion-map-intensity', 'SFFloat', 1) +.addField('ambient-occlusion-texture-offset', 'SFVec2f', '0 0') +.addField('ambient-occlusion-texture-repeat', 'SFVec2f', '1 1') +.addField('color', 'Color', '#FFF') +.addField('displacement-bias', 'SFFloat', 0.5) +.addField('displacement-map', 'SFString', '') +.addField('displacement-scale', 'SFFloat', 1) +.addField('displacement-texture-offset', 'SFVec2f', '0 0') +.addField('displacement-texture-repeat', 'SFVec2f', '1 1') +.addField('env-map', 'SFString', '') +.addField('fog', 'SFBool', true) +.addField('metalness', 'SFFloat', 0) +.addField('normal-map', 'SFString', '') +.addField('normal-scale', 'SFVec2f', '1 1') +.addField('normal-texture-offset', 'SFVec2f', '0 0') +.addField('normal-texture-repeat', 'SFVec2f', '1 1') +.addField('repeat', 'SFVec2f', '1 1') +.addField('roughness', 'SFFloat', 0.5) +.addField('spherical-env-map', 'SFString', '') +.addField('src', 'SFString', '') +.addField('wireframe', 'SFBool', false) +.addField('wireframe-linewidth', 'SFInt', 2) +.addNode(); +} +xseen.nodes._defineNode('a-entity', 'A-Frame', 'af_Entity') +.addField('geometry', 'SFString', '') +.addField('material', 'SFString', '') +.addField('light', 'SFString', '') +.addNode(); +var node; +node = xseen.nodes._defineNode('a-box', 'A-Frame', 'af_Box') +.addField('depth', 'SFFloat', 1) +.addField('height', 'SFFloat', 1) +.addField('width', 'SFFloat', 512) +.addField('segments-depth', 'SFInt', 1) +.addField('segments-height', 'SFInt', 1) +.addField('segments-width', 'SFInt', 1); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-cone', 'A-Frame', 'af_Cone') +.addField('height', 'SFFloat', 1) +.addField('radius', 'SFFloat', 1) +.addField('open-ended', 'SFBool', false) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 1) +.addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-cylinder', 'A-Frame', 'af_Cylinder') +.addField('height', 'SFFloat', 1) +.addField('open-ended', 'SFBool', false) +.addField('radius-bottom', 'SFFloat', 1) +.addField('radius-top', 'SFFloat', 1) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 1) +.addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-dodecahedron', 'A-Frame', 'af_Dodecahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-icosahedron', 'A-Frame', 'af_Icosahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-octahedron', 'A-Frame', 'af_Octahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-sphere', 'A-Frame', 'af_Sphere') +.addField('radius', 'SFFloat', 1) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 180) +.addField('phi-start', 'SFFloat', 0) +.addField('phi-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 18) +.addField('segments-width', 'SFInt', 36); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-tetrahedron', 'A-Frame', 'af_Tetrahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-torus', 'A-Frame', 'af_Torus') +.addField('radius', 'SFFloat', 2) +.addField('tube', 'SFFloat', 1) +.addField('arc', 'SFFloat', 360) +.addField('segments-radial', 'SFInt', 8) +.addField('segments-tubular', 'SFInt', 6); +xseen._addAframeAppearance (node); +xseen.nodes._defineNode('a-assets', 'A-Frame', 'af_Assets') +.addNode(); +xseen.nodes._defineNode('a-asset-item', 'A-Frame', 'af_AssetItem') +.addField('src', 'SFString', '') +.addNode(); +xseen.nodes._defineNode('a-mixin', 'A-Frame', 'af_Mixin') +.addField('*', 'SFString', '') +.addNode(); +xseen.nodes._defineNode('Cone', 'Geometry3D', 'geometry3D_Cone') +.addField('bottomRadius', 'SFFloat', 1) +.addField('height', 'SFFloat', 2) +.addField('bottom', 'SFBool', true) +.addField('side', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode('Box', 'Geometry3D', 'geometry3D_Box') +.addField('size', 'SFVec3f', [1,1,1]) +.addNode(); +xseen.nodes._defineNode('Sphere', 'Geometry3D', 'geometry3D_Sphere') +.addField('radius', 'SFFloat', '1') +.addNode(); +xseen.nodes._defineNode('Cylinder', 'Geometry3D', 'geometry3D_Cylinder') +.addField('radius', 'SFFloat', 1) +.addField('height', 'SFFloat', 2) +.addField('bottom', 'SFBool', true) +.addField('side', 'SFBool', true) +.addField('top', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode ('Material', 'Appearance', 'appearance_Material') +.addField({name:'diffuseColor', datatype:'SFColor', defaultValue:[.8,.8,.8], animatable:true}) +.addField({name:'emissiveColor',datatype: 'SFColor', defaultValue:[0,0,0], animatable:true}) +.addField({name:'specularColor', datatype:'SFColor', defaultValue:[0,0,0], animatable:true}) +.addField({name:'transparency', datatype:'SFFloat', defaultValue:'0', animatable:true}) +.addField({name:'shininess', datatype:'SFFloat', defaultValue:'0', animatable:true}) +.addNode(); +xseen.nodes._defineNode ('Transform', 'Grouping', 'grouping_Transform') +.addField({name:'translation', datatype:'SFVec3f', defaultValue:[0,0,0], animatable:true}) +.addField({name:'scale', datatype:'SFVec3f', defaultValue:[1,1,1], animatable:true}) +.addField({name:'rotation', datatype:'SFRotation', defaultValue:xseen.types.SFRotation('0 1 0 0',''), animatable:true}) +.addNode(); +xseen.nodes._defineNode ('Group', 'Grouping', 'grouping_Transform') +.addNode(); +xseen.nodes._defineNode ('Light', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) // DirectionalLight +.addField('location', 'SFVec3f', [0,0,0]) // PointLight & SpotLight +.addField('radius', 'SFFloat', '100') // PointLight & SpotLight +.addField('attenuation', 'SFVec3f', [1,0,0]) // PointLight & SpotLight +.addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // SpotLight +.addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // SpotLight +.addField('color', 'SFColor', [1,1,1]) // General +.addField('intensity', 'SFFloat', '1') // General +.addField({name:'type', datatype:'EnumerateString', defaultValue:'Directional', enumerated:['Directional', 'Spot', 'Point'], animatable:true}) +.addNode(); +xseen.nodes._defineNode ('DirectionalLight', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Directional') +.addNode(); +xseen.nodes._defineNode ('PointLight', 'Lighting', 'lighting_Light') +.addField('location', 'SFVec3f', [0,0,0]) +.addField('radius', 'SFFloat', '100') +.addField('attenuation', 'SFVec3f', [1,0,0]) +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Point') +.addNode(); +xseen.nodes._defineNode ('SpotLight', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) +.addField('radius', 'SFFloat', '100') +.addField('attenuation', 'SFVec3f', [1,0,0]) +.addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // pi/4 +.addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // pi/2 +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Spot') +.addNode(); +xseen.nodes._defineNode ('Viewpoint', 'Controls', 'unk_Viewpoint') +.addField('position', 'SFVec3f', '0 0 10') +.addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) +.addField('description', 'SFString', '') +.addField({name:'cameratype', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) +.addField({name:'type', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) +.addField({name:'motion', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'turntable', 'tilt'], animatable:false}) +.addField('motionspeed', 'SFFloat', 16) +.addField('active', 'SFBool', true) // incoming event +.addNode(); +xseen.nodes._defineNode ('NavigationMode', 'Controls', 'controls_Navigation') +.addField('speed', 'SFFloat', 1.) +.addField({name:'type', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'orbit', 'fly', 'examine', 'trackball'], animatable:false}) +.addNode(); +xseen.nodes._defineNode ('Camera', 'Controls', 'unk_Viewpoint') +.addField('position', 'SFVec3f', [0,0,10]) +.addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) +.addNode(); +xseen.nodes._defineNode ('Inline', 'Networking', 'networking_Inline') +.addField('url', 'SFString', '') +.addNode(); +xseen.nodes._defineNode ('scene', 'Core', 'core_Scene') +.addNode(); +xseen.nodes._defineNode ('canvas', 'Core', 'core_NOOP') +.addNode(); +xseen.nodes._defineNode ('WorldInfo', 'Core', 'core_WorldInfo') +.addNode(); +xseen.nodes._defineNode ('Appearance', 'Appearance', 'appearance_Appearance') +.addNode(); +xseen.nodes._defineNode ('ImageTexture', 'Appearance', 'appearance_ImageTexture') +.addField('url', 'SFString', '') +.addField('repeatS', 'SFBool', true) +.addField('repeatT', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode ('Shape', 'Shape', 'unk_Shape') +.addNode(); +xseen.nodes._defineNode('Background', 'Environmental', 'env_Background') +.addField('skyColor', 'SFColor', [0,0,0]) +.addField('srcFront', 'SFString', '') +.addField('srcBack', 'SFString', '') +.addField('srcTop', 'SFString', '') +.addField('srcBottom', 'SFString', '') +.addField('srcLeft', 'SFString', '') +.addField('srcRight', 'SFString', '') +.addField('backgroundIsCube', 'SFBool', 'true') +.addNode(); +xseen.nodes._defineNode('TriangleSet', 'Geometry', 'geometry_TriangleSet') +.addField('ccw', 'SFBool', 'true') +.addField('colorPerVertex', 'SFBool', 'true') +.addField('solid', 'SFBool', 'true') +.addNode(); +xseen.nodes._defineNode('IndexedTriangleSet', 'Geometry', 'geometry_IndexedTriangleSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('index', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('Coordinate', 'Geometry', 'geometry_Coordinate') +.addField('point', 'MFVec3f', []) +.addNode(); +xseen.nodes._defineNode('Normal', 'Geometry', 'geometry_Normal') +.addField('vector', 'MFVec3f', []) +.addNode(); +xseen.nodes._defineNode('Color', 'Geometry', 'geometry_Color') +.addField('color', 'MFColor', []) +.addNode(); +xseen.nodes._defineNode('IndexedFaceSet', 'Geometry', 'geometry_IndexedFaceSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('coordIndex', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('IndexedQuadSet', 'Geometry', 'geometry_IndexedQuadSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('index', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('QuadSet', 'Geometry', 'geometry_QuadSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode('model', 'XSeen', 'x_Model') +.addField('src', 'SFString', '') +.addField('playonload', 'SFString', '') +.addField('duration', 'SFFloat', '-1') +.addNode(); +xseen.nodes._defineNode('animate', 'XSeen', 'x_Animate') +.addField('field', 'SFString', '') +.addField('to', 'MFFloat', '') // Needs to be 'field' datatype. That is not known until node-parse. For now insist on numeric array +.addField('delay', 'SFTime', 0) +.addField('duration', 'SFTime', 0) +.addField('repeat', 'SFInt', 0) +.addField({name:'interpolator', datatype:'EnumerateString', defaultValue:'position', enumerated:['position', 'rotation', 'color'], animatable:false}) +.addField({name:'Easing', datatype:'EnumerateString', defaultValue:'', enumerated:['', 'in', 'out', 'inout'], animatable:false}) +.addField({name:'EasingType', datatype:'EnumerateString', defaultValue:'linear', enumerated:['linear', 'quadratic', 'sinusoidal', 'exponential', 'elastic', 'bounce'], animatable:false}) +.addField('start', 'SFBool', true) // incoming event, need to set timer trigger +.addField('stop', 'SFBool', true) // incoming event, need to set timer trigger +.addField('resetstart', 'SFBool', true) // incoming event, need to set timer trigger +.addField('pause', 'SFBool', true) // incoming event, need to set timer trigger +.addNode(); +xseen.nodes._defineNode('route', 'XSeen', 'x_Route') +.addField('source', 'SFString', '') +.addField('event', 'SFString', '') +.addField('destination', 'SFString', '') +.addField('field', 'SFString', '') +.addField('handler', 'SFString', '') +.addNode(); \ No newline at end of file diff --git a/Release/XSeen.js b/Release/XSeen.js new file mode 100644 index 0000000..23f143f --- /dev/null +++ b/Release/XSeen.js @@ -0,0 +1,14404 @@ +/* + * XSeen V0.4.6-rc1+25_0e9dbd1 + * Built Tue Jul 11 08:12:44 2017 + * + +Dual licensed under the MIT and GPL licenses. + +==[MIT]==================================================================== +Copyright (c) 2017, Daly Realism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +==[GPL]==================================================================== + +XSeen - Declarative 3D for HTML + +Copyright (C) 2017, Daly Realism + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +=== COPYRIGHT +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (C) 2017, Daly Realism for XSeen +Copyright, Fraunhofer for X3DOM +Copyright, Mozilla for A-Frame +Copyright, THREE and Khronos for various parts of THREE.js +Copyright (C) 2017, John Carlson for JSON->XML converter (JSONParser.js) + +=== +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + */ +// File: utils/ColladaLoader.js +/** +* @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com +* @author Tony Parisi / http://www.tonyparisi.com/ +*/ + +THREE.ColladaLoader = function () { + + var COLLADA = null; + var scene = null; + var visualScene; + var kinematicsModel; + + var readyCallbackFunc = null; + + var sources = {}; + var images = {}; + var animations = {}; + var controllers = {}; + var geometries = {}; + var materials = {}; + var effects = {}; + var cameras = {}; + var lights = {}; + + var animData; + var kinematics; + var visualScenes; + var kinematicsModels; + var baseUrl; + var morphs; + var skins; + + var flip_uv = true; + var preferredShading = THREE.SmoothShading; + + var options = { + // Force Geometry to always be centered at the local origin of the + // containing Mesh. + centerGeometry: false, + + // Axis conversion is done for geometries, animations, and controllers. + // If we ever pull cameras or lights out of the COLLADA file, they'll + // need extra work. + convertUpAxis: false, + + subdivideFaces: true, + + upAxis: 'Y', + + // For reflective or refractive materials we'll use this cubemap + defaultEnvMap: null + + }; + + var colladaUnit = 1.0; + var colladaUp = 'Y'; + var upConversion = null; + + function load ( url, readyCallback, progressCallback, failCallback ) { + + var length = 0; + + if ( document.implementation && document.implementation.createDocument ) { + + var request = new XMLHttpRequest(); + + request.onreadystatechange = function() { + + if ( request.readyState === 4 ) { + + if ( request.status === 0 || request.status === 200 ) { + + if ( request.response ) { + + readyCallbackFunc = readyCallback; + parse( request.response, undefined, url ); + + } else { + + if ( failCallback ) { + + failCallback( { type: 'error', url: url } ); + + } else { + + console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); + + } + + } + + }else{ + + if( failCallback ){ + + failCallback( { type: 'error', url: url } ); + + }else{ + + console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' ); + + } + + } + + } else if ( request.readyState === 3 ) { + + if ( progressCallback ) { + + if ( length === 0 ) { + + length = request.getResponseHeader( "Content-Length" ); + + } + + progressCallback( { total: length, loaded: request.responseText.length } ); + + } + + } + + }; + + request.open( "GET", url, true ); + request.send( null ); + + } else { + + alert( "Don't know how to parse XML!" ); + + } + + } + + function parse( text, callBack, url ) { + + COLLADA = new DOMParser().parseFromString( text, 'text/xml' ); + callBack = callBack || readyCallbackFunc; + + if ( url !== undefined ) { + + var parts = url.split( '/' ); + parts.pop(); + baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; + + } + + parseAsset(); + setUpConversion(); + images = parseLib( "library_images image", _Image, "image" ); + materials = parseLib( "library_materials material", Material, "material" ); + effects = parseLib( "library_effects effect", Effect, "effect" ); + geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); + cameras = parseLib( "library_cameras camera", Camera, "camera" ); + lights = parseLib( "library_lights light", Light, "light" ); + controllers = parseLib( "library_controllers controller", Controller, "controller" ); + animations = parseLib( "library_animations animation", Animation, "animation" ); + visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); + kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); + + morphs = []; + skins = []; + + visualScene = parseScene(); + scene = new THREE.Group(); + + for ( var i = 0; i < visualScene.nodes.length; i ++ ) { + + scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); + + } + + // unit conversion + scene.scale.multiplyScalar( colladaUnit ); + + createAnimations(); + + kinematicsModel = parseKinematicsModel(); + createKinematics(); + + var result = { + + scene: scene, + morphs: morphs, + skins: skins, + animations: animData, + kinematics: kinematics, + dae: { + images: images, + materials: materials, + cameras: cameras, + lights: lights, + effects: effects, + geometries: geometries, + controllers: controllers, + animations: animations, + visualScenes: visualScenes, + visualScene: visualScene, + scene: visualScene, + kinematicsModels: kinematicsModels, + kinematicsModel: kinematicsModel + } + + }; + + if ( callBack ) { + + callBack( result ); + + } + + return result; + + } + + function setPreferredShading ( shading ) { + + preferredShading = shading; + + } + + function parseAsset () { + + var elements = COLLADA.querySelectorAll('asset'); + + var element = elements[0]; + + if ( element && element.childNodes ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'unit': + + var meter = child.getAttribute( 'meter' ); + + if ( meter ) { + + colladaUnit = parseFloat( meter ); + + } + + break; + + case 'up_axis': + + colladaUp = child.textContent.charAt(0); + break; + + } + + } + + } + + } + + function parseLib ( q, classSpec, prefix ) { + + var elements = COLLADA.querySelectorAll(q); + + var lib = {}; + + var i = 0; + + var elementsLength = elements.length; + + for ( var j = 0; j < elementsLength; j ++ ) { + + var element = elements[j]; + var daeElement = ( new classSpec() ).parse( element ); + + if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); + lib[ daeElement.id ] = daeElement; + + } + + return lib; + + } + + function parseScene() { + + var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; + + if ( sceneElement ) { + + var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); + return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; + + } else { + + return null; + + } + + } + + function parseKinematicsModel() { + + var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; + + if ( kinematicsModelElement ) { + + var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); + return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; + + } else { + + return null; + + } + + } + + function createAnimations() { + + animData = []; + + // fill in the keys + recurseHierarchy( scene ); + + } + + function recurseHierarchy( node ) { + + var n = visualScene.getChildById( node.colladaId, true ), + newData = null; + + if ( n && n.keys ) { + + newData = { + fps: 60, + hierarchy: [ { + node: n, + keys: n.keys, + sids: n.sids + } ], + node: node, + name: 'animation_' + node.name, + length: 0 + }; + + animData.push(newData); + + for ( var i = 0, il = n.keys.length; i < il; i ++ ) { + + newData.length = Math.max( newData.length, n.keys[i].time ); + + } + + } else { + + newData = { + hierarchy: [ { + keys: [], + sids: [] + } ] + } + + } + + for ( var i = 0, il = node.children.length; i < il; i ++ ) { + + var d = recurseHierarchy( node.children[i] ); + + for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { + + newData.hierarchy.push( { + keys: [], + sids: [] + } ); + + } + + } + + return newData; + + } + + function calcAnimationBounds () { + + var start = 1000000; + var end = -start; + var frames = 0; + var ID; + for ( var id in animations ) { + + var animation = animations[ id ]; + ID = ID || animation.id; + for ( var i = 0; i < animation.sampler.length; i ++ ) { + + var sampler = animation.sampler[ i ]; + + sampler.create(); + + start = Math.min( start, sampler.startTime ); + end = Math.max( end, sampler.endTime ); + frames = Math.max( frames, sampler.input.length ); + + } + + } + + return { start:start, end:end, frames:frames,ID:ID }; + + } + + function createMorph ( geometry, ctrl ) { + + var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; + + if ( !morphCtrl || !morphCtrl.morph ) { + + console.log("could not find morph controller!"); + return; + + } + + var morph = morphCtrl.morph; + + for ( var i = 0; i < morph.targets.length; i ++ ) { + + var target_id = morph.targets[ i ]; + var daeGeometry = geometries[ target_id ]; + + if ( !daeGeometry.mesh || + !daeGeometry.mesh.primitives || + !daeGeometry.mesh.primitives.length ) { + continue; + } + + var target = daeGeometry.mesh.primitives[ 0 ].geometry; + + if ( target.vertices.length === geometry.vertices.length ) { + + geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); + + } + + } + + geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); + + } + + function createSkin ( geometry, ctrl, applyBindShape ) { + + var skinCtrl = controllers[ ctrl.url ]; + + if ( !skinCtrl || !skinCtrl.skin ) { + + console.log( "could not find skin controller!" ); + return; + + } + + if ( !ctrl.skeleton || !ctrl.skeleton.length ) { + + console.log( "could not find the skeleton for the skin!" ); + return; + + } + + var skin = skinCtrl.skin; + var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); + var hierarchy = []; + + applyBindShape = applyBindShape !== undefined ? applyBindShape : true; + + var bones = []; + geometry.skinWeights = []; + geometry.skinIndices = []; + + //createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 ); + //createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights ); + + /* + geometry.animation = { + name: 'take_001', + fps: 30, + length: 2, + JIT: true, + hierarchy: hierarchy + }; + */ + + if ( applyBindShape ) { + + for ( var i = 0; i < geometry.vertices.length; i ++ ) { + + geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); + + } + + } + + } + + function setupSkeleton ( node, bones, frame, parent ) { + + node.world = node.world || new THREE.Matrix4(); + node.localworld = node.localworld || new THREE.Matrix4(); + node.world.copy( node.matrix ); + node.localworld.copy( node.matrix ); + + if ( node.channels && node.channels.length ) { + + var channel = node.channels[ 0 ]; + var m = channel.sampler.output[ frame ]; + + if ( m instanceof THREE.Matrix4 ) { + + node.world.copy( m ); + node.localworld.copy(m); + if (frame === 0) + node.matrix.copy(m); + } + + } + + if ( parent ) { + + node.world.multiplyMatrices( parent, node.world ); + + } + + bones.push( node ); + + for ( var i = 0; i < node.nodes.length; i ++ ) { + + setupSkeleton( node.nodes[ i ], bones, frame, node.world ); + + } + + } + + function setupSkinningMatrices ( bones, skin ) { + + // FIXME: this is dumb... + + for ( var i = 0; i < bones.length; i ++ ) { + + var bone = bones[ i ]; + var found = -1; + + if ( bone.type != 'JOINT' ) continue; + + for ( var j = 0; j < skin.joints.length; j ++ ) { + + if ( bone.sid === skin.joints[ j ] ) { + + found = j; + break; + + } + + } + + if ( found >= 0 ) { + + var inv = skin.invBindMatrices[ found ]; + + bone.invBindMatrix = inv; + bone.skinningMatrix = new THREE.Matrix4(); + bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) + bone.animatrix = new THREE.Matrix4(); + + bone.animatrix.copy(bone.localworld); + bone.weights = []; + + for ( var j = 0; j < skin.weights.length; j ++ ) { + + for (var k = 0; k < skin.weights[ j ].length; k ++ ) { + + var w = skin.weights[ j ][ k ]; + + if ( w.joint === found ) { + + bone.weights.push( w ); + + } + + } + + } + + } else { + + console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); + + bone.skinningMatrix = new THREE.Matrix4(); + bone.weights = []; + + } + } + + } + + //Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix + function flattenSkeleton(skeleton) { + + var list = []; + var walk = function(parentid, node, list) { + + var bone = {}; + bone.name = node.sid; + bone.parent = parentid; + bone.matrix = node.matrix; + var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; + bone.matrix.decompose(data[0], data[1], data[2]); + + bone.pos = [ data[0].x,data[0].y,data[0].z ]; + + bone.scl = [ data[2].x,data[2].y,data[2].z ]; + bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; + list.push(bone); + + for (var i in node.nodes) { + + walk(node.sid, node.nodes[i], list); + + } + + }; + + walk(-1, skeleton, list); + return list; + + } + + //Move the vertices into the pose that is proper for the start of the animation + function skinToBindPose(geometry,skeleton,skinController) { + + var bones = []; + setupSkeleton( skeleton, bones, -1 ); + setupSkinningMatrices( bones, skinController.skin ); + var v = new THREE.Vector3(); + var skinned = []; + + for (var i = 0; i < geometry.vertices.length; i ++) { + + skinned.push(new THREE.Vector3()); + + } + + for ( i = 0; i < bones.length; i ++ ) { + + if ( bones[ i ].type != 'JOINT' ) continue; + + for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { + + var w = bones[ i ].weights[ j ]; + var vidx = w.index; + var weight = w.weight; + + var o = geometry.vertices[vidx]; + var s = skinned[vidx]; + + v.x = o.x; + v.y = o.y; + v.z = o.z; + + v.applyMatrix4( bones[i].skinningMatrix ); + + s.x += (v.x * weight); + s.y += (v.y * weight); + s.z += (v.z * weight); + } + + } + + for (var i = 0; i < geometry.vertices.length; i ++) { + + geometry.vertices[i] = skinned[i]; + + } + + } + + function applySkin ( geometry, instanceCtrl, frame ) { + + var skinController = controllers[ instanceCtrl.url ]; + + frame = frame !== undefined ? frame : 40; + + if ( !skinController || !skinController.skin ) { + + console.log( 'ColladaLoader: Could not find skin controller.' ); + return; + + } + + if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { + + console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); + return; + + } + + var animationBounds = calcAnimationBounds(); + var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); + + //flatten the skeleton into a list of bones + var bonelist = flattenSkeleton(skeleton); + var joints = skinController.skin.joints; + + //sort that list so that the order reflects the order in the joint list + var sortedbones = []; + for (var i = 0; i < joints.length; i ++) { + + for (var j = 0; j < bonelist.length; j ++) { + + if (bonelist[j].name === joints[i]) { + + sortedbones[i] = bonelist[j]; + + } + + } + + } + + //hook up the parents by index instead of name + for (var i = 0; i < sortedbones.length; i ++) { + + for (var j = 0; j < sortedbones.length; j ++) { + + if (sortedbones[i].parent === sortedbones[j].name) { + + sortedbones[i].parent = j; + + } + + } + + } + + + var i, j, w, vidx, weight; + var v = new THREE.Vector3(), o, s; + + // move vertices to bind shape + for ( i = 0; i < geometry.vertices.length; i ++ ) { + geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); + } + + var skinIndices = []; + var skinWeights = []; + var weights = skinController.skin.weights; + + // hook up the skin weights + // TODO - this might be a good place to choose greatest 4 weights + for ( var i =0; i < weights.length; i ++ ) { + + var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); + var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); + + skinIndices.push(indicies); + skinWeights.push(weight); + + } + + geometry.skinIndices = skinIndices; + geometry.skinWeights = skinWeights; + geometry.bones = sortedbones; + // process animation, or simply pose the rig if no animation + + //create an animation for the animated bones + //NOTE: this has no effect when using morphtargets + var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; + + for (var j = 0; j < sortedbones.length; j ++) { + + animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); + + } + + console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); + + + + skinToBindPose(geometry, skeleton, skinController); + + + for ( frame = 0; frame < animationBounds.frames; frame ++ ) { + + var bones = []; + var skinned = []; + // process the frame and setup the rig with a fresh + // transform, possibly from the bone's animation channel(s) + + setupSkeleton( skeleton, bones, frame ); + setupSkinningMatrices( bones, skinController.skin ); + + for (var i = 0; i < bones.length; i ++) { + + for (var j = 0; j < animationdata.hierarchy.length; j ++) { + + if (animationdata.hierarchy[j].name === bones[i].sid) { + + var key = {}; + key.time = (frame / 30); + key.matrix = bones[i].animatrix; + + if (frame === 0) + bones[i].matrix = key.matrix; + + var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; + key.matrix.decompose(data[0], data[1], data[2]); + + key.pos = [ data[0].x,data[0].y,data[0].z ]; + + key.scl = [ data[2].x,data[2].y,data[2].z ]; + key.rot = data[1]; + + animationdata.hierarchy[j].keys.push(key); + + } + + } + + } + + geometry.animation = animationdata; + + } + + } + + function createKinematics() { + + if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { + kinematics = undefined; + return; + } + + var jointMap = {}; + + var _addToMap = function( jointIndex, parentVisualElement ) { + + var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); + var colladaNode = visualScene.getChildById( parentVisualElementId, true ); + var joint = kinematicsModel.joints[ jointIndex ]; + + scene.traverse(function( node ) { + + if ( node.colladaId == parentVisualElementId ) { + + jointMap[ jointIndex ] = { + node: node, + transforms: colladaNode.transforms, + joint: joint, + position: joint.zeroPosition + }; + + } + + }); + + }; + + kinematics = { + + joints: kinematicsModel && kinematicsModel.joints, + + getJointValue: function( jointIndex ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + return jointData.position; + + } else { + + console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + }, + + setJointValue: function( jointIndex, value ) { + + var jointData = jointMap[ jointIndex ]; + + if ( jointData ) { + + var joint = jointData.joint; + + if ( value > joint.limits.max || value < joint.limits.min ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); + + } else if ( joint.static ) { + + console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); + + } else { + + var threejsNode = jointData.node; + var axis = joint.axis; + var transforms = jointData.transforms; + + var matrix = new THREE.Matrix4(); + var m1 = new THREE.Matrix4(); + + for (i = 0; i < transforms.length; i ++ ) { + + var transform = transforms[ i ]; + + // kinda ghetto joint detection + + if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { + + // apply actual joint value here + switch ( joint.type ) { + + case 'revolute': + + matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); + break; + + case 'prismatic': + + matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); + break; + + default: + + console.warn( 'setJointValue: unknown joint type: ' + joint.type ); + break; + + } + + } else { + + switch ( transform.type ) { + + case 'matrix': + + matrix.multiply( transform.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); + + break; + + } + } + } + + // apply the matrix to the threejs node + var elementsFloat32Arr = matrix.elements; + var elements = Array.prototype.slice.call( elementsFloat32Arr ); + + var elementsRowMajor = [ + elements[ 0 ], + elements[ 4 ], + elements[ 8 ], + elements[ 12 ], + elements[ 1 ], + elements[ 5 ], + elements[ 9 ], + elements[ 13 ], + elements[ 2 ], + elements[ 6 ], + elements[ 10 ], + elements[ 14 ], + elements[ 3 ], + elements[ 7 ], + elements[ 11 ], + elements[ 15 ] + ]; + + threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); + threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); + + jointMap[ jointIndex ].position = value; + + } + + } else { + + console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); + + } + + } + + }; + + var element = COLLADA.querySelector('scene instance_kinematics_scene'); + + if ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_joint_axis': + + var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); + var axis = child.querySelector('axis param').textContent; + var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); + var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); + + if ( visualTargetElement ) { + var parentVisualElement = visualTargetElement.parentElement; + _addToMap(jointIndex, parentVisualElement); + } + + break; + + default: + + break; + + } + + } + } + + } + + function createSceneGraph ( node, parent ) { + + var obj = new THREE.Object3D(); + var skinned = false; + var skinController; + var morphController; + var i, j; + + // FIXME: controllers + + for ( i = 0; i < node.controllers.length; i ++ ) { + + var controller = controllers[ node.controllers[ i ].url ]; + + switch ( controller.type ) { + + case 'skin': + + if ( geometries[ controller.skin.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.skin.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + skinned = true; + skinController = node.controllers[ i ]; + + } else if ( controllers[ controller.skin.source ] ) { + + // urgh: controller can be chained + // handle the most basic case... + + var second = controllers[ controller.skin.source ]; + morphController = second; + // skinController = node.controllers[i]; + + if ( second.morph && geometries[ second.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = second.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + + } + + } + + break; + + case 'morph': + + if ( geometries[ controller.morph.source ] ) { + + var inst_geom = new InstanceGeometry(); + + inst_geom.url = controller.morph.source; + inst_geom.instance_material = node.controllers[ i ].instance_material; + + node.geometries.push( inst_geom ); + morphController = node.controllers[ i ]; + + } + + console.log( 'ColladaLoader: Morph-controller partially supported.' ); + + default: + break; + + } + + } + + // geometries + + var double_sided_materials = {}; + + for ( i = 0; i < node.geometries.length; i ++ ) { + + var instance_geometry = node.geometries[i]; + var instance_materials = instance_geometry.instance_material; + var geometry = geometries[ instance_geometry.url ]; + var used_materials = {}; + var used_materials_array = []; + var num_materials = 0; + var first_material; + + if ( geometry ) { + + if ( !geometry.mesh || !geometry.mesh.primitives ) + continue; + + if ( obj.name.length === 0 ) { + + obj.name = geometry.id; + + } + + // collect used fx for this geometry-instance + + if ( instance_materials ) { + + for ( j = 0; j < instance_materials.length; j ++ ) { + + var instance_material = instance_materials[ j ]; + var mat = materials[ instance_material.target ]; + var effect_id = mat.instance_effect.url; + var shader = effects[ effect_id ].shader; + var material3js = shader.material; + + if ( geometry.doubleSided ) { + + if ( !( instance_material.symbol in double_sided_materials ) ) { + + var _copied_material = material3js.clone(); + _copied_material.side = THREE.DoubleSide; + double_sided_materials[ instance_material.symbol ] = _copied_material; + + } + + material3js = double_sided_materials[ instance_material.symbol ]; + + } + + material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; + used_materials[ instance_material.symbol ] = num_materials; + used_materials_array.push( material3js ); + first_material = material3js; + first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; + num_materials ++; + + } + + } + + var mesh; + var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); + var geom = geometry.mesh.geometry3js; + + if ( num_materials > 1 ) { + + material = new THREE.MultiMaterial( used_materials_array ); + + for ( j = 0; j < geom.faces.length; j ++ ) { + + var face = geom.faces[ j ]; + face.materialIndex = used_materials[ face.daeMaterial ] + + } + + } + + if ( skinController !== undefined ) { + + + applySkin( geom, skinController ); + + if ( geom.morphTargets.length > 0 ) { + + material.morphTargets = true; + material.skinning = false; + + } else { + + material.morphTargets = false; + material.skinning = true; + + } + + + mesh = new THREE.SkinnedMesh( geom, material, false ); + + + //mesh.skeleton = skinController.skeleton; + //mesh.skinController = controllers[ skinController.url ]; + //mesh.skinInstanceController = skinController; + mesh.name = 'skin_' + skins.length; + + + + //mesh.animationHandle.setKey(0); + skins.push( mesh ); + + } else if ( morphController !== undefined ) { + + createMorph( geom, morphController ); + + material.morphTargets = true; + + mesh = new THREE.Mesh( geom, material ); + mesh.name = 'morph_' + morphs.length; + + morphs.push( mesh ); + + } else { + + if ( geom.isLineStrip === true ) { + + mesh = new THREE.Line( geom ); + + } else { + + mesh = new THREE.Mesh( geom, material ); + + } + + } + + obj.add(mesh); + + } + + } + + for ( i = 0; i < node.cameras.length; i ++ ) { + + var instance_camera = node.cameras[i]; + var cparams = cameras[instance_camera.url]; + + var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), + parseFloat(cparams.znear), parseFloat(cparams.zfar)); + + obj.add(cam); + } + + for ( i = 0; i < node.lights.length; i ++ ) { + + var light = null; + var instance_light = node.lights[i]; + var lparams = lights[instance_light.url]; + + if ( lparams && lparams.technique ) { + + var color = lparams.color.getHex(); + var intensity = lparams.intensity; + var distance = lparams.distance; + var angle = lparams.falloff_angle; + + switch ( lparams.technique ) { + + case 'directional': + + light = new THREE.DirectionalLight( color, intensity, distance ); + light.position.set(0, 0, 1); + break; + + case 'point': + + light = new THREE.PointLight( color, intensity, distance ); + break; + + case 'spot': + + light = new THREE.SpotLight( color, intensity, distance, angle ); + light.position.set(0, 0, 1); + break; + + case 'ambient': + + light = new THREE.AmbientLight( color ); + break; + + } + + } + + if (light) { + obj.add(light); + } + } + + obj.name = node.name || node.id || ""; + obj.colladaId = node.id || ""; + obj.layer = node.layer || ""; + obj.matrix = node.matrix; + obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); + + if ( options.centerGeometry && obj.geometry ) { + + var delta = obj.geometry.center(); + delta.multiply( obj.scale ); + delta.applyQuaternion( obj.quaternion ); + + obj.position.sub( delta ); + + } + + for ( i = 0; i < node.nodes.length; i ++ ) { + + obj.add( createSceneGraph( node.nodes[i], node ) ); + + } + + return obj; + + } + + function getJointId( skin, id ) { + + for ( var i = 0; i < skin.joints.length; i ++ ) { + + if ( skin.joints[ i ] === id ) { + + return i; + + } + + } + + } + + function getLibraryNode( id ) { + + var nodes = COLLADA.querySelectorAll('library_nodes node'); + + for ( var i = 0; i < nodes.length; i++ ) { + + var attObj = nodes[i].attributes.getNamedItem('id'); + + if ( attObj && attObj.value === id ) { + + return nodes[i]; + + } + + } + + return undefined; + + } + + function getChannelsForNode ( node ) { + + var channels = []; + var startTime = 1000000; + var endTime = -1000000; + + for ( var id in animations ) { + + var animation = animations[id]; + + for ( var i = 0; i < animation.channel.length; i ++ ) { + + var channel = animation.channel[i]; + var sampler = animation.sampler[i]; + var id = channel.target.split('/')[0]; + + if ( id == node.id ) { + + sampler.create(); + channel.sampler = sampler; + startTime = Math.min(startTime, sampler.startTime); + endTime = Math.max(endTime, sampler.endTime); + channels.push(channel); + + } + + } + + } + + if ( channels.length ) { + + node.startTime = startTime; + node.endTime = endTime; + + } + + return channels; + + } + + function calcFrameDuration( node ) { + + var minT = 10000000; + + for ( var i = 0; i < node.channels.length; i ++ ) { + + var sampler = node.channels[i].sampler; + + for ( var j = 0; j < sampler.input.length - 1; j ++ ) { + + var t0 = sampler.input[ j ]; + var t1 = sampler.input[ j + 1 ]; + minT = Math.min( minT, t1 - t0 ); + + } + } + + return minT; + + } + + function calcMatrixAt( node, t ) { + + var animated = {}; + + var i, j; + + for ( i = 0; i < node.channels.length; i ++ ) { + + var channel = node.channels[ i ]; + animated[ channel.sid ] = channel; + + } + + var matrix = new THREE.Matrix4(); + + for ( i = 0; i < node.transforms.length; i ++ ) { + + var transform = node.transforms[ i ]; + var channel = animated[ transform.sid ]; + + if ( channel !== undefined ) { + + var sampler = channel.sampler; + var value; + + for ( j = 0; j < sampler.input.length - 1; j ++ ) { + + if ( sampler.input[ j + 1 ] > t ) { + + value = sampler.output[ j ]; + //console.log(value.flatten) + break; + + } + + } + + if ( value !== undefined ) { + + if ( value instanceof THREE.Matrix4 ) { + + matrix.multiplyMatrices( matrix, value ); + + } else { + + // FIXME: handle other types + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } else { + + matrix.multiplyMatrices( matrix, transform.matrix ); + + } + + } + + return matrix; + + } + + function bakeAnimations ( node ) { + + if ( node.channels && node.channels.length ) { + + var keys = [], + sids = []; + + for ( var i = 0, il = node.channels.length; i < il; i ++ ) { + + var channel = node.channels[i], + fullSid = channel.fullSid, + sampler = channel.sampler, + input = sampler.input, + transform = node.getTransformBySid( channel.sid ), + member; + + if ( channel.arrIndices ) { + + member = []; + + for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { + + member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); + + } + + } else { + + member = getConvertedMember( channel.member ); + + } + + if ( transform ) { + + if ( sids.indexOf( fullSid ) === -1 ) { + + sids.push( fullSid ); + + } + + for ( var j = 0, jl = input.length; j < jl; j ++ ) { + + var time = input[j], + data = sampler.getData( transform.type, j, member ), + key = findKey( keys, time ); + + if ( !key ) { + + key = new Key( time ); + var timeNdx = findTimeNdx( keys, time ); + keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); + + } + + key.addTarget( fullSid, transform, member, data ); + + } + + } else { + + console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); + + } + + } + + // post process + for ( var i = 0; i < sids.length; i ++ ) { + + var sid = sids[ i ]; + + for ( var j = 0; j < keys.length; j ++ ) { + + var key = keys[ j ]; + + if ( !key.hasTarget( sid ) ) { + + interpolateKeys( keys, key, j, sid ); + + } + + } + + } + + node.keys = keys; + node.sids = sids; + + } + + } + + function findKey ( keys, time) { + + var retVal = null; + + for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { + + var key = keys[i]; + + if ( key.time === time ) { + + retVal = key; + + } else if ( key.time > time ) { + + break; + + } + + } + + return retVal; + + } + + function findTimeNdx ( keys, time) { + + var ndx = -1; + + for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { + + var key = keys[i]; + + if ( key.time >= time ) { + + ndx = i; + + } + + } + + return ndx; + + } + + function interpolateKeys ( keys, key, ndx, fullSid ) { + + var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), + nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); + + if ( prevKey && nextKey ) { + + var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), + prevTarget = prevKey.getTarget( fullSid ), + nextData = nextKey.getTarget( fullSid ).data, + prevData = prevTarget.data, + data; + + if ( prevTarget.type === 'matrix' ) { + + data = prevData; + + } else if ( prevData.length ) { + + data = []; + + for ( var i = 0; i < prevData.length; ++ i ) { + + data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); + + } + + } + + // Get next key with given sid + + function getNextKeyWith( keys, fullSid, ndx ) { + + for ( ; ndx < keys.length; ndx ++ ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + } + + // Get previous key with given sid + + function getPrevKeyWith( keys, fullSid, ndx ) { + + ndx = ndx >= 0 ? ndx : ndx + keys.length; + + for ( ; ndx >= 0; ndx -- ) { + + var key = keys[ ndx ]; + + if ( key.hasTarget( fullSid ) ) { + + return key; + + } + + } + + return null; + + } + + function _Image() { + + this.id = ""; + this.init_from = ""; + + } + + _Image.prototype.parse = function(element) { + + this.id = element.getAttribute('id'); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'init_from' ) { + + this.init_from = child.textContent; + + } + + } + + return this; + + }; + + function Controller() { + + this.id = ""; + this.name = ""; + this.type = ""; + this.skin = null; + this.morph = null; + + } + + Controller.prototype.parse = function( element ) { + + this.id = element.getAttribute('id'); + this.name = element.getAttribute('name'); + this.type = "none"; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'skin': + + this.skin = (new Skin()).parse(child); + this.type = child.nodeName; + break; + + case 'morph': + + this.morph = (new Morph()).parse(child); + this.type = child.nodeName; + break; + + default: + break; + + } + } + + return this; + + }; + + function Morph() { + + this.method = null; + this.source = null; + this.targets = null; + this.weights = null; + + } + + Morph.prototype.parse = function( element ) { + + var sources = {}; + var inputs = []; + var i; + + this.method = element.getAttribute( 'method' ); + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + + for ( i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + var source = ( new Source() ).parse( child ); + sources[ source.id ] = source; + break; + + case 'targets': + + inputs = this.parseInputs( child ); + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + for ( i = 0; i < inputs.length; i ++ ) { + + var input = inputs[ i ]; + var source = sources[ input.source ]; + + switch ( input.semantic ) { + + case 'MORPH_TARGET': + + this.targets = source.read(); + break; + + case 'MORPH_WEIGHT': + + this.weights = source.read(); + break; + + default: + break; + + } + } + + return this; + + }; + + Morph.prototype.parseInputs = function(element) { + + var inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( (new Input()).parse(child) ); + break; + + default: + break; + } + } + + return inputs; + + }; + + function Skin() { + + this.source = ""; + this.bindShapeMatrix = null; + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + } + + Skin.prototype.parse = function( element ) { + + var sources = {}; + var joints, weights; + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.invBindMatrices = []; + this.joints = []; + this.weights = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bind_shape_matrix': + + var f = _floats(child.textContent); + this.bindShapeMatrix = getConvertedMat4( f ); + break; + + case 'source': + + var src = new Source().parse(child); + sources[ src.id ] = src; + break; + + case 'joints': + + joints = child; + break; + + case 'vertex_weights': + + weights = child; + break; + + default: + + console.log( child.nodeName ); + break; + + } + } + + this.parseJoints( joints, sources ); + this.parseWeights( weights, sources ); + + return this; + + }; + + Skin.prototype.parseJoints = function ( element, sources ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + var input = ( new Input() ).parse( child ); + var source = sources[ input.source ]; + + if ( input.semantic === 'JOINT' ) { + + this.joints = source.read(); + + } else if ( input.semantic === 'INV_BIND_MATRIX' ) { + + this.invBindMatrices = source.read(); + + } + + break; + + default: + break; + } + + } + + }; + + Skin.prototype.parseWeights = function ( element, sources ) { + + var v, vcount, inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + inputs.push( ( new Input() ).parse( child ) ); + break; + + case 'v': + + v = _ints( child.textContent ); + break; + + case 'vcount': + + vcount = _ints( child.textContent ); + break; + + default: + break; + + } + + } + + var index = 0; + + for ( var i = 0; i < vcount.length; i ++ ) { + + var numBones = vcount[i]; + var vertex_weights = []; + + for ( var j = 0; j < numBones; j ++ ) { + + var influence = {}; + + for ( var k = 0; k < inputs.length; k ++ ) { + + var input = inputs[ k ]; + var value = v[ index + input.offset ]; + + switch ( input.semantic ) { + + case 'JOINT': + + influence.joint = value;//this.joints[value]; + break; + + case 'WEIGHT': + + influence.weight = sources[ input.source ].data[ value ]; + break; + + default: + break; + + } + + } + + vertex_weights.push( influence ); + index += inputs.length; + } + + for ( var j = 0; j < vertex_weights.length; j ++ ) { + + vertex_weights[ j ].index = i; + + } + + this.weights.push( vertex_weights ); + + } + + }; + + function VisualScene () { + + this.id = ""; + this.name = ""; + this.nodes = []; + this.scene = new THREE.Group(); + + } + + VisualScene.prototype.getChildById = function( id, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildById( id, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.getChildBySid = function( sid, recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var node = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( node ) { + + return node; + + } + + } + + return null; + + }; + + VisualScene.prototype.parse = function( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.nodes = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Node() { + + this.id = ""; + this.name = ""; + this.sid = ""; + this.nodes = []; + this.controllers = []; + this.transforms = []; + this.geometries = []; + this.channels = []; + this.matrix = new THREE.Matrix4(); + + } + + Node.prototype.getChannelForTransform = function( transformSid ) { + + for ( var i = 0; i < this.channels.length; i ++ ) { + + var channel = this.channels[i]; + var parts = channel.target.split('/'); + var id = parts.shift(); + var sid = parts.shift(); + var dotSyntax = (sid.indexOf(".") >= 0); + var arrSyntax = (sid.indexOf("(") >= 0); + var arrIndices; + var member; + + if ( dotSyntax ) { + + parts = sid.split("."); + sid = parts.shift(); + member = parts.shift(); + + } else if ( arrSyntax ) { + + arrIndices = sid.split("("); + sid = arrIndices.shift(); + + for ( var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); + + } + + } + + if ( sid === transformSid ) { + + channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; + return channel; + + } + + } + + return null; + + }; + + Node.prototype.getChildById = function ( id, recursive ) { + + if ( this.id === id ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildById( id, recursive ); + + if ( n ) { + + return n; + + } + + } + + } + + return null; + + }; + + Node.prototype.getChildBySid = function ( sid, recursive ) { + + if ( this.sid === sid ) { + + return this; + + } + + if ( recursive ) { + + for ( var i = 0; i < this.nodes.length; i ++ ) { + + var n = this.nodes[ i ].getChildBySid( sid, recursive ); + + if ( n ) { + + return n; + + } + + } + } + + return null; + + }; + + Node.prototype.getTransformBySid = function ( sid ) { + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; + + } + + return null; + + }; + + Node.prototype.parse = function( element ) { + + var url; + + this.id = element.getAttribute('id'); + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.type = element.getAttribute('type'); + this.layer = element.getAttribute('layer'); + + this.type = this.type === 'JOINT' ? this.type : 'NODE'; + + this.nodes = []; + this.transforms = []; + this.geometries = []; + this.cameras = []; + this.lights = []; + this.controllers = []; + this.matrix = new THREE.Matrix4(); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'node': + + this.nodes.push( ( new Node() ).parse( child ) ); + break; + + case 'instance_camera': + + this.cameras.push( ( new InstanceCamera() ).parse( child ) ); + break; + + case 'instance_controller': + + this.controllers.push( ( new InstanceController() ).parse( child ) ); + break; + + case 'instance_geometry': + + this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); + break; + + case 'instance_light': + + this.lights.push( ( new InstanceLight() ).parse( child ) ); + break; + + case 'instance_node': + + url = child.getAttribute( 'url' ).replace( /^#/, '' ); + var iNode = getLibraryNode( url ); + + if ( iNode ) { + + this.nodes.push( ( new Node() ).parse( iNode )) ; + + } + + break; + + case 'rotate': + case 'translate': + case 'scale': + case 'matrix': + case 'lookat': + case 'skew': + + this.transforms.push( ( new Transform() ).parse( child ) ); + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + this.channels = getChannelsForNode( this ); + bakeAnimations( this ); + + this.updateMatrix(); + + return this; + + }; + + Node.prototype.updateMatrix = function () { + + this.matrix.identity(); + + for ( var i = 0; i < this.transforms.length; i ++ ) { + + this.transforms[ i ].apply( this.matrix ); + + } + + }; + + function Transform () { + + this.sid = ""; + this.type = ""; + this.data = []; + this.obj = null; + + } + + Transform.prototype.parse = function ( element ) { + + this.sid = element.getAttribute( 'sid' ); + this.type = element.nodeName; + this.data = _floats( element.textContent ); + this.convert(); + + return this; + + }; + + Transform.prototype.convert = function () { + + switch ( this.type ) { + + case 'matrix': + + this.obj = getConvertedMat4( this.data ); + break; + + case 'rotate': + + this.angle = THREE.Math.degToRad( this.data[3] ); + + case 'translate': + + fixCoords( this.data, -1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + case 'scale': + + fixCoords( this.data, 1 ); + this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); + break; + + default: + console.log( 'Can not convert Transform of type ' + this.type ); + break; + + } + + }; + + Transform.prototype.apply = function () { + + var m1 = new THREE.Matrix4(); + + return function ( matrix ) { + + switch ( this.type ) { + + case 'matrix': + + matrix.multiply( this.obj ); + + break; + + case 'translate': + + matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); + + break; + + case 'rotate': + + matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); + + break; + + case 'scale': + + matrix.scale( this.obj ); + + break; + + } + + }; + + }(); + + Transform.prototype.update = function ( data, member ) { + + var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; + + switch ( this.type ) { + + case 'matrix': + + if ( ! member ) { + + this.obj.copy( data ); + + } else if ( member.length === 1 ) { + + switch ( member[ 0 ] ) { + + case 0: + + this.obj.n11 = data[ 0 ]; + this.obj.n21 = data[ 1 ]; + this.obj.n31 = data[ 2 ]; + this.obj.n41 = data[ 3 ]; + + break; + + case 1: + + this.obj.n12 = data[ 0 ]; + this.obj.n22 = data[ 1 ]; + this.obj.n32 = data[ 2 ]; + this.obj.n42 = data[ 3 ]; + + break; + + case 2: + + this.obj.n13 = data[ 0 ]; + this.obj.n23 = data[ 1 ]; + this.obj.n33 = data[ 2 ]; + this.obj.n43 = data[ 3 ]; + + break; + + case 3: + + this.obj.n14 = data[ 0 ]; + this.obj.n24 = data[ 1 ]; + this.obj.n34 = data[ 2 ]; + this.obj.n44 = data[ 3 ]; + + break; + + } + + } else if ( member.length === 2 ) { + + var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); + this.obj[ propName ] = data; + + } else { + + console.log('Incorrect addressing of matrix in transform.'); + + } + + break; + + case 'translate': + case 'scale': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + break; + + } + + break; + + case 'rotate': + + if ( Object.prototype.toString.call( member ) === '[object Array]' ) { + + member = members[ member[ 0 ] ]; + + } + + switch ( member ) { + + case 'X': + + this.obj.x = data; + break; + + case 'Y': + + this.obj.y = data; + break; + + case 'Z': + + this.obj.z = data; + break; + + case 'ANGLE': + + this.angle = THREE.Math.degToRad( data ); + break; + + default: + + this.obj.x = data[ 0 ]; + this.obj.y = data[ 1 ]; + this.obj.z = data[ 2 ]; + this.angle = THREE.Math.degToRad( data[ 3 ] ); + break; + + } + break; + + } + + }; + + function InstanceController() { + + this.url = ""; + this.skeleton = []; + this.instance_material = []; + + } + + InstanceController.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + this.skeleton = []; + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType !== 1 ) continue; + + switch ( child.nodeName ) { + + case 'skeleton': + + this.skeleton.push( child.textContent.replace(/^#/, '') ); + break; + + case 'bind_material': + + var instances = child.querySelectorAll('instance_material'); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[j]; + this.instance_material.push( (new InstanceMaterial()).parse(instance) ); + + } + + + break; + + case 'extra': + break; + + default: + break; + + } + } + + return this; + + }; + + function InstanceMaterial () { + + this.symbol = ""; + this.target = ""; + + } + + InstanceMaterial.prototype.parse = function ( element ) { + + this.symbol = element.getAttribute('symbol'); + this.target = element.getAttribute('target').replace(/^#/, ''); + return this; + + }; + + function InstanceGeometry() { + + this.url = ""; + this.instance_material = []; + + } + + InstanceGeometry.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + this.instance_material = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + if ( child.nodeName === 'bind_material' ) { + + var instances = child.querySelectorAll('instance_material'); + + for ( var j = 0; j < instances.length; j ++ ) { + + var instance = instances[j]; + this.instance_material.push( (new InstanceMaterial()).parse(instance) ); + + } + + break; + + } + + } + + return this; + + }; + + function Geometry() { + + this.id = ""; + this.mesh = null; + + } + + Geometry.prototype.parse = function ( element ) { + + this.id = element.getAttribute('id'); + + extractDoubleSided( this, element ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + + switch ( child.nodeName ) { + + case 'mesh': + + this.mesh = (new Mesh(this)).parse(child); + break; + + case 'extra': + + // console.log( child ); + break; + + default: + break; + } + } + + return this; + + }; + + function Mesh( geometry ) { + + this.geometry = geometry.id; + this.primitives = []; + this.vertices = null; + this.geometry3js = null; + + } + + Mesh.prototype.parse = function ( element ) { + + this.primitives = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'source': + + _source( child ); + break; + + case 'vertices': + + this.vertices = ( new Vertices() ).parse( child ); + break; + + case 'linestrips': + + this.primitives.push( ( new LineStrips().parse( child ) ) ); + break; + + case 'triangles': + + this.primitives.push( ( new Triangles().parse( child ) ) ); + break; + + case 'polygons': + + this.primitives.push( ( new Polygons().parse( child ) ) ); + break; + + case 'polylist': + + this.primitives.push( ( new Polylist().parse( child ) ) ); + break; + + default: + break; + + } + + } + + this.geometry3js = new THREE.Geometry(); + + if ( this.vertices === null ) { + + // TODO (mrdoob): Study case when this is null (carrier.dae) + + return this; + + } + + var vertexData = sources[ this.vertices.input['POSITION'].source ].data; + + for ( var i = 0; i < vertexData.length; i += 3 ) { + + this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); + + } + + for ( var i = 0; i < this.primitives.length; i ++ ) { + + var primitive = this.primitives[ i ]; + primitive.setVertices( this.vertices ); + this.handlePrimitive( primitive, this.geometry3js ); + + } + + if ( this.geometry3js.calcNormals ) { + + this.geometry3js.computeVertexNormals(); + delete this.geometry3js.calcNormals; + + } + + return this; + + }; + + Mesh.prototype.handlePrimitive = function ( primitive, geom ) { + + if ( primitive instanceof LineStrips ) { + + // TODO: Handle indices. Maybe easier with BufferGeometry? + + geom.isLineStrip = true; + return; + + } + + var j, k, pList = primitive.p, inputs = primitive.inputs; + var input, index, idx32; + var source, numParams; + var vcIndex = 0, vcount = 3, maxOffset = 0; + var texture_sets = []; + + for ( j = 0; j < inputs.length; j ++ ) { + + input = inputs[ j ]; + + var offset = input.offset + 1; + maxOffset = (maxOffset < offset) ? offset : maxOffset; + + switch ( input.semantic ) { + + case 'TEXCOORD': + texture_sets.push( input.set ); + break; + + } + + } + + for ( var pCount = 0; pCount < pList.length; ++ pCount ) { + + var p = pList[ pCount ], i = 0; + + while ( i < p.length ) { + + var vs = []; + var ns = []; + var ts = null; + var cs = []; + + if ( primitive.vcount ) { + + vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; + + } else { + + vcount = p.length / maxOffset; + + } + + + for ( j = 0; j < vcount; j ++ ) { + + for ( k = 0; k < inputs.length; k ++ ) { + + input = inputs[ k ]; + source = sources[ input.source ]; + + index = p[ i + ( j * maxOffset ) + input.offset ]; + numParams = source.accessor.params.length; + idx32 = index * numParams; + + switch ( input.semantic ) { + + case 'VERTEX': + + vs.push( index ); + + break; + + case 'NORMAL': + + ns.push( getConvertedVec3( source.data, idx32 ) ); + + break; + + case 'TEXCOORD': + + ts = ts || { }; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); + + break; + + case 'COLOR': + + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + break; + + default: + + break; + + } + + } + + } + + if ( ns.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.NORMAL; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); + + } + + } else { + + geom.calcNormals = true; + + } + + } + + if ( !ts ) { + + ts = { }; + // check the vertices inputs + input = this.vertices.input.TEXCOORD; + + if ( input ) { + + texture_sets.push( input.set ); + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; + // invert the V + ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); + + } + + } + + } + + if ( cs.length === 0 ) { + + // check the vertices inputs + input = this.vertices.input.COLOR; + + if ( input ) { + + source = sources[ input.source ]; + numParams = source.accessor.params.length; + + for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { + + idx32 = vs[ ndx ] * numParams; + cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); + + } + + } + + } + + var face = null, faces = [], uv, uvArr; + + if ( vcount === 3 ) { + + faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); + + } else if ( vcount === 4 ) { + + faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); + + faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); + + } else if ( vcount > 4 && options.subdivideFaces ) { + + var clr = cs.length ? cs : new THREE.Color(), + vec1, vec2, vec3, v1, v2, norm; + + // subdivide into multiple Face3s + + for ( k = 1; k < vcount - 1; ) { + + faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) ); + + } + + } + + if ( faces.length ) { + + for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { + + face = faces[ndx]; + face.daeMaterial = primitive.material; + geom.faces.push( face ); + + for ( k = 0; k < texture_sets.length; k ++ ) { + + uv = ts[ texture_sets[k] ]; + + if ( vcount > 4 ) { + + // Grab the right UVs for the vertices in this face + uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; + + } else if ( vcount === 4 ) { + + if ( ndx === 0 ) { + + uvArr = [ uv[0], uv[1], uv[3] ]; + + } else { + + uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; + + } + + } else { + + uvArr = [ uv[0], uv[1], uv[2] ]; + + } + + if ( geom.faceVertexUvs[k] === undefined ) { + + geom.faceVertexUvs[k] = []; + + } + + geom.faceVertexUvs[k].push( uvArr ); + + } + + } + + } else { + + console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); + + } + + i += maxOffset * vcount; + + } + + } + + }; + + function Polygons () { + + this.material = ""; + this.count = 0; + this.inputs = []; + this.vcount = null; + this.p = []; + this.geometry = new THREE.Geometry(); + + } + + Polygons.prototype.setVertices = function ( vertices ) { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + if ( this.inputs[ i ].source === vertices.id ) { + + this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; + + } + + } + + }; + + Polygons.prototype.parse = function ( element ) { + + this.material = element.getAttribute( 'material' ); + this.count = _attr_as_int( element, 'count', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); + break; + + case 'vcount': + + this.vcount = _ints( child.textContent ); + break; + + case 'p': + + this.p.push( _ints( child.textContent ) ); + break; + + case 'ph': + + console.warn( 'polygon holes not yet supported!' ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Polylist () { + + Polygons.call( this ); + + this.vcount = []; + + } + + Polylist.prototype = Object.create( Polygons.prototype ); + Polylist.prototype.constructor = Polylist; + + function LineStrips() { + + Polygons.call( this ); + + this.vcount = 1; + + } + + LineStrips.prototype = Object.create( Polygons.prototype ); + LineStrips.prototype.constructor = LineStrips; + + function Triangles () { + + Polygons.call( this ); + + this.vcount = 3; + + } + + Triangles.prototype = Object.create( Polygons.prototype ); + Triangles.prototype.constructor = Triangles; + + function Accessor() { + + this.source = ""; + this.count = 0; + this.stride = 0; + this.params = []; + + } + + Accessor.prototype.parse = function ( element ) { + + this.params = []; + this.source = element.getAttribute( 'source' ); + this.count = _attr_as_int( element, 'count', 0 ); + this.stride = _attr_as_int( element, 'stride', 0 ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeName === 'param' ) { + + var param = {}; + param[ 'name' ] = child.getAttribute( 'name' ); + param[ 'type' ] = child.getAttribute( 'type' ); + this.params.push( param ); + + } + + } + + return this; + + }; + + function Vertices() { + + this.input = {}; + + } + + Vertices.prototype.parse = function ( element ) { + + this.id = element.getAttribute('id'); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[i].nodeName === 'input' ) { + + var input = ( new Input() ).parse( element.childNodes[ i ] ); + this.input[ input.semantic ] = input; + + } + + } + + return this; + + }; + + function Input () { + + this.semantic = ""; + this.offset = 0; + this.source = ""; + this.set = 0; + + } + + Input.prototype.parse = function ( element ) { + + this.semantic = element.getAttribute('semantic'); + this.source = element.getAttribute('source').replace(/^#/, ''); + this.set = _attr_as_int(element, 'set', -1); + this.offset = _attr_as_int(element, 'offset', 0); + + if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { + + this.set = 0; + + } + + return this; + + }; + + function Source ( id ) { + + this.id = id; + this.type = null; + + } + + Source.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + + switch ( child.nodeName ) { + + case 'bool_array': + + this.data = _bools( child.textContent ); + this.type = child.nodeName; + break; + + case 'float_array': + + this.data = _floats( child.textContent ); + this.type = child.nodeName; + break; + + case 'int_array': + + this.data = _ints( child.textContent ); + this.type = child.nodeName; + break; + + case 'IDREF_array': + case 'Name_array': + + this.data = _strings( child.textContent ); + this.type = child.nodeName; + break; + + case 'technique_common': + + for ( var j = 0; j < child.childNodes.length; j ++ ) { + + if ( child.childNodes[ j ].nodeName === 'accessor' ) { + + this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); + break; + + } + } + break; + + default: + // console.log(child.nodeName); + break; + + } + + } + + return this; + + }; + + Source.prototype.read = function () { + + var result = []; + + //for (var i = 0; i < this.accessor.params.length; i++) { + + var param = this.accessor.params[ 0 ]; + + //console.log(param.name + " " + param.type); + + switch ( param.type ) { + + case 'IDREF': + case 'Name': case 'name': + case 'float': + + return this.data; + + case 'float4x4': + + for ( var j = 0; j < this.data.length; j += 16 ) { + + var s = this.data.slice( j, j + 16 ); + var m = getConvertedMat4( s ); + result.push( m ); + } + + break; + + default: + + console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); + break; + + } + + //} + + return result; + + }; + + function Material () { + + this.id = ""; + this.name = ""; + this.instance_effect = null; + + } + + Material.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { + + this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); + break; + + } + + } + + return this; + + }; + + function ColorOrTexture () { + + this.color = new THREE.Color(); + this.color.setRGB( Math.random(), Math.random(), Math.random() ); + this.color.a = 1.0; + + this.texture = null; + this.texcoord = null; + this.texOpts = null; + + } + + ColorOrTexture.prototype.isColor = function () { + + return ( this.texture === null ); + + }; + + ColorOrTexture.prototype.isTexture = function () { + + return ( this.texture != null ); + + }; + + ColorOrTexture.prototype.parse = function ( element ) { + + if (element.nodeName === 'transparent') { + + this.opaque = element.getAttribute('opaque'); + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(); + this.color.setRGB( rgba[0], rgba[1], rgba[2] ); + this.color.a = rgba[3]; + break; + + case 'texture': + + this.texture = child.getAttribute('texture'); + this.texcoord = child.getAttribute('texcoord'); + // Defaults from: + // https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension + this.texOpts = { + offsetU: 0, + offsetV: 0, + repeatU: 1, + repeatV: 1, + wrapU: 1, + wrapV: 1 + }; + this.parseTexture( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + ColorOrTexture.prototype.parseTexture = function ( element ) { + + if ( ! element.childNodes ) return this; + + // This should be supported by Maya, 3dsMax, and MotionBuilder + + if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { + + element = element.childNodes[1]; + + if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { + + element = element.childNodes[1]; + + } + + } + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'offsetU': + case 'offsetV': + case 'repeatU': + case 'repeatV': + + this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); + + break; + + case 'wrapU': + case 'wrapV': + + // some dae have a value of true which becomes NaN via parseInt + + if ( child.textContent.toUpperCase() === 'TRUE' ) { + + this.texOpts[ child.nodeName ] = 1; + + } else { + + this.texOpts[ child.nodeName ] = parseInt( child.textContent ); + + } + break; + + default: + + this.texOpts[ child.nodeName ] = child.textContent; + + break; + + } + + } + + return this; + + }; + + function Shader ( type, effect ) { + + this.type = type; + this.effect = effect; + this.material = null; + + } + + Shader.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'emission': + case 'diffuse': + case 'specular': + case 'transparent': + + this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); + break; + + case 'bump': + + // If 'bumptype' is 'heightfield', create a 'bump' property + // Else if 'bumptype' is 'normalmap', create a 'normal' property + // (Default to 'bump') + var bumpType = child.getAttribute( 'bumptype' ); + if ( bumpType ) { + if ( bumpType.toLowerCase() === "heightfield" ) { + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } else if ( bumpType.toLowerCase() === "normalmap" ) { + this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); + } else { + console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } + } else { + console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); + this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); + } + + break; + + case 'shininess': + case 'reflectivity': + case 'index_of_refraction': + case 'transparency': + + var f = child.querySelectorAll('float'); + + if ( f.length > 0 ) + this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); + + break; + + default: + break; + + } + + } + + this.create(); + return this; + + }; + + Shader.prototype.create = function() { + + var props = {}; + + var transparent = false; + + if (this['transparency'] !== undefined && this['transparent'] !== undefined) { + // convert transparent color RBG to average value + var transparentColor = this['transparent']; + var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; + + if (transparencyLevel > 0) { + transparent = true; + props[ 'transparent' ] = true; + props[ 'opacity' ] = 1 - transparencyLevel; + + } + + } + + var keys = { + 'diffuse':'map', + 'ambient':'lightMap', + 'specular':'specularMap', + 'emission':'emissionMap', + 'bump':'bumpMap', + 'normal':'normalMap' + }; + + for ( var prop in this ) { + + switch ( prop ) { + + case 'ambient': + case 'emission': + case 'diffuse': + case 'specular': + case 'bump': + case 'normal': + + var cot = this[ prop ]; + + if ( cot instanceof ColorOrTexture ) { + + if ( cot.isTexture() ) { + + var samplerId = cot.texture; + var sampler = this.effect.sampler[samplerId]; + + if ( sampler !== undefined && sampler.source !== undefined ) { + + var surface = this.effect.surface[sampler.source]; + + if ( surface !== undefined ) { + + var image = images[ surface.init_from ]; + + if ( image ) { + + var url = baseUrl + image.init_from; + + var texture; + var loader = THREE.Loader.Handlers.get( url ); + + if ( loader !== null ) { + + texture = loader.load( url ); + + } else { + + texture = new THREE.Texture(); + + loadTextureImage( texture, url ); + + } + + if ( sampler.wrap_s === "MIRROR" ) { + + texture.wrapS = THREE.MirroredRepeatWrapping; + + } else if ( sampler.wrap_s === "WRAP" || cot.texOpts.wrapU ) { + + texture.wrapS = THREE.RepeatWrapping; + + } else { + + texture.wrapS = THREE.ClampToEdgeWrapping; + + } + + if ( sampler.wrap_t === "MIRROR" ) { + + texture.wrapT = THREE.MirroredRepeatWrapping; + + } else if ( sampler.wrap_t === "WRAP" || cot.texOpts.wrapV ) { + + texture.wrapT = THREE.RepeatWrapping; + + } else { + + texture.wrapT = THREE.ClampToEdgeWrapping; + + } + + texture.offset.x = cot.texOpts.offsetU; + texture.offset.y = cot.texOpts.offsetV; + texture.repeat.x = cot.texOpts.repeatU; + texture.repeat.y = cot.texOpts.repeatV; + props[keys[prop]] = texture; + + // Texture with baked lighting? + if (prop === 'emission') props['emissive'] = 0xffffff; + + } + + } + + } + + } else if ( prop === 'diffuse' || !transparent ) { + + if ( prop === 'emission' ) { + + props[ 'emissive' ] = cot.color.getHex(); + + } else { + + props[ prop ] = cot.color.getHex(); + + } + + } + + } + + break; + + case 'shininess': + + props[ prop ] = this[ prop ]; + break; + + case 'reflectivity': + + props[ prop ] = this[ prop ]; + if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; + props['combine'] = THREE.MixOperation; //mix regular shading with reflective component + break; + + case 'index_of_refraction': + + props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable + if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; + break; + + case 'transparency': + // gets figured out up top + break; + + default: + break; + + } + + } + + props[ 'shading' ] = preferredShading; + props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; + + if ( props.diffuse !== undefined ) { + + props.color = props.diffuse; + delete props.diffuse; + + } + + switch ( this.type ) { + + case 'constant': + + if (props.emissive != undefined) props.color = props.emissive; + this.material = new THREE.MeshBasicMaterial( props ); + break; + + case 'phong': + case 'blinn': + + this.material = new THREE.MeshPhongMaterial( props ); + break; + + case 'lambert': + default: + + this.material = new THREE.MeshLambertMaterial( props ); + break; + + } + + return this.material; + + }; + + function Surface ( effect ) { + + this.effect = effect; + this.init_from = null; + this.format = null; + + } + + Surface.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'init_from': + + this.init_from = child.textContent; + break; + + case 'format': + + this.format = child.textContent; + break; + + default: + + console.log( "unhandled Surface prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Sampler2D ( effect ) { + + this.effect = effect; + this.source = null; + this.wrap_s = null; + this.wrap_t = null; + this.minfilter = null; + this.magfilter = null; + this.mipfilter = null; + + } + + Sampler2D.prototype.parse = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'source': + + this.source = child.textContent; + break; + + case 'minfilter': + + this.minfilter = child.textContent; + break; + + case 'magfilter': + + this.magfilter = child.textContent; + break; + + case 'mipfilter': + + this.mipfilter = child.textContent; + break; + + case 'wrap_s': + + this.wrap_s = child.textContent; + break; + + case 'wrap_t': + + this.wrap_t = child.textContent; + break; + + default: + + console.log( "unhandled Sampler2D prop: " + child.nodeName ); + break; + + } + + } + + return this; + + }; + + function Effect () { + + this.id = ""; + this.name = ""; + this.shader = null; + this.surface = {}; + this.sampler = {}; + + } + + Effect.prototype.create = function () { + + if ( this.shader === null ) { + + return null; + + } + + }; + + Effect.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + extractDoubleSided( this, element ); + + this.shader = null; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseTechnique( this.parseProfileCOMMON( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Effect.prototype.parseNewparam = function ( element ) { + + var sid = element.getAttribute( 'sid' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'surface': + + this.surface[sid] = ( new Surface( this ) ).parse( child ); + break; + + case 'sampler2D': + + this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); + break; + + case 'extra': + + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + }; + + Effect.prototype.parseProfileCOMMON = function ( element ) { + + var technique; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'profile_COMMON': + + this.parseProfileCOMMON( child ); + break; + + case 'technique': + + technique = child; + break; + + case 'newparam': + + this.parseNewparam( child ); + break; + + case 'image': + + var _image = ( new _Image() ).parse( child ); + images[ _image.id ] = _image; + break; + + case 'extra': + break; + + default: + + console.log( child.nodeName ); + break; + + } + + } + + return technique; + + }; + + Effect.prototype.parseTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'constant': + case 'lambert': + case 'blinn': + case 'phong': + + this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); + break; + case 'extra': + this.parseExtra(child); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtra = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique': + this.parseExtraTechnique( child ); + break; + default: + break; + + } + + } + + }; + + Effect.prototype.parseExtraTechnique = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[i]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'bump': + this.shader.parse( element ); + break; + default: + break; + + } + + } + + }; + + function InstanceEffect () { + + this.url = ""; + + } + + InstanceEffect.prototype.parse = function ( element ) { + + this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); + return this; + + }; + + function Animation() { + + this.id = ""; + this.name = ""; + this.source = {}; + this.sampler = []; + this.channel = []; + + } + + Animation.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + this.source = {}; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'animation': + + var anim = ( new Animation() ).parse( child ); + + for ( var src in anim.source ) { + + this.source[ src ] = anim.source[ src ]; + + } + + for ( var j = 0; j < anim.channel.length; j ++ ) { + + this.channel.push( anim.channel[ j ] ); + this.sampler.push( anim.sampler[ j ] ); + + } + + break; + + case 'source': + + var src = ( new Source() ).parse( child ); + this.source[ src.id ] = src; + break; + + case 'sampler': + + this.sampler.push( ( new Sampler( this ) ).parse( child ) ); + break; + + case 'channel': + + this.channel.push( ( new Channel( this ) ).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Channel( animation ) { + + this.animation = animation; + this.source = ""; + this.target = ""; + this.fullSid = null; + this.sid = null; + this.dotSyntax = null; + this.arrSyntax = null; + this.arrIndices = null; + this.member = null; + + } + + Channel.prototype.parse = function ( element ) { + + this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); + this.target = element.getAttribute( 'target' ); + + var parts = this.target.split( '/' ); + + var id = parts.shift(); + var sid = parts.shift(); + + var dotSyntax = ( sid.indexOf(".") >= 0 ); + var arrSyntax = ( sid.indexOf("(") >= 0 ); + + if ( dotSyntax ) { + + parts = sid.split("."); + this.sid = parts.shift(); + this.member = parts.shift(); + + } else if ( arrSyntax ) { + + var arrIndices = sid.split("("); + this.sid = arrIndices.shift(); + + for (var j = 0; j < arrIndices.length; j ++ ) { + + arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); + + } + + this.arrIndices = arrIndices; + + } else { + + this.sid = sid; + + } + + this.fullSid = sid; + this.dotSyntax = dotSyntax; + this.arrSyntax = arrSyntax; + + return this; + + }; + + function Sampler ( animation ) { + + this.id = ""; + this.animation = animation; + this.inputs = []; + this.input = null; + this.output = null; + this.strideOut = null; + this.interpolation = null; + this.startTime = null; + this.endTime = null; + this.duration = 0; + + } + + Sampler.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.inputs = []; + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'input': + + this.inputs.push( (new Input()).parse( child ) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Sampler.prototype.create = function () { + + for ( var i = 0; i < this.inputs.length; i ++ ) { + + var input = this.inputs[ i ]; + var source = this.animation.source[ input.source ]; + + switch ( input.semantic ) { + + case 'INPUT': + + this.input = source.read(); + break; + + case 'OUTPUT': + + this.output = source.read(); + this.strideOut = source.accessor.stride; + break; + + case 'INTERPOLATION': + + this.interpolation = source.read(); + break; + + case 'IN_TANGENT': + + break; + + case 'OUT_TANGENT': + + break; + + default: + + console.log(input.semantic); + break; + + } + + } + + this.startTime = 0; + this.endTime = 0; + this.duration = 0; + + if ( this.input.length ) { + + this.startTime = 100000000; + this.endTime = -100000000; + + for ( var i = 0; i < this.input.length; i ++ ) { + + this.startTime = Math.min( this.startTime, this.input[ i ] ); + this.endTime = Math.max( this.endTime, this.input[ i ] ); + + } + + this.duration = this.endTime - this.startTime; + + } + + }; + + Sampler.prototype.getData = function ( type, ndx, member ) { + + var data; + + if ( type === 'matrix' && this.strideOut === 16 ) { + + data = this.output[ ndx ]; + + } else if ( this.strideOut > 1 ) { + + data = []; + ndx *= this.strideOut; + + for ( var i = 0; i < this.strideOut; ++ i ) { + + data[ i ] = this.output[ ndx + i ]; + + } + + if ( this.strideOut === 3 ) { + + switch ( type ) { + + case 'rotate': + case 'translate': + + fixCoords( data, -1 ); + break; + + case 'scale': + + fixCoords( data, 1 ); + break; + + } + + } else if ( this.strideOut === 4 && type === 'matrix' ) { + + fixCoords( data, -1 ); + + } + + } else { + + data = this.output[ ndx ]; + + if ( member && type === 'translate' ) { + data = getConvertedTranslation( member, data ); + } + + } + + return data; + + }; + + function Key ( time ) { + + this.targets = []; + this.time = time; + + } + + Key.prototype.addTarget = function ( fullSid, transform, member, data ) { + + this.targets.push( { + sid: fullSid, + member: member, + transform: transform, + data: data + } ); + + }; + + Key.prototype.apply = function ( opt_sid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + var target = this.targets[ i ]; + + if ( !opt_sid || target.sid === opt_sid ) { + + target.transform.update( target.data, target.member ); + + } + + } + + }; + + Key.prototype.getTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return this.targets[ i ]; + + } + + } + + return null; + + }; + + Key.prototype.hasTarget = function ( fullSid ) { + + for ( var i = 0; i < this.targets.length; ++ i ) { + + if ( this.targets[ i ].sid === fullSid ) { + + return true; + + } + + } + + return false; + + }; + + // TODO: Currently only doing linear interpolation. Should support full COLLADA spec. + Key.prototype.interpolate = function ( nextKey, time ) { + + for ( var i = 0, l = this.targets.length; i < l; i ++ ) { + + var target = this.targets[ i ], + nextTarget = nextKey.getTarget( target.sid ), + data; + + if ( target.transform.type !== 'matrix' && nextTarget ) { + + var scale = ( time - this.time ) / ( nextKey.time - this.time ), + nextData = nextTarget.data, + prevData = target.data; + + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; + + if ( prevData.length ) { + + data = []; + + for ( var j = 0; j < prevData.length; ++ j ) { + + data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; + + } + + } else { + + data = prevData + ( nextData - prevData ) * scale; + + } + + } else { + + data = target.data; + + } + + target.transform.update( data, target.member ); + + } + + }; + + // Camera + function Camera() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + } + + Camera.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'optics': + + this.parseOptics( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Camera.prototype.parseOptics = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + if ( element.childNodes[ i ].nodeName === 'technique_common' ) { + + var technique = element.childNodes[ i ]; + + for ( var j = 0; j < technique.childNodes.length; j ++ ) { + + this.technique = technique.childNodes[ j ].nodeName; + + if ( this.technique === 'perspective' ) { + + var perspective = technique.childNodes[ j ]; + + for ( var k = 0; k < perspective.childNodes.length; k ++ ) { + + var param = perspective.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'yfov': + this.yfov = param.textContent; + break; + case 'xfov': + this.xfov = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } else if ( this.technique === 'orthographic' ) { + + var orthographic = technique.childNodes[ j ]; + + for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { + + var param = orthographic.childNodes[ k ]; + + switch ( param.nodeName ) { + + case 'xmag': + this.xmag = param.textContent; + break; + case 'ymag': + this.ymag = param.textContent; + break; + case 'znear': + this.znear = param.textContent; + break; + case 'zfar': + this.zfar = param.textContent; + break; + case 'aspect_ratio': + this.aspect_ratio = param.textContent; + break; + + } + + } + + } + + } + + } + + } + + return this; + + }; + + function InstanceCamera() { + + this.url = ""; + + } + + InstanceCamera.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + + return this; + + }; + + // Light + + function Light() { + + this.id = ""; + this.name = ""; + this.technique = ""; + + } + + Light.prototype.parse = function ( element ) { + + this.id = element.getAttribute( 'id' ); + this.name = element.getAttribute( 'name' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon( child ); + break; + + case 'technique': + + this.parseTechnique( child ); + break; + + default: + break; + + } + + } + + return this; + + }; + + Light.prototype.parseCommon = function ( element ) { + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + switch ( element.childNodes[ i ].nodeName ) { + + case 'directional': + case 'point': + case 'spot': + case 'ambient': + + this.technique = element.childNodes[ i ].nodeName; + + var light = element.childNodes[ i ]; + + for ( var j = 0; j < light.childNodes.length; j ++ ) { + + var child = light.childNodes[j]; + + switch ( child.nodeName ) { + + case 'color': + + var rgba = _floats( child.textContent ); + this.color = new THREE.Color(0); + this.color.setRGB( rgba[0], rgba[1], rgba[2] ); + this.color.a = rgba[3]; + break; + + case 'falloff_angle': + + this.falloff_angle = parseFloat( child.textContent ); + break; + + case 'quadratic_attenuation': + var f = parseFloat( child.textContent ); + this.distance = f ? Math.sqrt( 1 / f ) : 0; + } + + } + + } + + } + + return this; + + }; + + Light.prototype.parseTechnique = function ( element ) { + + this.profile = element.getAttribute( 'profile' ); + + for ( var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + + switch ( child.nodeName ) { + + case 'intensity': + + this.intensity = parseFloat(child.textContent); + break; + + } + + } + + return this; + + }; + + function InstanceLight() { + + this.url = ""; + + } + + InstanceLight.prototype.parse = function ( element ) { + + this.url = element.getAttribute('url').replace(/^#/, ''); + + return this; + + }; + + function KinematicsModel( ) { + + this.id = ''; + this.name = ''; + this.joints = []; + this.links = []; + + } + + KinematicsModel.prototype.parse = function( element ) { + + this.id = element.getAttribute('id'); + this.name = element.getAttribute('name'); + this.joints = []; + this.links = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'technique_common': + + this.parseCommon(child); + break; + + default: + break; + + } + + } + + return this; + + }; + + KinematicsModel.prototype.parseCommon = function( element ) { + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( element.childNodes[ i ].nodeName ) { + + case 'joint': + this.joints.push( (new Joint()).parse(child) ); + break; + + case 'link': + this.links.push( (new Link()).parse(child) ); + break; + + default: + break; + + } + + } + + return this; + + }; + + function Joint( ) { + + this.sid = ''; + this.name = ''; + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + } + + Joint.prototype.parse = function( element ) { + + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.axis = new THREE.Vector3(); + this.limits = { + min: 0, + max: 0 + }; + this.type = ''; + this.static = false; + this.zeroPosition = 0.0; + this.middlePosition = 0.0; + + var axisElement = element.querySelector('axis'); + var _axis = _floats(axisElement.textContent); + this.axis = getConvertedVec3(_axis, 0); + + var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; + var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; + + this.limits = { + min: min, + max: max + }; + + var jointTypes = [ 'prismatic', 'revolute' ]; + for (var i = 0; i < jointTypes.length; i ++ ) { + + var type = jointTypes[ i ]; + + var jointElement = element.querySelector(type); + + if ( jointElement ) { + + this.type = type; + + } + + } + + // if the min is equal to or somehow greater than the max, consider the joint static + if ( this.limits.min >= this.limits.max ) { + + this.static = true; + + } + + this.middlePosition = (this.limits.min + this.limits.max) / 2.0; + return this; + + }; + + function Link( ) { + + this.sid = ''; + this.name = ''; + this.transforms = []; + this.attachments = []; + + } + + Link.prototype.parse = function( element ) { + + this.sid = element.getAttribute('sid'); + this.name = element.getAttribute('name'); + this.transforms = []; + this.attachments = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'attachment_full': + this.attachments.push( (new Attachment()).parse(child) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( (new Transform()).parse(child) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function Attachment( ) { + + this.joint = ''; + this.transforms = []; + this.links = []; + + } + + Attachment.prototype.parse = function( element ) { + + this.joint = element.getAttribute('joint').split('/').pop(); + this.links = []; + + for (var i = 0; i < element.childNodes.length; i ++ ) { + + var child = element.childNodes[ i ]; + if ( child.nodeType != 1 ) continue; + + switch ( child.nodeName ) { + + case 'link': + this.links.push( (new Link()).parse(child) ); + break; + + case 'rotate': + case 'translate': + case 'matrix': + + this.transforms.push( (new Transform()).parse(child) ); + break; + + default: + + break; + + } + + } + + return this; + + }; + + function _source( element ) { + + var id = element.getAttribute( 'id' ); + + if ( sources[ id ] != undefined ) { + + return sources[ id ]; + + } + + sources[ id ] = ( new Source(id )).parse( element ); + return sources[ id ]; + + } + + function _nsResolver( nsPrefix ) { + + if ( nsPrefix === "dae" ) { + + return "http://www.collada.org/2005/11/COLLADASchema"; + + } + + return null; + + } + + function _bools( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); + + } + + return data; + + } + + function _floats( str ) { + + var raw = _strings(str); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseFloat( raw[ i ] ) ); + + } + + return data; + + } + + function _ints( str ) { + + var raw = _strings( str ); + var data = []; + + for ( var i = 0, l = raw.length; i < l; i ++ ) { + + data.push( parseInt( raw[ i ], 10 ) ); + + } + + return data; + + } + + function _strings( str ) { + + return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; + + } + + function _trimString( str ) { + + return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); + + } + + function _attr_as_float( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseFloat( element.getAttribute( name ) ); + + } else { + + return defaultValue; + + } + + } + + function _attr_as_int( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return parseInt( element.getAttribute( name ), 10) ; + + } else { + + return defaultValue; + + } + + } + + function _attr_as_string( element, name, defaultValue ) { + + if ( element.hasAttribute( name ) ) { + + return element.getAttribute( name ); + + } else { + + return defaultValue; + + } + + } + + function _format_float( f, num ) { + + if ( f === undefined ) { + + var s = '0.'; + + while ( s.length < num + 2 ) { + + s += '0'; + + } + + return s; + + } + + num = num || 2; + + var parts = f.toString().split( '.' ); + parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; + + while ( parts[ 1 ].length < num ) { + + parts[ 1 ] += '0'; + + } + + return parts.join( '.' ); + + } + + function loadTextureImage ( texture, url ) { + + var loader = new THREE.ImageLoader(); + + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + } ); + + } + + function extractDoubleSided( obj, element ) { + + obj.doubleSided = false; + + var node = element.querySelectorAll('extra double_sided')[0]; + + if ( node ) { + + if ( node && parseInt( node.textContent, 10 ) === 1 ) { + + obj.doubleSided = true; + + } + + } + + } + + // Up axis conversion + + function setUpConversion() { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + upConversion = null; + + } else { + + switch ( colladaUp ) { + + case 'X': + + upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; + break; + + case 'Y': + + upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; + break; + + case 'Z': + + upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; + break; + + } + + } + + } + + function fixCoords( data, sign ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return; + + } + + switch ( upConversion ) { + + case 'XtoY': + + var tmp = data[ 0 ]; + data[ 0 ] = sign * data[ 1 ]; + data[ 1 ] = tmp; + break; + + case 'XtoZ': + + var tmp = data[ 2 ]; + data[ 2 ] = data[ 1 ]; + data[ 1 ] = data[ 0 ]; + data[ 0 ] = tmp; + break; + + case 'YtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = sign * tmp; + break; + + case 'YtoZ': + + var tmp = data[ 1 ]; + data[ 1 ] = sign * data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoX': + + var tmp = data[ 0 ]; + data[ 0 ] = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = tmp; + break; + + case 'ZtoY': + + var tmp = data[ 1 ]; + data[ 1 ] = data[ 2 ]; + data[ 2 ] = sign * tmp; + break; + + } + + } + + function getConvertedTranslation( axis, data ) { + + if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { + + return data; + + } + + switch ( axis ) { + case 'X': + data = upConversion === 'XtoY' ? data * -1 : data; + break; + case 'Y': + data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; + break; + case 'Z': + data = upConversion === 'ZtoY' ? data * -1 : data ; + break; + default: + break; + } + + return data; + } + + function getConvertedVec3( data, offset ) { + + var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; + fixCoords( arr, -1 ); + return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); + + } + + function getConvertedMat4( data ) { + + if ( options.convertUpAxis ) { + + // First fix rotation and scale + + // Columns first + var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; + fixCoords( arr, -1 ); + data[ 0 ] = arr[ 0 ]; + data[ 4 ] = arr[ 1 ]; + data[ 8 ] = arr[ 2 ]; + arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; + fixCoords( arr, -1 ); + data[ 1 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 9 ] = arr[ 2 ]; + arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; + fixCoords( arr, -1 ); + data[ 2 ] = arr[ 0 ]; + data[ 6 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + // Rows second + arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; + fixCoords( arr, -1 ); + data[ 0 ] = arr[ 0 ]; + data[ 1 ] = arr[ 1 ]; + data[ 2 ] = arr[ 2 ]; + arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; + fixCoords( arr, -1 ); + data[ 4 ] = arr[ 0 ]; + data[ 5 ] = arr[ 1 ]; + data[ 6 ] = arr[ 2 ]; + arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; + fixCoords( arr, -1 ); + data[ 8 ] = arr[ 0 ]; + data[ 9 ] = arr[ 1 ]; + data[ 10 ] = arr[ 2 ]; + + // Now fix translation + arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; + fixCoords( arr, -1 ); + data[ 3 ] = arr[ 0 ]; + data[ 7 ] = arr[ 1 ]; + data[ 11 ] = arr[ 2 ]; + + } + + return new THREE.Matrix4().set( + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], + data[12], data[13], data[14], data[15] + ); + + } + + function getConvertedIndex( index ) { + + if ( index > -1 && index < 3 ) { + + var members = [ 'X', 'Y', 'Z' ], + indices = { X: 0, Y: 1, Z: 2 }; + + index = getConvertedMember( members[ index ] ); + index = indices[ index ]; + + } + + return index; + + } + + function getConvertedMember( member ) { + + if ( options.convertUpAxis ) { + + switch ( member ) { + + case 'X': + + switch ( upConversion ) { + + case 'XtoY': + case 'XtoZ': + case 'YtoX': + + member = 'Y'; + break; + + case 'ZtoX': + + member = 'Z'; + break; + + } + + break; + + case 'Y': + + switch ( upConversion ) { + + case 'XtoY': + case 'YtoX': + case 'ZtoX': + + member = 'X'; + break; + + case 'XtoZ': + case 'YtoZ': + case 'ZtoY': + + member = 'Z'; + break; + + } + + break; + + case 'Z': + + switch ( upConversion ) { + + case 'XtoZ': + + member = 'X'; + break; + + case 'YtoZ': + case 'ZtoX': + case 'ZtoY': + + member = 'Y'; + break; + + } + + break; + + } + + } + + return member; + + } + + return { + + load: load, + parse: parse, + setPreferredShading: setPreferredShading, + applySkin: applySkin, + geometries : geometries, + options: options + + }; + +}; +// File: utils/GLTF2Loader.js +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + * @author Don McCurdy / https://www.donmccurdy.com + */ + +THREE.GLTF2Loader = ( function () { + + function GLTF2Loader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + GLTF2Loader.prototype = { + + constructor: GLTF2Loader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); + + var loader = new THREE.FileLoader( scope.manager ); + + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( data ) { + + scope.parse( data, onLoad, path ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setPath: function ( value ) { + + this.path = value; + + }, + + parse: function ( data, callback, path ) { + + var content; + var extensions = {}; + + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { + + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + + } else { + + content = convertUint8ArrayToString( new Uint8Array( data ) ); + + } + + var json = JSON.parse( content ); + + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + + } + + console.time( 'GLTF2Loader' ); + + var parser = new GLTFParser( json, extensions, { + + path: path || this.path, + crossOrigin: this.crossOrigin + + } ); + + parser.parse( function ( scene, scenes, cameras, animations ) { + + console.timeEnd( 'GLTF2Loader' ); + + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + + callback( glTF ); + + } ); + + } + + }; + + /* GLTFREGISTRY */ + + function GLTFRegistry() { + + var objects = {}; + + return { + + get: function ( key ) { + + return objects[ key ]; + + }, + + add: function ( key, object ) { + + objects[ key ] = object; + + }, + + remove: function ( key ) { + + delete objects[ key ]; + + }, + + removeAll: function () { + + objects = {}; + + }, + + update: function ( scene, camera ) { + + for ( var name in objects ) { + + var object = objects[ name ]; + + if ( object.update ) { + + object.update( scene, camera ); + + } + + } + + } + + }; + + } + + /* GLTFSHADER */ + + function GLTFShader( targetNode, allNodes ) { + + var boundUniforms = {}; + + // bind each uniform to its source node + + var uniforms = targetNode.material.uniforms; + + for ( var uniformId in uniforms ) { + + var uniform = uniforms[ uniformId ]; + + if ( uniform.semantic ) { + + var sourceNodeRef = uniform.node; + + var sourceNode = targetNode; + + if ( sourceNodeRef ) { + + sourceNode = allNodes[ sourceNodeRef ]; + + } + + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + + } + + } + + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + + } + + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + + var boundUniforms = this.boundUniforms; + + for ( var name in boundUniforms ) { + + var boundUniform = boundUniforms[ name ]; + + switch ( boundUniform.semantic ) { + + case "MODELVIEW": + + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + + case "MODELVIEWINVERSETRANSPOSE": + + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + + case "PROJECTION": + + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + + case "JOINTMATRIX": + + var m4v = boundUniform.uniform.value; + + for ( var mi = 0; mi < m4v.length; mi ++ ) { + + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + + } + + break; + + default : + + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + + } + + } + + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' + }; + + /* MATERIALS COMMON EXTENSION */ + + function GLTFMaterialsCommonExtension( json ) { + + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + + this.lights = {}; + + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + + for ( var lightId in lights ) { + + var light = lights[ lightId ]; + var lightNode; + + var lightParams = light[ light.type ]; + var color = new THREE.Color().fromArray( lightParams.color ); + + switch ( light.type ) { + + case "directional": + lightNode = new THREE.DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "point": + lightNode = new THREE.PointLight( color ); + break; + + case "spot": + lightNode = new THREE.SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + + case "ambient": + lightNode = new THREE.AmbientLight( color ); + break; + + } + + if ( lightNode ) { + + this.lights[ lightId ] = lightNode; + + } + + } + + } + + /* BINARY EXTENSION */ + + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; + var BINARY_EXTENSION_HEADER_LENGTH = 12; + var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; + + function GLTFBinaryExtension( data ) { + + this.name = EXTENSIONS.KHR_BINARY_GLTF; + this.content = null; + this.body = null; + + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + + this.header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ) + }; + + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { + + throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); + + } else if ( this.header.version < 2.0 ) { + + throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); + + } + + var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); + var chunkIndex = 0; + + while ( chunkIndex < chunkView.byteLength ) { + + var chunkLength = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + var chunkType = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; + + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { + + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); + this.content = convertUint8ArrayToString( contentArray ); + + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { + + var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; + this.body = data.slice( byteOffset, byteOffset + chunkLength ); + + } + + // Clients must ignore chunks with unknown types. + + chunkIndex += chunkLength; + + } + + if ( this.content === null ) { + + throw new Error( 'GLTF2Loader: JSON content not found.' ); + + } + + } + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + + /* CONSTANTS */ + + var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 + }; + + var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: THREE.Matrix3, + 35676: THREE.Matrix4, + 35664: THREE.Vector2, + 35665: THREE.Vector3, + 35666: THREE.Vector4, + 35678: THREE.Texture + }; + + var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + + var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipMapNearestFilter, + 9985: THREE.LinearMipMapNearestFilter, + 9986: THREE.NearestMipMapLinearFilter, + 9987: THREE.LinearMipMapLinearFilter + }; + + var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping + }; + + var WEBGL_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + + function _each( object, callback, thisObj ) { + + if ( !object ) { + return Promise.resolve(); + } + + var results; + var fns = []; + + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + + results = []; + + var length = object.length; + + for ( var idx = 0; idx < length; idx ++ ) { + + var value = callback.call( thisObj || this, object[ idx ], idx ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function( key, value ) { + + results[ key ] = value; + + }.bind( this, idx )); + + } else { + + results[ idx ] = value; + + } + + } + + } + + } else { + + results = {}; + + for ( var key in object ) { + + if ( object.hasOwnProperty( key ) ) { + + var value = callback.call( thisObj || this, object[ key ], key ); + + if ( value ) { + + fns.push( value ); + + if ( value instanceof Promise ) { + + value.then( function( key, value ) { + + results[ key ] = value; + + }.bind( this, key )); + + } else { + + results[ key ] = value; + + } + + } + + } + + } + + } + + return Promise.all( fns ).then( function() { + + return results; + + }); + + } + + function resolveURL( url, path ) { + + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + + return url; + + } + + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + + return url; + + } + + // Blob URL + if ( /^blob:.*$/i.test( url ) ) { + + return url; + + } + + // Relative URL + return ( path || '' ) + url; + + } + + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + function convertUint8ArrayToString( array ) { + + var s = ''; + + for ( var i = 0; i < array.length; i ++ ) { + + s += String.fromCharCode( array[ i ] ); + + } + + return s; + + } + + // Three.js seems too dependent on attribute names so globally + // replace those in the shader code + function replaceTHREEShaderAttributes( shaderText, technique ) { + + // Expected technique attributes + var attributes = {}; + + for ( var attributeId in technique.attributes ) { + + var pname = technique.attributes[ attributeId ]; + + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + + } + + // Figure out which attributes to change in technique + + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + + for ( var attributeId in attributes ) { + + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + + params[ attributeId ] = shaderParam; + + } + + } + + for ( var pname in params ) { + + var param = params[ pname ]; + var semantic = param.semantic; + + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + + switch ( semantic ) { + + case 'POSITION': + + shaderText = shaderText.replace( regEx, 'position' ); + break; + + case 'NORMAL': + + shaderText = shaderText.replace( regEx, 'normal' ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + shaderText = shaderText.replace( regEx, 'uv' ); + break; + + case 'TEXCOORD_1': + + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + shaderText = shaderText.replace( regEx, 'color' ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + + } + + } + + return shaderText; + + } + + function createDefaultMaterial() { + + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + + } + + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + + this.isDeferredShaderMaterial = true; + + this.params = params; + + } + + DeferredShaderMaterial.prototype.create = function () { + + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + + for ( var uniformId in this.params.uniforms ) { + + var originalUniform = this.params.uniforms[ uniformId ]; + + if ( originalUniform.value instanceof THREE.Texture ) { + + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + + } + + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + + } + + this.params.uniforms = uniforms; + + return new THREE.RawShaderMaterial( this.params ); + + }; + + /* GLTF PARSER */ + + function GLTFParser( json, extensions, options ) { + + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + + // loader object cache + this.cache = new GLTFRegistry(); + + } + + GLTFParser.prototype._withDependencies = function ( dependencies ) { + + var _dependencies = {}; + + for ( var i = 0; i < dependencies.length; i ++ ) { + + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + + var cached = this.cache.get( dependency ); + + if ( cached !== undefined ) { + + _dependencies[ dependency ] = cached; + + } else if ( this[ fnName ] ) { + + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + + _dependencies[ dependency ] = fn; + + } + + } + + return _each( _dependencies, function ( dependency ) { + + return dependency; + + } ); + + }; + + GLTFParser.prototype.parse = function ( callback ) { + + var json = this.json; + + // Clear the loader cache + this.cache.removeAll(); + + // Fire the callback on complete + this._withDependencies( [ + + "scenes", + "cameras", + "animations" + + ] ).then( function ( dependencies ) { + + var scenes = []; + + for ( var name in dependencies.scenes ) { + + scenes.push( dependencies.scenes[ name ] ); + + } + + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + + var cameras = []; + + for ( var name in dependencies.cameras ) { + + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + + } + + var animations = []; + + for ( var name in dependencies.animations ) { + + animations.push( dependencies.animations[ name ] ); + + } + + callback( scene, scenes, cameras, animations ); + + } ); + + }; + + GLTFParser.prototype.loadShaders = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.shaders, function ( shader ) { + + if ( shader.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ shader.bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + + resolve( shaderText ); + + } ); + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadBuffers = function () { + + var json = this.json; + var extensions = this.extensions; + var options = this.options; + + return _each( json.buffers, function ( buffer, name ) { + + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + + // If present, GLB container is required to be the first buffer. + if ( buffer.uri === undefined && name === 0 ) { + + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + + } + + return new Promise( function ( resolve ) { + + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + + resolve( buffer ); + + } ); + + } ); + + } else { + + console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); + + } + + } ); + + }; + + GLTFParser.prototype.loadBufferViews = function () { + + var json = this.json; + + return this._withDependencies( [ + + "buffers" + + ] ).then( function ( dependencies ) { + + return _each( json.bufferViews, function ( bufferView ) { + + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAccessors = function () { + + var json = this.json; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.accessors, function ( accessor ) { + + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + + var array; + + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + + // Use the full buffer if it's interleaved. + array = new TypedArray( arraybuffer ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + + } else { + + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + + return new THREE.BufferAttribute( array, itemSize ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadTextures = function () { + + var json = this.json; + var options = this.options; + + return this._withDependencies( [ + + "bufferViews" + + ] ).then( function ( dependencies ) { + + return _each( json.textures, function ( texture ) { + + if ( texture.source !== undefined ) { + + return new Promise( function ( resolve ) { + + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + + var urlCreator; + + if ( source.bufferView !== undefined ) { + + var bufferView = dependencies.bufferViews[ source.bufferView ]; + var blob = new Blob( [ bufferView ], { type: source.mimeType } ); + urlCreator = window.URL || window.webkitURL; + sourceUri = urlCreator.createObjectURL( blob ); + + } + + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + + if ( textureLoader === null ) { + + textureLoader = new THREE.TextureLoader(); + + } + + textureLoader.setCrossOrigin( options.crossOrigin ); + + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + + if ( urlCreator !== undefined ) { + + urlCreator.revokeObjectURL( sourceUri ); + + } + + _texture.flipY = false; + + if ( texture.name !== undefined ) _texture.name = texture.name; + + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + + console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + + } + + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + + if ( texture.sampler !== undefined ) { + + var sampler = json.samplers[ texture.sampler ]; + + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + + } + + resolve( _texture ); + + }, undefined, function () { + + resolve(); + + } ); + + } ); + + } + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMaterials = function () { + + var json = this.json; + + return this._withDependencies( [ + + "shaders", + "textures" + + ] ).then( function ( dependencies ) { + + return _each( json.materials, function ( material ) { + + var materialType; + var materialValues = {}; + var materialParams = {}; + + var khr_material; + + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + + } + + if ( khr_material ) { + + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + + switch ( khr_material.technique ) { + + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + + } + + keys.forEach( function( v ) { + + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + + } ); + + if ( khr_material.doubleSided || materialValues.doubleSided ) { + + materialParams.side = THREE.DoubleSide; + + } + + if ( khr_material.transparent || materialValues.transparent ) { + + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + + } + + } else if ( material.technique === undefined ) { + + if ( material.pbrMetallicRoughness !== undefined ) { + + // specification + // https://github.com/sbtron/glTF/blob/30de0b365d1566b1bbd8b9c140f9e995d3203226/specification/2.0/README.md#metallic-roughness-material + + materialType = THREE.MeshStandardMaterial; + + if ( material.pbrMetallicRoughness !== undefined ) { + + var metallicRoughness = material.pbrMetallicRoughness; + + materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + + var array = metallicRoughness.baseColorFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( metallicRoughness.baseColorTexture !== undefined ) { + + materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; + + } + + if ( materialParams.opacity < 1.0 || + ( materialParams.map !== undefined && + ( materialParams.map.format === THREE.AlphaFormat || + materialParams.map.format === THREE.RGBAFormat || + materialParams.map.format === THREE.LuminanceAlphaFormat ) ) ) { + + materialParams.transparent = true; + + } + + materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; + materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; + + if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { + + var textureIndex = metallicRoughness.metallicRoughnessTexture.index; + + // Note that currently metalnessMap would be entirely ignored because + // Three.js and glTF specification use different texture channels for metalness + // (Blue: Three.js, Red: glTF). + // But glTF specification team is discussing if they can change. + // Let's keep an eye on it so far. + // + // https://github.com/KhronosGroup/glTF/issues/857 + materialParams.metalnessMap = dependencies.textures[ textureIndex ]; + materialParams.roughnessMap = dependencies.textures[ textureIndex ]; + + } + + } + + } else { + + materialType = THREE.MeshPhongMaterial; + + } + + if ( material.normalTexture !== undefined ) { + + materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; + + } + + if ( material.occlusionTexture !== undefined ) { + + materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; + + } + + if ( material.emissiveTexture !== undefined ) { + + materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; + + } + + materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); + + if ( material.emissiveFactor !== undefined ) { + + materialParams.emissive.fromArray( material.emissiveFactor ); + + } + + Object.assign( materialValues, material.values ); + + } else { + + materialType = DeferredShaderMaterial; + + var technique = json.techniques[ material.technique ]; + + materialParams.uniforms = {}; + + var program = json.programs[ technique.program ]; + + if ( program ) { + + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + + if ( ! materialParams.fragmentShader ) { + + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = THREE.MeshPhongMaterial; + + } + + var vertexShader = dependencies.shaders[ program.vertexShader ]; + + if ( ! vertexShader ) { + + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = THREE.MeshPhongMaterial; + + } + + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + + var uniforms = technique.uniforms; + + for ( var uniformId in uniforms ) { + + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + + var ptype = shaderParam.type; + + if ( WEBGL_TYPE[ ptype ] ) { + + var pcount = shaderParam.count; + var value; + + if ( material.values !== undefined ) value = material.values[ pname ]; + + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + + switch ( ptype ) { + + case WEBGL_CONSTANTS.FLOAT: + + uvalue = shaderParam.value; + + if ( pname == "transparency" ) { + + materialParams.transparent = true; + + } + + if ( value !== undefined ) { + + uvalue = value; + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + + if ( shaderParam && shaderParam.value ) { + + uvalue.fromArray( shaderParam.value ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + break; + + case WEBGL_CONSTANTS.FLOAT_MAT2: + + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + + case WEBGL_CONSTANTS.FLOAT_MAT4: + + if ( pcount ) { + + uvalue = new Array( pcount ); + + for ( var mi = 0; mi < pcount; mi ++ ) { + + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + + } + + if ( shaderParam && shaderParam.value ) { + + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } else { + + if ( shaderParam && shaderParam.value ) { + + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + + } + + if ( value ) { + + uvalue.fromArray( value ); + + } + + } + + break; + + case WEBGL_CONSTANTS.SAMPLER_2D: + + if ( value !== undefined ) { + + uvalue = dependencies.textures[ value ]; + + } else if ( shaderParam.value !== undefined ) { + + uvalue = dependencies.textures[ shaderParam.value ]; + + } else { + + uvalue = null; + + } + + break; + + } + + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + + } else { + + throw new Error( "Unknown shader uniform param type: " + ptype ); + + } + + } + + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + + for ( var i = 0, il = enables.length; i < il; i ++ ) { + + var enable = enables[ i ]; + + switch ( STATES_ENABLES[ enable ] ) { + + case 'CULL_FACE': + + enableCullFace = true; + + break; + + case 'DEPTH_TEST': + + enableDepthTest = true; + + break; + + case 'BLEND': + + enableBlend = true; + + break; + + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + + break; + + default: + + throw new Error( "Unknown technique.states.enable: " + enable ); + + } + + } + + if ( enableCullFace ) { + + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + + } else { + + materialParams.side = THREE.DoubleSide; + + } + + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + + var blendEquationSeparate = functions.blendEquationSeparate; + + if ( blendEquationSeparate !== undefined ) { + + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + + } else { + + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + + } + + var blendFuncSeparate = functions.blendFuncSeparate; + + if ( blendFuncSeparate !== undefined ) { + + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + + } else { + + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + + } + + } + + } + + if ( Array.isArray( materialValues.diffuse ) ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); + + } else if ( typeof( materialValues.diffuse ) === 'string' ) { + + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + + } + + delete materialParams.diffuse; + + if ( typeof( materialValues.reflective ) === 'string' ) { + + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + + } + + if ( typeof( materialValues.bump ) === 'string' ) { + + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + + } + + if ( Array.isArray( materialValues.emission ) ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + + } else { + + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + + } + + } else if ( typeof( materialValues.emission ) === 'string' ) { + + if ( materialType === THREE.MeshBasicMaterial ) { + + materialParams.map = dependencies.textures[ materialValues.emission ]; + + } else { + + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + + } + + } + + if ( Array.isArray( materialValues.specular ) ) { + + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + + } else if ( typeof( materialValues.specular ) === 'string' ) { + + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + + } + + if ( materialValues.shininess !== undefined ) { + + materialParams.shininess = materialValues.shininess; + + } + + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + + return _material; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadMeshes = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "materials" + + ] ).then( function ( dependencies ) { + + return _each( json.meshes, function ( mesh ) { + + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + + if ( mesh.extras ) group.userData = mesh.extras; + + var primitives = mesh.primitives || []; + + for ( var name in primitives ) { + + var primitive = primitives[ name ]; + + var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + + var geometry; + + var meshNode; + + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( attributeEntry === undefined ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'NORMAL': + + geometry.addAttribute( 'normal', bufferAttribute ); + break; + + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + + geometry.addAttribute( 'uv', bufferAttribute ); + break; + + case 'TEXCOORD_1': + + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + + geometry.addAttribute( 'color', bufferAttribute ); + break; + + case 'WEIGHTS_0': + case 'WEIGHT': // WEIGHT semantic deprecated. + + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + + case 'JOINTS_0': + case 'JOINT': // JOINT semantic deprecated. + + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + } + + meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + + geometry = new THREE.BufferGeometry(); + + var attributes = primitive.attributes; + + for ( var attributeId in attributes ) { + + var attributeEntry = attributes[ attributeId ]; + + if ( ! attributeEntry ) return; + + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + + switch ( attributeId ) { + + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + + } + + } + + if ( primitive.indices !== undefined ) { + + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + + meshNode = new THREE.LineSegments( geometry, material ); + + } else { + + meshNode = new THREE.Line( geometry, material ); + + } + + } else { + + throw new Error( "Only triangular and line primitives are supported" ); + + } + + if ( geometry.attributes.color !== undefined ) { + + material.vertexColors = THREE.VertexColors; + material.needsUpdate = true; + + } + + meshNode.name = ( name === "0" ? group.name : group.name + name ); + + if ( primitive.extras ) meshNode.userData = primitive.extras; + + group.add( meshNode ); + + } + + return group; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadCameras = function () { + + var json = this.json; + + return _each( json.cameras, function ( camera ) { + + if ( camera.type == "perspective" && camera.perspective ) { + + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } else if ( camera.type == "orthographic" && camera.orthographic ) { + + var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + + if ( camera.extras ) _camera.userData = camera.extras; + + return _camera; + + } + + } ); + + }; + + GLTFParser.prototype.loadSkins = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors" + + ] ).then( function ( dependencies ) { + + return _each( json.skins, function ( skin ) { + + var bindShapeMatrix = new THREE.Matrix4(); + + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + + return _skin; + + } ); + + } ); + + }; + + GLTFParser.prototype.loadAnimations = function () { + + var json = this.json; + + return this._withDependencies( [ + + "accessors", + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.animations, function ( animation, animationId ) { + + var tracks = []; + + for ( var channelId in animation.channels ) { + + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + + if ( sampler ) { + + var target = channel.target; + var name = target.node || target.id; // NOTE: target.id is deprecated. + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + + var node = dependencies.nodes[ name ]; + + if ( node ) { + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + + } + + } + + } + + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + + return new THREE.AnimationClip( name, undefined, tracks ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadNodes = function () { + + var json = this.json; + var extensions = this.extensions; + var scope = this; + + return _each( json.nodes, function ( node ) { + + var matrix = new THREE.Matrix4(); + + var _node; + + if ( node.jointName ) { + + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + + } else { + + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + + } + + if ( node.extras ) _node.userData = node.extras; + + if ( node.matrix !== undefined ) { + + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + + } else { + + if ( node.translation !== undefined ) { + + _node.position.fromArray( node.translation ); + + } + + if ( node.rotation !== undefined ) { + + _node.quaternion.fromArray( node.rotation ); + + } + + if ( node.scale !== undefined ) { + + _node.scale.fromArray( node.scale ); + + } + + } + + return _node; + + } ).then( function ( __nodes ) { + + return scope._withDependencies( [ + + "meshes", + "skins", + "cameras" + + ] ).then( function ( dependencies ) { + + return _each( __nodes, function ( _node, nodeId ) { + + var node = json.nodes[ nodeId ]; + + var meshes; + + if ( node.mesh !== undefined) { + + meshes = [ node.mesh ]; + + } else if ( node.meshes !== undefined ) { + + console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); + + meshes = node.meshes; + + } + + if ( meshes !== undefined ) { + + for ( var meshId in meshes ) { + + var mesh = meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + + if ( group === undefined ) { + + console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); + continue; + + } + + for ( var childrenId in group.children ) { + + var child = group.children[ childrenId ]; + + // clone Mesh to add to _node + + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + + var material; + + if ( originalMaterial.isDeferredShaderMaterial ) { + + originalMaterial = material = originalMaterial.create(); + + } else { + + material = originalMaterial; + + } + + switch ( child.type ) { + + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + + default: + child = new THREE.Mesh( originalGeometry, material ); + + } + + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var skinEntry; + + if ( node.skin !== undefined ) { + + skinEntry = dependencies.skins[ node.skin ]; + + } + + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + + var getJointNode = function ( jointId ) { + + var keys = Object.keys( __nodes ); + + for ( var i = 0, il = keys.length; i < il; i ++ ) { + + var n = __nodes[ keys[ i ] ]; + + if ( n.jointName === jointId ) return n; + + } + + return null; + + }; + + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + + var bones = []; + var boneInverses = []; + + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + + if ( jointNode ) { + + bones.push( jointNode ); + + var m = skinEntry.inverseBindMatrices.array; + var mat = new THREE.Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + + } else { + + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + + } + + } + + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + + var buildBoneGraph = function ( parentJson, parentObject, property ) { + + var children = parentJson[ property ]; + + if ( children === undefined ) return; + + for ( var i = 0, il = children.length; i < il; i ++ ) { + + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + + } + + } + + }; + + buildBoneGraph( node, child, 'skeletons' ); + + } + + _node.add( child ); + + } + + } + + } + + if ( node.camera !== undefined ) { + + var camera = dependencies.cameras[ node.camera ]; + + _node.add( camera ); + + } + + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + + _node.add( light ); + + } + + return _node; + + } ); + + } ); + + } ); + + }; + + GLTFParser.prototype.loadScenes = function () { + + var json = this.json; + + // scene node hierachy builder + + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + + var node = json.nodes[ nodeId ]; + + if ( node.children ) { + + var children = node.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + + } + + } + + } + + return this._withDependencies( [ + + "nodes" + + ] ).then( function ( dependencies ) { + + return _each( json.scenes, function ( scene ) { + + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + + if ( scene.extras ) _scene.userData = scene.extras; + + var nodes = scene.nodes || []; + + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + + } + + _scene.traverse( function ( child ) { + + // Register raw material meshes with GLTF2Loader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + + } + + } ); + + return _scene; + + } ); + + } ); + + }; + + return GLTF2Loader; + +} )(); +// File: utils/GLTFLoader.js +/** + * @author Rich Tibbett / https://github.com/richtr + * @author mrdoob / http://mrdoob.com/ + * @author Tony Parisi / http://www.tonyparisi.com/ + * @author Takahiro / https://github.com/takahirox + */ + +THREE.GLTFLoader = ( function () { + function GLTFLoader( manager ) { + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + } + GLTFLoader.prototype = { + constructor: GLTFLoader, + load: function ( url, onLoad, onProgress, onError ) { + var scope = this; + var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); + var loader = new THREE.FileLoader( scope.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( data ) { + scope.parse( data, onLoad, path ); + }, onProgress, onError ); + }, + setCrossOrigin: function ( value ) { + this.crossOrigin = value; + }, + setPath: function ( value ) { + this.path = value; + }, + parse: function ( data, callback, path ) { + var content; + var extensions = {}; + var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); + if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { + extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); + content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; + } else { + content = convertUint8ArrayToString( new Uint8Array( data ) ); + } + var json = JSON.parse( content ); + if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { + extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); + } + console.time( 'GLTFLoader' ); + var parser = new GLTFParser( json, extensions, { + path: path || this.path, + crossOrigin: this.crossOrigin + } ); + parser.parse( function ( scene, scenes, cameras, animations ) { + console.timeEnd( 'GLTFLoader' ); + var glTF = { + "scene": scene, + "scenes": scenes, + "cameras": cameras, + "animations": animations + }; + callback( glTF ); + } ); + } + }; + + /* GLTFREGISTRY */ + function GLTFRegistry() { + var objects = {}; + return { + get: function ( key ) { + return objects[ key ]; + }, + add: function ( key, object ) { + objects[ key ] = object; + }, + remove: function ( key ) { + delete objects[ key ]; + }, + removeAll: function () { + objects = {}; + }, + update: function ( scene, camera ) { + for ( var name in objects ) { + var object = objects[ name ]; + if ( object.update ) { + object.update( scene, camera ); + } + } + } + }; + } + + /* GLTFSHADERS */ + GLTFLoader.Shaders = { + update: function () { + console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); + } + }; + + /* GLTFSHADER */ + function GLTFShader( targetNode, allNodes ) { + var boundUniforms = {}; + // bind each uniform to its source node + var uniforms = targetNode.material.uniforms; + for ( var uniformId in uniforms ) { + var uniform = uniforms[ uniformId ]; + if ( uniform.semantic ) { + var sourceNodeRef = uniform.node; + var sourceNode = targetNode; + if ( sourceNodeRef ) { + sourceNode = allNodes[ sourceNodeRef ]; + } + boundUniforms[ uniformId ] = { + semantic: uniform.semantic, + sourceNode: sourceNode, + targetNode: targetNode, + uniform: uniform + }; + } + } + this.boundUniforms = boundUniforms; + this._m4 = new THREE.Matrix4(); + } + // Update - update all the uniform values + GLTFShader.prototype.update = function ( scene, camera ) { + var boundUniforms = this.boundUniforms; + for ( var name in boundUniforms ) { + var boundUniform = boundUniforms[ name ]; + switch ( boundUniform.semantic ) { + case "MODELVIEW": + var m4 = boundUniform.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + break; + case "MODELVIEWINVERSETRANSPOSE": + var m3 = boundUniform.uniform.value; + this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); + m3.getNormalMatrix( this._m4 ); + break; + case "PROJECTION": + var m4 = boundUniform.uniform.value; + m4.copy( camera.projectionMatrix ); + break; + case "JOINTMATRIX": + var m4v = boundUniform.uniform.value; + for ( var mi = 0; mi < m4v.length; mi ++ ) { + // So it goes like this: + // SkinnedMesh world matrix is already baked into MODELVIEW; + // transform joints to local space, + // then transform using joint's inverse + m4v[ mi ] + .getInverse( boundUniform.sourceNode.matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) + .multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) + .multiply( boundUniform.targetNode.bindMatrix ); + } + break; + default : + console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); + break; + } + } + }; + + /* ANIMATION */ + GLTFLoader.Animations = { + update: function () { + console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); + } + }; + + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ + var EXTENSIONS = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_MATERIALS_COMMON: 'KHR_materials_common' + }; + + /* MATERIALS COMMON EXTENSION */ + function GLTFMaterialsCommonExtension( json ) { + this.name = EXTENSIONS.KHR_MATERIALS_COMMON; + this.lights = {}; + var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; + var lights = extension.lights || {}; + for ( var lightId in lights ) { + var light = lights[ lightId ]; + var lightNode; + var lightParams = light[ light.type ]; + var color = new THREE.Color().fromArray( lightParams.color ); + switch ( light.type ) { + case "directional": + lightNode = new THREE.DirectionalLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + case "point": + lightNode = new THREE.PointLight( color ); + break; + case "spot": + lightNode = new THREE.SpotLight( color ); + lightNode.position.set( 0, 0, 1 ); + break; + case "ambient": + lightNode = new THREE.AmbientLight( color ); + break; + } + if ( lightNode ) { + this.lights[ lightId ] = lightNode; + } + } + } + + /* BINARY EXTENSION */ + var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; + var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; + var BINARY_EXTENSION_HEADER_LENGTH = 20; + function GLTFBinaryExtension( data ) { + this.name = EXTENSIONS.KHR_BINARY_GLTF; + var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); + var header = { + magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ), + contentLength: headerView.getUint32( 12, true ), + contentFormat: headerView.getUint32( 16, true ) + }; + for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { + var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; + if ( header[ key ] !== value ) { + throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); + } + } + var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); + this.header = header; + this.content = convertUint8ArrayToString( contentArray ); + this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); + } + GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { + var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; + var array = new Uint8Array( bufferView ); + return convertUint8ArrayToString( array ); + }; + GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) { + var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; + var bufferView = bufferViews[ metadata.bufferView ]; + var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) ); + return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData ); + }; + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + /* CONSTANTS */ + var WEBGL_CONSTANTS = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + TRIANGLES: 4, + LINES: 1, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123, + VERTEX_SHADER: 35633, + FRAGMENT_SHADER: 35632 + }; + var WEBGL_TYPE = { + 5126: Number, + //35674: THREE.Matrix2, + 35675: THREE.Matrix3, + 35676: THREE.Matrix4, + 35664: THREE.Vector2, + 35665: THREE.Vector3, + 35666: THREE.Vector4, + 35678: THREE.Texture + }; + var WEBGL_COMPONENT_TYPES = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + var WEBGL_FILTERS = { + 9728: THREE.NearestFilter, + 9729: THREE.LinearFilter, + 9984: THREE.NearestMipMapNearestFilter, + 9985: THREE.LinearMipMapNearestFilter, + 9986: THREE.NearestMipMapLinearFilter, + 9987: THREE.LinearMipMapLinearFilter + }; + var WEBGL_WRAPPINGS = { + 33071: THREE.ClampToEdgeWrapping, + 33648: THREE.MirroredRepeatWrapping, + 10497: THREE.RepeatWrapping + }; + var WEBGL_TEXTURE_FORMATS = { + 6406: THREE.AlphaFormat, + 6407: THREE.RGBFormat, + 6408: THREE.RGBAFormat, + 6409: THREE.LuminanceFormat, + 6410: THREE.LuminanceAlphaFormat + }; + var WEBGL_TEXTURE_DATATYPES = { + 5121: THREE.UnsignedByteType, + 32819: THREE.UnsignedShort4444Type, + 32820: THREE.UnsignedShort5551Type, + 33635: THREE.UnsignedShort565Type + }; + var WEBGL_SIDES = { + 1028: THREE.BackSide, // Culling front + 1029: THREE.FrontSide // Culling back + //1032: THREE.NoSide // Culling front and back, what to do? + }; + var WEBGL_DEPTH_FUNCS = { + 512: THREE.NeverDepth, + 513: THREE.LessDepth, + 514: THREE.EqualDepth, + 515: THREE.LessEqualDepth, + 516: THREE.GreaterEqualDepth, + 517: THREE.NotEqualDepth, + 518: THREE.GreaterEqualDepth, + 519: THREE.AlwaysDepth + }; + var WEBGL_BLEND_EQUATIONS = { + 32774: THREE.AddEquation, + 32778: THREE.SubtractEquation, + 32779: THREE.ReverseSubtractEquation + }; + var WEBGL_BLEND_FUNCS = { + 0: THREE.ZeroFactor, + 1: THREE.OneFactor, + 768: THREE.SrcColorFactor, + 769: THREE.OneMinusSrcColorFactor, + 770: THREE.SrcAlphaFactor, + 771: THREE.OneMinusSrcAlphaFactor, + 772: THREE.DstAlphaFactor, + 773: THREE.OneMinusDstAlphaFactor, + 774: THREE.DstColorFactor, + 775: THREE.OneMinusDstColorFactor, + 776: THREE.SrcAlphaSaturateFactor + // The followings are not supported by Three.js yet + //32769: CONSTANT_COLOR, + //32770: ONE_MINUS_CONSTANT_COLOR, + //32771: CONSTANT_ALPHA, + //32772: ONE_MINUS_CONSTANT_COLOR + }; + var WEBGL_TYPE_SIZES = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + var PATH_PROPERTIES = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion' + }; + var INTERPOLATION = { + LINEAR: THREE.InterpolateLinear, + STEP: THREE.InterpolateDiscrete + }; + var STATES_ENABLES = { + 2884: 'CULL_FACE', + 2929: 'DEPTH_TEST', + 3042: 'BLEND', + 3089: 'SCISSOR_TEST', + 32823: 'POLYGON_OFFSET_FILL', + 32926: 'SAMPLE_ALPHA_TO_COVERAGE' + }; + + /* UTILITY FUNCTIONS */ + function _each( object, callback, thisObj ) { + if ( !object ) { + return Promise.resolve(); + } + var results; + var fns = []; + if ( Object.prototype.toString.call( object ) === '[object Array]' ) { + results = []; + var length = object.length; + for ( var idx = 0; idx < length; idx ++ ) { + var value = callback.call( thisObj || this, object[ idx ], idx ); + if ( value ) { + fns.push( value ); + if ( value instanceof Promise ) { + value.then( function( key, value ) { + results[ key ] = value; + }.bind( this, idx )); + } else { + results[ idx ] = value; + } + } + } + } else { + results = {}; + for ( var key in object ) { + if ( object.hasOwnProperty( key ) ) { + var value = callback.call( thisObj || this, object[ key ], key ); + if ( value ) { + fns.push( value ); + if ( value instanceof Promise ) { + value.then( function( key, value ) { + results[ key ] = value; + }.bind( this, key )); + } else { + results[ key ] = value; + } + } + } + } + } + return Promise.all( fns ).then( function() { + return results; + }); + } + function resolveURL( url, path ) { + // Invalid URL + if ( typeof url !== 'string' || url === '' ) + return ''; + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) { + return url; + } + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) { + return url; + } + // Relative URL + return ( path || '' ) + url; + } + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + function convertUint8ArrayToString( array ) { + var s = ''; + for ( var i = 0; i < array.length; i ++ ) { + s += String.fromCharCode( array[ i ] ); + } + return s; + } + // Three.js seems too dependent on attribute names so globally + // replace those in the shader code + function replaceTHREEShaderAttributes( shaderText, technique ) { + // Expected technique attributes + var attributes = {}; + for ( var attributeId in technique.attributes ) { + var pname = technique.attributes[ attributeId ]; + var param = technique.parameters[ pname ]; + var atype = param.type; + var semantic = param.semantic; + attributes[ attributeId ] = { + type: atype, + semantic: semantic + }; + } + // Figure out which attributes to change in technique + var shaderParams = technique.parameters; + var shaderAttributes = technique.attributes; + var params = {}; + for ( var attributeId in attributes ) { + var pname = shaderAttributes[ attributeId ]; + var shaderParam = shaderParams[ pname ]; + var semantic = shaderParam.semantic; + if ( semantic ) { + params[ attributeId ] = shaderParam; + } + } + for ( var pname in params ) { + var param = params[ pname ]; + var semantic = param.semantic; + var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); + switch ( semantic ) { + case "POSITION": + shaderText = shaderText.replace( regEx, 'position' ); + break; + case "NORMAL": + shaderText = shaderText.replace( regEx, 'normal' ); + break; + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + shaderText = shaderText.replace( regEx, 'uv' ); + break; + case 'TEXCOORD_1': + shaderText = shaderText.replace( regEx, 'uv2' ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + shaderText = shaderText.replace( regEx, 'color' ); + break; + case "WEIGHT": + shaderText = shaderText.replace( regEx, 'skinWeight' ); + break; + case "JOINT": + shaderText = shaderText.replace( regEx, 'skinIndex' ); + break; + } + } + return shaderText; + } + function createDefaultMaterial() { + return new THREE.MeshPhongMaterial( { + color: 0x00000, + emissive: 0x888888, + specular: 0x000000, + shininess: 0, + transparent: false, + depthTest: true, + side: THREE.FrontSide + } ); + } + // Deferred constructor for RawShaderMaterial types + function DeferredShaderMaterial( params ) { + this.isDeferredShaderMaterial = true; + this.params = params; + } + DeferredShaderMaterial.prototype.create = function () { + var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); + for ( var uniformId in this.params.uniforms ) { + var originalUniform = this.params.uniforms[ uniformId ]; + if ( originalUniform.value instanceof THREE.Texture ) { + uniforms[ uniformId ].value = originalUniform.value; + uniforms[ uniformId ].value.needsUpdate = true; + } + uniforms[ uniformId ].semantic = originalUniform.semantic; + uniforms[ uniformId ].node = originalUniform.node; + } + this.params.uniforms = uniforms; + return new THREE.RawShaderMaterial( this.params ); + }; + + /* GLTF PARSER */ + function GLTFParser( json, extensions, options ) { + this.json = json || {}; + this.extensions = extensions || {}; + this.options = options || {}; + // loader object cache + this.cache = new GLTFRegistry(); + } + GLTFParser.prototype._withDependencies = function ( dependencies ) { + var _dependencies = {}; + for ( var i = 0; i < dependencies.length; i ++ ) { + var dependency = dependencies[ i ]; + var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); + var cached = this.cache.get( dependency ); + if ( cached !== undefined ) { + _dependencies[ dependency ] = cached; + } else if ( this[ fnName ] ) { + var fn = this[ fnName ](); + this.cache.add( dependency, fn ); + _dependencies[ dependency ] = fn; + } + } + return _each( _dependencies, function ( dependency ) { + return dependency; + } ); + }; + GLTFParser.prototype.parse = function ( callback ) { + var json = this.json; + // Clear the loader cache + this.cache.removeAll(); + // Fire the callback on complete + this._withDependencies( [ + "scenes", + "cameras", + "animations" + ] ).then( function ( dependencies ) { + var scenes = []; + for ( var name in dependencies.scenes ) { + scenes.push( dependencies.scenes[ name ] ); + } + var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; + var cameras = []; + for ( var name in dependencies.cameras ) { + var camera = dependencies.cameras[ name ]; + cameras.push( camera ); + } + var animations = []; + for ( var name in dependencies.animations ) { + animations.push( dependencies.animations[ name ] ); + } + callback( scene, scenes, cameras, animations ); + } ); + }; + GLTFParser.prototype.loadShaders = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.shaders, function ( shader ) { + if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); + } + return new Promise( function ( resolve ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'text' ); + loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { + resolve( shaderText ); + } ); + } ); + } ); + } ); + }; + GLTFParser.prototype.loadBuffers = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return _each( json.buffers, function ( buffer, name ) { + if ( name === BINARY_EXTENSION_BUFFER_NAME ) { + return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; + } + if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { + return new Promise( function ( resolve ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { + resolve( buffer ); + } ); + } ); + } else { + console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); + } + } ); + }; + GLTFParser.prototype.loadBufferViews = function () { + var json = this.json; + return this._withDependencies( [ + "buffers" + ] ).then( function ( dependencies ) { + return _each( json.bufferViews, function ( bufferView ) { + var arraybuffer = dependencies.buffers[ bufferView.buffer ]; + var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; + return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); + } ); + } ); + }; + GLTFParser.prototype.loadAccessors = function () { + var json = this.json; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.accessors, function ( accessor ) { + var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; + var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; + var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + var elementBytes = TypedArray.BYTES_PER_ELEMENT; + var itemBytes = elementBytes * itemSize; + // The buffer is not interleaved if the stride is the item size in bytes. + if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { + // Use the full buffer if it's interleaved. + var array = new TypedArray( arraybuffer ); + // Integer parameters to IB/IBA are in array elements, not bytes. + var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); + return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); + } else { + array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); + return new THREE.BufferAttribute( array, itemSize ); + } + } ); + } ); + }; + GLTFParser.prototype.loadTextures = function () { + var json = this.json; + var extensions = this.extensions; + var options = this.options; + return this._withDependencies( [ + "bufferViews" + ] ).then( function ( dependencies ) { + return _each( json.textures, function ( texture ) { + if ( texture.source ) { + return new Promise( function ( resolve ) { + var source = json.images[ texture.source ]; + var sourceUri = source.uri; + if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { + sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); + } + var textureLoader = THREE.Loader.Handlers.get( sourceUri ); + if ( textureLoader === null ) { + textureLoader = new THREE.TextureLoader(); + } + textureLoader.setCrossOrigin( options.crossOrigin ); + textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { + _texture.flipY = false; + if ( texture.name !== undefined ) _texture.name = texture.name; + _texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; + if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { + console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + + 'internalFormat will be forced to be the same value as format.' ); + } + _texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; + if ( texture.sampler ) { + var sampler = json.samplers[ texture.sampler ]; + _texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; + _texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; + _texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; + _texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; + } + resolve( _texture ); + }, undefined, function () { + resolve(); + } ); + } ); + } + } ); + } ); + }; + GLTFParser.prototype.loadMaterials = function () { + var json = this.json; + return this._withDependencies( [ + "shaders", + "textures" + ] ).then( function ( dependencies ) { + return _each( json.materials, function ( material ) { + var materialType; + var materialValues = {}; + var materialParams = {}; + var khr_material; + if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { + khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; + } + if ( khr_material ) { + // don't copy over unused values to avoid material warning spam + var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; + switch ( khr_material.technique ) { + case 'BLINN' : + case 'PHONG' : + materialType = THREE.MeshPhongMaterial; + keys.push( 'diffuse', 'specular', 'shininess' ); + break; + case 'LAMBERT' : + materialType = THREE.MeshLambertMaterial; + keys.push( 'diffuse' ); + break; + case 'CONSTANT' : + default : + materialType = THREE.MeshBasicMaterial; + break; + } + keys.forEach( function( v ) { + if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; + } ); + if ( khr_material.doubleSided || materialValues.doubleSided ) { + materialParams.side = THREE.DoubleSide; + } + if ( khr_material.transparent || materialValues.transparent ) { + materialParams.transparent = true; + materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; + } + } else if ( material.technique === undefined ) { + materialType = THREE.MeshPhongMaterial; + Object.assign( materialValues, material.values ); + } else { + materialType = DeferredShaderMaterial; + var technique = json.techniques[ material.technique ]; + materialParams.uniforms = {}; + var program = json.programs[ technique.program ]; + if ( program ) { + materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; + if ( ! materialParams.fragmentShader ) { + console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); + materialType = THREE.MeshPhongMaterial; + } + var vertexShader = dependencies.shaders[ program.vertexShader ]; + if ( ! vertexShader ) { + console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); + materialType = THREE.MeshPhongMaterial; + } + // IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS + materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); + var uniforms = technique.uniforms; + for ( var uniformId in uniforms ) { + var pname = uniforms[ uniformId ]; + var shaderParam = technique.parameters[ pname ]; + var ptype = shaderParam.type; + if ( WEBGL_TYPE[ ptype ] ) { + var pcount = shaderParam.count; + var value; + if ( material.values !== undefined ) value = material.values[ pname ]; + var uvalue = new WEBGL_TYPE[ ptype ](); + var usemantic = shaderParam.semantic; + var unode = shaderParam.node; + switch ( ptype ) { + case WEBGL_CONSTANTS.FLOAT: + uvalue = shaderParam.value; + if ( pname == "transparency" ) { + materialParams.transparent = true; + } + if ( value !== undefined ) { + uvalue = value; + } + break; + case WEBGL_CONSTANTS.FLOAT_VEC2: + case WEBGL_CONSTANTS.FLOAT_VEC3: + case WEBGL_CONSTANTS.FLOAT_VEC4: + case WEBGL_CONSTANTS.FLOAT_MAT3: + if ( shaderParam && shaderParam.value ) { + uvalue.fromArray( shaderParam.value ); + } + if ( value ) { + uvalue.fromArray( value ); + } + break; + case WEBGL_CONSTANTS.FLOAT_MAT2: + // what to do? + console.warn( "FLOAT_MAT2 is not a supported uniform type" ); + break; + case WEBGL_CONSTANTS.FLOAT_MAT4: + if ( pcount ) { + uvalue = new Array( pcount ); + for ( var mi = 0; mi < pcount; mi ++ ) { + uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); + } + if ( shaderParam && shaderParam.value ) { + var m4v = shaderParam.value; + uvalue.fromArray( m4v ); + } + if ( value ) { + uvalue.fromArray( value ); + } + } else { + if ( shaderParam && shaderParam.value ) { + var m4 = shaderParam.value; + uvalue.fromArray( m4 ); + } + if ( value ) { + uvalue.fromArray( value ); + } + } + break; + case WEBGL_CONSTANTS.SAMPLER_2D: + if ( value !== undefined ) { + uvalue = dependencies.textures[ value ]; + } else if ( shaderParam.value !== undefined ) { + uvalue = dependencies.textures[ shaderParam.value ]; + } else { + uvalue = null; + } + break; + } + materialParams.uniforms[ uniformId ] = { + value: uvalue, + semantic: usemantic, + node: unode + }; + } else { + throw new Error( "Unknown shader uniform param type: " + ptype ); + } + } + var states = technique.states || {}; + var enables = states.enable || []; + var functions = states.functions || {}; + var enableCullFace = false; + var enableDepthTest = false; + var enableBlend = false; + for ( var i = 0, il = enables.length; i < il; i ++ ) { + var enable = enables[ i ]; + switch ( STATES_ENABLES[ enable ] ) { + case 'CULL_FACE': + enableCullFace = true; + break; + case 'DEPTH_TEST': + enableDepthTest = true; + break; + case 'BLEND': + enableBlend = true; + break; + // TODO: implement + case 'SCISSOR_TEST': + case 'POLYGON_OFFSET_FILL': + case 'SAMPLE_ALPHA_TO_COVERAGE': + break; + default: + throw new Error( "Unknown technique.states.enable: " + enable ); + } + } + if ( enableCullFace ) { + materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; + } else { + materialParams.side = THREE.DoubleSide; + } + materialParams.depthTest = enableDepthTest; + materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; + materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; + materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; + materialParams.transparent = enableBlend; + var blendEquationSeparate = functions.blendEquationSeparate; + if ( blendEquationSeparate !== undefined ) { + materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; + materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; + } else { + materialParams.blendEquation = THREE.AddEquation; + materialParams.blendEquationAlpha = THREE.AddEquation; + } + var blendFuncSeparate = functions.blendFuncSeparate; + if ( blendFuncSeparate !== undefined ) { + materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; + materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; + materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; + materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; + } else { + materialParams.blendSrc = THREE.OneFactor; + materialParams.blendDst = THREE.ZeroFactor; + materialParams.blendSrcAlpha = THREE.OneFactor; + materialParams.blendDstAlpha = THREE.ZeroFactor; + } + } + } + if ( Array.isArray( materialValues.diffuse ) ) { + materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); + } else if ( typeof( materialValues.diffuse ) === 'string' ) { + materialParams.map = dependencies.textures[ materialValues.diffuse ]; + } + delete materialParams.diffuse; + if ( typeof( materialValues.reflective ) === 'string' ) { + materialParams.envMap = dependencies.textures[ materialValues.reflective ]; + } + if ( typeof( materialValues.bump ) === 'string' ) { + materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; + } + if ( Array.isArray( materialValues.emission ) ) { + if ( materialType === THREE.MeshBasicMaterial ) { + materialParams.color = new THREE.Color().fromArray( materialValues.emission ); + } else { + materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); + } + } else if ( typeof( materialValues.emission ) === 'string' ) { + if ( materialType === THREE.MeshBasicMaterial ) { + materialParams.map = dependencies.textures[ materialValues.emission ]; + } else { + materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; + } + } + if ( Array.isArray( materialValues.specular ) ) { + materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); + } else if ( typeof( materialValues.specular ) === 'string' ) { + materialParams.specularMap = dependencies.textures[ materialValues.specular ]; + } + if ( materialValues.shininess !== undefined ) { + materialParams.shininess = materialValues.shininess; + } + var _material = new materialType( materialParams ); + if ( material.name !== undefined ) _material.name = material.name; + return _material; + } ); + } ); + }; + GLTFParser.prototype.loadMeshes = function () { + var json = this.json; + return this._withDependencies( [ + "accessors", + "materials" + ] ).then( function ( dependencies ) { + return _each( json.meshes, function ( mesh ) { + var group = new THREE.Group(); + if ( mesh.name !== undefined ) group.name = mesh.name; + if ( mesh.extras ) group.userData = mesh.extras; + var primitives = mesh.primitives || []; + for ( var name in primitives ) { + var primitive = primitives[ name ]; + if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { + var geometry = new THREE.BufferGeometry(); + var attributes = primitive.attributes; + for ( var attributeId in attributes ) { + var attributeEntry = attributes[ attributeId ]; + if ( ! attributeEntry ) return; + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + switch ( attributeId ) { + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + case 'NORMAL': + geometry.addAttribute( 'normal', bufferAttribute ); + break; + case 'TEXCOORD_0': + case 'TEXCOORD0': + case 'TEXCOORD': + geometry.addAttribute( 'uv', bufferAttribute ); + break; + case 'TEXCOORD_1': + geometry.addAttribute( 'uv2', bufferAttribute ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + case 'WEIGHT': + geometry.addAttribute( 'skinWeight', bufferAttribute ); + break; + case 'JOINT': + geometry.addAttribute( 'skinIndex', bufferAttribute ); + break; + } + } + if ( primitive.indices ) { + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + } + var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); + var meshNode = new THREE.Mesh( geometry, material ); + meshNode.castShadow = true; + meshNode.name = ( name === "0" ? group.name : group.name + name ); + if ( primitive.extras ) meshNode.userData = primitive.extras; + group.add( meshNode ); + } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { + var geometry = new THREE.BufferGeometry(); + var attributes = primitive.attributes; + for ( var attributeId in attributes ) { + var attributeEntry = attributes[ attributeId ]; + if ( ! attributeEntry ) return; + var bufferAttribute = dependencies.accessors[ attributeEntry ]; + switch ( attributeId ) { + case 'POSITION': + geometry.addAttribute( 'position', bufferAttribute ); + break; + case 'COLOR_0': + case 'COLOR0': + case 'COLOR': + geometry.addAttribute( 'color', bufferAttribute ); + break; + } + } + var material = dependencies.materials[ primitive.material ]; + var meshNode; + if ( primitive.indices ) { + geometry.setIndex( dependencies.accessors[ primitive.indices ] ); + meshNode = new THREE.LineSegments( geometry, material ); + } else { + meshNode = new THREE.Line( geometry, material ); + } + meshNode.name = ( name === "0" ? group.name : group.name + name ); + if ( primitive.extras ) meshNode.userData = primitive.extras; + group.add( meshNode ); + } else { + console.warn( "Only triangular and line primitives are supported" ); + } + } + return group; + } ); + } ); + }; + GLTFParser.prototype.loadCameras = function () { + var json = this.json; + return _each( json.cameras, function ( camera ) { + if ( camera.type == "perspective" && camera.perspective ) { + var yfov = camera.perspective.yfov; + var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; + // According to COLLADA spec... + // aspectRatio = xfov / yfov + var xfov = yfov * aspectRatio; + var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); + if ( camera.name !== undefined ) _camera.name = camera.name; + if ( camera.extras ) _camera.userData = camera.extras; + return _camera; + } else if ( camera.type == "orthographic" && camera.orthographic ) { + var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); + if ( camera.name !== undefined ) _camera.name = camera.name; + if ( camera.extras ) _camera.userData = camera.extras; + return _camera; + } + } ); + }; + GLTFParser.prototype.loadSkins = function () { + var json = this.json; + return this._withDependencies( [ + "accessors" + ] ).then( function ( dependencies ) { + return _each( json.skins, function ( skin ) { + var bindShapeMatrix = new THREE.Matrix4(); + if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); + var _skin = { + bindShapeMatrix: bindShapeMatrix, + jointNames: skin.jointNames, + inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] + }; + return _skin; + } ); + } ); + }; + GLTFParser.prototype.loadAnimations = function () { + var json = this.json; + return this._withDependencies( [ + "accessors", + "nodes" + ] ).then( function ( dependencies ) { + return _each( json.animations, function ( animation, animationId ) { + var tracks = []; + for ( var channelId in animation.channels ) { + var channel = animation.channels[ channelId ]; + var sampler = animation.samplers[ channel.sampler ]; + if ( sampler ) { + var target = channel.target; + var name = target.id; + var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; + var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; + var inputAccessor = dependencies.accessors[ input ]; + var outputAccessor = dependencies.accessors[ output ]; + var node = dependencies.nodes[ name ]; + if ( node ) { + node.updateMatrix(); + node.matrixAutoUpdate = true; + var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation + ? THREE.QuaternionKeyframeTrack + : THREE.VectorKeyframeTrack; + var targetName = node.name ? node.name : node.uuid; + var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; + // KeyframeTrack.optimize() will modify given 'times' and 'values' + // buffers before creating a truncated copy to keep. Because buffers may + // be reused by other tracks, make copies here. + tracks.push( new TypedKeyframeTrack( + targetName + '.' + PATH_PROPERTIES[ target.path ], + THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), + THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), + interpolation + ) ); + } + } + } + var name = animation.name !== undefined ? animation.name : "animation_" + animationId; + return new THREE.AnimationClip( name, undefined, tracks ); + } ); + } ); + }; + GLTFParser.prototype.loadNodes = function () { + var json = this.json; + var extensions = this.extensions; + var scope = this; + return _each( json.nodes, function ( node ) { + var matrix = new THREE.Matrix4(); + var _node; + if ( node.jointName ) { + _node = new THREE.Bone(); + _node.name = node.name !== undefined ? node.name : node.jointName; + _node.jointName = node.jointName; + } else { + _node = new THREE.Object3D(); + if ( node.name !== undefined ) _node.name = node.name; + } + if ( node.extras ) _node.userData = node.extras; + if ( node.matrix !== undefined ) { + matrix.fromArray( node.matrix ); + _node.applyMatrix( matrix ); + } else { + if ( node.translation !== undefined ) { + _node.position.fromArray( node.translation ); + } + if ( node.rotation !== undefined ) { + _node.quaternion.fromArray( node.rotation ); + } + if ( node.scale !== undefined ) { + _node.scale.fromArray( node.scale ); + } + } + return _node; + } ).then( function ( __nodes ) { + return scope._withDependencies( [ + "meshes", + "skins", + "cameras" + ] ).then( function ( dependencies ) { + return _each( __nodes, function ( _node, nodeId ) { + var node = json.nodes[ nodeId ]; + if ( node.meshes !== undefined ) { + for ( var meshId in node.meshes ) { + var mesh = node.meshes[ meshId ]; + var group = dependencies.meshes[ mesh ]; + if ( group === undefined ) { + console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); + continue; + } + for ( var childrenId in group.children ) { + var child = group.children[ childrenId ]; + // clone Mesh to add to _node + var originalMaterial = child.material; + var originalGeometry = child.geometry; + var originalUserData = child.userData; + var originalName = child.name; + var material; + if ( originalMaterial.isDeferredShaderMaterial ) { + originalMaterial = material = originalMaterial.create(); + } else { + material = originalMaterial; + } + switch ( child.type ) { + case 'LineSegments': + child = new THREE.LineSegments( originalGeometry, material ); + break; + case 'LineLoop': + child = new THREE.LineLoop( originalGeometry, material ); + break; + case 'Line': + child = new THREE.Line( originalGeometry, material ); + break; + default: + child = new THREE.Mesh( originalGeometry, material ); + } + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + var skinEntry; + if ( node.skin ) { + skinEntry = dependencies.skins[ node.skin ]; + } + // Replace Mesh with SkinnedMesh in library + if ( skinEntry ) { + var getJointNode = function ( jointId ) { + var keys = Object.keys( __nodes ); + for ( var i = 0, il = keys.length; i < il; i ++ ) { + var n = __nodes[ keys[ i ] ]; + if ( n.jointName === jointId ) return n; + } + return null; + }; + var geometry = originalGeometry; + var material = originalMaterial; + material.skinning = true; + child = new THREE.SkinnedMesh( geometry, material, false ); + child.castShadow = true; + child.userData = originalUserData; + child.name = originalName; + var bones = []; + var boneInverses = []; + for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { + var jointId = skinEntry.jointNames[ i ]; + var jointNode = getJointNode( jointId ); + if ( jointNode ) { + bones.push( jointNode ); + var m = skinEntry.inverseBindMatrices.array; + var mat = new THREE.Matrix4().fromArray( m, i * 16 ); + boneInverses.push( mat ); + } else { + console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); + } + } + child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); + var buildBoneGraph = function ( parentJson, parentObject, property ) { + var children = parentJson[ property ]; + if ( children === undefined ) return; + for ( var i = 0, il = children.length; i < il; i ++ ) { + var nodeId = children[ i ]; + var bone = __nodes[ nodeId ]; + var boneJson = json.nodes[ nodeId ]; + if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { + parentObject.add( bone ); + buildBoneGraph( boneJson, bone, 'children' ); + } + } + }; + buildBoneGraph( node, child, 'skeletons' ); + } + _node.add( child ); + } + } + } + if ( node.camera !== undefined ) { + var camera = dependencies.cameras[ node.camera ]; + _node.add( camera ); + } + if ( node.extensions + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] + && node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { + var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; + var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; + _node.add( light ); + } + return _node; + } ); + } ); + } ); + }; + GLTFParser.prototype.loadScenes = function () { + var json = this.json; + // scene node hierachy builder + function buildNodeHierachy( nodeId, parentObject, allNodes ) { + var _node = allNodes[ nodeId ]; + parentObject.add( _node ); + var node = json.nodes[ nodeId ]; + if ( node.children ) { + var children = node.children; + for ( var i = 0, l = children.length; i < l; i ++ ) { + var child = children[ i ]; + buildNodeHierachy( child, _node, allNodes ); + } + } + } + return this._withDependencies( [ + "nodes" + ] ).then( function ( dependencies ) { + return _each( json.scenes, function ( scene ) { + var _scene = new THREE.Scene(); + if ( scene.name !== undefined ) _scene.name = scene.name; + if ( scene.extras ) _scene.userData = scene.extras; + var nodes = scene.nodes || []; + for ( var i = 0, l = nodes.length; i < l; i ++ ) { + var nodeId = nodes[ i ]; + buildNodeHierachy( nodeId, _scene, dependencies.nodes ); + } + _scene.traverse( function ( child ) { + // Register raw material meshes with GLTFLoader.Shaders + if ( child.material && child.material.isRawShaderMaterial ) { + child.gltfShader = new GLTFShader( child, dependencies.nodes ); + child.onBeforeRender = function(renderer, scene, camera){ + this.gltfShader.update(scene, camera); + }; + } + } ); + return _scene; + } ); + } ); + }; + return GLTFLoader; +} )(); +// File: utils/JSONParser.js +"use strict"; + +var JSONParser = function() +{ +} + +JSONParser.prototype.constructor = JSONParser; + + /** + * Load X3D JSON into an element. + * jsobj - the JavaScript object to convert to DOM. + */ +JSONParser.prototype.parseJavaScript = function(jsobj) { + var child = this.CreateElement('scene'); + this.ConvertToX3DOM(jsobj, "", child); + console.log(jsobj, child); + return child; + }; + + // 'http://www.web3d.org/specifications/x3d-namespace' + + // Load X3D JavaScript object into XML or DOM + + /** + * Yet another way to set an attribute on an element. does not allow you to + * set JSON schema or encoding. + */ +JSONParser.prototype.elementSetAttribute = function(element, key, value) { + if (key === 'SON schema') { + // JSON Schema + } else if (key === 'ncoding') { + // encoding, UTF-8, UTF-16 or UTF-32 + } else { + if (typeof element.setAttribute === 'function') { + element.setAttribute(key, value); + } + } + }; + + /** + * converts children of object to DOM. + */ +JSONParser.prototype.ConvertChildren = function(parentkey, object, element) { + var key; + + for (key in object) { + if (typeof object[key] === 'object') { + if (isNaN(parseInt(key))) { + this.ConvertObject(key, object, element, parentkey.substr(1)); + } else { + this.ConvertToX3DOM(object[key], key, element, parentkey.substr(1)); + } + } + } + }; + + /** + * a method to create and element with tagnam key to DOM in a namespace. If + * containerField is set, then the containerField is set in the elemetn. + */ +JSONParser.prototype.CreateElement = function(key, containerField) { + var child = document.createElement(key); + if (typeof containerField !== 'undefined') { + this.elementSetAttribute(child, 'containerField', containerField); + } + return child; + }; + + /** + * a way to create a CDATA function or script in HTML, by using a DOM parser. + */ +JSONParser.prototype.CDATACreateFunction = function(document, element, str) { + var y = str.replace(/\\"/g, "\\\"") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/&/g, "&"); + do { + str = y; + y = str.replace(/'([^'\r\n]*)\n([^']*)'/g, "'$1\\n$2'"); + if (str !== y) { + console.log("CDATA Replacing",str,"with",y); + } + } while (y != str); + var domParser = new DOMParser(); + var cdataStr = ''; // has to be wrapped into an element + var scriptDoc = domParser .parseFromString (cdataStr, 'application/xml'); + var cdata = scriptDoc .children[0] .childNodes[1]; // space after script is childNode[0] + element .appendChild(cdata); + }; + + /** + * convert the object at object[key] to DOM. + */ +JSONParser.prototype.ConvertObject = function(key, object, element, containerField) { + var child; + if (object !== null && typeof object[key] === 'object') { + if (key.substr(0,1) === '@') { + this.ConvertToX3DOM(object[key], key, element); + } else if (key.substr(0,1) === '-') { + this.ConvertChildren(key, object[key], element); + } else if (key === '#comment') { + for (var c in object[key]) { + child = document.createComment(this.CommentStringToXML(object[key][c])); + element.appendChild(child); + } + } else if (key === '#text') { + child = document.createTextNode(object[key].join("")); + element.appendChild(child); + } else if (key === '#sourceText') { + this.CDATACreateFunction(document, element, object[key].join("\r\n")+"\r\n"); + } else { + if (key === 'connect' || key === 'fieldValue' || key === 'field' || key === 'meta' || key === 'component') { + for (var childkey in object[key]) { // for each field + if (typeof object[key][childkey] === 'object') { + child = this.CreateElement(key, containerField); + this.ConvertToX3DOM(object[key][childkey], childkey, child); + element.appendChild(child); + element.appendChild(document.createTextNode("\n")); + } + } + } else { + child = this.CreateElement(key, containerField); + this.ConvertToX3DOM(object[key], key, child); + element.appendChild(child); + element.appendChild(document.createTextNode("\n")); + } + } + } + }; + + /** + * convert a comment string in JavaScript to XML. Pass the string + */ +JSONParser.prototype.CommentStringToXML = function(str) { + var y = str; + str = str.replace(/\\\\/g, '\\'); + if (y !== str) { + console.log("X3DJSONLD replacing", y, "with", str); + } + return str; + }; + + /** + * convert an SFString to XML. + */ +JSONParser.prototype.SFStringToXML = function(str) { + var y = str; + /* + str = (""+str).replace(/\\\\/g, '\\\\'); + str = str.replace(/\\\\\\\\/g, '\\\\'); + str = str.replace(/(\\+)"/g, '\\"'); + */ + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/"/g, '\\\"'); + if (y !== str) { + console.log("X3DJSONLD [] replacing", y, "with", str); + } + return str; + }; + + /** + * convert a JSON String to XML. + */ +JSONParser.prototype.JSONStringToXML = function(str) { + var y = str; + str = str.replace(/\\/g, '\\\\'); + str = str.replace(/\n/g, '\\n'); + if (y !== str) { + console.log("X3DJSONLD replacing", y, "with", str); + } + return str; + }; + + /** + * main routine for converting a JavaScript object to DOM. + * object is the object to convert. + * parentkey is the key of the object in the parent. + * element is the parent element. + * containerField is a possible containerField. + */ +JSONParser.prototype.ConvertToX3DOM = function(object, parentkey, element, containerField) { + var key; + var localArray = []; + var isArray = false; + var arrayOfStrings = false; + for (key in object) { + if (isNaN(parseInt(key))) { + isArray = false; + } else { + isArray = true; + } + if (isArray) { + if (typeof object[key] === 'number') { + localArray.push(object[key]); + } else if (typeof object[key] === 'string') { + localArray.push(object[key]); + arrayOfStrings = true; + } else if (typeof object[key] === 'boolean') { + localArray.push(object[key]); + } else if (typeof object[key] === 'object') { + /* + if (object[key] != null && typeof object[key].join === 'function') { + localArray.push(object[key].join(" ")); + } + */ + this.ConvertToX3DOM(object[key], key, element); + } else if (typeof object[key] === 'undefined') { + } else { + console.error("Unknown type found in array "+typeof object[key]); + } + } else if (typeof object[key] === 'object') { + // This is where the whole thing starts + if (key === 'scene') { + this.ConvertToX3DOM(object[key], key, element); + } else { + this.ConvertObject(key, object, element); + } + } else if (typeof object[key] === 'number') { + this.elementSetAttribute(element, key.substr(1),object[key]); + } else if (typeof object[key] === 'string') { + if (key === '#comment') { + var child = document.createComment(this.CommentStringToXML(object[key])); + element.appendChild(child); + } else if (key === '#text') { + var child = document.createTextNode(object[key]); + element.appendChild(child); + } else { + // ordinary string attributes + this.elementSetAttribute(element, key.substr(1), this.JSONStringToXML(object[key])); + } + } else if (typeof object[key] === 'boolean') { + this.elementSetAttribute(element, key.substr(1),object[key]); + } else if (typeof object[key] === 'undefined') { + } else { + console.error("Unknown type found in object "+typeof object[key]); + console.error(object); + } + } + if (isArray) { + if (parentkey.substr(0,1) === '@') { + if (arrayOfStrings) { + arrayOfStrings = false; + for (var str in localArray) { + localArray[str] = this.SFStringToXML(localArray[str]); + } + this.elementSetAttribute(element, parentkey.substr(1),'"'+localArray.join('" "')+'"'); + } else { + // if non string array + this.elementSetAttribute(element, parentkey.substr(1),localArray.join(" ")); + } + } + isArray = false; + } + return element; + }; +// File: utils/LoadManager.js +/* + * For use with XSeen JavaScript Library + * http://tools.realism.com/... + * + * Licensed under MIT or GNU in the same manner as XSeen + * + * (C)2017 Daly Realiusm, Los Angeles + * + */ + +/* + * Manages all download requests. + * Requests are queued up and processed to the maximum limit (.MaxRequests) + * Use this for processing text (X3D, XML, JSON, HTML) files. + * Not really setup for binary files (.jpg, png, etc.) + * + * Requires jQuery -- should work on removing that... + * + */ + +function LoadManager () { + this.urlQueue = []; + this.urlNext = -1; + this.MaxRequests = 3; + this.totalRequests = 0; + this.totalResponses = 0; + this.requestCount = 0; + var lmThat = this; + + this.load = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadText = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'text', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadHtml = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'html', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadXml = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'xml', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadJson = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'json', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.loadImage = function (url, success, progress, failed, userdata) { + this.urlQueue.push( {'url':url, 'type':'image', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); + this.loadNextUrl(); + } + + this.success = function (response, string, xhr) { + if (typeof(xhr._loadManager.success) !== undefined) { + xhr._loadManager.success (response, xhr._loadManager.userdata, xhr); + } + } + + this.failure = function (xhr, errorCode, errorText) { + if (typeof(xhr._loadManager.failure) !== undefined) { + xhr._loadManager.failure (xhr, xhr._loadManager.userdata, errorCode, errorText); + } + } + + this.requestComplete = function (event, xhr, settings) { + lmThat.requestCount --; + lmThat.totalResponses++; + lmThat.loadNextUrl(); + } + + this.loadNextUrl = function () { + if (this.requestCount >= this.MaxRequests) {return; } + if (this.urlNext >= this.urlQueue.length || this.urlNext < 0) { + this.urlNext = -1; + for (var i=0; i 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.CODE_SLASH: + slashes[ slashesPointer++ ] = i; + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.CODE_LF: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); + slashesPointer = 0; + bufferPointer = 0; + break; + + case Consts.CODE_CR: + break; + + default: + word += String.fromCharCode( code ); + break; + } + } + }; + + /** + * Parse the provided text + * @memberOf Parser + * + * @param {string} text OBJ data as string + */ + Parser.prototype.parseText = function ( text ) { + var length = text.length; + var buffer = new Array( 32 ); + var bufferPointer = 0; + var slashes = new Array( 32 ); + var slashesPointer = 0; + var reachedFaces = false; + var char; + var word = ''; + for ( var i = 0; i < length; i++ ) { + + char = text[ i ]; + switch ( char ) { + case Consts.STRING_SPACE: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.STRING_SLASH: + slashes[ slashesPointer++ ] = i; + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + break; + + case Consts.STRING_LF: + if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; + word = ''; + reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); + slashesPointer = 0; + bufferPointer = 0; + break; + + case Consts.STRING_CR: + break; + + default: + word += char; + } + } + }; + + Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) { + if ( bufferPointer < 1 ) return reachedFaces; + + var bufferLength = bufferPointer - 1; + switch ( buffer[ 0 ] ) { + case Consts.LINE_V: + + // object complete instance required if reached faces already (= reached next block of v) + if ( reachedFaces ) { + + this.processCompletedObject( null, this.rawObject.groupName ); + reachedFaces = false; + + } + this.rawObject.pushVertex( buffer ); + break; + + case Consts.LINE_VT: + this.rawObject.pushUv( buffer ); + break; + + case Consts.LINE_VN: + this.rawObject.pushNormal( buffer ); + break; + + case Consts.LINE_F: + reachedFaces = true; + /* + * 0: "f vertex/uv/normal ..." + * 1: "f vertex/uv ..." + * 2: "f vertex//normal ..." + * 3: "f vertex ..." + */ + var haveQuad = bufferLength % 4 === 0; + if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVn( buffer ); + } else { + this.rawObject.buildFaceVVn( buffer ); + } + + } else if ( bufferLength === slashesPointer * 2 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVt( buffer ); + } else { + this.rawObject.buildFaceVVt( buffer ); + } + + } else if ( bufferLength * 2 === slashesPointer * 3 ) { + + if ( haveQuad ) { + this.rawObject.buildQuadVVtVn( buffer ); + } else { + this.rawObject.buildFaceVVtVn( buffer ); + } + + } else { + + if ( haveQuad ) { + this.rawObject.buildQuadV( buffer ); + } else { + this.rawObject.buildFaceV( buffer ); + } + + } + break; + + case Consts.LINE_L: + if ( bufferLength === slashesPointer * 2 ) { + + this.rawObject.buildLineVvt( buffer ); + + } else { + + this.rawObject.buildLineV( buffer ); + + } + break; + + case Consts.LINE_S: + this.rawObject.pushSmoothingGroup( buffer[ 1 ] ); + break; + + case Consts.LINE_G: + this.processCompletedGroup( buffer[ 1 ] ); + break; + + case Consts.LINE_O: + if ( this.rawObject.vertices.length > 0 ) { + + this.processCompletedObject( buffer[ 1 ], null ); + reachedFaces = false; + + } else { + + this.rawObject.pushObject( buffer[ 1 ] ); + + } + break; + + case Consts.LINE_MTLLIB: + this.rawObject.pushMtllib( buffer[ 1 ] ); + break; + + case Consts.LINE_USEMTL: + this.rawObject.pushUsemtl( buffer[ 1 ] ); + break; + + default: + break; + } + return reachedFaces; + }; + + Parser.prototype.processCompletedObject = function ( objectName, groupName ) { + this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + this.inputObjectCount++; + this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName ); + }; + + Parser.prototype.processCompletedGroup = function ( groupName ) { + var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + if ( notEmpty ) { + + this.inputObjectCount ++; + this.rawObject = this.rawObject.newInstanceFromGroup( groupName ); + + } else { + + // if a group was set that did not lead to object creation in finalize, then the group name has to be updated + this.rawObject.pushGroup( groupName ); + + } + }; + + Parser.prototype.finalize = function () { + this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); + this.inputObjectCount++; + }; + + return Parser; + })(); + + /** + * {@link RawObject} is only used by {@link Parser}. + * The user of OBJLoader2 does not need to care about this class. + * It is defined publicly for inclusion in web worker based OBJ loader ({@link THREE.OBJLoader2.WWOBJLoader2}) + */ + var RawObject = (function () { + + function RawObject( objectName, groupName, mtllibName ) { + this.globalVertexOffset = 1; + this.globalUvOffset = 1; + this.globalNormalOffset = 1; + + this.vertices = []; + this.normals = []; + this.uvs = []; + + // faces are stored according combined index of group, material and smoothingGroup (0 or not) + this.mtllibName = Validator.verifyInput( mtllibName, 'none' ); + this.objectName = Validator.verifyInput( objectName, 'none' ); + this.groupName = Validator.verifyInput( groupName, 'none' ); + this.activeMtlName = 'none'; + this.activeSmoothingGroup = 1; + + this.mtlCount = 0; + this.smoothingGroupCount = 0; + + this.rawObjectDescriptions = []; + // this default index is required as it is possible to define faces without 'g' or 'usemtl' + var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; + } + + RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) { + return materialName + '|' + smoothingGroup; + }; + + RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) { + var newRawObject = new RawObject( objectName, groupName, this.mtllibName ); + + // move indices forward + newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; + newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; + newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; + + return newRawObject; + }; + + RawObject.prototype.newInstanceFromGroup = function ( groupName ) { + var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName ); + + // keep current buffers and indices forward + newRawObject.vertices = this.vertices; + newRawObject.uvs = this.uvs; + newRawObject.normals = this.normals; + newRawObject.globalVertexOffset = this.globalVertexOffset; + newRawObject.globalUvOffset = this.globalUvOffset; + newRawObject.globalNormalOffset = this.globalNormalOffset; + + return newRawObject; + }; + + RawObject.prototype.pushVertex = function ( buffer ) { + this.vertices.push( parseFloat( buffer[ 1 ] ) ); + this.vertices.push( parseFloat( buffer[ 2 ] ) ); + this.vertices.push( parseFloat( buffer[ 3 ] ) ); + }; + + RawObject.prototype.pushUv = function ( buffer ) { + this.uvs.push( parseFloat( buffer[ 1 ] ) ); + this.uvs.push( parseFloat( buffer[ 2 ] ) ); + }; + + RawObject.prototype.pushNormal = function ( buffer ) { + this.normals.push( parseFloat( buffer[ 1 ] ) ); + this.normals.push( parseFloat( buffer[ 2 ] ) ); + this.normals.push( parseFloat( buffer[ 3 ] ) ); + }; + + RawObject.prototype.pushObject = function ( objectName ) { + this.objectName = objectName; + }; + + RawObject.prototype.pushMtllib = function ( mtllibName ) { + this.mtllibName = mtllibName; + }; + + RawObject.prototype.pushGroup = function ( groupName ) { + this.groupName = groupName; + this.verifyIndex(); + }; + + RawObject.prototype.pushUsemtl = function ( mtlName ) { + if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; + this.activeMtlName = mtlName; + this.mtlCount++; + + this.verifyIndex(); + }; + + RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) { + var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup; + if ( this.activeSmoothingGroup === normalized ) return; + this.activeSmoothingGroup = normalized; + this.smoothingGroupCount++; + + this.verifyIndex(); + }; + + RawObject.prototype.verifyIndex = function () { + var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 ); + this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ]; + if ( ! Validator.isValid( this.rawObjectDescriptionInUse ) ) { + + this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); + this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; + + } + }; + + RawObject.prototype.buildQuadVVtVn = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] ); + this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] ); + this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] ); + } + }; + + RawObject.prototype.buildQuadVVt = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); + this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); + } + }; + + RawObject.prototype.buildQuadVVn = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); + this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); + } + }; + + RawObject.prototype.buildQuadV = function ( indexArray ) { + for ( var i = 0; i < 6; i ++ ) { + this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] ); + } + }; + + RawObject.prototype.buildFaceVVtVn = function ( indexArray ) { + for ( var i = 1; i < 10; i += 3 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVt( indexArray[ i + 1 ] ); + this.attachFaceVn( indexArray[ i + 2 ] ); + } + }; + + RawObject.prototype.buildFaceVVt = function ( indexArray ) { + for ( var i = 1; i < 7; i += 2 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVt( indexArray[ i + 1 ] ); + } + }; + + RawObject.prototype.buildFaceVVn = function ( indexArray ) { + for ( var i = 1; i < 7; i += 2 ) { + this.attachFaceV_( indexArray[ i ] ); + this.attachFaceVn( indexArray[ i + 1 ] ); + } + }; + + RawObject.prototype.buildFaceV = function ( indexArray ) { + for ( var i = 1; i < 4; i ++ ) { + this.attachFaceV_( indexArray[ i ] ); + } + }; + + RawObject.prototype.attachFaceV_ = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalVertexOffset ) * 3; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.vertices.push( this.vertices[ index++ ] ); + rodiu.vertices.push( this.vertices[ index++ ] ); + rodiu.vertices.push( this.vertices[ index ] ); + }; + + RawObject.prototype.attachFaceVt = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalUvOffset ) * 2; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.uvs.push( this.uvs[ index++ ] ); + rodiu.uvs.push( this.uvs[ index ] ); + }; + + RawObject.prototype.attachFaceVn = function ( faceIndex ) { + var faceIndexInt = parseInt( faceIndex ); + var index = ( faceIndexInt - this.globalNormalOffset ) * 3; + + var rodiu = this.rawObjectDescriptionInUse; + rodiu.normals.push( this.normals[ index++ ] ); + rodiu.normals.push( this.normals[ index++ ] ); + rodiu.normals.push( this.normals[ index ] ); + }; + + /* + * Support for lines with or without texture. irst element in indexArray is the line identification + * 0: "f vertex/uv vertex/uv ..." + * 1: "f vertex vertex ..." + */ + RawObject.prototype.buildLineVvt = function ( lineArray ) { + var length = lineArray.length; + for ( var i = 1; i < length; i ++ ) { + this.vertices.push( parseInt( lineArray[ i ] ) ); + this.uvs.push( parseInt( lineArray[ i ] ) ); + } + }; + + RawObject.prototype.buildLineV = function ( lineArray ) { + var length = lineArray.length; + for ( var i = 1; i < length; i++ ) { + this.vertices.push( parseInt( lineArray[ i ] ) ); + } + }; + + /** + * Clear any empty rawObjectDescription and calculate absolute vertex, normal and uv counts + */ + RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) { + var temp = this.rawObjectDescriptions; + this.rawObjectDescriptions = []; + var rawObjectDescription; + var index = 0; + var absoluteVertexCount = 0; + var absoluteNormalCount = 0; + var absoluteUvCount = 0; + + for ( var name in temp ) { + + rawObjectDescription = temp[ name ]; + if ( rawObjectDescription.vertices.length > 0 ) { + + if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName; + this.rawObjectDescriptions[ index++ ] = rawObjectDescription; + absoluteVertexCount += rawObjectDescription.vertices.length; + absoluteUvCount += rawObjectDescription.uvs.length; + absoluteNormalCount += rawObjectDescription.normals.length; + + } + } + + // don not continue if no result + var notEmpty = false; + if ( index > 0 ) { + + if ( debug ) this.createReport( inputObjectCount, true ); + meshCreator.buildMesh( + this.rawObjectDescriptions, + inputObjectCount, + absoluteVertexCount, + absoluteNormalCount, + absoluteUvCount + ); + notEmpty = true; + + } + return notEmpty; + }; + + RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) { + var report = { + name: this.objectName ? this.objectName : 'groups', + mtllibName: this.mtllibName, + vertexCount: this.vertices.length / 3, + normalCount: this.normals.length / 3, + uvCount: this.uvs.length / 2, + smoothingGroupCount: this.smoothingGroupCount, + mtlCount: this.mtlCount, + rawObjectDescriptions: this.rawObjectDescriptions.length + }; + + if ( printDirectly ) { + console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name ); + console.log( 'Mtllib name: ' + report.mtllibName ); + console.log( 'Vertex count: ' + report.vertexCount ); + console.log( 'Normal count: ' + report.normalCount ); + console.log( 'UV count: ' + report.uvCount ); + console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount ); + console.log( 'Material count: ' + report.mtlCount ); + console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions ); + console.log( '' ); + } + + return report; + }; + + return RawObject; + })(); + + /** + * Descriptive information and data (vertices, normals, uvs) to passed on to mesh building function. + * @class + * + * @param {string} objectName Name of the mesh + * @param {string} groupName Name of the group + * @param {string} materialName Name of the material + * @param {number} smoothingGroup Normalized smoothingGroup (0: THREE.FlatShading, 1: THREE.SmoothShading) + */ + var RawObjectDescription = (function () { + + function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) { + this.objectName = objectName; + this.groupName = groupName; + this.materialName = materialName; + this.smoothingGroup = smoothingGroup; + this.vertices = []; + this.uvs = []; + this.normals = []; + } + + return RawObjectDescription; + })(); + + /** + * MeshCreator is used to transform RawObjectDescriptions to THREE.Mesh + * + * @class + */ + var MeshCreator = (function () { + + function MeshCreator() { + this.sceneGraphBaseNode = null; + this.materials = null; + this.debug = false; + this.globalObjectCount = 1; + + this.validated = false; + } + + MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) { + this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, this.sceneGraphBaseNode ); + this.sceneGraphBaseNode = Validator.verifyInput( this.sceneGraphBaseNode, new THREE.Group() ); + }; + + MeshCreator.prototype.setMaterials = function ( materials ) { + this.materials = Validator.verifyInput( materials, this.materials ); + this.materials = Validator.verifyInput( this.materials, { materials: [] } ); + }; + + MeshCreator.prototype.setDebug = function ( debug ) { + if ( debug === true || debug === false ) this.debug = debug; + }; + + MeshCreator.prototype.validate = function () { + if ( this.validated ) return; + + this.setSceneGraphBaseNode( null ); + this.setMaterials( null ); + this.setDebug( null ); + this.globalObjectCount = 1; + }; + + MeshCreator.prototype.finalize = function () { + this.sceneGraphBaseNode = null; + this.materials = null; + this.validated = false; + }; + + /** + * This is an internal function, but due to its importance to Parser it is documented. + * RawObjectDescriptions are transformed to THREE.Mesh. + * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check). + * This method shall be overridden by the web worker implementation + * + * @param {RawObjectDescription[]} rawObjectDescriptions Array of descriptive information and data (vertices, normals, uvs) about the parsed object(s) + * @param {number} inputObjectCount Number of objects already retrieved from OBJ + * @param {number} absoluteVertexCount Sum of all vertices of all rawObjectDescriptions + * @param {number} absoluteNormalCount Sum of all normals of all rawObjectDescriptions + * @param {number} absoluteUvCount Sum of all uvs of all rawObjectDescriptions + */ + MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) { + + if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount ); + + var bufferGeometry = new THREE.BufferGeometry(); + var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 ); + bufferGeometry.addAttribute( 'position', vertexBA ); + + var normalBA; + if ( absoluteNormalCount > 0 ) { + + normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 ); + bufferGeometry.addAttribute( 'normal', normalBA ); + + } + var uvBA; + if ( absoluteUvCount > 0 ) { + + uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 ); + bufferGeometry.addAttribute( 'uv', uvBA ); + + } + + if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount ); + + var rawObjectDescription; + var material; + var materialName; + var createMultiMaterial = rawObjectDescriptions.length > 1; + var materials = []; + var materialIndex = 0; + var materialIndexMapping = []; + var selectedMaterialIndex; + + var vertexBAOffset = 0; + var vertexGroupOffset = 0; + var vertexLength; + var normalOffset = 0; + var uvOffset = 0; + + for ( var oodIndex in rawObjectDescriptions ) { + rawObjectDescription = rawObjectDescriptions[ oodIndex ]; + + materialName = rawObjectDescription.materialName; + material = this.materials[ materialName ]; + if ( ! material ) { + + material = this.materials[ 'defaultMaterial' ]; + if ( ! material ) { + + material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} ); + material.name = 'defaultMaterial'; + this.materials[ 'defaultMaterial' ] = material; + + } + console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' ); + + } + // clone material in case flat shading is needed due to smoothingGroup 0 + if ( rawObjectDescription.smoothingGroup === 0 ) { + + materialName = material.name + '_flat'; + var materialClone = this.materials[ materialName ]; + if ( ! materialClone ) { + + materialClone = material.clone(); + materialClone.name = materialName; + materialClone.shading = THREE.FlatShading; + this.materials[ materialName ] = name; + + } + + } + + vertexLength = rawObjectDescription.vertices.length; + if ( createMultiMaterial ) { + + // re-use material if already used before. Reduces materials array size and eliminates duplicates + selectedMaterialIndex = materialIndexMapping[ materialName ]; + if ( ! selectedMaterialIndex ) { + + selectedMaterialIndex = materialIndex; + materialIndexMapping[ materialName ] = materialIndex; + materials.push( material ); + materialIndex++; + + } + + bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex ); + vertexGroupOffset += vertexLength / 3; + } + + vertexBA.set( rawObjectDescription.vertices, vertexBAOffset ); + vertexBAOffset += vertexLength; + + if ( normalBA ) { + + normalBA.set( rawObjectDescription.normals, normalOffset ); + normalOffset += rawObjectDescription.normals.length; + + } + if ( uvBA ) { + + uvBA.set( rawObjectDescription.uvs, uvOffset ); + uvOffset += rawObjectDescription.uvs.length; + + } + if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex ); + + } + if ( ! normalBA ) bufferGeometry.computeVertexNormals(); + + if ( createMultiMaterial ) material = materials; + var mesh = new THREE.Mesh( bufferGeometry, material ); + this.sceneGraphBaseNode.add( mesh ); + + this.globalObjectCount++; + }; + + MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) { + console.log( + ' Output Object no.: ' + this.globalObjectCount + + '\n objectName: ' + rawObjectDescription.objectName + + '\n groupName: ' + rawObjectDescription.groupName + + '\n materialName: ' + rawObjectDescription.materialName + + '\n materialIndex: ' + selectedMaterialIndex + + '\n smoothingGroup: ' + rawObjectDescription.smoothingGroup + + '\n #vertices: ' + rawObjectDescription.vertices.length / 3 + + '\n #uvs: ' + rawObjectDescription.uvs.length / 2 + + '\n #normals: ' + rawObjectDescription.normals.length / 3 + ); + }; + + return MeshCreator; + })(); + + OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) { + var workerCode = ''; + workerCode += funcBuildObject( 'Consts', Consts ); + workerCode += funcBuildObject( 'Validator', Validator ); + workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); + workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject ); + workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription ); + return workerCode; + }; + + return OBJLoader2; +})(); +// File: init/Definitions.js +/* + * XSeen JavaScript Library + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + * + * Based on code originally provided by + * Philip Taylor: http://philip.html5.org + */ + +/** + * The Namespace container for x3dom objects. + * @namespace x3dom + * + * Removed THREE loaders + loaders: { + 'file' : new THREE.FileLoader(), + 'image' : 0, + }, + + * */ +var xseen = { + canvases : [], + sceneInfo : [], + nodeDefinitions : {}, + parseTable : {}, + node : {}, + utils : {}, + eventManager : {}, + Events : {}, + Navigation : {}, + + loadMgr : {}, + loader : { + 'Null' : '', + 'ColladaLoader' : '', + 'GltfLegacy' : '', + 'GltfLoader' : '', + 'ObjLoader' : '', + 'ImageLoader' : '', + 'X3dLoader' : '', + }, + loadProgress : function (xhr) { + if (xhr.total != 0) { + console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); + } + }, + loadError : function (xhr, userdata, code, message) { + console.error('An error happened on '+userdata.e.id+'\n'+code+'\n'+message); + }, + loadMime : { + '' : {name: 'Null', loader: 'Null'}, + 'dae' : {name: 'Collada', loader: 'ColladaLoader'}, + 'glb' : {name: 'glTF Binary', loader: 'GltfLoader'}, + 'glbl' : {name: 'glTF Binary', loader: 'GltfLegacy'}, + 'gltf' : {name: 'glTF JSON', loader: 'GltfLoader'}, + 'obj' : {name: 'OBJ', loader: 'ObjLoader'}, + 'png' : {name: 'PNG', loader: 'ImageLoader'}, + 'jpg' : {name: 'JPEG', loader: 'ImageLoader'}, + 'jpeg' : {name: 'JPEG', loader: 'ImageLoader'}, + 'gif' : {name: 'GIF', loader: 'ImageLoader'}, + 'x3d' : {name: 'X3D XML', loader: 'X3dLoader'}, + }, +// helper + array_to_object : function (a) { + var o = {}; + for(var i=0;i ' + obj.type + ' (' + obj.name + ')'); + for (var i=0; i + var validParams = xseen.array_to_object([ + 'showLog', + 'showStat', + 'showProgress', + 'PrimitiveQuality', + 'components', + 'loadpath', + 'disableDoubleClick', + 'backend', + 'altImg', + 'runtimeEnabled', + 'keysEnabled', + 'showTouchpoints', + 'disableTouch', + 'maxActiveDownloads' + ]); + var components, prefix; + var showLoggingConsole = false; + + // for each XSeens element + for (var i=0; i < xseens.length; i++) { + + // default parameters + settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'false'); + settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'true'); + settings.setProperty("showStat", xseens[i].getAttribute("showStat") || 'false'); + settings.setProperty("showProgress", xseens[i].getAttribute("showProgress") || 'true'); + settings.setProperty("PrimitiveQuality", xseens[i].getAttribute("PrimitiveQuality") || 'High'); + + // for each param element inside the X3D element + // add settings to properties object + params = xseens[i].getElementsByTagName('PARAM'); + for (var j=0; j < params.length; j++) { + if (params[j].getAttribute('name') in validParams) { + settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value')); + } else { + //xseen.debug.logError("Unknown parameter: " + params[j].getAttribute('name')); + } + } + + // enable log + if (settings.getProperty('showLog') === 'true') { + showLoggingConsole = true; + } + + if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) { + // load components from params or default to x3d attribute + components = settings.getProperty('components', xseens[i].getAttribute("components")); + if (components) { + prefix = settings.getProperty('loadpath', xseens[i].getAttribute("loadpath")); + components = components.trim().split(','); + for (j=0; j < components.length; j++) { + xseen.loadJS(components[j] + ".js", prefix); + } + } + + // src=foo.x3d adding inline node, not a good idea, but... + if (xseens[i].getAttribute("src")) { + var _scene = document.createElement("scene"); + var _inl = document.createElement("Inline"); + _inl.setAttribute("url", xseens[i].getAttribute("src")); + _scene.appendChild(_inl); + xseens[i].appendChild(_scene); + } + } + } + // }}} + + if (showLoggingConsole == true) { + xseen.debug.activate(true); + } else { + xseen.debug.activate(false); + } + + // Convert the collection into a simple array (is this necessary?) +/* Don't think so -- commented out + xseens = Array.map(xseens, function (n) { + n.hasRuntime = true; + return n; + }); + */ + + if (xseen.versionInfo !== undefined) { + xseen.debug.logInfo("XSeen version " + xseen.versionInfo.version + ", " + + "Date " + xseen.versionInfo.date); + xseen.debug.logInfo(xseen.versionInfo.splashText); + } + + //xseen.debug.logInfo("Found " + xseen.length + " XSeen nodes"); + + + // Create a HTML canvas for every XSeen scene and wrap it with + // an X3D canvas and load the content + var x_element; + var x_canvas; + var altDiv, altP, aLnk, altImg; + var t0, t1; + + for (var i=0; i < xseens.length; i++) + { + x_element = xseens[i]; // The XSeen DOM element + + x_canvas = new THREE.Scene(); // May need addtl info if multiple: xseen.X3DCanvas(x_element, xseen.canvases.length); + xseen.canvases.push(x_canvas); // TODO: Need to handle failure to initialize? + t0 = new Date().getTime(); + +/* + * Handle opening tag attributes + * divHeight + * divWidth + * turntable Indicates if view automatically rotates (independent of navigation) + */ + + var divWidth = x_element.getAttribute('width'); + var divHeight = x_element.getAttribute('height'); + if (divHeight + divWidth < 100) { + divHeight = 450; + divWidth = 800; + } else if (divHeight < 50) { + divHeight = Math.floor(divWidth/2) + 50; + } else if (divWidth < 50) { + divWidth = divHeight * 2 - 100; + } + var turntable = (x_element.getAttribute('turntable') || '').toLowerCase(); + if (turntable == 'on' || turntable == 'yes' || turntable == 'y' || turntable == '1') { + turntable = true; + } else { + turntable = false; + } + turntable = false; +/* + * Removed because camera is stored in the Scene node (x_element._xseen.renderer.camera + * Leave variable definition so other code works... + var x_camera = new THREE.PerspectiveCamera( 75, divWidth / divHeight, 0.1, 1000 ); + x_camera.position.x = 0; + x_camera.position.z = 10; + */ + var x_camera = {}; + var x_renderer = new THREE.WebGLRenderer(); + x_renderer.setSize (divWidth, divHeight); + //x_element.appendChild (x_renderer.domElement); + + // Stereo camera effect + // from http://charliegerard.github.io/blog/Virtual-Reality-ThreeJs/ + var x_effect = new THREE.StereoEffect(x_renderer); + x_renderer.controls = {'update' : function() {return;}}; + +/* + * Add event handler to XSeen tag (x_element) + * These handle all mouse/cursor/button controls when the cursor is + * in the XSeen region of the page + */ + + x_element.addEventListener ('dblclick', xseen.Events.canvasHandler, true); + x_element.addEventListener ('click', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mousedown', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mousemove', xseen.Events.canvasHandler, true); + x_element.addEventListener ('mouseup', xseen.Events.canvasHandler, true); + x_element.addEventListener ('xseen', xseen.Events.XSeenHandler); // Last chance for XSeen handling of event + x_element.addEventListener ('change', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event +/* + x_element.addEventListener ('mousedown', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + x_element.addEventListener ('mouseup', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + x_element.addEventListener ('mousemove', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event + */ + xseen.sceneInfo.push ({ + 'size' : {'width':divWidth, 'height':divHeight}, + 'scene' : x_canvas, + 'renderer' : x_renderer, + 'effect' : x_effect, + 'camera' : [x_camera], + 'turntable' : turntable, + 'mixers' : [], + 'clock' : new THREE.Clock(), + 'element' : x_element, + 'selectable': [], + 'stacks' : [], + 'tmp' : {activeViewpoint:false}, + 'xseen' : xseen, + }); + x_element._xseen = {}; + x_element._xseen.children = []; + x_element._xseen.sceneInfo = xseen.sceneInfo[xseen.sceneInfo.length-1]; + + t1 = new Date().getTime() - t0; + xseen.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms."); + } + + var ready = (function(eventType) { + var evt = null; + + if (document.createEvent) { + evt = document.createEvent("Events"); + evt.initEvent(eventType, true, true); + document.dispatchEvent(evt); + } else if (document.createEventObject) { + evt = document.createEventObject(); + // http://stackoverflow.com/questions/1874866/how-to-fire-onload-event-on-document-in-ie + document.body.fireEvent('on' + eventType, evt); + } + })('load'); + + // for each X-Scene tag, parse and load the contents + var t=[]; + for (var i=0; i root element that was added after document load. */ + xseen.reload = function() { + onload(); + }; + + /* FIX PROBLEM IN CHROME - HACK - searching for better solution !!! */ + if (navigator.userAgent.indexOf("Chrome") != -1) { + document.__getElementsByTagName = document.getElementsByTagName; + + document.getElementsByTagName = function(tag) { + var obj = []; + var elems = this.__getElementsByTagName("*"); + + if(tag =="*"){ + obj = elems; + } else { + tag = tag.toUpperCase(); + for (var i = 0; i < elems.length; i++) { + var tagName = elems[i].tagName.toUpperCase(); + if (tagName === tag) { + obj.push(elems[i]); + } + } + } + + return obj; + }; + + document.__getElementById = document.getElementById; + document.getElementById = function(id) { + var obj = this.__getElementById(id); + + if (!obj) { + var elems = this.__getElementsByTagName("*"); + for (var i=0; i + * MODE_NAVIGATION events are 'captured' (not bubbled) and drive the camera position + * + * In MODE_SELECT + * mousedown sets redispatch to TRUE + * click Activates + * dblclick ?? + * mouseup terminates select + * mousemove sets redispatch to FALSE + * In all cases, recreate event as type='xseen' and dispatch from geometry when + * redispatch is TRUE. + */ + canvasHandler: function (ev) + { + //console.log ('Primary canvas event handler for event type: ' + ev.type); + var sceneInfo = ev.currentTarget._xseen.sceneInfo; + var localXseen = sceneInfo.xseen; + var lEvents = localXseen.Events; + var type = ev.type; + if (type == 'mousedown') { + lEvents.redispatch = true; + lEvents.mode = lEvents.MODE_SELECT; + lEvents.mouse.x = (ev.clientX / 800) * 2 -1; // TODO: Use real XSeen display sizes + lEvents.mouse.y = (ev.clientY / 450) * 2 -1; + // + lEvents.raycaster.setFromCamera(lEvents.mouse, sceneInfo.element._xseen.renderer.activeCamera); + var hitGeometryList = lEvents.raycaster.intersectObjects (sceneInfo.selectable, true); + if (hitGeometryList.length != 0) { + lEvents.object = hitGeometryList[0]; + } else { + lEvents.object = {}; + lEvents.redispatch = false; + lEvents.mode = lEvents.MODE_NAVIGATION; + } + } + if ((lEvents.redispatch || type == 'click' || type == 'dblclick') && typeof(lEvents.object.object) !== 'undefined') { + // Generate an XSeen (Custom)Event of the same type and dispatch it + var newEv = lEvents.createEvent (ev, lEvents.object); + lEvents.object.object.userData.dispatchEvent(newEv); + ev.stopPropagation(); // No propagation beyond this tag + } else { + //console.log ('Navigation mode...'); + } + if (type == 'mouseup') { + lEvents.redispatch = false; + lEvents.mode = lEvents.MODE_NAVIGATION; + } + }, + + createEvent: function (ev, selectedObject) + { + var properties = { + 'detail': { // This object contains all of the XSeen data + 'type': ev.type, + 'originalType': ev.type, + 'originator': selectedObject.object.userData, + 'position': { + 'x': selectedObject.point.x, + 'y': selectedObject.point.y, + 'z': selectedObject.point.z, + }, + 'normal': { + 'x': 0, + 'y': 0, + 'z': 0, + }, + 'uv': { + 'x': selectedObject.uv.x, + 'y': selectedObject.uv.y, + }, + 'screenX': ev.screenX, + 'screenY': ev.screenY, + 'clientX': ev.clientX, + 'clientY': ev.clientY, + 'ctrlKey': ev.ctrlKey, + 'shiftKey': ev.shiftKey, + 'altKey': ev.altKey, + 'metaKey': ev.metaKey, + 'button': ev.button, + 'buttons': ev.buttons, + }, + 'bubbles': ev.bubbles, + 'cancelable': ev.cancelable, + 'composed': ev.composed, + }; + + var newEvent = new CustomEvent('xseen', properties); + return newEvent; + }, + // Uses method described in https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + // to change 'this' for the handler method. Want 'this' to refer to the target node. + addHandler: function (route, source, eventName, destination, field) + { + var handler = {}; + handler.route = route; // Route element + handler.source = source; // Source element + handler.type = eventName; // Event type + handler.destination = destination; // Destination element + handler.field = field; // Destination field structure + handler.handler = destination._xseen.handlers[field.handlerName]; + this.routes.push (handler); + if (typeof(source._xseen) === 'undefined') { // DOM event + source.addEventListener (eventName, function(ev) { + handler.handler(ev) + }); + } else { // XSeen event + source.addEventListener ('xseen', function(ev) { + //console.log ('New event of original type: |'+ev.detail.originalType+'|; Desired type: |'+handler.type+'|'); + if (ev.detail.originalType == handler.type) { + handler.handler(ev) + } + }); + } + }, + + // Generic notification handler for XSeen's canvas + XSeenHandler: function (ev) + { + //console.log ('XSeen DEBUG Event Bubble handler ('+ev.type+'/'+ev.eventPhase+').'); + }, + XSeenDebugHandler : function (ev) + { + console.log ('XSeen DEBUG Event Capture handler ('+ev.type+'/'+ev.eventPhase+').'); + }, + }; + + this.debug = { + INFO: "INFO", + WARNING: "WARNING", + ERROR: "ERROR", + EXCEPTION: "EXCEPTION", + + // determines whether debugging/logging is active. If set to "false" + // no debugging messages will be logged. + isActive: false, + + // stores if firebug is available + isFirebugAvailable: false, + + // stores if the xseen.debug object is initialized already + isSetup: false, + + // stores if xseen.debug object is append already (Need for IE integration) + isAppend: false, + + // stores the number of lines logged + numLinesLogged: 0, + + // the maximum number of lines to log in order to prevent + // the browser to slow down + maxLinesToLog: 10000, + + // the container div for the logging messages + logContainer: null, + + /** @brief Setup the xseen.debug object. + + Checks for firebug and creates the container div for the logging + messages. + */ + setup: function() { + // If debugging is already setup simply return + if (xseen.debug.isSetup) { return; } + + // Check for firebug console + try { + if (window.console.firebug !== undefined) { + xseen.debug.isFirebugAvailable = true; + } + } + catch (err) { + xseen.debug.isFirebugAvailable = false; + } + + xseen.debug.setupLogContainer(); + + // setup should be setup only once, thus store if we done that already + xseen.debug.isSetup = true; + }, + + /** @brief Activates the log + */ + activate: function(visible) { + xseen.debug.isActive = true; + + //var aDiv = document.createElement("div"); + //aDiv.style.clear = "both"; + //aDiv.appendChild(document.createTextNode("\r\n")); + //aDiv.style.display = (visible) ? "block" : "none"; + xseen.debug.logContainer.style.display = (visible) ? "block" : "none"; + + //Need this HACK for IE/Flash integration. IE don't have a document.body at this time when starting Flash-Backend + if(!xseen.debug.isAppend) { + if(navigator.appName == "Microsoft Internet Explorer") { + //document.documentElement.appendChild(aDiv); + xseen.debug.logContainer.style.marginLeft = "8px"; + document.documentElement.appendChild(xseen.debug.logContainer); + }else{ + //document.body.appendChild(aDiv); + document.body.appendChild(xseen.debug.logContainer); + } + xseen.debug.isAppend = true; + } + }, + + /** @brief Inserts a container div for the logging messages into the HTML page + */ + setupLogContainer: function() { + xseen.debug.logContainer = document.createElement("div"); + xseen.debug.logContainer.id = "xseen_logdiv"; + xseen.debug.logContainer.setAttribute("class", "xseen-logContainer"); + xseen.debug.logContainer.style.clear = "both"; + //document.body.appendChild(xseen.debug.logContainer); + }, + + /** @brief Generic logging function which does all the work. + + @param msg the log message + @param logType the type of the log message. One of INFO, WARNING, ERROR + or EXCEPTION. + */ + doLog: function(msg, logType) { + + // If logging is deactivated do nothing and simply return + if (!xseen.debug.isActive) { return; } + + // If we have reached the maximum number of logged lines output + // a warning message + if (xseen.debug.numLinesLogged === xseen.debug.maxLinesToLog) { + msg = "Maximum number of log lines (=" + xseen.debug.maxLinesToLog + ") reached. Deactivating logging..."; + } + + // If the maximum number of log lines is exceeded do not log anything + // but simply return + if (xseen.debug.numLinesLogged > xseen.debug.maxLinesToLog) { return; } + + // Output a log line to the HTML page + var node = document.createElement("p"); + node.style.margin = 0; + switch (logType) { + case xseen.debug.INFO: + node.style.color = "#009900"; + break; + case xseen.debug.WARNING: + node.style.color = "#cd853f"; + break; + case xseen.debug.ERROR: + node.style.color = "#ff4500"; + break; + case xseen.debug.EXCEPTION: + node.style.color = "#ffff00"; + break; + default: + node.style.color = "#009900"; + break; + } + + // not sure if try/catch solves problem http://sourceforge.net/apps/trac/x3dom/ticket/52 + // but due to no avail of ATI gfxcard can't test + try { + node.innerHTML = logType + ": " + msg; + xseen.debug.logContainer.insertBefore(node, xseen.debug.logContainer.firstChild); + } catch (err) { + if (window.console.firebug !== undefined) { + window.console.warn(msg); + } + } + + // Use firebug's console if available + if (xseen.debug.isFirebugAvailable) { + switch (logType) { + case xseen.debug.INFO: + window.console.info(msg); + break; + case xseen.debug.WARNING: + window.console.warn(msg); + break; + case xseen.debug.ERROR: + window.console.error(msg); + break; + case xseen.debug.EXCEPTION: + window.console.debug(msg); + break; + default: + break; + } + } + + xseen.debug.numLinesLogged++; + }, + + /** Log an info message. */ + logInfo: function(msg) { + xseen.debug.doLog(msg, xseen.debug.INFO); + }, + + /** Log a warning message. */ + logWarning: function(msg) { + xseen.debug.doLog(msg, xseen.debug.WARNING); + }, + + /** Log an error message. */ + logError: function(msg) { + xseen.debug.doLog(msg, xseen.debug.ERROR); + }, + + /** Log an exception message. */ + logException: function(msg) { + xseen.debug.doLog(msg, xseen.debug.EXCEPTION); + }, + + /** Log an assertion. */ + assert: function(c, msg) { + if (!c) { + xseen.debug.doLog("Assertion failed in " + xseen.debug.assert.caller.name + ': ' + msg, xseen.debug.ERROR); + } + }, + + /** + Checks the type of a given object. + + @param obj the object to check. + @returns one of; "boolean", "number", "string", "object", + "function", or "null". + */ + typeOf: function (obj) { + var type = typeof obj; + return type === "object" && !obj ? "null" : type; + }, + + /** + Checks if a property of a specified object has the given type. + + @param obj the object to check. + @param name the property name. + @param type the property type (optional, default is "function"). + @returns true if the property exists and has the specified type, + otherwise false. + */ + exists: function (obj, name, type) { + type = type || "function"; + return (obj ? this.typeOf(obj[name]) : "null") === type; + }, + + /** + Dumps all members of the given object. + */ + dumpFields: function (node) { + var str = ""; + for (var fName in node) { + str += (fName + ", "); + } + str += '\n'; + xseen.debug.logInfo(str); + return str; + } + }; +// Call the setup function to... umm, well, setup xseen.debug + this.debug.setup(); + + }; +// File: ./Nav-Viewpoint.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.Navigation.(label); + * Computes the new viewing location for the specific mode. + * + * Each Navigation method takes the following parameters: + * speed Floating point value indicating motion speed. + * Units are distance per milli-second for linear motion or + * revolutions (2*pi) per milli-second for angular motion + * deltaT Time since last update in milli-seconds + * TODO: This is not true for the Turntable class of camera motion -- which isn't really Navigation anyway + * scene The 'sceneInfo' object for this HTML instance + * camera The current (active) camera (aka scene.element._xseen.renderer.activeCamera) + * + * Navigation is the user-controlled process of moving in the 3D world. + * + */ + +xseen.Navigation = { + 'TwoPi' : 2 * Math.PI, + 'none' : function () {}, // Does not allow user-controlled navigation + + 'turntable' : function (speed, deltaT, scene, camera) + { + var T, radians, radius, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + radius = vp.fields._radius0; + camera.position.x = radius * Math.sin(radians) + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.position.z = radius * Math.cos(radians); + camera.lookAt(scene.ORIGIN); + }, + + 'tilt' : function (speed, deltaT, scene, camera) + { + var T, radians, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.lookAt(scene.ORIGIN); + }, + + 'setup' : { + 'none' : function () {return null;}, + + 'orbit' : function (camera, renderer) + { + var controls; + controls = new THREE.OrbitControls( camera, renderer.domElement ); + //controls.addEventListener( 'change', render ); // remove when using animation loop + // enable animation loop when using damping or autorotation + //controls.enableDamping = true; + //controls.dampingFactor = 0.25; + controls.enableZoom = false; + controls.enableZoom = true; + return controls; + }, + + 'trackball' : function (camera, renderer) + { + var controls; + controls = new THREE.TrackballControls(camera, renderer.domElement); + + // These are from the example code at https://github.com/mrdoob/three.js/blob/master/examples/misc_controls_trackball.html + controls.rotateSpeed = 1.0; + controls.zoomSpeed = 1.2; + controls.panSpeed = 0.8; + controls.noZoom = false; + controls.noPan = false; + controls.staticMoving = true; + controls.dynamicDampingFactor = 0.3; + controls.keys = [ 65, 83, 68 ]; + + // Render function is 'xseen.renderFrame' + // remove when using animation loop + //controls.addEventListener( 'change', xseen.renderFrame ); + return controls; + }, + }, +}; +// File: ./Navigation.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.Navigation.(label); + * Computes the new viewing location for the specific mode. + * + * Each Navigation method takes the following parameters: + * speed Floating point value indicating motion speed. + * Units are distance per milli-second for linear motion or + * revolutions (2*pi) per milli-second for angular motion + * deltaT Time since last update in milli-seconds + * TODO: This is not true for the Turntable class of camera motion -- which isn't really Navigation anyway + * scene The 'sceneInfo' object for this HTML instance + * camera The current (active) camera (aka scene.element._xseen.renderer.activeCamera) + * + * Navigation is the user-controlled process of moving in the 3D world. + * + */ + +xseen.Navigation = { + 'TwoPi' : 2 * Math.PI, + 'none' : function () {}, // Does not allow user-controlled navigation + + 'turntable' : function (speed, deltaT, scene, camera) + { + var T, radians, radius, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + radius = vp.fields._radius0; + camera.position.x = radius * Math.sin(radians) + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.position.z = radius * Math.cos(radians); + camera.lookAt(scene.ORIGIN); + }, + + 'tilt' : function (speed, deltaT, scene, camera) + { + var T, radians, vp; + T = (new Date()).getTime() - xseen.timeStart; + radians = T * speed * this.TwoPi; + vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration + camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); + camera.lookAt(scene.ORIGIN); + }, + + 'setup' : { + 'none' : function () {return null;}, + + 'orbit' : function (camera, renderer) + { + var controls; + controls = new THREE.OrbitControls( camera, renderer.domElement ); + //controls.addEventListener( 'change', render ); // remove when using animation loop + // enable animation loop when using damping or autorotation + //controls.enableDamping = true; + //controls.dampingFactor = 0.25; + controls.enableZoom = false; + controls.enableZoom = true; + return controls; + }, + }, +}; +// File: ./NodeDefinitions.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.nodes. is the definition of + * All internal variables are stored in ._internal. All functions start with '_' + * + * This is a bare-bones setup. There is no error checking - missing arguments or + * methods that do not exist (e.g., .init) + * + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump (_dumpTable) would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + * Fields are added with the .addField method. It takes its values from the argument list + * or an object passed as the first argument. The properties of the argument are: + * name - the name of the field. This is converted to lowercase before use + * datatype - the datatype of the field. There must be a method in xseen.types by this name + * defaultValue - the default value of the field to be used if the field is not present or incorrectly defined. + * If this argument is an array, then it is the set of allowed values. The first element is the default. + * enumerated - the list of allowed values when the datatype only allows specific values for this field (optional) + * animatable - Flag (T/F) indicating if the field is animatable. Generally speaking, enumerated fieles are not animatable + */ + +xseen.nodes = { + '_defineNode' : function(nodeName, nodeComponent, nodeMethod) { + //methodBase = 'xseen.node.'; + var methodBase = ''; + node = { + 'tag' : nodeName, + 'taglc' : nodeName.toLowerCase(), + 'component' : nodeComponent, + 'method' : methodBase + nodeMethod, + 'fields' : [], + 'fieldIndex': [], + 'addField' : function (fieldObj, datatype, defaultValue) { + var fieldName, namelc, enumerated, animatable; + if (typeof(fieldObj) === 'object') { + fieldName = fieldObj.name; + datatype = fieldObj.datatype; + defaultValue = fieldObj.defaultValue; + enumerated = (typeof(fieldObj.enumerated) === 'undefined') ? [] : fieldObj.enumerated; + animatable = (typeof(fieldObj.animatable) === 'undefined') ? false : fieldObj.animatable; + } else { + fieldName = fieldObj; + animatable = false; + if (typeof(defaultValue) == 'array') { + enumerated = defaultValue; + defaultValue = enumerated[0]; + } else { + enumerated = []; + } + } + namelc = fieldName.toLowerCase(); + this.fields.push ({ + 'field' : fieldName, + 'fieldlc' : namelc, + 'type' : datatype, + 'default' : defaultValue, + 'enumeration' : enumerated, + 'animatable' : animatable, + 'clone' : this.cloneField, + 'setFieldName' : this.setFieldName, + }); + this.fieldIndex[namelc] = this.fields.length-1; + return this; + }, + 'addNode' : function () { + xseen.parseTable[this.taglc] = this; + }, + 'cloneField' : function () { + var newFieldObject = { + 'field' : this.field, + 'fieldlc' : this.fieldlc, + 'type' : this.type, + 'default' : 0, + 'enumeration' : [], + 'animatable' : this.animatable, + 'clone' : this.clone, + 'setFieldName' : this.setFieldName, + }; + for (var i=0; i xseen.node[xseen.nodeDefinitions[nodeName].method].endParse (element, parent); + } + //xseen.debug.logInfo(" reached bottom, heading back up from |" + nodeName + "|"); +} +// File: ./Properties.js +/* + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + * + * Based on code originally provided by + * Philip Taylor: http://philip.html5.org + */ + + +xseen.Properties = function() { + this.properties = {}; +}; + +xseen.Properties.prototype.setProperty = function(name, value) { + xseen.debug.logInfo("Properties: Setting property '"+ name + "' to value '" + value + "'"); + this.properties[name] = value; +}; + +xseen.Properties.prototype.getProperty = function(name, def) { + if (this.properties[name]) { + return this.properties[name] + } else { + return def; + } +}; + +xseen.Properties.prototype.merge = function(other) { + for (var attrname in other.properties) { + this.properties[attrname] = other.properties[attrname]; + } +}; + +xseen.Properties.prototype.toString = function() { + var str = ""; + for (var name in this.properties) { + str += "Name: " + name + " Value: " + this.properties[name] + "\n"; + } + return str; +}; +// File: ./StackHandler.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * This is all new code. + * Portions of XSeen extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * Dual licensed under the MIT and GPL + */ + + +/* + * xseen.utlis.StackHandler(label); + * Creates a new stack that is managed by this class + * + * Note that the Push-Down stack (aka FILO) is implemented as a reverse list + * so that the Array methods .push and .pop can be used. The end of the array + * is the "top-most" element in the stack. + */ + +xseen.utils.StackHandler = function (label) { + this._internals = {}; // Internal class storage + this._internals.label = label; // Unique user-supplied name for this stack + this._internals.stack = []; // Maintains the stack. Last entry on stack is active + this._internals.active = -1; // Index of currently active list element + this._internals.activeNode = {}; // Entry of currently active list element + this._internals.defaultNode = {}; // The default entry to be active if nothing else is + + this._setActiveNode = function() { // Sets the entry specified by nodeId as active + this._internals.active = this._internals.stack.length-1; + if (this._internals.active >= 0) { + this._internals.activeNode = this._internals.stack[this._internals.active]; + } else { + this._internals.activeNode = this._internals.defaultNode; + } + } + + this.init = function() { // Clears existing stack + this._internals.stack = []; + } + + this.pushDown = function(node) { // Push new node onto stack and make active + this._internals.stack.push (node); + this._setActiveNode(); + } + + this.popOff = function() { // Pop node off stack and make next one active + this._internals.stack.pop(); + this._setActiveNode(); + } + + this.getActive = function() { + return this._internals.activeNode; + } + + this.setDefault = function(node) { + this._internals.defaultNode = node; + if (Object.keys(this._internals.activeNode).length === 0) { + this._internals.activeNode = this._internals.defaultNode; + } + } +} +// File: ./Types.js +/* + * xseen.types contains the datatype and conversion utilities. These convert one format to another. + * Any method ending in 'toX' where 'X' is some datatype is a conversion to that type + * Other methods convert from string with space-spearated values + */ +xseen.types = { + 'Deg2Rad' : Math.PI / 180, + + 'SFFloat' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return value; + }, + + 'SFInt' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return Math.round(value); + }, + + 'SFBool' : function (value, def) + { + if (value === null) {return def;} + if (value) {return true;} + if (!value) {return false;} + return def; + }, + + 'SFTime' : function (value, def) + { + if (value === null) {return def;} + if (Number.isNaN(value)) {return def}; + return value; + }, + + 'SFVec3f' : function (value, def) + { + if (value === null) {return def;} + var v3 = value.split(' '); + if (v3.length < 3 || Number.isNaN(v3[0]) || Number.isNaN(v3[1]) || Number.isNaN(v3[2])) { + return def; + } + return [v3[0]-0, v3[1]-0, v3[2]-0]; + }, + + 'SFVec2f' : function (value, def) + { + if (value === null) {return def;} + var v2 = value.split(' '); + if (v2.length != 2 || Number.isNaN(v2[0]) || Number.isNaN(v2[1])) { + return def; + } + return [v2[0]-0, v2[1]-0]; + }, + + 'SFRotation' : function (value, def) + { + if (value === null) {return def;} + var v4 = value.split(' '); + if (v4.length != 4 || Number.isNaN(v4[0]) || Number.isNaN(v4[1]) || Number.isNaN(v4[2]) || Number.isNaN(v4[3])) { + return def; + } + var result = { + 'vector' : [v4[0], v4[1], v4[2], v4[3]], + 'axis_angle' : [{'x': v4[0], 'y': v4[1], 'z': v4[2]}, v4[3]], + }; + return result; + }, + + 'SFColor' : function (value, defaultString) + { + var v3 = this.SFVec3f(value, defaultString); + v3[0] = Math.min(Math.max(v3[0], 0.0), 1.0); + v3[1] = Math.min(Math.max(v3[1], 0.0), 1.0); + v3[2] = Math.min(Math.max(v3[2], 0.0), 1.0); + return v3; + }, + + 'SFString' : function (value, def) + { + if (value === null) {value = def;} + return value; + }, + +// For MF* types, a default of '' means to return an empty array on parsing error + 'MFFloat' : function (value, def) + { + var defReturn = (def == '') ? [] : def; + if (value === null) {return defReturn;} + var mi = value.split(' '); + var rv = []; + for (var i=0; iDocumentation."]; +/* + * All X3D and A-Frame pre-defined solids, fixed camera, directional light, Material texture only, glTF model loader with animations, Assets and reuse, Viewpoint, Background, Lighting, Image Texture, [Indexed]TriangleSet, IndexedFaceSet, [Indexed]QuadSet
\nNext work
  • Event Model/Animation
  • Extrusion
  • Navigation
", + * + * All of the following are ALPHA releases for V0.4.x + * V0.4.0+13 Feature -- events (from HTML to XSeen) + * V0.4.1+14 Fix - minor text correction in xseen.node.geometry__TriangulateFix (nodes-x3d_Geometry.js) + * V0.4.1+15 Modified build.pl to increase compression by removing block comments + * V0.4.1+16 Feature -- XSeen events (from XSeen to HTML) + * V0.4.2+17 Feature -- XSeen internals events (from XSeen to XSeen) with changes to fix previous event handling + * V0.4.2+18 Feature -- Split screen VR display + * V0.4.3+19 Rebuild and fix loading caused by new Stereo library + * V0.4.3+20 Feature -- Navigation (orbit), including Stack update for Viewpoint and restructuring the rendering loop + * V0.4.3+21 Feature -- Changed handling of Viewpoint to include camera motion + * V0.4.4+22 Fix -- Internal event handling in passing on events of the proper type + * V0.4.5+23 Feature -- Navigation (trackball) + * V0.4.5+24 Fix -- when there is no navigation + * V0.4.6+25 Update -- JSON Parser (contributed by John Carlson) + * + * In progress + */ + var version = { + major : Major, + minor : Minor, + patch : Patch, + preRelease : PreRelease, + release : Release, + version : '', + date : RDate, + splashText : SplashText + }; +// Using the scheme at http://semver.org/ + version.version = version.major + '.' + version.minor + '.' + version.patch; + version.version += (version.preRelease != '') ? '-' + version.preRelease : ''; + version.version += (version.release != '') ? '+' + version.release : ''; + return version; +} +// File: nodes/nodes-af.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code for A-Frame nodes + + +xseen.node.core_NOOP = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.parsing = function (s, e) { + xseen.debug.logInfo ('Parsing init details stub for ' + s); +} + +xseen.node.af_Entity = { + 'init' : function (e,p) + { + xseen.node.parsing('A-Frame Entity'); + }, + 'fin' : function (e,p) {} +}; +xseen.node.af_Assets = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.af_AssetItem = { + 'init' : function (e,p) // Only field is SRC. + { + }, + 'fin' : function (e,p) {} +}; +xseen.node.af_Mixin = { + 'init' : function (e,p) // Lots of fields -- all nebelous until used + { + }, + 'fin' : function (e,p) {} +}; + + + +xseen.node.af_Appearance = function (e) { + var parameters = { + 'aoMap' : e._xseen.fields['ambient-occlusion-map'], + 'aoMapIntensity' : e._xseen.fields['ambient-occlusion-map-intensity'], + 'color' : e._xseen.fields['color'], + 'displacementMap' : e._xseen.fields['displacement-map'], + 'displacementScale' : e._xseen.fields['displacement-scale'], + 'displacementBias' : e._xseen.fields['displacement-bias'], + 'envMap' : e._xseen.fields['env-map'], + 'normalMap' : e._xseen.fields['normal-map'], + 'normalScale' : e._xseen.fields['normal-scale'], + 'wireframe' : e._xseen.fields['wireframe'], + 'wireframeLinewidth' : e._xseen.fields['wireframe-linewidth'], + }; + var material = new THREE.MeshPhongMaterial(parameters); + return material; +/* + * === All Entries === +.aoMap +.aoMapIntensity +.color + .combine +.displacementMap +.displacementScale +.displacementBias + .emissive + .emissiveMap + .emissiveIntensity +.envMap + .lightMap + .lightMapIntensity + .map + .morphNormals + .morphTargets +.normalMap +.normalScale + .reflectivity + .refractionRatio + .shininess + .skinning + .specular + .specularMap +.wireframe + .wireframeLinecap + .wireframeLinejoin +.wireframeLinewidth +/////////////////////////////////////////////////////////////////////////////// +e._xseen.fields['ambient-occlusion-map'] +e._xseen.fields['ambient-occlusion-map-intensity'] + e._xseen.fields['ambient-occlusion-texture-offset'] + e._xseen.fields['ambient-occlusion-texture-repeat'] +e._xseen.fields['color'] +e._xseen.fields['displacement-bias'] +e._xseen.fields['displacement-map'] +e._xseen.fields['displacement-scale'] + e._xseen.fields['displacement-texture-offset'] + e._xseen.fields['displacement-texture-repeat'] +e._xseen.fields['env-map'] + e._xseen.fields['fog'] + e._xseen.fields['metalness'] +e._xseen.fields['normal-map'] +e._xseen.fields['normal-scale'] + e._xseen.fields['normal-texture-offset'] + e._xseen.fields['normal-texture-repeat'] + e._xseen.fields['repeat'] + e._xseen.fields['roughness'] + e._xseen.fields['spherical-env-map'] + e._xseen.fields['src'] +e._xseen.fields['wireframe'] +e._xseen.fields['wireframe-linewidth'] + + * === Unused Entries === + .combine + .emissive + .emissiveMap + .emissiveIntensity + .lightMap + .lightMapIntensity + .map + .morphNormals + .morphTargets + .reflectivity + .refractionRatio + .shininess + .skinning + .specular + .specularMap + .wireframeLinecap + .wireframeLinejoin +/////////////////////////////////////////////////////////////////////////////// + e._xseen.fields['ambient-occlusion-texture-offset'] + e._xseen.fields['ambient-occlusion-texture-repeat'] + e._xseen.fields['displacement-texture-offset'] + e._xseen.fields['displacement-texture-repeat'] + e._xseen.fields['fog'] + e._xseen.fields['metalness'] + e._xseen.fields['normal-texture-offset'] + e._xseen.fields['normal-texture-repeat'] + e._xseen.fields['repeat'] + e._xseen.fields['roughness'] + e._xseen.fields['spherical-env-map'] + e._xseen.fields['src'] + */ +} + +xseen.node.af_Box = { + 'init' : function (e,p) + { + var geometry = new THREE.BoxGeometry( + e._xseen.fields.width, + e._xseen.fields.height, + e._xseen.fields.depth, + e._xseen.fields['segments-width'], + e._xseen.fields['segments-height'], + e._xseen.fields['segments-depth'] + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Cone = { + 'init' : function (e,p) + { + var geometry = new THREE.ConeGeometry( + e._xseen.fields.radius, + e._xseen.fields.height, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-height'], + e._xseen.fields['open-ended'], + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Cylinder = { + 'init' : function (e,p) + { + var geometry = new THREE.CylinderGeometry( + e._xseen.fields['radius-top'], + e._xseen.fields['radius-bottom'], + e._xseen.fields.height, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-height'], + e._xseen.fields['open-ended'], + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Dodecahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.DodecahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Icosahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.IcosahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Octahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.OctahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Sphere = { + 'init' : function (e,p) + { + var geometry = new THREE.SphereGeometry( + e._xseen.fields.radius, + e._xseen.fields['segments-width'], + e._xseen.fields['segments-height'], + e._xseen.fields['phi-start'] * xseen.types.Deg2Rad, + e._xseen.fields['phi-length'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, + e._xseen.fields['theta-length'] * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Tetrahedron = { + 'init' : function (e,p) + { + var geometry = new THREE.TetrahedronGeometry( + e._xseen.fields.radius, + e._xseen.fields.detail + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; + +xseen.node.af_Torus = { + 'init' : function (e,p) + { + var geometry = new THREE.TorusGeometry( + e._xseen.fields.radius, + e._xseen.fields.tube, + e._xseen.fields['segments-radial'], + e._xseen.fields['segments-tubular'], + e._xseen.fields.arc * xseen.types.Deg2Rad + ); + var appearance = xseen.node.af_Appearance (e); + var mesh = new THREE.Mesh (geometry, appearance); + mesh.userData = e; + p._xseen.sceneInfo.selectable.push(mesh); + var group = new THREE.Group(); + group.add (mesh); + if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(group); + }, + 'fin' : function (e,p) {} +}; +// File: nodes/nodes-Viewing.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Control Node definitions + + +xseen.node.unk_Viewpoint = { + 'init' : function (e,p) + { // This should really go in a separate push-down list for Viewpoints + e._xseen.fields._radius0 = Math.sqrt( e._xseen.fields.position[0]*e._xseen.fields.position[0] + + e._xseen.fields.position[1]*e._xseen.fields.position[1] + + e._xseen.fields.position[2]*e._xseen.fields.position[2]); + e._xseen.domNode = e; // Back-link to node if needed later on + e._xseen.position = new THREE.Vector3(e._xseen.fields.position[0], e._xseen.fields.position[1], e._xseen.fields.position[2]); + e._xseen.type = e._xseen.fields.cameratype; + e._xseen.motion = e._xseen.fields.motion; + e._xseen.motionspeed = e._xseen.fields.motionspeed * 1000; + if (e._xseen.motion == 'turntable' || e._xseen.motion == 'tilt') {e._xseen.motionspeed = 1.0/e._xseen.motionspeed;} + + if (!e._xseen.sceneInfo.tmp.activeViewpoint) { + e._xseen.sceneInfo.stacks.Viewpoints.pushDown(e._xseen); + e._xseen.sceneInfo.tmp.activeViewpoint = true; + } + + e._xseen.handlers = {}; + e._xseen.handlers.setactive = this.setactive; + }, + 'fin' : function (e,p) {}, + + 'setactive' : function (ev) + { + var xseenNode = this.destination._xseen; + xseenNode.sceneInfo.stacks.Viewpoints.pushDown(xseenNode); // TODO: This is probably not the right way to change VP in the stack + xseenNode.sceneInfo.element._xseen.renderer.activeCamera = + xseenNode.sceneInfo.element._xseen.renderer.cameras[xseenNode.fields.type]; + xseenNode.sceneInfo.element._xseen.renderer.activeRender = + xseenNode.sceneInfo.element._xseen.renderer.renderEffects[xseenNode.fields.type]; + if (xseenNode.fields.type != 'stereo') { + xseenNode.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, xseenNode.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); + } + }, +}; + +xseen.node.controls_Navigation = { + 'init' : function (e,p) + { // This should really go in a separate push-down list for Viewpoints + + e._xseen.domNode = e; // Back-link to node if needed later on + e._xseen.speed = e._xseen.fields.speed; + if (e._xseen.setup == 'examine') {e._xseen.setup == 'trackball';} + //e._xseen.type = e._xseen.fields.type; + e._xseen.type = 'none'; + e._xseen.setup = e._xseen.fields.type; + if (!(e._xseen.setup == 'orbit' || e._xseen.setup == 'trackball')) {e._xseen.setup = 'none';} + + if (!e._xseen.sceneInfo.tmp.activeNavigation) { + e._xseen.sceneInfo.stacks.Navigation.pushDown(e._xseen); + e._xseen.sceneInfo.tmp.activeNavigation = true; + } + + e._xseen.handlers = {}; + e._xseen.handlers.setactive = this.setactive; + }, + 'fin' : function (e,p) {}, + + 'setactive' : function (ev) + { +/* + this.destination._xseen.sceneInfo.stacks.Viewpoints.pushDown(this.destination); // TODO: This is probably not the right way to change VP in the stack + this.destination._xseen.sceneInfo.element._xseen.renderer.activeCamera = + this.destination._xseen.sceneInfo.element._xseen.renderer.cameras[this.destination._xseen.fields.type]; + this.destination._xseen.sceneInfo.element._xseen.renderer.activeRender = + this.destination._xseen.sceneInfo.element._xseen.renderer.renderEffects[this.destination._xseen.fields.type]; + if (this.destination._xseen.fields.type != 'stereo') { + this.destination._xseen.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, this.destination._xseen.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); + } + */ + }, +}; + +xseen.node.lighting_Light = { + 'init' : function (e,p) + { + var color = xseen.types.Color3toInt (e._xseen.fields.color); + var intensity = e._xseen.fields.intensity - 0; + var lamp, type=e._xseen.fields.type.toLowerCase(); +/* + if (typeof(p._xseen.children) == 'undefined') { + console.log('Parent of Light does not have children...'); + p._xseen.children = []; + } + */ + + if (type == 'point') { + // Ignored field -- e._xseen.fields.location + lamp = new THREE.PointLight (color, intensity); + lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); + lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); + + } else if (type == 'spot') { + lamp = new THREE.SpotLight (color, intensity); + lamp.position.set(0-e._xseen.fields.direction[0], 0-e._xseen.fields.direction[1], 0-e._xseen.fields.direction[2]); + lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); + lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); + lamp.angle = Math.max(0.0, Math.min(1.5707963267948966192313216916398, e._xseen.fields.cutoffangle)); + lamp.penumbra = 1 - Math.max(0.0, Math.min(lamp.angle, e._xseen.fields.beamwidth)) / lamp.angle; + + } else { // DirectionalLight (by default) + lamp = new THREE.DirectionalLight (color, intensity); + lamp.position.x = 0-e._xseen.fields.direction[0]; + lamp.position.y = 0-e._xseen.fields.direction[1]; + lamp.position.z = 0-e._xseen.fields.direction[2]; + } + p._xseen.children.push(lamp); + lamp = null; + } + , + 'fin' : function (e,p) + { + } +}; +// File: nodes/nodes-x3d_Appearance.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.appearance_Material = { + 'init' : function (e,p) + { + var transparency = e._xseen.fields.transparency - 0; + var shininess = e._xseen.fields.shininess - 0; + var colorDiffuse = xseen.types.Color3toInt (e._xseen.fields.diffusecolor); + var colorEmissive = xseen.types.Color3toInt (e._xseen.fields.emissivecolor); + var colorSpecular = xseen.types.Color3toInt (e._xseen.fields.specularcolor); + p._xseen.material = new THREE.MeshPhongMaterial( { +// p._xseen.material = new THREE.MeshBasicMaterial( { + 'color' : colorDiffuse, + 'emissive' : colorEmissive, + 'specular' : colorSpecular, + 'shininess' : shininess, + 'opacity' : 1.0-transparency, + 'transparent' : (transparency > 0.0) ? true : false + } ); + e._xseen.animate['diffusecolor'] = p._xseen.material.color; + e._xseen.animate['emissivecolor'] = p._xseen.material.emissive; + e._xseen.animate['specularcolor'] = p._xseen.material.specular; + e._xseen.animate['transparency'] = p._xseen.material.opacity; + e._xseen.animate['shininess'] = p._xseen.material.shininess; + }, + 'fin' : function (e,p) {} +}; + +xseen.node.appearance_ImageTexture = { + 'init' : function (e,p) + { + p._xseen.texture = xseen.loader.ImageLoader.load(e._xseen.fields.url); + p._xseen.texture.wrapS = (e._xseen.fields.repeats) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + p._xseen.texture.wrapT = (e._xseen.fields.repeatt) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + }, + 'fin' : function (e,p) {} +}; + +xseen.node.appearance_Appearance = { + 'init' : function (e,p) {}, + + 'fin' : function (e,p) + { + if (typeof(e._xseen.texture) !== 'undefined' && e._xseen.texture !== null) { + e._xseen.material.map = e._xseen.texture; + } + p._xseen.appearance = e._xseen.material; + } +}; +// File: nodes/nodes-x3d_Geometry.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.geometry_Coordinate = { + 'init' : function (e,p) + { + var vertices = []; + for (var i=0; i= 0 where each triple defines a triangle face. It is assumed that the + * indices define the face in counter-clockwise order when looking at the face. + * vertices - an array of THREE.Vector3 points + */ +xseen.node.geometry__Indexed3 = function (indices, vertices, normals=[], color=[]) { + var i, face, normal=[], faceCount=0, n; + var useNormals = (normals.length > 0) ? true : false; + var useColor = (color.length > 0) ? true : false; + var maxIndex = Math.max.apply(null, indices); + var minIndex = Math.min.apply(null, indices); + if (maxIndex >= vertices.length) { + console.log ('Maximum index ('+maxIndex+') exceeds vertex count ('+vertices.length+'). No geometry is created'); + return; + } + if (useNormals && maxIndex >= normals.length) { + console.log ('Maximum index ('+maxIndex+') exceeds normal count ('+normals.length+'). No geometry is created'); + return; + } + if (useColor && maxIndex >= color.length) { + console.log ('Maximum index ('+maxIndex+') exceeds color count ('+color.length+'). No geometry is created'); + return; + } + if (minIndex < 0) { + console.log ('Minimum index ('+minIndex+') less than zero. No geometry is created'); + return; + } + if (indices.length % 3 != 0) { + console.log ('Number of indices ('+indices.length+') not divisible by 3. No geometry is created'); + return; + } + + var geometry = new THREE.Geometry(); + var normal_pz = new THREE.Vector3 (0, 0, 1); + var normal_mz = new THREE.Vector3 (0, 0, -1); + for (var i=0; i 0) {clip.duration = e._xseen.fields.duration;} + e._xseen.mixer.clipAction( clip ).play(); + } ); + } else { // Play a specific animation + var clip = THREE.AnimationClip.findByName(response.animations, e._xseen.fields.playonload); + var action = e._xseen.mixer.clipAction (clip); + action.play(); + } + } + } + } +}; + + +xseen.node.x_Route = { + 'init' : function (e,p) + { + var dest = e._xseen.fields.destination; + var hand = e._xseen.fields.handler; + var externalHandler = false; + + // Make sure sufficient data is provided + if (e._xseen.fields.source == '' || + typeof(window[hand]) !== 'function' && + (dest == '' || e._xseen.fields.event == '' || e._xseen.fields.field == '')) { + xseen.debug.logError ('Route node missing field. No route setup. Source: '+e._xseen.fields.source+'.'+e._xseen.fields.event+'; Destination: '+dest+'.'+e._xseen.fields.field+'; Handler: '+hand); + return; + } else if (typeof(window[hand]) === 'function') { + externalHandler = true; + } + + // For toNode routing, check existence of source and destination elements + var eSource = document.getElementById (e._xseen.fields.source); + if (! externalHandler) { + var eDestination = document.getElementById (dest); + if (typeof(eSource) === 'undefined' || typeof(eDestination) === 'undefined') { + xseen.debug.logError ('Source or Destination node does not exist. No route setup'); + return; + } + // Get field information -- perhaps there is some use in the Animate node? + var fField = xseen.nodes._getFieldInfo (eDestination.nodeName, e._xseen.fields.field); + if (typeof(fField) === 'undefined' || !fField.good) { + xseen.debug.logError ('Destination field does not exist or incorrectly specified. No route setup'); + return; + } + // Set up listener on source node for specified event. The listener code is the 'set' method for the + // node. It is passed the DOM 'event' data structure. Since there may be more than one node of the type + // specified by 'destination', the event handler is attached to the node in e._xseen.handlers. This is done + // when the node is parsed + xseen.Events.addHandler (e, eSource, e._xseen.fields.event, eDestination, fField); + +/* + * External (to XSeen) event handler + * TODO: limit the events to those requested if e._xseen.fields.event != 'xseen' + * This probably requires an intermediatiary event handler + */ + } else { + var handler = window[hand]; + eSource.addEventListener ('xseen', handler); + } + }, + + 'fin' : function (e,p) + { + }, + 'evHandler' : function (u) + { + var de = u.e; + var df = u.f; + return de._xseen.handlers[df.handlerName]; + }, +}; +// File: nodes/nodes.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.core_NOOP = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) {} +}; +xseen.node.core_WorldInfo = { + 'init' : function (e,p) {parsing('WorldInfo', e)}, + 'fin' : function (e,p) {} +}; + +function parsing (s, e) { + xseen.debug.logInfo ('Parsing init details stub for ' + s); +} + + +xseen.node.unk_Shape = { + 'init' : function (e,p) {}, + 'fin' : function (e,p) + { +// if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + if (typeof(e._xseen.materialProperty) !== 'undefined') { + e._xseen.appearance.vertexColors = THREE.VertexColors; + //e._xseen.appearance.vertexColors = THREE.FaceColors; + e._xseen.appearance._needsUpdate = true; + e._xseen.appearance.needsUpdate = true; + } + var mesh = new THREE.Mesh (e._xseen.geometry, e._xseen.appearance); + mesh.userData = e; + p._xseen.children.push(mesh); + p._xseen.sceneInfo.selectable.push(mesh); + mesh = null; + } +}; + +xseen.node.grouping_Transform = { + 'init' : function (e,p) + { + var group = new THREE.Group(); + if (e.nodeName == "TRANSFORM") { + var rotation = xseen.types.Rotation2Quat(e._xseen.fields.rotation); + group.name = 'Transform children [' + e.id + ']'; + group.position.x = e._xseen.fields.translation[0]; + group.position.y = e._xseen.fields.translation[1]; + group.position.z = e._xseen.fields.translation[2]; + group.scale.x = e._xseen.fields.scale[0]; + group.scale.y = e._xseen.fields.scale[1]; + group.scale.z = e._xseen.fields.scale[2]; + group.quaternion.x = rotation.x; + group.quaternion.y = rotation.y; + group.quaternion.z = rotation.z; + group.quaternion.w = rotation.w; + + e._xseen.animate['translation'] = group.position; + e._xseen.animate['rotation'] = group.quaternion; + e._xseen.animate['scale'] = group.scale; + } + e._xseen.sceneNode = group; + }, + 'fin' : function (e,p) + { + // Apply transform to all objects in e._xseen.children + e._xseen.children.forEach (function (child, ndx, wholeThing) + { + e._xseen.sceneNode.add(child); + }); + p._xseen.children.push(e._xseen.sceneNode); + } +}; + +xseen.node.networking_Inline = { + 'init' : function (e,p) + { + if (typeof(e._xseen.processedUrl) === 'undefined' || !e._xseen.requestedUrl) { + var uri = xseen.parseUrl (e._xseen.fields.url); + var type = uri.extension; + e._xseen.loadGroup = new THREE.Group(); + e._xseen.loadGroup.name = 'Inline content [' + e.id + ']'; + console.log ('Created Inline Group with UUID ' + e._xseen.loadGroup.uuid); + var userdata = {'requestType':'x3d', 'e':e, 'p':p} + if (type.toLowerCase() == 'json') { + userdata.requestType = 'json'; + xseen.loadMgr.loadJson (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); + } else { + xseen.loadMgr.loadXml (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); + } + e._xseen.requestedUrl = true; + } + //if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} + p._xseen.children.push(e._xseen.loadGroup); + console.log ('Using Inline Group with UUID ' + e._xseen.loadGroup.uuid); + }, + 'fin' : function (e,p) + { + }, + + 'loadSuccess' : + function (response, userdata, xhr) { + userdata.e._xseen.processedUrl = true; + userdata.e._xseen.loadResponse = response; + console.log("download successful for "+userdata.e.id); + if (userdata.requestType == 'json') { + var tmp = {'scene': response}; + response = null; + response = (new JSONParser()).parseJavaScript(tmp); + } + var start = {'_xseen':0}; + var findSceneTag = function (fragment) { + if (typeof(fragment._xseen) === 'undefined') {fragment._xseen = {'childCount': -1};} + if (fragment.nodeName.toLowerCase() == 'scene') { + start = fragment; + return; + } else if (fragment.children.length > 0) { + for (fragment._xseen.childCount=0; fragment._xseen.childCount 0) { + userdata.e.appendChild(start.children[0]); + } + xseen.Parse(userdata.e, userdata.p, userdata.p._xseen.sceneInfo); + userdata.e._xseen.children.forEach (function (child, ndx, wholeThing) + { + userdata.e._xseen.loadGroup.add(child); +console.log ('...Adding ' + child.type + ' (' + child.name + ') to Inline Group? with UUID ' + userdata.e._xseen.loadGroup.uuid + ' (' + userdata.e._xseen.loadGroup.name + ')'); + }); + userdata.p._xseen.sceneInfo.scene.updateMatrixWorld(); + //xseen.debug.logInfo("Complete work on Inline..."); + } else { + console.log("Found illegal X3D file -- no 'scene' tag"); + } + // Parse (start, userdata.p)... + } +}; + +/* + * Most of this stuff is only done once per XSeen element. Loading of Inline contents should not + * repeat the definitions and canvas creation + */ +xseen.node.core_Scene = { + 'DEFAULT' : { + 'Viewpoint' : { + 'Position' : new THREE.Vector3 (0, 0, 10), + 'Orientation' : '0 1 0 0', // TODO: fix (and below) when handling orientation + 'Type' : 'perpsective', + 'Motion' : 'none', + 'MotionSpeed' : 1.0, + }, + 'Navigation' : { + 'Speed' : 1.0, // 16 spr (1 revolution per 16 seconds), in mseconds. + 'Type' : 'none', + 'Setup' : 'none', + } + }, + 'init' : function (e,p) + { + // Create default Viewpoint and Navigation + xseen.sceneInfo[0].stacks.Viewpoints.setDefault( + { + 'position' : this.DEFAULT.Viewpoint.Position, + 'type' : this.DEFAULT.Viewpoint.Type, + 'motion' : this.DEFAULT.Viewpoint.Motion, + 'motionspeed': this.DEFAULT.Viewpoint.MotionSpeed / 1000, + 'domNode' : e, + 'fields' : {}, + } + ); + xseen.sceneInfo[0].stacks.Navigation.setDefault( + { + 'speed' : this.DEFAULT.Navigation.Speed / 1000, + 'type' : this.DEFAULT.Navigation.Type, + 'setup' : this.DEFAULT.Navigation.Setup, + 'domNode' : e, + 'fields' : {}, + } + ); + + var width = e._xseen.sceneInfo.size.width; + var height = e._xseen.sceneInfo.size.height; + var x_renderer = new THREE.WebGLRenderer(); + x_renderer.setSize (width, height); + var perspectiveCamera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 ); + var orthoCamera = new THREE.OrthographicCamera( 75, width / height, 0.1, 1000 ); + //perspectiveCamera.translateX(this.DEFAULT.Viewpoint.Position.x).translateY(this.DEFAULT.Viewpoint.Position.y).translateZ(this.DEFAULT.Viewpoint.Position.z); // Default position + //orthoCamera.translateX(this.DEFAULT.Viewpoint.Position.x).translateY(this.DEFAULT.Viewpoint.Position.y).translateZ(this.DEFAULT.Viewpoint.Position.z); // Default position + perspectiveCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position + perspectiveCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position + perspectiveCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position + orthoCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position + orthoCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position + orthoCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position + + // Stereo viewing effect + // from http://charliegerard.github.io/blog/Virtual-Reality-ThreeJs/ + var x_effect = new THREE.StereoEffect(x_renderer); + + e.appendChild (x_renderer.domElement); + e._xseen.renderer = { + 'canvas' : e._xseen.sceneInfo.scene, + 'width' : width, + 'height' : height, + 'cameras' : { + 'perspective' : perspectiveCamera, + 'ortho' : orthoCamera, + 'stereo' : perspectiveCamera, + }, // Removed .sceneInfo camera because this node defines the camera + 'effects' : x_effect, + 'renderEffects' : { + 'normal' : x_renderer, + 'perspective' : x_renderer, + 'ortho' : x_renderer, + 'stereo' : x_effect, + }, + 'activeRender' : {}, + 'activeCamera' : {}, + 'controls' : {}, // Used for navigation + }; + e._xseen.renderer.activeRender = e._xseen.renderer.renderEffects.normal; + e._xseen.renderer.activeCamera = e._xseen.renderer.cameras.perspective; + }, + +/* + * This appears now to be working!!! + * + * Late loading content is not getting inserted into the scene graph for rendering. Need to read + * THREE docs about how to do that. + * Camera will need to be redone. Existing camera is treated as a special child. A separate camera + * should be established and Viewpoint nodes define "photostops" rather than a camera. The camera is + * in effect, parented to the "photostop". This probably needs to list of Viewpoints discussed in the + * X3D specification. + */ + 'fin' : function (e,p) + { + // Render all Children + //xseen.renderNewChildren (e._xseen.children, e._xseen.renderer.canvas); + e._xseen.children.forEach (function (child, ndx, wholeThing) + { + console.log('Adding child of type ' + child.type + ' (' + child.name + ')'); + e._xseen.renderer.canvas.add(child); + }); + xseen.dumpSceneGraph (); +// e._xseen.renderer.renderer.render( e._xseen.renderer.canvas, e._xseen.renderer.camera ); +// xseen.debug.logInfo("Rendered all elements -- Starting animation"); +/* + * TODO: Need to get current top-of-stack for all stack-bound nodes and set them as active. + * This only happens the initial time for each XSeen tag in the main HTML file + * + * At this time, only Viewpoint is stack-bound. Probably need to stack just the ._xseen object. + * Also, .fields.position is the initial specified location; not the navigated/animated one + */ + var vp = xseen.sceneInfo[0].stacks.Viewpoints.getActive(); + var nav = xseen.sceneInfo[0].stacks.Navigation.getActive(); + var currentCamera = e._xseen.renderer.activeCamera; + var currentRenderer = e._xseen.renderer.activeRender; + currentCamera.position.x = vp.position.x; + currentCamera.position.y = vp.position.y; + currentCamera.position.z = vp.position.z; + e._xseen.renderer.controls = xseen.Navigation.setup[nav.setup] (currentCamera, currentRenderer); + xseen.debug.logInfo("Ready to kick off rendering loop"); + xseen.renderFrame(); + }, + +}; + +xseen.node.env_Background = { + 'init' : function (e,p) + { + var color = new THREE.Color(e._xseen.fields.skycolor[0], e._xseen.fields.skycolor[1], e._xseen.fields.skycolor[2]); + var textureCube = new THREE.CubeTextureLoader() + .load ([e._xseen.fields.srcright, + e._xseen.fields.srcleft, + e._xseen.fields.srctop, + e._xseen.fields.srcbottom, + e._xseen.fields.srcfront, + e._xseen.fields.srcback], + this.loadSuccess({'e':e, 'p':p}) + ); + e._xseen.sceneInfo.scene.background = color; +/* + var material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } ); + var size = 1; + //var geometry = new THREE.BoxGeometry(200, 200, 2); + var geometry = new THREE.Geometry(); + geometry.vertices.push ( + new THREE.Vector3(-size, -size, size), + new THREE.Vector3( size, -size, size), + new THREE.Vector3( size, -size, -size), + new THREE.Vector3(-size, -size, -size), + new THREE.Vector3(-size, size, size), + new THREE.Vector3( size, size, size), + new THREE.Vector3( size, size, -size), + new THREE.Vector3(-size, size, -size) + ); + + geometry.faces.push ( // external facing geometry + new THREE.Face3(0, 1, 5), + new THREE.Face3(0, 5, 4), + new THREE.Face3(1, 2, 6), + new THREE.Face3(1, 6, 5), + new THREE.Face3(2, 3, 7), + new THREE.Face3(2, 7, 6), + new THREE.Face3(3, 0, 4), + new THREE.Face3(3, 4, 7), + new THREE.Face3(4, 5, 6), + new THREE.Face3(4, 6, 7), + new THREE.Face3(0, 2, 1), + new THREE.Face3(0, 3, 2), + ); + geometry.computeBoundingSphere(); + var mesh = new THREE.Mesh (geometry, material); + e._xseen.sceneInfo.element._xseen.renderer.canvas.add(mesh); +*/ + }, + + 'fin' : function (e,p) + { + p._xseen.appearance = e._xseen.material; + }, + + 'loadSuccess' : function (userdata) + { + var e = userdata.e; + var p = userdata.p; + return function (textureCube) + { + e._xseen.processedUrl = true; + e._xseen.loadTexture = textureCube; + e._xseen.sceneInfo.scene.background = textureCube; + } + }, + +}; +// File: nodes/nodes_Animate.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + // Node definition code (just stubs right now...) + + +xseen.node.x_Animate = { + 'init' : function (e,p) + { + var delay = e._xseen.fields.delay * 1000; // Convert to milliseconds + var duration = e._xseen.fields.duration * 1000; // Convert to milliseconds + var repeat = (e._xseen.fields.repeat < 0) ? Infinity : e._xseen.fields.repeat; + var interpolator = e._xseen.fields.interpolator; + var easing = e._xseen.fields.easing; + + var fields = xseen.parseTable[p.localName.toLowerCase()].fields; + var fieldIndex = xseen.parseTable[p.localName.toLowerCase()].fieldIndex; + var toField = e._xseen.fields.field; + var toFieldIndex = fieldIndex[toField]; + if (typeof(fields[toFieldIndex]) === 'undefined') { + xseen.debug.logInfo("Field '" + toField + "' not found in parent (" + p.localName.toLowerCase() + "). No animation performed."); + return; + } + var fieldObject = fields[toFieldIndex].clone().setFieldName('to'); // Parse table entry for 'toField' + var to = xseen.nodes._parseField(fieldObject, e); // Parsed data -- need to convert to THREE format + +// Convert 'to' to the datatype of 'field' and set interpolation type. + var interpolation; + if (fieldObject.type == 'SFVec3f') { + interpolation = TWEEN.Interpolation.Linear; + to = xseen.types.Vector3(to); + xseen.debug.logInfo("Interpolating field '" + toField + "' as 3-space."); + + } else if (fieldObject.type == 'SFColor') { + interpolation = this.Interpolator.color; + to = new THREE.Color (xseen.types.Color3toInt(to)); + xseen.debug.logInfo("Interpolation field '" + toField + "' as color."); + + } else if (fieldObject.type == 'SFRotation') { + interpolation = this.Interpolator.slerp; + to = xseen.types.Rotation2Quat(to); + xseen.debug.logInfo("Interpolation field '" + toField + "' as rotation."); + + } else { + xseen.debug.logInfo("Field '" + toField + "' not converted to THREE format. No animation performed."); + return; + } + var fieldTHREE = p._xseen.animate[toField]; // THREE field for animation + + var tween = new TWEEN.Tween(fieldTHREE) + .to(to, duration) + .delay(delay) + .repeat(repeat) + .interpolation(interpolation); + var easingType = e._xseen.fields.easingtype; + easingType = easingType.charAt(0).toUpperCase() + easingType.slice(1); + easing = (easingType != 'Linear' && easing == '') ? 'inout' : easing; + if (easing != '') { + easing = easing.replace('in', 'In').replace('out', 'Out'); + easingType = (easingType == 'Linear') ? 'Quadratic' : easingType; + e._xseen.fields.easing = easing; + e._xseen.fields.easingtype = easingType; + tween.easing(TWEEN.Easing[easingType][easing]); + } + +/* + * Put animation-specific data in node (e._xseen) so it can be accessed on events (through 'this') + * This includes initial value and field + * All handlers (goes into .handlers) + * TWEEN object + */ + e._xseen.initialValue = fieldTHREE.clone(); + e._xseen.animatingField = fieldTHREE; + e._xseen.handlers = {}; + e._xseen.handlers.setstart = this.setstart; + e._xseen.handlers.setstop = this.setstop; + e._xseen.handlers.setpause = this.setpause; + e._xseen.handlers.setresetstart = this.setresetstart; + e._xseen.animating = tween; + p._xseen.animation.push (tween); + tween.start(); + }, + 'fin' : function (e,p) {}, + 'setstart' : function (ev) + { + console.log ('Starting animation'); + this.destination._xseen.animating.start(); + }, + 'setstop' : function (ev) + { + console.log ('Stopping animation'); + this.destination._xseen.animating.stop(); + }, +/* + * TODO: Update TWEEN to support real pause & resume. + * Pause needs to hold current position + * Resume needs to restart the timer to current time so there is no "jump" + */ + 'setpause' : function (ev) + { + console.log ('Pausing (really stopping) animation'); + this.destination._xseen.animating.stop(); + }, + 'setresetstart' : function (ev) // TODO: Create seperate 'reset' method + { + console.log ('Reset and start animation'); + this.destination._xseen.animatingField = this.destination._xseen.initialValue; + this.destination._xseen.animating.start(); + }, + +/* + * Various interpolator functions for use with different data types + * All are designed to be used within TWEEN and take two arguments + * v A vector of way points (key values) that define the interpolated path + * k The interpolating factor that defines how far along the path for the current result + * + * Functions + * slerp - Linear in quaterian space (though not yet) + * color - Linear in color space (currently HSL as used by THREE) + * + */ + 'Interpolator' : { + 'slerp' : function (v,k) + { + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + + if (k < 0) { + return v[0].slerp(v[1], f); + } + + if (k > 1) { + return v[m].slerp(v[m-1], m-f); + } + + return v[i].slerp (v[i + 1 > m ? m : i + 1], f-i); + }, + 'color' : function (v,k) + { + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + var fn = this.slerpCompute; + + if (k < 0) { + return v[0].lerp(v[1], f); + } + if (k > 1) { + return v[m].lerp(v[m-1], m-f); + } + return v[i].lerp (v[i + 1 > m ? m : i + 1], f - i); + }, + }, +}; +// File: nodes/_Definitions-aframe.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen._addAframeAppearance = function (node) { + node + .addField('ambient-occlusion-map', 'SFString', '') + .addField('ambient-occlusion-map-intensity', 'SFFloat', 1) + .addField('ambient-occlusion-texture-offset', 'SFVec2f', '0 0') + .addField('ambient-occlusion-texture-repeat', 'SFVec2f', '1 1') + .addField('color', 'Color', '#FFF') + .addField('displacement-bias', 'SFFloat', 0.5) + .addField('displacement-map', 'SFString', '') + .addField('displacement-scale', 'SFFloat', 1) + .addField('displacement-texture-offset', 'SFVec2f', '0 0') + .addField('displacement-texture-repeat', 'SFVec2f', '1 1') + .addField('env-map', 'SFString', '') + .addField('fog', 'SFBool', true) + .addField('metalness', 'SFFloat', 0) + .addField('normal-map', 'SFString', '') + .addField('normal-scale', 'SFVec2f', '1 1') + .addField('normal-texture-offset', 'SFVec2f', '0 0') + .addField('normal-texture-repeat', 'SFVec2f', '1 1') + .addField('repeat', 'SFVec2f', '1 1') + .addField('roughness', 'SFFloat', 0.5) + .addField('spherical-env-map', 'SFString', '') + .addField('src', 'SFString', '') + .addField('wireframe', 'SFBool', false) + .addField('wireframe-linewidth', 'SFInt', 2) + .addNode(); +} + +xseen.nodes._defineNode('a-entity', 'A-Frame', 'af_Entity') + .addField('geometry', 'SFString', '') + .addField('material', 'SFString', '') + .addField('light', 'SFString', '') + .addNode(); + +var node; +node = xseen.nodes._defineNode('a-box', 'A-Frame', 'af_Box') + .addField('depth', 'SFFloat', 1) + .addField('height', 'SFFloat', 1) + .addField('width', 'SFFloat', 512) + .addField('segments-depth', 'SFInt', 1) + .addField('segments-height', 'SFInt', 1) + .addField('segments-width', 'SFInt', 1); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-cone', 'A-Frame', 'af_Cone') + .addField('height', 'SFFloat', 1) + .addField('radius', 'SFFloat', 1) + .addField('open-ended', 'SFBool', false) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 1) + .addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-cylinder', 'A-Frame', 'af_Cylinder') + .addField('height', 'SFFloat', 1) + .addField('open-ended', 'SFBool', false) + .addField('radius-bottom', 'SFFloat', 1) + .addField('radius-top', 'SFFloat', 1) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 1) + .addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-dodecahedron', 'A-Frame', 'af_Dodecahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-icosahedron', 'A-Frame', 'af_Icosahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-octahedron', 'A-Frame', 'af_Octahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-sphere', 'A-Frame', 'af_Sphere') + .addField('radius', 'SFFloat', 1) + .addField('theta-start', 'SFFloat', 0) + .addField('theta-length', 'SFFloat', 180) + .addField('phi-start', 'SFFloat', 0) + .addField('phi-length', 'SFFloat', 360) + .addField('segments-height', 'SFInt', 18) + .addField('segments-width', 'SFInt', 36); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-tetrahedron', 'A-Frame', 'af_Tetrahedron') + .addField('radius', 'SFFloat', 1) + .addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); + +node = xseen.nodes._defineNode('a-torus', 'A-Frame', 'af_Torus') + .addField('radius', 'SFFloat', 2) + .addField('tube', 'SFFloat', 1) + .addField('arc', 'SFFloat', 360) + .addField('segments-radial', 'SFInt', 8) + .addField('segments-tubular', 'SFInt', 6); +xseen._addAframeAppearance (node); + +/* + * Asset management system nodes + */ +xseen.nodes._defineNode('a-assets', 'A-Frame', 'af_Assets') + .addNode(); +xseen.nodes._defineNode('a-asset-item', 'A-Frame', 'af_AssetItem') + .addField('src', 'SFString', '') + .addNode(); +xseen.nodes._defineNode('a-mixin', 'A-Frame', 'af_Mixin') + .addField('*', 'SFString', '') + .addNode(); +// File: nodes/_Definitions-x3d.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen.nodes._defineNode('Cone', 'Geometry3D', 'geometry3D_Cone') + .addField('bottomRadius', 'SFFloat', 1) + .addField('height', 'SFFloat', 2) + .addField('bottom', 'SFBool', true) + .addField('side', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode('Box', 'Geometry3D', 'geometry3D_Box') + .addField('size', 'SFVec3f', [1,1,1]) + .addNode(); + +xseen.nodes._defineNode('Sphere', 'Geometry3D', 'geometry3D_Sphere') + .addField('radius', 'SFFloat', '1') + .addNode(); + +xseen.nodes._defineNode('Cylinder', 'Geometry3D', 'geometry3D_Cylinder') + .addField('radius', 'SFFloat', 1) + .addField('height', 'SFFloat', 2) + .addField('bottom', 'SFBool', true) + .addField('side', 'SFBool', true) + .addField('top', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode ('Material', 'Appearance', 'appearance_Material') + .addField({name:'diffuseColor', datatype:'SFColor', defaultValue:[.8,.8,.8], animatable:true}) + .addField({name:'emissiveColor',datatype: 'SFColor', defaultValue:[0,0,0], animatable:true}) + .addField({name:'specularColor', datatype:'SFColor', defaultValue:[0,0,0], animatable:true}) + .addField({name:'transparency', datatype:'SFFloat', defaultValue:'0', animatable:true}) + .addField({name:'shininess', datatype:'SFFloat', defaultValue:'0', animatable:true}) + .addNode(); + +xseen.nodes._defineNode ('Transform', 'Grouping', 'grouping_Transform') + .addField({name:'translation', datatype:'SFVec3f', defaultValue:[0,0,0], animatable:true}) + .addField({name:'scale', datatype:'SFVec3f', defaultValue:[1,1,1], animatable:true}) + .addField({name:'rotation', datatype:'SFRotation', defaultValue:xseen.types.SFRotation('0 1 0 0',''), animatable:true}) + .addNode(); +xseen.nodes._defineNode ('Group', 'Grouping', 'grouping_Transform') + .addNode(); + +xseen.nodes._defineNode ('Light', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) // DirectionalLight + .addField('location', 'SFVec3f', [0,0,0]) // PointLight & SpotLight + .addField('radius', 'SFFloat', '100') // PointLight & SpotLight + .addField('attenuation', 'SFVec3f', [1,0,0]) // PointLight & SpotLight + .addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // SpotLight + .addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // SpotLight + .addField('color', 'SFColor', [1,1,1]) // General + .addField('intensity', 'SFFloat', '1') // General + .addField({name:'type', datatype:'EnumerateString', defaultValue:'Directional', enumerated:['Directional', 'Spot', 'Point'], animatable:true}) + .addNode(); +xseen.nodes._defineNode ('DirectionalLight', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Directional') + .addNode(); +xseen.nodes._defineNode ('PointLight', 'Lighting', 'lighting_Light') + .addField('location', 'SFVec3f', [0,0,0]) + .addField('radius', 'SFFloat', '100') + .addField('attenuation', 'SFVec3f', [1,0,0]) + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Point') + .addNode(); +xseen.nodes._defineNode ('SpotLight', 'Lighting', 'lighting_Light') + .addField('direction', 'SFVec3f', [0,0,-1]) + .addField('radius', 'SFFloat', '100') + .addField('attenuation', 'SFVec3f', [1,0,0]) + .addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // pi/4 + .addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // pi/2 + .addField('color', 'SFColor', [1,1,1]) + .addField('intensity', 'SFFloat', '1') + .addField('type', 'SFString', 'Spot') + .addNode(); + +xseen.nodes._defineNode ('Viewpoint', 'Controls', 'unk_Viewpoint') + .addField('position', 'SFVec3f', '0 0 10') + .addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) + .addField('description', 'SFString', '') + .addField({name:'cameratype', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) + .addField({name:'type', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) + .addField({name:'motion', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'turntable', 'tilt'], animatable:false}) + .addField('motionspeed', 'SFFloat', 16) + .addField('active', 'SFBool', true) // incoming event + .addNode(); +xseen.nodes._defineNode ('NavigationMode', 'Controls', 'controls_Navigation') + .addField('speed', 'SFFloat', 1.) + .addField({name:'type', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'orbit', 'fly', 'examine', 'trackball'], animatable:false}) + .addNode(); +xseen.nodes._defineNode ('Camera', 'Controls', 'unk_Viewpoint') + .addField('position', 'SFVec3f', [0,0,10]) + .addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) + .addNode(); + +xseen.nodes._defineNode ('Inline', 'Networking', 'networking_Inline') + .addField('url', 'SFString', '') + .addNode(); + +xseen.nodes._defineNode ('scene', 'Core', 'core_Scene') + .addNode(); +xseen.nodes._defineNode ('canvas', 'Core', 'core_NOOP') + .addNode(); +xseen.nodes._defineNode ('WorldInfo', 'Core', 'core_WorldInfo') + .addNode(); +xseen.nodes._defineNode ('Appearance', 'Appearance', 'appearance_Appearance') + .addNode(); +xseen.nodes._defineNode ('ImageTexture', 'Appearance', 'appearance_ImageTexture') + .addField('url', 'SFString', '') + .addField('repeatS', 'SFBool', true) + .addField('repeatT', 'SFBool', true) + .addNode(); + +xseen.nodes._defineNode ('Shape', 'Shape', 'unk_Shape') + .addNode(); +xseen.nodes._defineNode('Background', 'Environmental', 'env_Background') + .addField('skyColor', 'SFColor', [0,0,0]) + .addField('srcFront', 'SFString', '') + .addField('srcBack', 'SFString', '') + .addField('srcTop', 'SFString', '') + .addField('srcBottom', 'SFString', '') + .addField('srcLeft', 'SFString', '') + .addField('srcRight', 'SFString', '') + .addField('backgroundIsCube', 'SFBool', 'true') + .addNode(); + +xseen.nodes._defineNode('TriangleSet', 'Geometry', 'geometry_TriangleSet') + .addField('ccw', 'SFBool', 'true') + .addField('colorPerVertex', 'SFBool', 'true') + .addField('solid', 'SFBool', 'true') + .addNode(); +xseen.nodes._defineNode('IndexedTriangleSet', 'Geometry', 'geometry_IndexedTriangleSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('index', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('Coordinate', 'Geometry', 'geometry_Coordinate') + .addField('point', 'MFVec3f', []) + .addNode(); +xseen.nodes._defineNode('Normal', 'Geometry', 'geometry_Normal') + .addField('vector', 'MFVec3f', []) + .addNode(); +xseen.nodes._defineNode('Color', 'Geometry', 'geometry_Color') + .addField('color', 'MFColor', []) + .addNode(); +xseen.nodes._defineNode('IndexedFaceSet', 'Geometry', 'geometry_IndexedFaceSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('coordIndex', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('IndexedQuadSet', 'Geometry', 'geometry_IndexedQuadSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addField('index', 'MFInt', '') + .addNode(); +xseen.nodes._defineNode('QuadSet', 'Geometry', 'geometry_QuadSet') + .addField('ccw', 'SFBool', true) + .addField('colorPerVertex', 'SFBool', true) + .addField('solid', 'SFBool', true) + .addNode(); + +//xseen.nodes._dumpTable(); +// File: nodes/_Definitions-xseen.js +/* + * XSeen JavaScript library + * + * (c)2017, Daly Realism, Los Angeles + * + * portions extracted from or inspired by + * X3DOM JavaScript Library + * http://www.x3dom.org + * + * (C)2009 Fraunhofer IGD, Darmstadt, Germany + * Dual licensed under the MIT and GPL + */ + + +/* + * These are intended to be development support routines. It is anticipated that in + * production systems the array dump would be loaded. As a result, it is necessary + * to have a routine that dumps out the Object (_dumpTable) so it can be captured and saved. A routine + * or documentation on how to load the Object would also be good. + * + */ + +xseen.nodes._defineNode('model', 'XSeen', 'x_Model') + .addField('src', 'SFString', '') + .addField('playonload', 'SFString', '') + .addField('duration', 'SFFloat', '-1') + .addNode(); + +xseen.nodes._defineNode('animate', 'XSeen', 'x_Animate') + .addField('field', 'SFString', '') + .addField('to', 'MFFloat', '') // Needs to be 'field' datatype. That is not known until node-parse. For now insist on numeric array + .addField('delay', 'SFTime', 0) + .addField('duration', 'SFTime', 0) + .addField('repeat', 'SFInt', 0) + .addField({name:'interpolator', datatype:'EnumerateString', defaultValue:'position', enumerated:['position', 'rotation', 'color'], animatable:false}) + .addField({name:'Easing', datatype:'EnumerateString', defaultValue:'', enumerated:['', 'in', 'out', 'inout'], animatable:false}) + .addField({name:'EasingType', datatype:'EnumerateString', defaultValue:'linear', enumerated:['linear', 'quadratic', 'sinusoidal', 'exponential', 'elastic', 'bounce'], animatable:false}) + .addField('start', 'SFBool', true) // incoming event, need to set timer trigger + .addField('stop', 'SFBool', true) // incoming event, need to set timer trigger + .addField('resetstart', 'SFBool', true) // incoming event, need to set timer trigger + .addField('pause', 'SFBool', true) // incoming event, need to set timer trigger + .addNode(); + +xseen.nodes._defineNode('route', 'XSeen', 'x_Route') + .addField('source', 'SFString', '') + .addField('event', 'SFString', '') + .addField('destination', 'SFString', '') + .addField('field', 'SFString', '') + .addField('handler', 'SFString', '') + .addNode(); + + +// Dump parse table +//xseen.nodes._dumpTable(); \ No newline at end of file diff --git a/Release/XSeen.min.js b/Release/XSeen.min.js new file mode 100644 index 0000000..d6670e1 --- /dev/null +++ b/Release/XSeen.min.js @@ -0,0 +1,9290 @@ +/* + * XSeen V0.4.6-rc1+25_0e9dbd1 + * Built Tue Jul 11 08:12:44 2017 + * + +Dual licensed under the MIT and GPL licenses. + +==[MIT]==================================================================== +Copyright (c) 2017, Daly Realism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +==[GPL]==================================================================== + +XSeen - Declarative 3D for HTML + +Copyright (C) 2017, Daly Realism + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +=== COPYRIGHT +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (C) 2017, Daly Realism for XSeen +Copyright, Fraunhofer for X3DOM +Copyright, Mozilla for A-Frame +Copyright, THREE and Khronos for various parts of THREE.js +Copyright (C) 2017, John Carlson for JSON->XML converter (JSONParser.js) + +=== +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + */ +THREE.ColladaLoader = function () { +var COLLADA = null; +var scene = null; +var visualScene; +var kinematicsModel; +var readyCallbackFunc = null; +var sources = {}; +var images = {}; +var animations = {}; +var controllers = {}; +var geometries = {}; +var materials = {}; +var effects = {}; +var cameras = {}; +var lights = {}; +var animData; +var kinematics; +var visualScenes; +var kinematicsModels; +var baseUrl; +var morphs; +var skins; +var flip_uv = true; +var preferredShading = THREE.SmoothShading; +var options = { +centerGeometry: false, +convertUpAxis: false, +subdivideFaces: true, +upAxis: 'Y', +defaultEnvMap: null +}; +var colladaUnit = 1.0; +var colladaUp = 'Y'; +var upConversion = null; +function load ( url, readyCallback, progressCallback, failCallback ) { +var length = 0; +if ( document.implementation && document.implementation.createDocument ) { +var request = new XMLHttpRequest(); +request.onreadystatechange = function() { +if ( request.readyState === 4 ) { +if ( request.status === 0 || request.status === 200 ) { +if ( request.response ) { +readyCallbackFunc = readyCallback; +parse( request.response, undefined, url ); +} else { +if ( failCallback ) { +failCallback( { type: 'error', url: url } ); +} else { +console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); +} +} +}else{ +if( failCallback ){ +failCallback( { type: 'error', url: url } ); +}else{ +console.error( 'ColladaLoader: Couldn\'t load "' + url + '" (' + request.status + ')' ); +} +} +} else if ( request.readyState === 3 ) { +if ( progressCallback ) { +if ( length === 0 ) { +length = request.getResponseHeader( "Content-Length" ); +} +progressCallback( { total: length, loaded: request.responseText.length } ); +} +} +}; +request.open( "GET", url, true ); +request.send( null ); +} else { +alert( "Don't know how to parse XML!" ); +} +} +function parse( text, callBack, url ) { +COLLADA = new DOMParser().parseFromString( text, 'text/xml' ); +callBack = callBack || readyCallbackFunc; +if ( url !== undefined ) { +var parts = url.split( '/' ); +parts.pop(); +baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; +} +parseAsset(); +setUpConversion(); +images = parseLib( "library_images image", _Image, "image" ); +materials = parseLib( "library_materials material", Material, "material" ); +effects = parseLib( "library_effects effect", Effect, "effect" ); +geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); +cameras = parseLib( "library_cameras camera", Camera, "camera" ); +lights = parseLib( "library_lights light", Light, "light" ); +controllers = parseLib( "library_controllers controller", Controller, "controller" ); +animations = parseLib( "library_animations animation", Animation, "animation" ); +visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); +kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); +morphs = []; +skins = []; +visualScene = parseScene(); +scene = new THREE.Group(); +for ( var i = 0; i < visualScene.nodes.length; i ++ ) { +scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); +} +scene.scale.multiplyScalar( colladaUnit ); +createAnimations(); +kinematicsModel = parseKinematicsModel(); +createKinematics(); +var result = { +scene: scene, +morphs: morphs, +skins: skins, +animations: animData, +kinematics: kinematics, +dae: { +images: images, +materials: materials, +cameras: cameras, +lights: lights, +effects: effects, +geometries: geometries, +controllers: controllers, +animations: animations, +visualScenes: visualScenes, +visualScene: visualScene, +scene: visualScene, +kinematicsModels: kinematicsModels, +kinematicsModel: kinematicsModel +} +}; +if ( callBack ) { +callBack( result ); +} +return result; +} +function setPreferredShading ( shading ) { +preferredShading = shading; +} +function parseAsset () { +var elements = COLLADA.querySelectorAll('asset'); +var element = elements[0]; +if ( element && element.childNodes ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'unit': +var meter = child.getAttribute( 'meter' ); +if ( meter ) { +colladaUnit = parseFloat( meter ); +} +break; +case 'up_axis': +colladaUp = child.textContent.charAt(0); +break; +} +} +} +} +function parseLib ( q, classSpec, prefix ) { +var elements = COLLADA.querySelectorAll(q); +var lib = {}; +var i = 0; +var elementsLength = elements.length; +for ( var j = 0; j < elementsLength; j ++ ) { +var element = elements[j]; +var daeElement = ( new classSpec() ).parse( element ); +if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); +lib[ daeElement.id ] = daeElement; +} +return lib; +} +function parseScene() { +var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; +if ( sceneElement ) { +var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); +return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; +} else { +return null; +} +} +function parseKinematicsModel() { +var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; +if ( kinematicsModelElement ) { +var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); +return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; +} else { +return null; +} +} +function createAnimations() { +animData = []; +recurseHierarchy( scene ); +} +function recurseHierarchy( node ) { +var n = visualScene.getChildById( node.colladaId, true ), +newData = null; +if ( n && n.keys ) { +newData = { +fps: 60, +hierarchy: [ { +node: n, +keys: n.keys, +sids: n.sids +} ], +node: node, +name: 'animation_' + node.name, +length: 0 +}; +animData.push(newData); +for ( var i = 0, il = n.keys.length; i < il; i ++ ) { +newData.length = Math.max( newData.length, n.keys[i].time ); +} +} else { +newData = { +hierarchy: [ { +keys: [], +sids: [] +} ] +} +} +for ( var i = 0, il = node.children.length; i < il; i ++ ) { +var d = recurseHierarchy( node.children[i] ); +for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { +newData.hierarchy.push( { +keys: [], +sids: [] +} ); +} +} +return newData; +} +function calcAnimationBounds () { +var start = 1000000; +var end = -start; +var frames = 0; +var ID; +for ( var id in animations ) { +var animation = animations[ id ]; +ID = ID || animation.id; +for ( var i = 0; i < animation.sampler.length; i ++ ) { +var sampler = animation.sampler[ i ]; +sampler.create(); +start = Math.min( start, sampler.startTime ); +end = Math.max( end, sampler.endTime ); +frames = Math.max( frames, sampler.input.length ); +} +} +return { start:start, end:end, frames:frames,ID:ID }; +} +function createMorph ( geometry, ctrl ) { +var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; +if ( !morphCtrl || !morphCtrl.morph ) { +console.log("could not find morph controller!"); +return; +} +var morph = morphCtrl.morph; +for ( var i = 0; i < morph.targets.length; i ++ ) { +var target_id = morph.targets[ i ]; +var daeGeometry = geometries[ target_id ]; +if ( !daeGeometry.mesh || +!daeGeometry.mesh.primitives || +!daeGeometry.mesh.primitives.length ) { +continue; +} +var target = daeGeometry.mesh.primitives[ 0 ].geometry; +if ( target.vertices.length === geometry.vertices.length ) { +geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); +} +} +geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); +} +function createSkin ( geometry, ctrl, applyBindShape ) { +var skinCtrl = controllers[ ctrl.url ]; +if ( !skinCtrl || !skinCtrl.skin ) { +console.log( "could not find skin controller!" ); +return; +} +if ( !ctrl.skeleton || !ctrl.skeleton.length ) { +console.log( "could not find the skeleton for the skin!" ); +return; +} +var skin = skinCtrl.skin; +var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); +var hierarchy = []; +applyBindShape = applyBindShape !== undefined ? applyBindShape : true; +var bones = []; +geometry.skinWeights = []; +geometry.skinIndices = []; +if ( applyBindShape ) { +for ( var i = 0; i < geometry.vertices.length; i ++ ) { +geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); +} +} +} +function setupSkeleton ( node, bones, frame, parent ) { +node.world = node.world || new THREE.Matrix4(); +node.localworld = node.localworld || new THREE.Matrix4(); +node.world.copy( node.matrix ); +node.localworld.copy( node.matrix ); +if ( node.channels && node.channels.length ) { +var channel = node.channels[ 0 ]; +var m = channel.sampler.output[ frame ]; +if ( m instanceof THREE.Matrix4 ) { +node.world.copy( m ); +node.localworld.copy(m); +if (frame === 0) +node.matrix.copy(m); +} +} +if ( parent ) { +node.world.multiplyMatrices( parent, node.world ); +} +bones.push( node ); +for ( var i = 0; i < node.nodes.length; i ++ ) { +setupSkeleton( node.nodes[ i ], bones, frame, node.world ); +} +} +function setupSkinningMatrices ( bones, skin ) { +for ( var i = 0; i < bones.length; i ++ ) { +var bone = bones[ i ]; +var found = -1; +if ( bone.type != 'JOINT' ) continue; +for ( var j = 0; j < skin.joints.length; j ++ ) { +if ( bone.sid === skin.joints[ j ] ) { +found = j; +break; +} +} +if ( found >= 0 ) { +var inv = skin.invBindMatrices[ found ]; +bone.invBindMatrix = inv; +bone.skinningMatrix = new THREE.Matrix4(); +bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) +bone.animatrix = new THREE.Matrix4(); +bone.animatrix.copy(bone.localworld); +bone.weights = []; +for ( var j = 0; j < skin.weights.length; j ++ ) { +for (var k = 0; k < skin.weights[ j ].length; k ++ ) { +var w = skin.weights[ j ][ k ]; +if ( w.joint === found ) { +bone.weights.push( w ); +} +} +} +} else { +console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); +bone.skinningMatrix = new THREE.Matrix4(); +bone.weights = []; +} +} +} +function flattenSkeleton(skeleton) { +var list = []; +var walk = function(parentid, node, list) { +var bone = {}; +bone.name = node.sid; +bone.parent = parentid; +bone.matrix = node.matrix; +var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; +bone.matrix.decompose(data[0], data[1], data[2]); +bone.pos = [ data[0].x,data[0].y,data[0].z ]; +bone.scl = [ data[2].x,data[2].y,data[2].z ]; +bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; +list.push(bone); +for (var i in node.nodes) { +walk(node.sid, node.nodes[i], list); +} +}; +walk(-1, skeleton, list); +return list; +} +function skinToBindPose(geometry,skeleton,skinController) { +var bones = []; +setupSkeleton( skeleton, bones, -1 ); +setupSkinningMatrices( bones, skinController.skin ); +var v = new THREE.Vector3(); +var skinned = []; +for (var i = 0; i < geometry.vertices.length; i ++) { +skinned.push(new THREE.Vector3()); +} +for ( i = 0; i < bones.length; i ++ ) { +if ( bones[ i ].type != 'JOINT' ) continue; +for ( var j = 0; j < bones[ i ].weights.length; j ++ ) { +var w = bones[ i ].weights[ j ]; +var vidx = w.index; +var weight = w.weight; +var o = geometry.vertices[vidx]; +var s = skinned[vidx]; +v.x = o.x; +v.y = o.y; +v.z = o.z; +v.applyMatrix4( bones[i].skinningMatrix ); +s.x += (v.x * weight); +s.y += (v.y * weight); +s.z += (v.z * weight); +} +} +for (var i = 0; i < geometry.vertices.length; i ++) { +geometry.vertices[i] = skinned[i]; +} +} +function applySkin ( geometry, instanceCtrl, frame ) { +var skinController = controllers[ instanceCtrl.url ]; +frame = frame !== undefined ? frame : 40; +if ( !skinController || !skinController.skin ) { +console.log( 'ColladaLoader: Could not find skin controller.' ); +return; +} +if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { +console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); +return; +} +var animationBounds = calcAnimationBounds(); +var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); +var bonelist = flattenSkeleton(skeleton); +var joints = skinController.skin.joints; +var sortedbones = []; +for (var i = 0; i < joints.length; i ++) { +for (var j = 0; j < bonelist.length; j ++) { +if (bonelist[j].name === joints[i]) { +sortedbones[i] = bonelist[j]; +} +} +} +for (var i = 0; i < sortedbones.length; i ++) { +for (var j = 0; j < sortedbones.length; j ++) { +if (sortedbones[i].parent === sortedbones[j].name) { +sortedbones[i].parent = j; +} +} +} +var i, j, w, vidx, weight; +var v = new THREE.Vector3(), o, s; +for ( i = 0; i < geometry.vertices.length; i ++ ) { +geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); +} +var skinIndices = []; +var skinWeights = []; +var weights = skinController.skin.weights; +for ( var i =0; i < weights.length; i ++ ) { +var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); +var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); +skinIndices.push(indicies); +skinWeights.push(weight); +} +geometry.skinIndices = skinIndices; +geometry.skinWeights = skinWeights; +geometry.bones = sortedbones; +var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; +for (var j = 0; j < sortedbones.length; j ++) { +animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); +} +console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); +skinToBindPose(geometry, skeleton, skinController); +for ( frame = 0; frame < animationBounds.frames; frame ++ ) { +var bones = []; +var skinned = []; +setupSkeleton( skeleton, bones, frame ); +setupSkinningMatrices( bones, skinController.skin ); +for (var i = 0; i < bones.length; i ++) { +for (var j = 0; j < animationdata.hierarchy.length; j ++) { +if (animationdata.hierarchy[j].name === bones[i].sid) { +var key = {}; +key.time = (frame / 30); +key.matrix = bones[i].animatrix; +if (frame === 0) +bones[i].matrix = key.matrix; +var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; +key.matrix.decompose(data[0], data[1], data[2]); +key.pos = [ data[0].x,data[0].y,data[0].z ]; +key.scl = [ data[2].x,data[2].y,data[2].z ]; +key.rot = data[1]; +animationdata.hierarchy[j].keys.push(key); +} +} +} +geometry.animation = animationdata; +} +} +function createKinematics() { +if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { +kinematics = undefined; +return; +} +var jointMap = {}; +var _addToMap = function( jointIndex, parentVisualElement ) { +var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); +var colladaNode = visualScene.getChildById( parentVisualElementId, true ); +var joint = kinematicsModel.joints[ jointIndex ]; +scene.traverse(function( node ) { +if ( node.colladaId == parentVisualElementId ) { +jointMap[ jointIndex ] = { +node: node, +transforms: colladaNode.transforms, +joint: joint, +position: joint.zeroPosition +}; +} +}); +}; +kinematics = { +joints: kinematicsModel && kinematicsModel.joints, +getJointValue: function( jointIndex ) { +var jointData = jointMap[ jointIndex ]; +if ( jointData ) { +return jointData.position; +} else { +console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); +} +}, +setJointValue: function( jointIndex, value ) { +var jointData = jointMap[ jointIndex ]; +if ( jointData ) { +var joint = jointData.joint; +if ( value > joint.limits.max || value < joint.limits.min ) { +console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); +} else if ( joint.static ) { +console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); +} else { +var threejsNode = jointData.node; +var axis = joint.axis; +var transforms = jointData.transforms; +var matrix = new THREE.Matrix4(); +var m1 = new THREE.Matrix4(); +for (i = 0; i < transforms.length; i ++ ) { +var transform = transforms[ i ]; +if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { +switch ( joint.type ) { +case 'revolute': +matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); +break; +case 'prismatic': +matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); +break; +default: +console.warn( 'setJointValue: unknown joint type: ' + joint.type ); +break; +} +} else { +switch ( transform.type ) { +case 'matrix': +matrix.multiply( transform.obj ); +break; +case 'translate': +matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); +break; +case 'rotate': +matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); +break; +} +} +} +var elementsFloat32Arr = matrix.elements; +var elements = Array.prototype.slice.call( elementsFloat32Arr ); +var elementsRowMajor = [ +elements[ 0 ], +elements[ 4 ], +elements[ 8 ], +elements[ 12 ], +elements[ 1 ], +elements[ 5 ], +elements[ 9 ], +elements[ 13 ], +elements[ 2 ], +elements[ 6 ], +elements[ 10 ], +elements[ 14 ], +elements[ 3 ], +elements[ 7 ], +elements[ 11 ], +elements[ 15 ] +]; +threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); +threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); +jointMap[ jointIndex ].position = value; +} +} else { +console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); +} +} +}; +var element = COLLADA.querySelector('scene instance_kinematics_scene'); +if ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bind_joint_axis': +var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); +var axis = child.querySelector('axis param').textContent; +var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); +var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); +if ( visualTargetElement ) { +var parentVisualElement = visualTargetElement.parentElement; +_addToMap(jointIndex, parentVisualElement); +} +break; +default: +break; +} +} +} +} +function createSceneGraph ( node, parent ) { +var obj = new THREE.Object3D(); +var skinned = false; +var skinController; +var morphController; +var i, j; +for ( i = 0; i < node.controllers.length; i ++ ) { +var controller = controllers[ node.controllers[ i ].url ]; +switch ( controller.type ) { +case 'skin': +if ( geometries[ controller.skin.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = controller.skin.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +skinned = true; +skinController = node.controllers[ i ]; +} else if ( controllers[ controller.skin.source ] ) { +var second = controllers[ controller.skin.source ]; +morphController = second; +if ( second.morph && geometries[ second.morph.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = second.morph.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +} +} +break; +case 'morph': +if ( geometries[ controller.morph.source ] ) { +var inst_geom = new InstanceGeometry(); +inst_geom.url = controller.morph.source; +inst_geom.instance_material = node.controllers[ i ].instance_material; +node.geometries.push( inst_geom ); +morphController = node.controllers[ i ]; +} +console.log( 'ColladaLoader: Morph-controller partially supported.' ); +default: +break; +} +} +var double_sided_materials = {}; +for ( i = 0; i < node.geometries.length; i ++ ) { +var instance_geometry = node.geometries[i]; +var instance_materials = instance_geometry.instance_material; +var geometry = geometries[ instance_geometry.url ]; +var used_materials = {}; +var used_materials_array = []; +var num_materials = 0; +var first_material; +if ( geometry ) { +if ( !geometry.mesh || !geometry.mesh.primitives ) +continue; +if ( obj.name.length === 0 ) { +obj.name = geometry.id; +} +if ( instance_materials ) { +for ( j = 0; j < instance_materials.length; j ++ ) { +var instance_material = instance_materials[ j ]; +var mat = materials[ instance_material.target ]; +var effect_id = mat.instance_effect.url; +var shader = effects[ effect_id ].shader; +var material3js = shader.material; +if ( geometry.doubleSided ) { +if ( !( instance_material.symbol in double_sided_materials ) ) { +var _copied_material = material3js.clone(); +_copied_material.side = THREE.DoubleSide; +double_sided_materials[ instance_material.symbol ] = _copied_material; +} +material3js = double_sided_materials[ instance_material.symbol ]; +} +material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; +used_materials[ instance_material.symbol ] = num_materials; +used_materials_array.push( material3js ); +first_material = material3js; +first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; +num_materials ++; +} +} +var mesh; +var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); +var geom = geometry.mesh.geometry3js; +if ( num_materials > 1 ) { +material = new THREE.MultiMaterial( used_materials_array ); +for ( j = 0; j < geom.faces.length; j ++ ) { +var face = geom.faces[ j ]; +face.materialIndex = used_materials[ face.daeMaterial ] +} +} +if ( skinController !== undefined ) { +applySkin( geom, skinController ); +if ( geom.morphTargets.length > 0 ) { +material.morphTargets = true; +material.skinning = false; +} else { +material.morphTargets = false; +material.skinning = true; +} +mesh = new THREE.SkinnedMesh( geom, material, false ); +mesh.name = 'skin_' + skins.length; +skins.push( mesh ); +} else if ( morphController !== undefined ) { +createMorph( geom, morphController ); +material.morphTargets = true; +mesh = new THREE.Mesh( geom, material ); +mesh.name = 'morph_' + morphs.length; +morphs.push( mesh ); +} else { +if ( geom.isLineStrip === true ) { +mesh = new THREE.Line( geom ); +} else { +mesh = new THREE.Mesh( geom, material ); +} +} +obj.add(mesh); +} +} +for ( i = 0; i < node.cameras.length; i ++ ) { +var instance_camera = node.cameras[i]; +var cparams = cameras[instance_camera.url]; +var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), +parseFloat(cparams.znear), parseFloat(cparams.zfar)); +obj.add(cam); +} +for ( i = 0; i < node.lights.length; i ++ ) { +var light = null; +var instance_light = node.lights[i]; +var lparams = lights[instance_light.url]; +if ( lparams && lparams.technique ) { +var color = lparams.color.getHex(); +var intensity = lparams.intensity; +var distance = lparams.distance; +var angle = lparams.falloff_angle; +switch ( lparams.technique ) { +case 'directional': +light = new THREE.DirectionalLight( color, intensity, distance ); +light.position.set(0, 0, 1); +break; +case 'point': +light = new THREE.PointLight( color, intensity, distance ); +break; +case 'spot': +light = new THREE.SpotLight( color, intensity, distance, angle ); +light.position.set(0, 0, 1); +break; +case 'ambient': +light = new THREE.AmbientLight( color ); +break; +} +} +if (light) { +obj.add(light); +} +} +obj.name = node.name || node.id || ""; +obj.colladaId = node.id || ""; +obj.layer = node.layer || ""; +obj.matrix = node.matrix; +obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); +if ( options.centerGeometry && obj.geometry ) { +var delta = obj.geometry.center(); +delta.multiply( obj.scale ); +delta.applyQuaternion( obj.quaternion ); +obj.position.sub( delta ); +} +for ( i = 0; i < node.nodes.length; i ++ ) { +obj.add( createSceneGraph( node.nodes[i], node ) ); +} +return obj; +} +function getJointId( skin, id ) { +for ( var i = 0; i < skin.joints.length; i ++ ) { +if ( skin.joints[ i ] === id ) { +return i; +} +} +} +function getLibraryNode( id ) { +var nodes = COLLADA.querySelectorAll('library_nodes node'); +for ( var i = 0; i < nodes.length; i++ ) { +var attObj = nodes[i].attributes.getNamedItem('id'); +if ( attObj && attObj.value === id ) { +return nodes[i]; +} +} +return undefined; +} +function getChannelsForNode ( node ) { +var channels = []; +var startTime = 1000000; +var endTime = -1000000; +for ( var id in animations ) { +var animation = animations[id]; +for ( var i = 0; i < animation.channel.length; i ++ ) { +var channel = animation.channel[i]; +var sampler = animation.sampler[i]; +var id = channel.target.split('/')[0]; +if ( id == node.id ) { +sampler.create(); +channel.sampler = sampler; +startTime = Math.min(startTime, sampler.startTime); +endTime = Math.max(endTime, sampler.endTime); +channels.push(channel); +} +} +} +if ( channels.length ) { +node.startTime = startTime; +node.endTime = endTime; +} +return channels; +} +function calcFrameDuration( node ) { +var minT = 10000000; +for ( var i = 0; i < node.channels.length; i ++ ) { +var sampler = node.channels[i].sampler; +for ( var j = 0; j < sampler.input.length - 1; j ++ ) { +var t0 = sampler.input[ j ]; +var t1 = sampler.input[ j + 1 ]; +minT = Math.min( minT, t1 - t0 ); +} +} +return minT; +} +function calcMatrixAt( node, t ) { +var animated = {}; +var i, j; +for ( i = 0; i < node.channels.length; i ++ ) { +var channel = node.channels[ i ]; +animated[ channel.sid ] = channel; +} +var matrix = new THREE.Matrix4(); +for ( i = 0; i < node.transforms.length; i ++ ) { +var transform = node.transforms[ i ]; +var channel = animated[ transform.sid ]; +if ( channel !== undefined ) { +var sampler = channel.sampler; +var value; +for ( j = 0; j < sampler.input.length - 1; j ++ ) { +if ( sampler.input[ j + 1 ] > t ) { +value = sampler.output[ j ]; +break; +} +} +if ( value !== undefined ) { +if ( value instanceof THREE.Matrix4 ) { +matrix.multiplyMatrices( matrix, value ); +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} else { +matrix.multiplyMatrices( matrix, transform.matrix ); +} +} +return matrix; +} +function bakeAnimations ( node ) { +if ( node.channels && node.channels.length ) { +var keys = [], +sids = []; +for ( var i = 0, il = node.channels.length; i < il; i ++ ) { +var channel = node.channels[i], +fullSid = channel.fullSid, +sampler = channel.sampler, +input = sampler.input, +transform = node.getTransformBySid( channel.sid ), +member; +if ( channel.arrIndices ) { +member = []; +for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { +member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); +} +} else { +member = getConvertedMember( channel.member ); +} +if ( transform ) { +if ( sids.indexOf( fullSid ) === -1 ) { +sids.push( fullSid ); +} +for ( var j = 0, jl = input.length; j < jl; j ++ ) { +var time = input[j], +data = sampler.getData( transform.type, j, member ), +key = findKey( keys, time ); +if ( !key ) { +key = new Key( time ); +var timeNdx = findTimeNdx( keys, time ); +keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); +} +key.addTarget( fullSid, transform, member, data ); +} +} else { +console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); +} +} +for ( var i = 0; i < sids.length; i ++ ) { +var sid = sids[ i ]; +for ( var j = 0; j < keys.length; j ++ ) { +var key = keys[ j ]; +if ( !key.hasTarget( sid ) ) { +interpolateKeys( keys, key, j, sid ); +} +} +} +node.keys = keys; +node.sids = sids; +} +} +function findKey ( keys, time) { +var retVal = null; +for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { +var key = keys[i]; +if ( key.time === time ) { +retVal = key; +} else if ( key.time > time ) { +break; +} +} +return retVal; +} +function findTimeNdx ( keys, time) { +var ndx = -1; +for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { +var key = keys[i]; +if ( key.time >= time ) { +ndx = i; +} +} +return ndx; +} +function interpolateKeys ( keys, key, ndx, fullSid ) { +var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), +nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); +if ( prevKey && nextKey ) { +var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), +prevTarget = prevKey.getTarget( fullSid ), +nextData = nextKey.getTarget( fullSid ).data, +prevData = prevTarget.data, +data; +if ( prevTarget.type === 'matrix' ) { +data = prevData; +} else if ( prevData.length ) { +data = []; +for ( var i = 0; i < prevData.length; ++ i ) { +data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; +} +} else { +data = prevData + ( nextData - prevData ) * scale; +} +key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); +} +} +function getNextKeyWith( keys, fullSid, ndx ) { +for ( ; ndx < keys.length; ndx ++ ) { +var key = keys[ ndx ]; +if ( key.hasTarget( fullSid ) ) { +return key; +} +} +return null; +} +function getPrevKeyWith( keys, fullSid, ndx ) { +ndx = ndx >= 0 ? ndx : ndx + keys.length; +for ( ; ndx >= 0; ndx -- ) { +var key = keys[ ndx ]; +if ( key.hasTarget( fullSid ) ) { +return key; +} +} +return null; +} +function _Image() { +this.id = ""; +this.init_from = ""; +} +_Image.prototype.parse = function(element) { +this.id = element.getAttribute('id'); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeName === 'init_from' ) { +this.init_from = child.textContent; +} +} +return this; +}; +function Controller() { +this.id = ""; +this.name = ""; +this.type = ""; +this.skin = null; +this.morph = null; +} +Controller.prototype.parse = function( element ) { +this.id = element.getAttribute('id'); +this.name = element.getAttribute('name'); +this.type = "none"; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'skin': +this.skin = (new Skin()).parse(child); +this.type = child.nodeName; +break; +case 'morph': +this.morph = (new Morph()).parse(child); +this.type = child.nodeName; +break; +default: +break; +} +} +return this; +}; +function Morph() { +this.method = null; +this.source = null; +this.targets = null; +this.weights = null; +} +Morph.prototype.parse = function( element ) { +var sources = {}; +var inputs = []; +var i; +this.method = element.getAttribute( 'method' ); +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +for ( i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'source': +var source = ( new Source() ).parse( child ); +sources[ source.id ] = source; +break; +case 'targets': +inputs = this.parseInputs( child ); +break; +default: +console.log( child.nodeName ); +break; +} +} +for ( i = 0; i < inputs.length; i ++ ) { +var input = inputs[ i ]; +var source = sources[ input.source ]; +switch ( input.semantic ) { +case 'MORPH_TARGET': +this.targets = source.read(); +break; +case 'MORPH_WEIGHT': +this.weights = source.read(); +break; +default: +break; +} +} +return this; +}; +Morph.prototype.parseInputs = function(element) { +var inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1) continue; +switch ( child.nodeName ) { +case 'input': +inputs.push( (new Input()).parse(child) ); +break; +default: +break; +} +} +return inputs; +}; +function Skin() { +this.source = ""; +this.bindShapeMatrix = null; +this.invBindMatrices = []; +this.joints = []; +this.weights = []; +} +Skin.prototype.parse = function( element ) { +var sources = {}; +var joints, weights; +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +this.invBindMatrices = []; +this.joints = []; +this.weights = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bind_shape_matrix': +var f = _floats(child.textContent); +this.bindShapeMatrix = getConvertedMat4( f ); +break; +case 'source': +var src = new Source().parse(child); +sources[ src.id ] = src; +break; +case 'joints': +joints = child; +break; +case 'vertex_weights': +weights = child; +break; +default: +console.log( child.nodeName ); +break; +} +} +this.parseJoints( joints, sources ); +this.parseWeights( weights, sources ); +return this; +}; +Skin.prototype.parseJoints = function ( element, sources ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +var input = ( new Input() ).parse( child ); +var source = sources[ input.source ]; +if ( input.semantic === 'JOINT' ) { +this.joints = source.read(); +} else if ( input.semantic === 'INV_BIND_MATRIX' ) { +this.invBindMatrices = source.read(); +} +break; +default: +break; +} +} +}; +Skin.prototype.parseWeights = function ( element, sources ) { +var v, vcount, inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +inputs.push( ( new Input() ).parse( child ) ); +break; +case 'v': +v = _ints( child.textContent ); +break; +case 'vcount': +vcount = _ints( child.textContent ); +break; +default: +break; +} +} +var index = 0; +for ( var i = 0; i < vcount.length; i ++ ) { +var numBones = vcount[i]; +var vertex_weights = []; +for ( var j = 0; j < numBones; j ++ ) { +var influence = {}; +for ( var k = 0; k < inputs.length; k ++ ) { +var input = inputs[ k ]; +var value = v[ index + input.offset ]; +switch ( input.semantic ) { +case 'JOINT': +influence.joint = value;//this.joints[value]; +break; +case 'WEIGHT': +influence.weight = sources[ input.source ].data[ value ]; +break; +default: +break; +} +} +vertex_weights.push( influence ); +index += inputs.length; +} +for ( var j = 0; j < vertex_weights.length; j ++ ) { +vertex_weights[ j ].index = i; +} +this.weights.push( vertex_weights ); +} +}; +function VisualScene () { +this.id = ""; +this.name = ""; +this.nodes = []; +this.scene = new THREE.Group(); +} +VisualScene.prototype.getChildById = function( id, recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var node = this.nodes[ i ].getChildById( id, recursive ); +if ( node ) { +return node; +} +} +return null; +}; +VisualScene.prototype.getChildBySid = function( sid, recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var node = this.nodes[ i ].getChildBySid( sid, recursive ); +if ( node ) { +return node; +} +} +return null; +}; +VisualScene.prototype.parse = function( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +this.nodes = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'node': +this.nodes.push( ( new Node() ).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +function Node() { +this.id = ""; +this.name = ""; +this.sid = ""; +this.nodes = []; +this.controllers = []; +this.transforms = []; +this.geometries = []; +this.channels = []; +this.matrix = new THREE.Matrix4(); +} +Node.prototype.getChannelForTransform = function( transformSid ) { +for ( var i = 0; i < this.channels.length; i ++ ) { +var channel = this.channels[i]; +var parts = channel.target.split('/'); +var id = parts.shift(); +var sid = parts.shift(); +var dotSyntax = (sid.indexOf(".") >= 0); +var arrSyntax = (sid.indexOf("(") >= 0); +var arrIndices; +var member; +if ( dotSyntax ) { +parts = sid.split("."); +sid = parts.shift(); +member = parts.shift(); +} else if ( arrSyntax ) { +arrIndices = sid.split("("); +sid = arrIndices.shift(); +for ( var j = 0; j < arrIndices.length; j ++ ) { +arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); +} +} +if ( sid === transformSid ) { +channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; +return channel; +} +} +return null; +}; +Node.prototype.getChildById = function ( id, recursive ) { +if ( this.id === id ) { +return this; +} +if ( recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var n = this.nodes[ i ].getChildById( id, recursive ); +if ( n ) { +return n; +} +} +} +return null; +}; +Node.prototype.getChildBySid = function ( sid, recursive ) { +if ( this.sid === sid ) { +return this; +} +if ( recursive ) { +for ( var i = 0; i < this.nodes.length; i ++ ) { +var n = this.nodes[ i ].getChildBySid( sid, recursive ); +if ( n ) { +return n; +} +} +} +return null; +}; +Node.prototype.getTransformBySid = function ( sid ) { +for ( var i = 0; i < this.transforms.length; i ++ ) { +if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; +} +return null; +}; +Node.prototype.parse = function( element ) { +var url; +this.id = element.getAttribute('id'); +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.type = element.getAttribute('type'); +this.layer = element.getAttribute('layer'); +this.type = this.type === 'JOINT' ? this.type : 'NODE'; +this.nodes = []; +this.transforms = []; +this.geometries = []; +this.cameras = []; +this.lights = []; +this.controllers = []; +this.matrix = new THREE.Matrix4(); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'node': +this.nodes.push( ( new Node() ).parse( child ) ); +break; +case 'instance_camera': +this.cameras.push( ( new InstanceCamera() ).parse( child ) ); +break; +case 'instance_controller': +this.controllers.push( ( new InstanceController() ).parse( child ) ); +break; +case 'instance_geometry': +this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); +break; +case 'instance_light': +this.lights.push( ( new InstanceLight() ).parse( child ) ); +break; +case 'instance_node': +url = child.getAttribute( 'url' ).replace( /^#/, '' ); +var iNode = getLibraryNode( url ); +if ( iNode ) { +this.nodes.push( ( new Node() ).parse( iNode )) ; +} +break; +case 'rotate': +case 'translate': +case 'scale': +case 'matrix': +case 'lookat': +case 'skew': +this.transforms.push( ( new Transform() ).parse( child ) ); +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +this.channels = getChannelsForNode( this ); +bakeAnimations( this ); +this.updateMatrix(); +return this; +}; +Node.prototype.updateMatrix = function () { +this.matrix.identity(); +for ( var i = 0; i < this.transforms.length; i ++ ) { +this.transforms[ i ].apply( this.matrix ); +} +}; +function Transform () { +this.sid = ""; +this.type = ""; +this.data = []; +this.obj = null; +} +Transform.prototype.parse = function ( element ) { +this.sid = element.getAttribute( 'sid' ); +this.type = element.nodeName; +this.data = _floats( element.textContent ); +this.convert(); +return this; +}; +Transform.prototype.convert = function () { +switch ( this.type ) { +case 'matrix': +this.obj = getConvertedMat4( this.data ); +break; +case 'rotate': +this.angle = THREE.Math.degToRad( this.data[3] ); +case 'translate': +fixCoords( this.data, -1 ); +this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); +break; +case 'scale': +fixCoords( this.data, 1 ); +this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); +break; +default: +console.log( 'Can not convert Transform of type ' + this.type ); +break; +} +}; +Transform.prototype.apply = function () { +var m1 = new THREE.Matrix4(); +return function ( matrix ) { +switch ( this.type ) { +case 'matrix': +matrix.multiply( this.obj ); +break; +case 'translate': +matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); +break; +case 'rotate': +matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); +break; +case 'scale': +matrix.scale( this.obj ); +break; +} +}; +}(); +Transform.prototype.update = function ( data, member ) { +var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; +switch ( this.type ) { +case 'matrix': +if ( ! member ) { +this.obj.copy( data ); +} else if ( member.length === 1 ) { +switch ( member[ 0 ] ) { +case 0: +this.obj.n11 = data[ 0 ]; +this.obj.n21 = data[ 1 ]; +this.obj.n31 = data[ 2 ]; +this.obj.n41 = data[ 3 ]; +break; +case 1: +this.obj.n12 = data[ 0 ]; +this.obj.n22 = data[ 1 ]; +this.obj.n32 = data[ 2 ]; +this.obj.n42 = data[ 3 ]; +break; +case 2: +this.obj.n13 = data[ 0 ]; +this.obj.n23 = data[ 1 ]; +this.obj.n33 = data[ 2 ]; +this.obj.n43 = data[ 3 ]; +break; +case 3: +this.obj.n14 = data[ 0 ]; +this.obj.n24 = data[ 1 ]; +this.obj.n34 = data[ 2 ]; +this.obj.n44 = data[ 3 ]; +break; +} +} else if ( member.length === 2 ) { +var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); +this.obj[ propName ] = data; +} else { +console.log('Incorrect addressing of matrix in transform.'); +} +break; +case 'translate': +case 'scale': +if ( Object.prototype.toString.call( member ) === '[object Array]' ) { +member = members[ member[ 0 ] ]; +} +switch ( member ) { +case 'X': +this.obj.x = data; +break; +case 'Y': +this.obj.y = data; +break; +case 'Z': +this.obj.z = data; +break; +default: +this.obj.x = data[ 0 ]; +this.obj.y = data[ 1 ]; +this.obj.z = data[ 2 ]; +break; +} +break; +case 'rotate': +if ( Object.prototype.toString.call( member ) === '[object Array]' ) { +member = members[ member[ 0 ] ]; +} +switch ( member ) { +case 'X': +this.obj.x = data; +break; +case 'Y': +this.obj.y = data; +break; +case 'Z': +this.obj.z = data; +break; +case 'ANGLE': +this.angle = THREE.Math.degToRad( data ); +break; +default: +this.obj.x = data[ 0 ]; +this.obj.y = data[ 1 ]; +this.obj.z = data[ 2 ]; +this.angle = THREE.Math.degToRad( data[ 3 ] ); +break; +} +break; +} +}; +function InstanceController() { +this.url = ""; +this.skeleton = []; +this.instance_material = []; +} +InstanceController.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +this.skeleton = []; +this.instance_material = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType !== 1 ) continue; +switch ( child.nodeName ) { +case 'skeleton': +this.skeleton.push( child.textContent.replace(/^#/, '') ); +break; +case 'bind_material': +var instances = child.querySelectorAll('instance_material'); +for ( var j = 0; j < instances.length; j ++ ) { +var instance = instances[j]; +this.instance_material.push( (new InstanceMaterial()).parse(instance) ); +} +break; +case 'extra': +break; +default: +break; +} +} +return this; +}; +function InstanceMaterial () { +this.symbol = ""; +this.target = ""; +} +InstanceMaterial.prototype.parse = function ( element ) { +this.symbol = element.getAttribute('symbol'); +this.target = element.getAttribute('target').replace(/^#/, ''); +return this; +}; +function InstanceGeometry() { +this.url = ""; +this.instance_material = []; +} +InstanceGeometry.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +this.instance_material = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +if ( child.nodeName === 'bind_material' ) { +var instances = child.querySelectorAll('instance_material'); +for ( var j = 0; j < instances.length; j ++ ) { +var instance = instances[j]; +this.instance_material.push( (new InstanceMaterial()).parse(instance) ); +} +break; +} +} +return this; +}; +function Geometry() { +this.id = ""; +this.mesh = null; +} +Geometry.prototype.parse = function ( element ) { +this.id = element.getAttribute('id'); +extractDoubleSided( this, element ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +switch ( child.nodeName ) { +case 'mesh': +this.mesh = (new Mesh(this)).parse(child); +break; +case 'extra': +break; +default: +break; +} +} +return this; +}; +function Mesh( geometry ) { +this.geometry = geometry.id; +this.primitives = []; +this.vertices = null; +this.geometry3js = null; +} +Mesh.prototype.parse = function ( element ) { +this.primitives = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'source': +_source( child ); +break; +case 'vertices': +this.vertices = ( new Vertices() ).parse( child ); +break; +case 'linestrips': +this.primitives.push( ( new LineStrips().parse( child ) ) ); +break; +case 'triangles': +this.primitives.push( ( new Triangles().parse( child ) ) ); +break; +case 'polygons': +this.primitives.push( ( new Polygons().parse( child ) ) ); +break; +case 'polylist': +this.primitives.push( ( new Polylist().parse( child ) ) ); +break; +default: +break; +} +} +this.geometry3js = new THREE.Geometry(); +if ( this.vertices === null ) { +return this; +} +var vertexData = sources[ this.vertices.input['POSITION'].source ].data; +for ( var i = 0; i < vertexData.length; i += 3 ) { +this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); +} +for ( var i = 0; i < this.primitives.length; i ++ ) { +var primitive = this.primitives[ i ]; +primitive.setVertices( this.vertices ); +this.handlePrimitive( primitive, this.geometry3js ); +} +if ( this.geometry3js.calcNormals ) { +this.geometry3js.computeVertexNormals(); +delete this.geometry3js.calcNormals; +} +return this; +}; +Mesh.prototype.handlePrimitive = function ( primitive, geom ) { +if ( primitive instanceof LineStrips ) { +geom.isLineStrip = true; +return; +} +var j, k, pList = primitive.p, inputs = primitive.inputs; +var input, index, idx32; +var source, numParams; +var vcIndex = 0, vcount = 3, maxOffset = 0; +var texture_sets = []; +for ( j = 0; j < inputs.length; j ++ ) { +input = inputs[ j ]; +var offset = input.offset + 1; +maxOffset = (maxOffset < offset) ? offset : maxOffset; +switch ( input.semantic ) { +case 'TEXCOORD': +texture_sets.push( input.set ); +break; +} +} +for ( var pCount = 0; pCount < pList.length; ++ pCount ) { +var p = pList[ pCount ], i = 0; +while ( i < p.length ) { +var vs = []; +var ns = []; +var ts = null; +var cs = []; +if ( primitive.vcount ) { +vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; +} else { +vcount = p.length / maxOffset; +} +for ( j = 0; j < vcount; j ++ ) { +for ( k = 0; k < inputs.length; k ++ ) { +input = inputs[ k ]; +source = sources[ input.source ]; +index = p[ i + ( j * maxOffset ) + input.offset ]; +numParams = source.accessor.params.length; +idx32 = index * numParams; +switch ( input.semantic ) { +case 'VERTEX': +vs.push( index ); +break; +case 'NORMAL': +ns.push( getConvertedVec3( source.data, idx32 ) ); +break; +case 'TEXCOORD': +ts = ts || { }; +if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; +ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); +break; +case 'COLOR': +cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); +break; +default: +break; +} +} +} +if ( ns.length === 0 ) { +input = this.vertices.input.NORMAL; +if ( input ) { +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); +} +} else { +geom.calcNormals = true; +} +} +if ( !ts ) { +ts = { }; +input = this.vertices.input.TEXCOORD; +if ( input ) { +texture_sets.push( input.set ); +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +idx32 = vs[ ndx ] * numParams; +if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; +ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); +} +} +} +if ( cs.length === 0 ) { +input = this.vertices.input.COLOR; +if ( input ) { +source = sources[ input.source ]; +numParams = source.accessor.params.length; +for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { +idx32 = vs[ ndx ] * numParams; +cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); +} +} +} +var face = null, faces = [], uv, uvArr; +if ( vcount === 3 ) { +faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); +} else if ( vcount === 4 ) { +faces.push( new THREE.Face3( vs[0], vs[1], vs[3], ns.length ? [ ns[0].clone(), ns[1].clone(), ns[3].clone() ] : [], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); +faces.push( new THREE.Face3( vs[1], vs[2], vs[3], ns.length ? [ ns[1].clone(), ns[2].clone(), ns[3].clone() ] : [], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); +} else if ( vcount > 4 && options.subdivideFaces ) { +var clr = cs.length ? cs : new THREE.Color(), +vec1, vec2, vec3, v1, v2, norm; +for ( k = 1; k < vcount - 1; ) { +faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], ns.length ? [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ] : [], clr ) ); +} +} +if ( faces.length ) { +for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { +face = faces[ndx]; +face.daeMaterial = primitive.material; +geom.faces.push( face ); +for ( k = 0; k < texture_sets.length; k ++ ) { +uv = ts[ texture_sets[k] ]; +if ( vcount > 4 ) { +uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; +} else if ( vcount === 4 ) { +if ( ndx === 0 ) { +uvArr = [ uv[0], uv[1], uv[3] ]; +} else { +uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; +} +} else { +uvArr = [ uv[0], uv[1], uv[2] ]; +} +if ( geom.faceVertexUvs[k] === undefined ) { +geom.faceVertexUvs[k] = []; +} +geom.faceVertexUvs[k].push( uvArr ); +} +} +} else { +console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); +} +i += maxOffset * vcount; +} +} +}; +function Polygons () { +this.material = ""; +this.count = 0; +this.inputs = []; +this.vcount = null; +this.p = []; +this.geometry = new THREE.Geometry(); +} +Polygons.prototype.setVertices = function ( vertices ) { +for ( var i = 0; i < this.inputs.length; i ++ ) { +if ( this.inputs[ i ].source === vertices.id ) { +this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; +} +} +}; +Polygons.prototype.parse = function ( element ) { +this.material = element.getAttribute( 'material' ); +this.count = _attr_as_int( element, 'count', 0 ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'input': +this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); +break; +case 'vcount': +this.vcount = _ints( child.textContent ); +break; +case 'p': +this.p.push( _ints( child.textContent ) ); +break; +case 'ph': +console.warn( 'polygon holes not yet supported!' ); +break; +default: +break; +} +} +return this; +}; +function Polylist () { +Polygons.call( this ); +this.vcount = []; +} +Polylist.prototype = Object.create( Polygons.prototype ); +Polylist.prototype.constructor = Polylist; +function LineStrips() { +Polygons.call( this ); +this.vcount = 1; +} +LineStrips.prototype = Object.create( Polygons.prototype ); +LineStrips.prototype.constructor = LineStrips; +function Triangles () { +Polygons.call( this ); +this.vcount = 3; +} +Triangles.prototype = Object.create( Polygons.prototype ); +Triangles.prototype.constructor = Triangles; +function Accessor() { +this.source = ""; +this.count = 0; +this.stride = 0; +this.params = []; +} +Accessor.prototype.parse = function ( element ) { +this.params = []; +this.source = element.getAttribute( 'source' ); +this.count = _attr_as_int( element, 'count', 0 ); +this.stride = _attr_as_int( element, 'stride', 0 ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeName === 'param' ) { +var param = {}; +param[ 'name' ] = child.getAttribute( 'name' ); +param[ 'type' ] = child.getAttribute( 'type' ); +this.params.push( param ); +} +} +return this; +}; +function Vertices() { +this.input = {}; +} +Vertices.prototype.parse = function ( element ) { +this.id = element.getAttribute('id'); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[i].nodeName === 'input' ) { +var input = ( new Input() ).parse( element.childNodes[ i ] ); +this.input[ input.semantic ] = input; +} +} +return this; +}; +function Input () { +this.semantic = ""; +this.offset = 0; +this.source = ""; +this.set = 0; +} +Input.prototype.parse = function ( element ) { +this.semantic = element.getAttribute('semantic'); +this.source = element.getAttribute('source').replace(/^#/, ''); +this.set = _attr_as_int(element, 'set', -1); +this.offset = _attr_as_int(element, 'offset', 0); +if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { +this.set = 0; +} +return this; +}; +function Source ( id ) { +this.id = id; +this.type = null; +} +Source.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +switch ( child.nodeName ) { +case 'bool_array': +this.data = _bools( child.textContent ); +this.type = child.nodeName; +break; +case 'float_array': +this.data = _floats( child.textContent ); +this.type = child.nodeName; +break; +case 'int_array': +this.data = _ints( child.textContent ); +this.type = child.nodeName; +break; +case 'IDREF_array': +case 'Name_array': +this.data = _strings( child.textContent ); +this.type = child.nodeName; +break; +case 'technique_common': +for ( var j = 0; j < child.childNodes.length; j ++ ) { +if ( child.childNodes[ j ].nodeName === 'accessor' ) { +this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); +break; +} +} +break; +default: +break; +} +} +return this; +}; +Source.prototype.read = function () { +var result = []; +var param = this.accessor.params[ 0 ]; +switch ( param.type ) { +case 'IDREF': +case 'Name': case 'name': +case 'float': +return this.data; +case 'float4x4': +for ( var j = 0; j < this.data.length; j += 16 ) { +var s = this.data.slice( j, j + 16 ); +var m = getConvertedMat4( s ); +result.push( m ); +} +break; +default: +console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); +break; +} +return result; +}; +function Material () { +this.id = ""; +this.name = ""; +this.instance_effect = null; +} +Material.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { +this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); +break; +} +} +return this; +}; +function ColorOrTexture () { +this.color = new THREE.Color(); +this.color.setRGB( Math.random(), Math.random(), Math.random() ); +this.color.a = 1.0; +this.texture = null; +this.texcoord = null; +this.texOpts = null; +} +ColorOrTexture.prototype.isColor = function () { +return ( this.texture === null ); +}; +ColorOrTexture.prototype.isTexture = function () { +return ( this.texture != null ); +}; +ColorOrTexture.prototype.parse = function ( element ) { +if (element.nodeName === 'transparent') { +this.opaque = element.getAttribute('opaque'); +} +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'color': +var rgba = _floats( child.textContent ); +this.color = new THREE.Color(); +this.color.setRGB( rgba[0], rgba[1], rgba[2] ); +this.color.a = rgba[3]; +break; +case 'texture': +this.texture = child.getAttribute('texture'); +this.texcoord = child.getAttribute('texcoord'); +this.texOpts = { +offsetU: 0, +offsetV: 0, +repeatU: 1, +repeatV: 1, +wrapU: 1, +wrapV: 1 +}; +this.parseTexture( child ); +break; +default: +break; +} +} +return this; +}; +ColorOrTexture.prototype.parseTexture = function ( element ) { +if ( ! element.childNodes ) return this; +if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { +element = element.childNodes[1]; +if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { +element = element.childNodes[1]; +} +} +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'offsetU': +case 'offsetV': +case 'repeatU': +case 'repeatV': +this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); +break; +case 'wrapU': +case 'wrapV': +if ( child.textContent.toUpperCase() === 'TRUE' ) { +this.texOpts[ child.nodeName ] = 1; +} else { +this.texOpts[ child.nodeName ] = parseInt( child.textContent ); +} +break; +default: +this.texOpts[ child.nodeName ] = child.textContent; +break; +} +} +return this; +}; +function Shader ( type, effect ) { +this.type = type; +this.effect = effect; +this.material = null; +} +Shader.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'emission': +case 'diffuse': +case 'specular': +case 'transparent': +this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); +break; +case 'bump': +var bumpType = child.getAttribute( 'bumptype' ); +if ( bumpType ) { +if ( bumpType.toLowerCase() === "heightfield" ) { +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} else if ( bumpType.toLowerCase() === "normalmap" ) { +this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); +} else { +console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} +} else { +console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); +this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); +} +break; +case 'shininess': +case 'reflectivity': +case 'index_of_refraction': +case 'transparency': +var f = child.querySelectorAll('float'); +if ( f.length > 0 ) +this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); +break; +default: +break; +} +} +this.create(); +return this; +}; +Shader.prototype.create = function() { +var props = {}; +var transparent = false; +if (this['transparency'] !== undefined && this['transparent'] !== undefined) { +var transparentColor = this['transparent']; +var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; +if (transparencyLevel > 0) { +transparent = true; +props[ 'transparent' ] = true; +props[ 'opacity' ] = 1 - transparencyLevel; +} +} +var keys = { +'diffuse':'map', +'ambient':'lightMap', +'specular':'specularMap', +'emission':'emissionMap', +'bump':'bumpMap', +'normal':'normalMap' +}; +for ( var prop in this ) { +switch ( prop ) { +case 'ambient': +case 'emission': +case 'diffuse': +case 'specular': +case 'bump': +case 'normal': +var cot = this[ prop ]; +if ( cot instanceof ColorOrTexture ) { +if ( cot.isTexture() ) { +var samplerId = cot.texture; +var sampler = this.effect.sampler[samplerId]; +if ( sampler !== undefined && sampler.source !== undefined ) { +var surface = this.effect.surface[sampler.source]; +if ( surface !== undefined ) { +var image = images[ surface.init_from ]; +if ( image ) { +var url = baseUrl + image.init_from; +var texture; +var loader = THREE.Loader.Handlers.get( url ); +if ( loader !== null ) { +texture = loader.load( url ); +} else { +texture = new THREE.Texture(); +loadTextureImage( texture, url ); +} +if ( sampler.wrap_s === "MIRROR" ) { +texture.wrapS = THREE.MirroredRepeatWrapping; +} else if ( sampler.wrap_s === "WRAP" || cot.texOpts.wrapU ) { +texture.wrapS = THREE.RepeatWrapping; +} else { +texture.wrapS = THREE.ClampToEdgeWrapping; +} +if ( sampler.wrap_t === "MIRROR" ) { +texture.wrapT = THREE.MirroredRepeatWrapping; +} else if ( sampler.wrap_t === "WRAP" || cot.texOpts.wrapV ) { +texture.wrapT = THREE.RepeatWrapping; +} else { +texture.wrapT = THREE.ClampToEdgeWrapping; +} +texture.offset.x = cot.texOpts.offsetU; +texture.offset.y = cot.texOpts.offsetV; +texture.repeat.x = cot.texOpts.repeatU; +texture.repeat.y = cot.texOpts.repeatV; +props[keys[prop]] = texture; +if (prop === 'emission') props['emissive'] = 0xffffff; +} +} +} +} else if ( prop === 'diffuse' || !transparent ) { +if ( prop === 'emission' ) { +props[ 'emissive' ] = cot.color.getHex(); +} else { +props[ prop ] = cot.color.getHex(); +} +} +} +break; +case 'shininess': +props[ prop ] = this[ prop ]; +break; +case 'reflectivity': +props[ prop ] = this[ prop ]; +if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; +props['combine'] = THREE.MixOperation; //mix regular shading with reflective component +break; +case 'index_of_refraction': +props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable +if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; +break; +case 'transparency': +break; +default: +break; +} +} +props[ 'shading' ] = preferredShading; +props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; +if ( props.diffuse !== undefined ) { +props.color = props.diffuse; +delete props.diffuse; +} +switch ( this.type ) { +case 'constant': +if (props.emissive != undefined) props.color = props.emissive; +this.material = new THREE.MeshBasicMaterial( props ); +break; +case 'phong': +case 'blinn': +this.material = new THREE.MeshPhongMaterial( props ); +break; +case 'lambert': +default: +this.material = new THREE.MeshLambertMaterial( props ); +break; +} +return this.material; +}; +function Surface ( effect ) { +this.effect = effect; +this.init_from = null; +this.format = null; +} +Surface.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'init_from': +this.init_from = child.textContent; +break; +case 'format': +this.format = child.textContent; +break; +default: +console.log( "unhandled Surface prop: " + child.nodeName ); +break; +} +} +return this; +}; +function Sampler2D ( effect ) { +this.effect = effect; +this.source = null; +this.wrap_s = null; +this.wrap_t = null; +this.minfilter = null; +this.magfilter = null; +this.mipfilter = null; +} +Sampler2D.prototype.parse = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'source': +this.source = child.textContent; +break; +case 'minfilter': +this.minfilter = child.textContent; +break; +case 'magfilter': +this.magfilter = child.textContent; +break; +case 'mipfilter': +this.mipfilter = child.textContent; +break; +case 'wrap_s': +this.wrap_s = child.textContent; +break; +case 'wrap_t': +this.wrap_t = child.textContent; +break; +default: +console.log( "unhandled Sampler2D prop: " + child.nodeName ); +break; +} +} +return this; +}; +function Effect () { +this.id = ""; +this.name = ""; +this.shader = null; +this.surface = {}; +this.sampler = {}; +} +Effect.prototype.create = function () { +if ( this.shader === null ) { +return null; +} +}; +Effect.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +extractDoubleSided( this, element ); +this.shader = null; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'profile_COMMON': +this.parseTechnique( this.parseProfileCOMMON( child ) ); +break; +default: +break; +} +} +return this; +}; +Effect.prototype.parseNewparam = function ( element ) { +var sid = element.getAttribute( 'sid' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'surface': +this.surface[sid] = ( new Surface( this ) ).parse( child ); +break; +case 'sampler2D': +this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +}; +Effect.prototype.parseProfileCOMMON = function ( element ) { +var technique; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'profile_COMMON': +this.parseProfileCOMMON( child ); +break; +case 'technique': +technique = child; +break; +case 'newparam': +this.parseNewparam( child ); +break; +case 'image': +var _image = ( new _Image() ).parse( child ); +images[ _image.id ] = _image; +break; +case 'extra': +break; +default: +console.log( child.nodeName ); +break; +} +} +return technique; +}; +Effect.prototype.parseTechnique = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'constant': +case 'lambert': +case 'blinn': +case 'phong': +this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); +break; +case 'extra': +this.parseExtra(child); +break; +default: +break; +} +} +}; +Effect.prototype.parseExtra = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique': +this.parseExtraTechnique( child ); +break; +default: +break; +} +} +}; +Effect.prototype.parseExtraTechnique = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[i]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'bump': +this.shader.parse( element ); +break; +default: +break; +} +} +}; +function InstanceEffect () { +this.url = ""; +} +InstanceEffect.prototype.parse = function ( element ) { +this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); +return this; +}; +function Animation() { +this.id = ""; +this.name = ""; +this.source = {}; +this.sampler = []; +this.channel = []; +} +Animation.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +this.source = {}; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'animation': +var anim = ( new Animation() ).parse( child ); +for ( var src in anim.source ) { +this.source[ src ] = anim.source[ src ]; +} +for ( var j = 0; j < anim.channel.length; j ++ ) { +this.channel.push( anim.channel[ j ] ); +this.sampler.push( anim.sampler[ j ] ); +} +break; +case 'source': +var src = ( new Source() ).parse( child ); +this.source[ src.id ] = src; +break; +case 'sampler': +this.sampler.push( ( new Sampler( this ) ).parse( child ) ); +break; +case 'channel': +this.channel.push( ( new Channel( this ) ).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +function Channel( animation ) { +this.animation = animation; +this.source = ""; +this.target = ""; +this.fullSid = null; +this.sid = null; +this.dotSyntax = null; +this.arrSyntax = null; +this.arrIndices = null; +this.member = null; +} +Channel.prototype.parse = function ( element ) { +this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); +this.target = element.getAttribute( 'target' ); +var parts = this.target.split( '/' ); +var id = parts.shift(); +var sid = parts.shift(); +var dotSyntax = ( sid.indexOf(".") >= 0 ); +var arrSyntax = ( sid.indexOf("(") >= 0 ); +if ( dotSyntax ) { +parts = sid.split("."); +this.sid = parts.shift(); +this.member = parts.shift(); +} else if ( arrSyntax ) { +var arrIndices = sid.split("("); +this.sid = arrIndices.shift(); +for (var j = 0; j < arrIndices.length; j ++ ) { +arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); +} +this.arrIndices = arrIndices; +} else { +this.sid = sid; +} +this.fullSid = sid; +this.dotSyntax = dotSyntax; +this.arrSyntax = arrSyntax; +return this; +}; +function Sampler ( animation ) { +this.id = ""; +this.animation = animation; +this.inputs = []; +this.input = null; +this.output = null; +this.strideOut = null; +this.interpolation = null; +this.startTime = null; +this.endTime = null; +this.duration = 0; +} +Sampler.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.inputs = []; +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'input': +this.inputs.push( (new Input()).parse( child ) ); +break; +default: +break; +} +} +return this; +}; +Sampler.prototype.create = function () { +for ( var i = 0; i < this.inputs.length; i ++ ) { +var input = this.inputs[ i ]; +var source = this.animation.source[ input.source ]; +switch ( input.semantic ) { +case 'INPUT': +this.input = source.read(); +break; +case 'OUTPUT': +this.output = source.read(); +this.strideOut = source.accessor.stride; +break; +case 'INTERPOLATION': +this.interpolation = source.read(); +break; +case 'IN_TANGENT': +break; +case 'OUT_TANGENT': +break; +default: +console.log(input.semantic); +break; +} +} +this.startTime = 0; +this.endTime = 0; +this.duration = 0; +if ( this.input.length ) { +this.startTime = 100000000; +this.endTime = -100000000; +for ( var i = 0; i < this.input.length; i ++ ) { +this.startTime = Math.min( this.startTime, this.input[ i ] ); +this.endTime = Math.max( this.endTime, this.input[ i ] ); +} +this.duration = this.endTime - this.startTime; +} +}; +Sampler.prototype.getData = function ( type, ndx, member ) { +var data; +if ( type === 'matrix' && this.strideOut === 16 ) { +data = this.output[ ndx ]; +} else if ( this.strideOut > 1 ) { +data = []; +ndx *= this.strideOut; +for ( var i = 0; i < this.strideOut; ++ i ) { +data[ i ] = this.output[ ndx + i ]; +} +if ( this.strideOut === 3 ) { +switch ( type ) { +case 'rotate': +case 'translate': +fixCoords( data, -1 ); +break; +case 'scale': +fixCoords( data, 1 ); +break; +} +} else if ( this.strideOut === 4 && type === 'matrix' ) { +fixCoords( data, -1 ); +} +} else { +data = this.output[ ndx ]; +if ( member && type === 'translate' ) { +data = getConvertedTranslation( member, data ); +} +} +return data; +}; +function Key ( time ) { +this.targets = []; +this.time = time; +} +Key.prototype.addTarget = function ( fullSid, transform, member, data ) { +this.targets.push( { +sid: fullSid, +member: member, +transform: transform, +data: data +} ); +}; +Key.prototype.apply = function ( opt_sid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +var target = this.targets[ i ]; +if ( !opt_sid || target.sid === opt_sid ) { +target.transform.update( target.data, target.member ); +} +} +}; +Key.prototype.getTarget = function ( fullSid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +if ( this.targets[ i ].sid === fullSid ) { +return this.targets[ i ]; +} +} +return null; +}; +Key.prototype.hasTarget = function ( fullSid ) { +for ( var i = 0; i < this.targets.length; ++ i ) { +if ( this.targets[ i ].sid === fullSid ) { +return true; +} +} +return false; +}; +Key.prototype.interpolate = function ( nextKey, time ) { +for ( var i = 0, l = this.targets.length; i < l; i ++ ) { +var target = this.targets[ i ], +nextTarget = nextKey.getTarget( target.sid ), +data; +if ( target.transform.type !== 'matrix' && nextTarget ) { +var scale = ( time - this.time ) / ( nextKey.time - this.time ), +nextData = nextTarget.data, +prevData = target.data; +if ( scale < 0 ) scale = 0; +if ( scale > 1 ) scale = 1; +if ( prevData.length ) { +data = []; +for ( var j = 0; j < prevData.length; ++ j ) { +data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; +} +} else { +data = prevData + ( nextData - prevData ) * scale; +} +} else { +data = target.data; +} +target.transform.update( data, target.member ); +} +}; +function Camera() { +this.id = ""; +this.name = ""; +this.technique = ""; +} +Camera.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'optics': +this.parseOptics( child ); +break; +default: +break; +} +} +return this; +}; +Camera.prototype.parseOptics = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +if ( element.childNodes[ i ].nodeName === 'technique_common' ) { +var technique = element.childNodes[ i ]; +for ( var j = 0; j < technique.childNodes.length; j ++ ) { +this.technique = technique.childNodes[ j ].nodeName; +if ( this.technique === 'perspective' ) { +var perspective = technique.childNodes[ j ]; +for ( var k = 0; k < perspective.childNodes.length; k ++ ) { +var param = perspective.childNodes[ k ]; +switch ( param.nodeName ) { +case 'yfov': +this.yfov = param.textContent; +break; +case 'xfov': +this.xfov = param.textContent; +break; +case 'znear': +this.znear = param.textContent; +break; +case 'zfar': +this.zfar = param.textContent; +break; +case 'aspect_ratio': +this.aspect_ratio = param.textContent; +break; +} +} +} else if ( this.technique === 'orthographic' ) { +var orthographic = technique.childNodes[ j ]; +for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { +var param = orthographic.childNodes[ k ]; +switch ( param.nodeName ) { +case 'xmag': +this.xmag = param.textContent; +break; +case 'ymag': +this.ymag = param.textContent; +break; +case 'znear': +this.znear = param.textContent; +break; +case 'zfar': +this.zfar = param.textContent; +break; +case 'aspect_ratio': +this.aspect_ratio = param.textContent; +break; +} +} +} +} +} +} +return this; +}; +function InstanceCamera() { +this.url = ""; +} +InstanceCamera.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +return this; +}; +function Light() { +this.id = ""; +this.name = ""; +this.technique = ""; +} +Light.prototype.parse = function ( element ) { +this.id = element.getAttribute( 'id' ); +this.name = element.getAttribute( 'name' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique_common': +this.parseCommon( child ); +break; +case 'technique': +this.parseTechnique( child ); +break; +default: +break; +} +} +return this; +}; +Light.prototype.parseCommon = function ( element ) { +for ( var i = 0; i < element.childNodes.length; i ++ ) { +switch ( element.childNodes[ i ].nodeName ) { +case 'directional': +case 'point': +case 'spot': +case 'ambient': +this.technique = element.childNodes[ i ].nodeName; +var light = element.childNodes[ i ]; +for ( var j = 0; j < light.childNodes.length; j ++ ) { +var child = light.childNodes[j]; +switch ( child.nodeName ) { +case 'color': +var rgba = _floats( child.textContent ); +this.color = new THREE.Color(0); +this.color.setRGB( rgba[0], rgba[1], rgba[2] ); +this.color.a = rgba[3]; +break; +case 'falloff_angle': +this.falloff_angle = parseFloat( child.textContent ); +break; +case 'quadratic_attenuation': +var f = parseFloat( child.textContent ); +this.distance = f ? Math.sqrt( 1 / f ) : 0; +} +} +} +} +return this; +}; +Light.prototype.parseTechnique = function ( element ) { +this.profile = element.getAttribute( 'profile' ); +for ( var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +switch ( child.nodeName ) { +case 'intensity': +this.intensity = parseFloat(child.textContent); +break; +} +} +return this; +}; +function InstanceLight() { +this.url = ""; +} +InstanceLight.prototype.parse = function ( element ) { +this.url = element.getAttribute('url').replace(/^#/, ''); +return this; +}; +function KinematicsModel( ) { +this.id = ''; +this.name = ''; +this.joints = []; +this.links = []; +} +KinematicsModel.prototype.parse = function( element ) { +this.id = element.getAttribute('id'); +this.name = element.getAttribute('name'); +this.joints = []; +this.links = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'technique_common': +this.parseCommon(child); +break; +default: +break; +} +} +return this; +}; +KinematicsModel.prototype.parseCommon = function( element ) { +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( element.childNodes[ i ].nodeName ) { +case 'joint': +this.joints.push( (new Joint()).parse(child) ); +break; +case 'link': +this.links.push( (new Link()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function Joint( ) { +this.sid = ''; +this.name = ''; +this.axis = new THREE.Vector3(); +this.limits = { +min: 0, +max: 0 +}; +this.type = ''; +this.static = false; +this.zeroPosition = 0.0; +this.middlePosition = 0.0; +} +Joint.prototype.parse = function( element ) { +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.axis = new THREE.Vector3(); +this.limits = { +min: 0, +max: 0 +}; +this.type = ''; +this.static = false; +this.zeroPosition = 0.0; +this.middlePosition = 0.0; +var axisElement = element.querySelector('axis'); +var _axis = _floats(axisElement.textContent); +this.axis = getConvertedVec3(_axis, 0); +var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; +var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; +this.limits = { +min: min, +max: max +}; +var jointTypes = [ 'prismatic', 'revolute' ]; +for (var i = 0; i < jointTypes.length; i ++ ) { +var type = jointTypes[ i ]; +var jointElement = element.querySelector(type); +if ( jointElement ) { +this.type = type; +} +} +if ( this.limits.min >= this.limits.max ) { +this.static = true; +} +this.middlePosition = (this.limits.min + this.limits.max) / 2.0; +return this; +}; +function Link( ) { +this.sid = ''; +this.name = ''; +this.transforms = []; +this.attachments = []; +} +Link.prototype.parse = function( element ) { +this.sid = element.getAttribute('sid'); +this.name = element.getAttribute('name'); +this.transforms = []; +this.attachments = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'attachment_full': +this.attachments.push( (new Attachment()).parse(child) ); +break; +case 'rotate': +case 'translate': +case 'matrix': +this.transforms.push( (new Transform()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function Attachment( ) { +this.joint = ''; +this.transforms = []; +this.links = []; +} +Attachment.prototype.parse = function( element ) { +this.joint = element.getAttribute('joint').split('/').pop(); +this.links = []; +for (var i = 0; i < element.childNodes.length; i ++ ) { +var child = element.childNodes[ i ]; +if ( child.nodeType != 1 ) continue; +switch ( child.nodeName ) { +case 'link': +this.links.push( (new Link()).parse(child) ); +break; +case 'rotate': +case 'translate': +case 'matrix': +this.transforms.push( (new Transform()).parse(child) ); +break; +default: +break; +} +} +return this; +}; +function _source( element ) { +var id = element.getAttribute( 'id' ); +if ( sources[ id ] != undefined ) { +return sources[ id ]; +} +sources[ id ] = ( new Source(id )).parse( element ); +return sources[ id ]; +} +function _nsResolver( nsPrefix ) { +if ( nsPrefix === "dae" ) { +return "http://www.collada.org/2005/11/COLLADASchema"; +} +return null; +} +function _bools( str ) { +var raw = _strings( str ); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); +} +return data; +} +function _floats( str ) { +var raw = _strings(str); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( parseFloat( raw[ i ] ) ); +} +return data; +} +function _ints( str ) { +var raw = _strings( str ); +var data = []; +for ( var i = 0, l = raw.length; i < l; i ++ ) { +data.push( parseInt( raw[ i ], 10 ) ); +} +return data; +} +function _strings( str ) { +return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; +} +function _trimString( str ) { +return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); +} +function _attr_as_float( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return parseFloat( element.getAttribute( name ) ); +} else { +return defaultValue; +} +} +function _attr_as_int( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return parseInt( element.getAttribute( name ), 10) ; +} else { +return defaultValue; +} +} +function _attr_as_string( element, name, defaultValue ) { +if ( element.hasAttribute( name ) ) { +return element.getAttribute( name ); +} else { +return defaultValue; +} +} +function _format_float( f, num ) { +if ( f === undefined ) { +var s = '0.'; +while ( s.length < num + 2 ) { +s += '0'; +} +return s; +} +num = num || 2; +var parts = f.toString().split( '.' ); +parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; +while ( parts[ 1 ].length < num ) { +parts[ 1 ] += '0'; +} +return parts.join( '.' ); +} +function loadTextureImage ( texture, url ) { +var loader = new THREE.ImageLoader(); +loader.load( url, function ( image ) { +texture.image = image; +texture.needsUpdate = true; +} ); +} +function extractDoubleSided( obj, element ) { +obj.doubleSided = false; +var node = element.querySelectorAll('extra double_sided')[0]; +if ( node ) { +if ( node && parseInt( node.textContent, 10 ) === 1 ) { +obj.doubleSided = true; +} +} +} +function setUpConversion() { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +upConversion = null; +} else { +switch ( colladaUp ) { +case 'X': +upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; +break; +case 'Y': +upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; +break; +case 'Z': +upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; +break; +} +} +} +function fixCoords( data, sign ) { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +return; +} +switch ( upConversion ) { +case 'XtoY': +var tmp = data[ 0 ]; +data[ 0 ] = sign * data[ 1 ]; +data[ 1 ] = tmp; +break; +case 'XtoZ': +var tmp = data[ 2 ]; +data[ 2 ] = data[ 1 ]; +data[ 1 ] = data[ 0 ]; +data[ 0 ] = tmp; +break; +case 'YtoX': +var tmp = data[ 0 ]; +data[ 0 ] = data[ 1 ]; +data[ 1 ] = sign * tmp; +break; +case 'YtoZ': +var tmp = data[ 1 ]; +data[ 1 ] = sign * data[ 2 ]; +data[ 2 ] = tmp; +break; +case 'ZtoX': +var tmp = data[ 0 ]; +data[ 0 ] = data[ 1 ]; +data[ 1 ] = data[ 2 ]; +data[ 2 ] = tmp; +break; +case 'ZtoY': +var tmp = data[ 1 ]; +data[ 1 ] = data[ 2 ]; +data[ 2 ] = sign * tmp; +break; +} +} +function getConvertedTranslation( axis, data ) { +if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { +return data; +} +switch ( axis ) { +case 'X': +data = upConversion === 'XtoY' ? data * -1 : data; +break; +case 'Y': +data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; +break; +case 'Z': +data = upConversion === 'ZtoY' ? data * -1 : data ; +break; +default: +break; +} +return data; +} +function getConvertedVec3( data, offset ) { +var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; +fixCoords( arr, -1 ); +return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); +} +function getConvertedMat4( data ) { +if ( options.convertUpAxis ) { +var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; +fixCoords( arr, -1 ); +data[ 0 ] = arr[ 0 ]; +data[ 4 ] = arr[ 1 ]; +data[ 8 ] = arr[ 2 ]; +arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; +fixCoords( arr, -1 ); +data[ 1 ] = arr[ 0 ]; +data[ 5 ] = arr[ 1 ]; +data[ 9 ] = arr[ 2 ]; +arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; +fixCoords( arr, -1 ); +data[ 2 ] = arr[ 0 ]; +data[ 6 ] = arr[ 1 ]; +data[ 10 ] = arr[ 2 ]; +arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; +fixCoords( arr, -1 ); +data[ 0 ] = arr[ 0 ]; +data[ 1 ] = arr[ 1 ]; +data[ 2 ] = arr[ 2 ]; +arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; +fixCoords( arr, -1 ); +data[ 4 ] = arr[ 0 ]; +data[ 5 ] = arr[ 1 ]; +data[ 6 ] = arr[ 2 ]; +arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; +fixCoords( arr, -1 ); +data[ 8 ] = arr[ 0 ]; +data[ 9 ] = arr[ 1 ]; +data[ 10 ] = arr[ 2 ]; +arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; +fixCoords( arr, -1 ); +data[ 3 ] = arr[ 0 ]; +data[ 7 ] = arr[ 1 ]; +data[ 11 ] = arr[ 2 ]; +} +return new THREE.Matrix4().set( +data[0], data[1], data[2], data[3], +data[4], data[5], data[6], data[7], +data[8], data[9], data[10], data[11], +data[12], data[13], data[14], data[15] +); +} +function getConvertedIndex( index ) { +if ( index > -1 && index < 3 ) { +var members = [ 'X', 'Y', 'Z' ], +indices = { X: 0, Y: 1, Z: 2 }; +index = getConvertedMember( members[ index ] ); +index = indices[ index ]; +} +return index; +} +function getConvertedMember( member ) { +if ( options.convertUpAxis ) { +switch ( member ) { +case 'X': +switch ( upConversion ) { +case 'XtoY': +case 'XtoZ': +case 'YtoX': +member = 'Y'; +break; +case 'ZtoX': +member = 'Z'; +break; +} +break; +case 'Y': +switch ( upConversion ) { +case 'XtoY': +case 'YtoX': +case 'ZtoX': +member = 'X'; +break; +case 'XtoZ': +case 'YtoZ': +case 'ZtoY': +member = 'Z'; +break; +} +break; +case 'Z': +switch ( upConversion ) { +case 'XtoZ': +member = 'X'; +break; +case 'YtoZ': +case 'ZtoX': +case 'ZtoY': +member = 'Y'; +break; +} +break; +} +} +return member; +} +return { +load: load, +parse: parse, +setPreferredShading: setPreferredShading, +applySkin: applySkin, +geometries : geometries, +options: options +}; +}; +THREE.GLTF2Loader = ( function () { +function GLTF2Loader( manager ) { +this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +} +GLTF2Loader.prototype = { +constructor: GLTF2Loader, +load: function ( url, onLoad, onProgress, onError ) { +var scope = this; +var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); +var loader = new THREE.FileLoader( scope.manager ); +loader.setResponseType( 'arraybuffer' ); +loader.load( url, function ( data ) { +scope.parse( data, onLoad, path ); +}, onProgress, onError ); +}, +setCrossOrigin: function ( value ) { +this.crossOrigin = value; +}, +setPath: function ( value ) { +this.path = value; +}, +parse: function ( data, callback, path ) { +var content; +var extensions = {}; +var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); +if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { +extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); +content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; +} else { +content = convertUint8ArrayToString( new Uint8Array( data ) ); +} +var json = JSON.parse( content ); +if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { +extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); +} +console.time( 'GLTF2Loader' ); +var parser = new GLTFParser( json, extensions, { +path: path || this.path, +crossOrigin: this.crossOrigin +} ); +parser.parse( function ( scene, scenes, cameras, animations ) { +console.timeEnd( 'GLTF2Loader' ); +var glTF = { +"scene": scene, +"scenes": scenes, +"cameras": cameras, +"animations": animations +}; +callback( glTF ); +} ); +} +}; +function GLTFRegistry() { +var objects = {}; +return { +get: function ( key ) { +return objects[ key ]; +}, +add: function ( key, object ) { +objects[ key ] = object; +}, +remove: function ( key ) { +delete objects[ key ]; +}, +removeAll: function () { +objects = {}; +}, +update: function ( scene, camera ) { +for ( var name in objects ) { +var object = objects[ name ]; +if ( object.update ) { +object.update( scene, camera ); +} +} +} +}; +} +function GLTFShader( targetNode, allNodes ) { +var boundUniforms = {}; +var uniforms = targetNode.material.uniforms; +for ( var uniformId in uniforms ) { +var uniform = uniforms[ uniformId ]; +if ( uniform.semantic ) { +var sourceNodeRef = uniform.node; +var sourceNode = targetNode; +if ( sourceNodeRef ) { +sourceNode = allNodes[ sourceNodeRef ]; +} +boundUniforms[ uniformId ] = { +semantic: uniform.semantic, +sourceNode: sourceNode, +targetNode: targetNode, +uniform: uniform +}; +} +} +this.boundUniforms = boundUniforms; +this._m4 = new THREE.Matrix4(); +} +GLTFShader.prototype.update = function ( scene, camera ) { +var boundUniforms = this.boundUniforms; +for ( var name in boundUniforms ) { +var boundUniform = boundUniforms[ name ]; +switch ( boundUniform.semantic ) { +case "MODELVIEW": +var m4 = boundUniform.uniform.value; +m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +break; +case "MODELVIEWINVERSETRANSPOSE": +var m3 = boundUniform.uniform.value; +this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +m3.getNormalMatrix( this._m4 ); +break; +case "PROJECTION": +var m4 = boundUniform.uniform.value; +m4.copy( camera.projectionMatrix ); +break; +case "JOINTMATRIX": +var m4v = boundUniform.uniform.value; +for ( var mi = 0; mi < m4v.length; mi ++ ) { +m4v[ mi ] +.getInverse( boundUniform.sourceNode.matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) +.multiply( boundUniform.targetNode.bindMatrix ); +} +break; +default : +console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); +break; +} +} +}; +var EXTENSIONS = { +KHR_BINARY_GLTF: 'KHR_binary_glTF', +KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; +function GLTFMaterialsCommonExtension( json ) { +this.name = EXTENSIONS.KHR_MATERIALS_COMMON; +this.lights = {}; +var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; +var lights = extension.lights || {}; +for ( var lightId in lights ) { +var light = lights[ lightId ]; +var lightNode; +var lightParams = light[ light.type ]; +var color = new THREE.Color().fromArray( lightParams.color ); +switch ( light.type ) { +case "directional": +lightNode = new THREE.DirectionalLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "point": +lightNode = new THREE.PointLight( color ); +break; +case "spot": +lightNode = new THREE.SpotLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "ambient": +lightNode = new THREE.AmbientLight( color ); +break; +} +if ( lightNode ) { +this.lights[ lightId ] = lightNode; +} +} +} +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; +var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; +var BINARY_EXTENSION_HEADER_LENGTH = 12; +var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; +function GLTFBinaryExtension( data ) { +this.name = EXTENSIONS.KHR_BINARY_GLTF; +this.content = null; +this.body = null; +var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); +this.header = { +magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), +version: headerView.getUint32( 4, true ), +length: headerView.getUint32( 8, true ) +}; +if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { +throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' ); +} else if ( this.header.version < 2.0 ) { +throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' ); +} +var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); +var chunkIndex = 0; +while ( chunkIndex < chunkView.byteLength ) { +var chunkLength = chunkView.getUint32( chunkIndex, true ); +chunkIndex += 4; +var chunkType = chunkView.getUint32( chunkIndex, true ); +chunkIndex += 4; +if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { +var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); +this.content = convertUint8ArrayToString( contentArray ); +} else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { +var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; +this.body = data.slice( byteOffset, byteOffset + chunkLength ); +} +chunkIndex += chunkLength; +} +if ( this.content === null ) { +throw new Error( 'GLTF2Loader: JSON content not found.' ); +} +} +var WEBGL_CONSTANTS = { +FLOAT: 5126, +FLOAT_MAT3: 35675, +FLOAT_MAT4: 35676, +FLOAT_VEC2: 35664, +FLOAT_VEC3: 35665, +FLOAT_VEC4: 35666, +LINEAR: 9729, +REPEAT: 10497, +SAMPLER_2D: 35678, +TRIANGLES: 4, +LINES: 1, +UNSIGNED_BYTE: 5121, +UNSIGNED_SHORT: 5123, +VERTEX_SHADER: 35633, +FRAGMENT_SHADER: 35632 +}; +var WEBGL_TYPE = { +5126: Number, +35675: THREE.Matrix3, +35676: THREE.Matrix4, +35664: THREE.Vector2, +35665: THREE.Vector3, +35666: THREE.Vector4, +35678: THREE.Texture +}; +var WEBGL_COMPONENT_TYPES = { +5120: Int8Array, +5121: Uint8Array, +5122: Int16Array, +5123: Uint16Array, +5125: Uint32Array, +5126: Float32Array +}; +var WEBGL_FILTERS = { +9728: THREE.NearestFilter, +9729: THREE.LinearFilter, +9984: THREE.NearestMipMapNearestFilter, +9985: THREE.LinearMipMapNearestFilter, +9986: THREE.NearestMipMapLinearFilter, +9987: THREE.LinearMipMapLinearFilter +}; +var WEBGL_WRAPPINGS = { +33071: THREE.ClampToEdgeWrapping, +33648: THREE.MirroredRepeatWrapping, +10497: THREE.RepeatWrapping +}; +var WEBGL_TEXTURE_FORMATS = { +6406: THREE.AlphaFormat, +6407: THREE.RGBFormat, +6408: THREE.RGBAFormat, +6409: THREE.LuminanceFormat, +6410: THREE.LuminanceAlphaFormat +}; +var WEBGL_TEXTURE_DATATYPES = { +5121: THREE.UnsignedByteType, +32819: THREE.UnsignedShort4444Type, +32820: THREE.UnsignedShort5551Type, +33635: THREE.UnsignedShort565Type +}; +var WEBGL_SIDES = { +1028: THREE.BackSide, // Culling front +1029: THREE.FrontSide // Culling back +}; +var WEBGL_DEPTH_FUNCS = { +512: THREE.NeverDepth, +513: THREE.LessDepth, +514: THREE.EqualDepth, +515: THREE.LessEqualDepth, +516: THREE.GreaterEqualDepth, +517: THREE.NotEqualDepth, +518: THREE.GreaterEqualDepth, +519: THREE.AlwaysDepth +}; +var WEBGL_BLEND_EQUATIONS = { +32774: THREE.AddEquation, +32778: THREE.SubtractEquation, +32779: THREE.ReverseSubtractEquation +}; +var WEBGL_BLEND_FUNCS = { +0: THREE.ZeroFactor, +1: THREE.OneFactor, +768: THREE.SrcColorFactor, +769: THREE.OneMinusSrcColorFactor, +770: THREE.SrcAlphaFactor, +771: THREE.OneMinusSrcAlphaFactor, +772: THREE.DstAlphaFactor, +773: THREE.OneMinusDstAlphaFactor, +774: THREE.DstColorFactor, +775: THREE.OneMinusDstColorFactor, +776: THREE.SrcAlphaSaturateFactor +}; +var WEBGL_TYPE_SIZES = { +'SCALAR': 1, +'VEC2': 2, +'VEC3': 3, +'VEC4': 4, +'MAT2': 4, +'MAT3': 9, +'MAT4': 16 +}; +var PATH_PROPERTIES = { +scale: 'scale', +translation: 'position', +rotation: 'quaternion' +}; +var INTERPOLATION = { +LINEAR: THREE.InterpolateLinear, +STEP: THREE.InterpolateDiscrete +}; +var STATES_ENABLES = { +2884: 'CULL_FACE', +2929: 'DEPTH_TEST', +3042: 'BLEND', +3089: 'SCISSOR_TEST', +32823: 'POLYGON_OFFSET_FILL', +32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; +function _each( object, callback, thisObj ) { +if ( !object ) { +return Promise.resolve(); +} +var results; +var fns = []; +if ( Object.prototype.toString.call( object ) === '[object Array]' ) { +results = []; +var length = object.length; +for ( var idx = 0; idx < length; idx ++ ) { +var value = callback.call( thisObj || this, object[ idx ], idx ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, idx )); +} else { +results[ idx ] = value; +} +} +} +} else { +results = {}; +for ( var key in object ) { +if ( object.hasOwnProperty( key ) ) { +var value = callback.call( thisObj || this, object[ key ], key ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, key )); +} else { +results[ key ] = value; +} +} +} +} +} +return Promise.all( fns ).then( function() { +return results; +}); +} +function resolveURL( url, path ) { +if ( typeof url !== 'string' || url === '' ) +return ''; +if ( /^(https?:)?\/\//i.test( url ) ) { +return url; +} +if ( /^data:.*,.*$/i.test( url ) ) { +return url; +} +if ( /^blob:.*$/i.test( url ) ) { +return url; +} +return ( path || '' ) + url; +} +function convertUint8ArrayToString( array ) { +var s = ''; +for ( var i = 0; i < array.length; i ++ ) { +s += String.fromCharCode( array[ i ] ); +} +return s; +} +function replaceTHREEShaderAttributes( shaderText, technique ) { +var attributes = {}; +for ( var attributeId in technique.attributes ) { +var pname = technique.attributes[ attributeId ]; +var param = technique.parameters[ pname ]; +var atype = param.type; +var semantic = param.semantic; +attributes[ attributeId ] = { +type: atype, +semantic: semantic +}; +} +var shaderParams = technique.parameters; +var shaderAttributes = technique.attributes; +var params = {}; +for ( var attributeId in attributes ) { +var pname = shaderAttributes[ attributeId ]; +var shaderParam = shaderParams[ pname ]; +var semantic = shaderParam.semantic; +if ( semantic ) { +params[ attributeId ] = shaderParam; +} +} +for ( var pname in params ) { +var param = params[ pname ]; +var semantic = param.semantic; +var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); +switch ( semantic ) { +case 'POSITION': +shaderText = shaderText.replace( regEx, 'position' ); +break; +case 'NORMAL': +shaderText = shaderText.replace( regEx, 'normal' ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +shaderText = shaderText.replace( regEx, 'uv' ); +break; +case 'TEXCOORD_1': +shaderText = shaderText.replace( regEx, 'uv2' ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +shaderText = shaderText.replace( regEx, 'color' ); +break; +case 'WEIGHTS_0': +case 'WEIGHT': // WEIGHT semantic deprecated. +shaderText = shaderText.replace( regEx, 'skinWeight' ); +break; +case 'JOINTS_0': +case 'JOINT': // JOINT semantic deprecated. +shaderText = shaderText.replace( regEx, 'skinIndex' ); +break; +} +} +return shaderText; +} +function createDefaultMaterial() { +return new THREE.MeshPhongMaterial( { +color: 0x00000, +emissive: 0x888888, +specular: 0x000000, +shininess: 0, +transparent: false, +depthTest: true, +side: THREE.FrontSide +} ); +} +function DeferredShaderMaterial( params ) { +this.isDeferredShaderMaterial = true; +this.params = params; +} +DeferredShaderMaterial.prototype.create = function () { +var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); +for ( var uniformId in this.params.uniforms ) { +var originalUniform = this.params.uniforms[ uniformId ]; +if ( originalUniform.value instanceof THREE.Texture ) { +uniforms[ uniformId ].value = originalUniform.value; +uniforms[ uniformId ].value.needsUpdate = true; +} +uniforms[ uniformId ].semantic = originalUniform.semantic; +uniforms[ uniformId ].node = originalUniform.node; +} +this.params.uniforms = uniforms; +return new THREE.RawShaderMaterial( this.params ); +}; +function GLTFParser( json, extensions, options ) { +this.json = json || {}; +this.extensions = extensions || {}; +this.options = options || {}; +this.cache = new GLTFRegistry(); +} +GLTFParser.prototype._withDependencies = function ( dependencies ) { +var _dependencies = {}; +for ( var i = 0; i < dependencies.length; i ++ ) { +var dependency = dependencies[ i ]; +var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); +var cached = this.cache.get( dependency ); +if ( cached !== undefined ) { +_dependencies[ dependency ] = cached; +} else if ( this[ fnName ] ) { +var fn = this[ fnName ](); +this.cache.add( dependency, fn ); +_dependencies[ dependency ] = fn; +} +} +return _each( _dependencies, function ( dependency ) { +return dependency; +} ); +}; +GLTFParser.prototype.parse = function ( callback ) { +var json = this.json; +this.cache.removeAll(); +this._withDependencies( [ +"scenes", +"cameras", +"animations" +] ).then( function ( dependencies ) { +var scenes = []; +for ( var name in dependencies.scenes ) { +scenes.push( dependencies.scenes[ name ] ); +} +var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; +var cameras = []; +for ( var name in dependencies.cameras ) { +var camera = dependencies.cameras[ name ]; +cameras.push( camera ); +} +var animations = []; +for ( var name in dependencies.animations ) { +animations.push( dependencies.animations[ name ] ); +} +callback( scene, scenes, cameras, animations ); +} ); +}; +GLTFParser.prototype.loadShaders = function () { +var json = this.json; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.shaders, function ( shader ) { +if ( shader.bufferView !== undefined ) { +var bufferView = dependencies.bufferViews[ shader.bufferView ]; +var array = new Uint8Array( bufferView ); +return convertUint8ArrayToString( array ); +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'text' ); +loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { +resolve( shaderText ); +} ); +} ); +} ); +} ); +}; +GLTFParser.prototype.loadBuffers = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return _each( json.buffers, function ( buffer, name ) { +if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { +if ( buffer.uri === undefined && name === 0 ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'arraybuffer' ); +loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { +resolve( buffer ); +} ); +} ); +} else { +console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' ); +} +} ); +}; +GLTFParser.prototype.loadBufferViews = function () { +var json = this.json; +return this._withDependencies( [ +"buffers" +] ).then( function ( dependencies ) { +return _each( json.bufferViews, function ( bufferView ) { +var arraybuffer = dependencies.buffers[ bufferView.buffer ]; +var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; +return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); +} ); +} ); +}; +GLTFParser.prototype.loadAccessors = function () { +var json = this.json; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.accessors, function ( accessor ) { +var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; +var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; +var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; +var elementBytes = TypedArray.BYTES_PER_ELEMENT; +var itemBytes = elementBytes * itemSize; +var array; +if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { +array = new TypedArray( arraybuffer ); +var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); +return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); +} else { +array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); +return new THREE.BufferAttribute( array, itemSize ); +} +} ); +} ); +}; +GLTFParser.prototype.loadTextures = function () { +var json = this.json; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.textures, function ( texture ) { +if ( texture.source !== undefined ) { +return new Promise( function ( resolve ) { +var source = json.images[ texture.source ]; +var sourceUri = source.uri; +var urlCreator; +if ( source.bufferView !== undefined ) { +var bufferView = dependencies.bufferViews[ source.bufferView ]; +var blob = new Blob( [ bufferView ], { type: source.mimeType } ); +urlCreator = window.URL || window.webkitURL; +sourceUri = urlCreator.createObjectURL( blob ); +} +var textureLoader = THREE.Loader.Handlers.get( sourceUri ); +if ( textureLoader === null ) { +textureLoader = new THREE.TextureLoader(); +} +textureLoader.setCrossOrigin( options.crossOrigin ); +textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { +if ( urlCreator !== undefined ) { +urlCreator.revokeObjectURL( sourceUri ); +} +_texture.flipY = false; +if ( texture.name !== undefined ) _texture.name = texture.name; +_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; +if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { +console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + +'internalFormat will be forced to be the same value as format.' ); +} +_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; +if ( texture.sampler !== undefined ) { +var sampler = json.samplers[ texture.sampler ]; +_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; +_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; +_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; +_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; +} +resolve( _texture ); +}, undefined, function () { +resolve(); +} ); +} ); +} +} ); +} ); +}; +GLTFParser.prototype.loadMaterials = function () { +var json = this.json; +return this._withDependencies( [ +"shaders", +"textures" +] ).then( function ( dependencies ) { +return _each( json.materials, function ( material ) { +var materialType; +var materialValues = {}; +var materialParams = {}; +var khr_material; +if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { +khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; +} +if ( khr_material ) { +var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; +switch ( khr_material.technique ) { +case 'BLINN' : +case 'PHONG' : +materialType = THREE.MeshPhongMaterial; +keys.push( 'diffuse', 'specular', 'shininess' ); +break; +case 'LAMBERT' : +materialType = THREE.MeshLambertMaterial; +keys.push( 'diffuse' ); +break; +case 'CONSTANT' : +default : +materialType = THREE.MeshBasicMaterial; +break; +} +keys.forEach( function( v ) { +if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; +} ); +if ( khr_material.doubleSided || materialValues.doubleSided ) { +materialParams.side = THREE.DoubleSide; +} +if ( khr_material.transparent || materialValues.transparent ) { +materialParams.transparent = true; +materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; +} +} else if ( material.technique === undefined ) { +if ( material.pbrMetallicRoughness !== undefined ) { +materialType = THREE.MeshStandardMaterial; +if ( material.pbrMetallicRoughness !== undefined ) { +var metallicRoughness = material.pbrMetallicRoughness; +materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); +materialParams.opacity = 1.0; +if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { +var array = metallicRoughness.baseColorFactor; +materialParams.color.fromArray( array ); +materialParams.opacity = array[ 3 ]; +} +if ( metallicRoughness.baseColorTexture !== undefined ) { +materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ]; +} +if ( materialParams.opacity < 1.0 || +( materialParams.map !== undefined && +( materialParams.map.format === THREE.AlphaFormat || +materialParams.map.format === THREE.RGBAFormat || +materialParams.map.format === THREE.LuminanceAlphaFormat ) ) ) { +materialParams.transparent = true; +} +materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; +materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; +if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { +var textureIndex = metallicRoughness.metallicRoughnessTexture.index; +materialParams.metalnessMap = dependencies.textures[ textureIndex ]; +materialParams.roughnessMap = dependencies.textures[ textureIndex ]; +} +} +} else { +materialType = THREE.MeshPhongMaterial; +} +if ( material.normalTexture !== undefined ) { +materialParams.normalMap = dependencies.textures[ material.normalTexture.index ]; +} +if ( material.occlusionTexture !== undefined ) { +materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ]; +} +if ( material.emissiveTexture !== undefined ) { +materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ]; +} +materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); +if ( material.emissiveFactor !== undefined ) { +materialParams.emissive.fromArray( material.emissiveFactor ); +} +Object.assign( materialValues, material.values ); +} else { +materialType = DeferredShaderMaterial; +var technique = json.techniques[ material.technique ]; +materialParams.uniforms = {}; +var program = json.programs[ technique.program ]; +if ( program ) { +materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; +if ( ! materialParams.fragmentShader ) { +console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); +materialType = THREE.MeshPhongMaterial; +} +var vertexShader = dependencies.shaders[ program.vertexShader ]; +if ( ! vertexShader ) { +console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); +materialType = THREE.MeshPhongMaterial; +} +materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); +var uniforms = technique.uniforms; +for ( var uniformId in uniforms ) { +var pname = uniforms[ uniformId ]; +var shaderParam = technique.parameters[ pname ]; +var ptype = shaderParam.type; +if ( WEBGL_TYPE[ ptype ] ) { +var pcount = shaderParam.count; +var value; +if ( material.values !== undefined ) value = material.values[ pname ]; +var uvalue = new WEBGL_TYPE[ ptype ](); +var usemantic = shaderParam.semantic; +var unode = shaderParam.node; +switch ( ptype ) { +case WEBGL_CONSTANTS.FLOAT: +uvalue = shaderParam.value; +if ( pname == "transparency" ) { +materialParams.transparent = true; +} +if ( value !== undefined ) { +uvalue = value; +} +break; +case WEBGL_CONSTANTS.FLOAT_VEC2: +case WEBGL_CONSTANTS.FLOAT_VEC3: +case WEBGL_CONSTANTS.FLOAT_VEC4: +case WEBGL_CONSTANTS.FLOAT_MAT3: +if ( shaderParam && shaderParam.value ) { +uvalue.fromArray( shaderParam.value ); +} +if ( value ) { +uvalue.fromArray( value ); +} +break; +case WEBGL_CONSTANTS.FLOAT_MAT2: +console.warn( "FLOAT_MAT2 is not a supported uniform type" ); +break; +case WEBGL_CONSTANTS.FLOAT_MAT4: +if ( pcount ) { +uvalue = new Array( pcount ); +for ( var mi = 0; mi < pcount; mi ++ ) { +uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); +} +if ( shaderParam && shaderParam.value ) { +var m4v = shaderParam.value; +uvalue.fromArray( m4v ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} else { +if ( shaderParam && shaderParam.value ) { +var m4 = shaderParam.value; +uvalue.fromArray( m4 ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} +break; +case WEBGL_CONSTANTS.SAMPLER_2D: +if ( value !== undefined ) { +uvalue = dependencies.textures[ value ]; +} else if ( shaderParam.value !== undefined ) { +uvalue = dependencies.textures[ shaderParam.value ]; +} else { +uvalue = null; +} +break; +} +materialParams.uniforms[ uniformId ] = { +value: uvalue, +semantic: usemantic, +node: unode +}; +} else { +throw new Error( "Unknown shader uniform param type: " + ptype ); +} +} +var states = technique.states || {}; +var enables = states.enable || []; +var functions = states.functions || {}; +var enableCullFace = false; +var enableDepthTest = false; +var enableBlend = false; +for ( var i = 0, il = enables.length; i < il; i ++ ) { +var enable = enables[ i ]; +switch ( STATES_ENABLES[ enable ] ) { +case 'CULL_FACE': +enableCullFace = true; +break; +case 'DEPTH_TEST': +enableDepthTest = true; +break; +case 'BLEND': +enableBlend = true; +break; +case 'SCISSOR_TEST': +case 'POLYGON_OFFSET_FILL': +case 'SAMPLE_ALPHA_TO_COVERAGE': +break; +default: +throw new Error( "Unknown technique.states.enable: " + enable ); +} +} +if ( enableCullFace ) { +materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; +} else { +materialParams.side = THREE.DoubleSide; +} +materialParams.depthTest = enableDepthTest; +materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; +materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; +materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; +materialParams.transparent = enableBlend; +var blendEquationSeparate = functions.blendEquationSeparate; +if ( blendEquationSeparate !== undefined ) { +materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; +materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; +} else { +materialParams.blendEquation = THREE.AddEquation; +materialParams.blendEquationAlpha = THREE.AddEquation; +} +var blendFuncSeparate = functions.blendFuncSeparate; +if ( blendFuncSeparate !== undefined ) { +materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; +materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; +materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; +materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; +} else { +materialParams.blendSrc = THREE.OneFactor; +materialParams.blendDst = THREE.ZeroFactor; +materialParams.blendSrcAlpha = THREE.OneFactor; +materialParams.blendDstAlpha = THREE.ZeroFactor; +} +} +} +if ( Array.isArray( materialValues.diffuse ) ) { +materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); +} else if ( typeof( materialValues.diffuse ) === 'string' ) { +materialParams.map = dependencies.textures[ materialValues.diffuse ]; +} +delete materialParams.diffuse; +if ( typeof( materialValues.reflective ) === 'string' ) { +materialParams.envMap = dependencies.textures[ materialValues.reflective ]; +} +if ( typeof( materialValues.bump ) === 'string' ) { +materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; +} +if ( Array.isArray( materialValues.emission ) ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.color = new THREE.Color().fromArray( materialValues.emission ); +} else { +materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); +} +} else if ( typeof( materialValues.emission ) === 'string' ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.map = dependencies.textures[ materialValues.emission ]; +} else { +materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; +} +} +if ( Array.isArray( materialValues.specular ) ) { +materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); +} else if ( typeof( materialValues.specular ) === 'string' ) { +materialParams.specularMap = dependencies.textures[ materialValues.specular ]; +} +if ( materialValues.shininess !== undefined ) { +materialParams.shininess = materialValues.shininess; +} +var _material = new materialType( materialParams ); +if ( material.name !== undefined ) _material.name = material.name; +return _material; +} ); +} ); +}; +GLTFParser.prototype.loadMeshes = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"materials" +] ).then( function ( dependencies ) { +return _each( json.meshes, function ( mesh ) { +var group = new THREE.Group(); +if ( mesh.name !== undefined ) group.name = mesh.name; +if ( mesh.extras ) group.userData = mesh.extras; +var primitives = mesh.primitives || []; +for ( var name in primitives ) { +var primitive = primitives[ name ]; +var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); +var geometry; +var meshNode; +if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { +geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( attributeEntry === undefined ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'NORMAL': +geometry.addAttribute( 'normal', bufferAttribute ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +geometry.addAttribute( 'uv', bufferAttribute ); +break; +case 'TEXCOORD_1': +geometry.addAttribute( 'uv2', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +case 'WEIGHTS_0': +case 'WEIGHT': // WEIGHT semantic deprecated. +geometry.addAttribute( 'skinWeight', bufferAttribute ); +break; +case 'JOINTS_0': +case 'JOINT': // JOINT semantic deprecated. +geometry.addAttribute( 'skinIndex', bufferAttribute ); +break; +} +} +if ( primitive.indices !== undefined ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +} +meshNode = new THREE.Mesh( geometry, material ); +meshNode.castShadow = true; +} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { +geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +} +} +if ( primitive.indices !== undefined ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +meshNode = new THREE.LineSegments( geometry, material ); +} else { +meshNode = new THREE.Line( geometry, material ); +} +} else { +throw new Error( "Only triangular and line primitives are supported" ); +} +if ( geometry.attributes.color !== undefined ) { +material.vertexColors = THREE.VertexColors; +material.needsUpdate = true; +} +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} +return group; +} ); +} ); +}; +GLTFParser.prototype.loadCameras = function () { +var json = this.json; +return _each( json.cameras, function ( camera ) { +if ( camera.type == "perspective" && camera.perspective ) { +var yfov = camera.perspective.yfov; +var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; +var xfov = yfov * aspectRatio; +var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} else if ( camera.type == "orthographic" && camera.orthographic ) { +var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} +} ); +}; +GLTFParser.prototype.loadSkins = function () { +var json = this.json; +return this._withDependencies( [ +"accessors" +] ).then( function ( dependencies ) { +return _each( json.skins, function ( skin ) { +var bindShapeMatrix = new THREE.Matrix4(); +if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); +var _skin = { +bindShapeMatrix: bindShapeMatrix, +jointNames: skin.jointNames, +inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] +}; +return _skin; +} ); +} ); +}; +GLTFParser.prototype.loadAnimations = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"nodes" +] ).then( function ( dependencies ) { +return _each( json.animations, function ( animation, animationId ) { +var tracks = []; +for ( var channelId in animation.channels ) { +var channel = animation.channels[ channelId ]; +var sampler = animation.samplers[ channel.sampler ]; +if ( sampler ) { +var target = channel.target; +var name = target.node || target.id; // NOTE: target.id is deprecated. +var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; +var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; +var inputAccessor = dependencies.accessors[ input ]; +var outputAccessor = dependencies.accessors[ output ]; +var node = dependencies.nodes[ name ]; +if ( node ) { +node.updateMatrix(); +node.matrixAutoUpdate = true; +var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation +? THREE.QuaternionKeyframeTrack +: THREE.VectorKeyframeTrack; +var targetName = node.name ? node.name : node.uuid; +var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; +tracks.push( new TypedKeyframeTrack( +targetName + '.' + PATH_PROPERTIES[ target.path ], +THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), +THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), +interpolation +) ); +} +} +} +var name = animation.name !== undefined ? animation.name : "animation_" + animationId; +return new THREE.AnimationClip( name, undefined, tracks ); +} ); +} ); +}; +GLTFParser.prototype.loadNodes = function () { +var json = this.json; +var extensions = this.extensions; +var scope = this; +return _each( json.nodes, function ( node ) { +var matrix = new THREE.Matrix4(); +var _node; +if ( node.jointName ) { +_node = new THREE.Bone(); +_node.name = node.name !== undefined ? node.name : node.jointName; +_node.jointName = node.jointName; +} else { +_node = new THREE.Object3D(); +if ( node.name !== undefined ) _node.name = node.name; +} +if ( node.extras ) _node.userData = node.extras; +if ( node.matrix !== undefined ) { +matrix.fromArray( node.matrix ); +_node.applyMatrix( matrix ); +} else { +if ( node.translation !== undefined ) { +_node.position.fromArray( node.translation ); +} +if ( node.rotation !== undefined ) { +_node.quaternion.fromArray( node.rotation ); +} +if ( node.scale !== undefined ) { +_node.scale.fromArray( node.scale ); +} +} +return _node; +} ).then( function ( __nodes ) { +return scope._withDependencies( [ +"meshes", +"skins", +"cameras" +] ).then( function ( dependencies ) { +return _each( __nodes, function ( _node, nodeId ) { +var node = json.nodes[ nodeId ]; +var meshes; +if ( node.mesh !== undefined) { +meshes = [ node.mesh ]; +} else if ( node.meshes !== undefined ) { +console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' ); +meshes = node.meshes; +} +if ( meshes !== undefined ) { +for ( var meshId in meshes ) { +var mesh = meshes[ meshId ]; +var group = dependencies.meshes[ mesh ]; +if ( group === undefined ) { +console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' ); +continue; +} +for ( var childrenId in group.children ) { +var child = group.children[ childrenId ]; +var originalMaterial = child.material; +var originalGeometry = child.geometry; +var originalUserData = child.userData; +var originalName = child.name; +var material; +if ( originalMaterial.isDeferredShaderMaterial ) { +originalMaterial = material = originalMaterial.create(); +} else { +material = originalMaterial; +} +switch ( child.type ) { +case 'LineSegments': +child = new THREE.LineSegments( originalGeometry, material ); +break; +case 'LineLoop': +child = new THREE.LineLoop( originalGeometry, material ); +break; +case 'Line': +child = new THREE.Line( originalGeometry, material ); +break; +default: +child = new THREE.Mesh( originalGeometry, material ); +} +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var skinEntry; +if ( node.skin !== undefined ) { +skinEntry = dependencies.skins[ node.skin ]; +} +if ( skinEntry ) { +var getJointNode = function ( jointId ) { +var keys = Object.keys( __nodes ); +for ( var i = 0, il = keys.length; i < il; i ++ ) { +var n = __nodes[ keys[ i ] ]; +if ( n.jointName === jointId ) return n; +} +return null; +}; +var geometry = originalGeometry; +var material = originalMaterial; +material.skinning = true; +child = new THREE.SkinnedMesh( geometry, material, false ); +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var bones = []; +var boneInverses = []; +for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { +var jointId = skinEntry.jointNames[ i ]; +var jointNode = getJointNode( jointId ); +if ( jointNode ) { +bones.push( jointNode ); +var m = skinEntry.inverseBindMatrices.array; +var mat = new THREE.Matrix4().fromArray( m, i * 16 ); +boneInverses.push( mat ); +} else { +console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); +} +} +child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); +var buildBoneGraph = function ( parentJson, parentObject, property ) { +var children = parentJson[ property ]; +if ( children === undefined ) return; +for ( var i = 0, il = children.length; i < il; i ++ ) { +var nodeId = children[ i ]; +var bone = __nodes[ nodeId ]; +var boneJson = json.nodes[ nodeId ]; +if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { +parentObject.add( bone ); +buildBoneGraph( boneJson, bone, 'children' ); +} +} +}; +buildBoneGraph( node, child, 'skeletons' ); +} +_node.add( child ); +} +} +} +if ( node.camera !== undefined ) { +var camera = dependencies.cameras[ node.camera ]; +_node.add( camera ); +} +if ( node.extensions +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { +var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; +var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; +_node.add( light ); +} +return _node; +} ); +} ); +} ); +}; +GLTFParser.prototype.loadScenes = function () { +var json = this.json; +function buildNodeHierachy( nodeId, parentObject, allNodes ) { +var _node = allNodes[ nodeId ]; +parentObject.add( _node ); +var node = json.nodes[ nodeId ]; +if ( node.children ) { +var children = node.children; +for ( var i = 0, l = children.length; i < l; i ++ ) { +var child = children[ i ]; +buildNodeHierachy( child, _node, allNodes ); +} +} +} +return this._withDependencies( [ +"nodes" +] ).then( function ( dependencies ) { +return _each( json.scenes, function ( scene ) { +var _scene = new THREE.Scene(); +if ( scene.name !== undefined ) _scene.name = scene.name; +if ( scene.extras ) _scene.userData = scene.extras; +var nodes = scene.nodes || []; +for ( var i = 0, l = nodes.length; i < l; i ++ ) { +var nodeId = nodes[ i ]; +buildNodeHierachy( nodeId, _scene, dependencies.nodes ); +} +_scene.traverse( function ( child ) { +if ( child.material && child.material.isRawShaderMaterial ) { +child.gltfShader = new GLTFShader( child, dependencies.nodes ); +child.onBeforeRender = function(renderer, scene, camera){ +this.gltfShader.update(scene, camera); +}; +} +} ); +return _scene; +} ); +} ); +}; +return GLTF2Loader; +} )(); +THREE.GLTFLoader = ( function () { +function GLTFLoader( manager ) { +this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +} +GLTFLoader.prototype = { +constructor: GLTFLoader, +load: function ( url, onLoad, onProgress, onError ) { +var scope = this; +var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); +var loader = new THREE.FileLoader( scope.manager ); +loader.setResponseType( 'arraybuffer' ); +loader.load( url, function ( data ) { +scope.parse( data, onLoad, path ); +}, onProgress, onError ); +}, +setCrossOrigin: function ( value ) { +this.crossOrigin = value; +}, +setPath: function ( value ) { +this.path = value; +}, +parse: function ( data, callback, path ) { +var content; +var extensions = {}; +var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); +if ( magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic ) { +extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); +content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; +} else { +content = convertUint8ArrayToString( new Uint8Array( data ) ); +} +var json = JSON.parse( content ); +if ( json.extensionsUsed && json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { +extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); +} +console.time( 'GLTFLoader' ); +var parser = new GLTFParser( json, extensions, { +path: path || this.path, +crossOrigin: this.crossOrigin +} ); +parser.parse( function ( scene, scenes, cameras, animations ) { +console.timeEnd( 'GLTFLoader' ); +var glTF = { +"scene": scene, +"scenes": scenes, +"cameras": cameras, +"animations": animations +}; +callback( glTF ); +} ); +} +}; +function GLTFRegistry() { +var objects = {}; +return { +get: function ( key ) { +return objects[ key ]; +}, +add: function ( key, object ) { +objects[ key ] = object; +}, +remove: function ( key ) { +delete objects[ key ]; +}, +removeAll: function () { +objects = {}; +}, +update: function ( scene, camera ) { +for ( var name in objects ) { +var object = objects[ name ]; +if ( object.update ) { +object.update( scene, camera ); +} +} +} +}; +} +GLTFLoader.Shaders = { +update: function () { +console.warn( 'THREE.GLTFLoader.Shaders has been deprecated, and now updates automatically.' ); +} +}; +function GLTFShader( targetNode, allNodes ) { +var boundUniforms = {}; +var uniforms = targetNode.material.uniforms; +for ( var uniformId in uniforms ) { +var uniform = uniforms[ uniformId ]; +if ( uniform.semantic ) { +var sourceNodeRef = uniform.node; +var sourceNode = targetNode; +if ( sourceNodeRef ) { +sourceNode = allNodes[ sourceNodeRef ]; +} +boundUniforms[ uniformId ] = { +semantic: uniform.semantic, +sourceNode: sourceNode, +targetNode: targetNode, +uniform: uniform +}; +} +} +this.boundUniforms = boundUniforms; +this._m4 = new THREE.Matrix4(); +} +GLTFShader.prototype.update = function ( scene, camera ) { +var boundUniforms = this.boundUniforms; +for ( var name in boundUniforms ) { +var boundUniform = boundUniforms[ name ]; +switch ( boundUniform.semantic ) { +case "MODELVIEW": +var m4 = boundUniform.uniform.value; +m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +break; +case "MODELVIEWINVERSETRANSPOSE": +var m3 = boundUniform.uniform.value; +this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld ); +m3.getNormalMatrix( this._m4 ); +break; +case "PROJECTION": +var m4 = boundUniform.uniform.value; +m4.copy( camera.projectionMatrix ); +break; +case "JOINTMATRIX": +var m4v = boundUniform.uniform.value; +for ( var mi = 0; mi < m4v.length; mi ++ ) { +m4v[ mi ] +.getInverse( boundUniform.sourceNode.matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld ) +.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] ) +.multiply( boundUniform.targetNode.bindMatrix ); +} +break; +default : +console.warn( "Unhandled shader semantic: " + boundUniform.semantic ); +break; +} +} +}; +GLTFLoader.Animations = { +update: function () { +console.warn( 'THREE.GLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.' ); +} +}; +var EXTENSIONS = { +KHR_BINARY_GLTF: 'KHR_binary_glTF', +KHR_MATERIALS_COMMON: 'KHR_materials_common' +}; +function GLTFMaterialsCommonExtension( json ) { +this.name = EXTENSIONS.KHR_MATERIALS_COMMON; +this.lights = {}; +var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) || {}; +var lights = extension.lights || {}; +for ( var lightId in lights ) { +var light = lights[ lightId ]; +var lightNode; +var lightParams = light[ light.type ]; +var color = new THREE.Color().fromArray( lightParams.color ); +switch ( light.type ) { +case "directional": +lightNode = new THREE.DirectionalLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "point": +lightNode = new THREE.PointLight( color ); +break; +case "spot": +lightNode = new THREE.SpotLight( color ); +lightNode.position.set( 0, 0, 1 ); +break; +case "ambient": +lightNode = new THREE.AmbientLight( color ); +break; +} +if ( lightNode ) { +this.lights[ lightId ] = lightNode; +} +} +} +var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; +var BINARY_EXTENSION_HEADER_DEFAULTS = { magic: 'glTF', version: 1, contentFormat: 0 }; +var BINARY_EXTENSION_HEADER_LENGTH = 20; +function GLTFBinaryExtension( data ) { +this.name = EXTENSIONS.KHR_BINARY_GLTF; +var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); +var header = { +magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), +version: headerView.getUint32( 4, true ), +length: headerView.getUint32( 8, true ), +contentLength: headerView.getUint32( 12, true ), +contentFormat: headerView.getUint32( 16, true ) +}; +for ( var key in BINARY_EXTENSION_HEADER_DEFAULTS ) { +var value = BINARY_EXTENSION_HEADER_DEFAULTS[ key ]; +if ( header[ key ] !== value ) { +throw new Error( 'Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value ); +} +} +var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength ); +this.header = header; +this.content = convertUint8ArrayToString( contentArray ); +this.body = data.slice( BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length ); +} +GLTFBinaryExtension.prototype.loadShader = function ( shader, bufferViews ) { +var bufferView = bufferViews[ shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].bufferView ]; +var array = new Uint8Array( bufferView ); +return convertUint8ArrayToString( array ); +}; +GLTFBinaryExtension.prototype.loadTextureSourceUri = function ( source, bufferViews ) { +var metadata = source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ]; +var bufferView = bufferViews[ metadata.bufferView ]; +var stringData = convertUint8ArrayToString( new Uint8Array( bufferView ) ); +return 'data:' + metadata.mimeType + ';base64,' + btoa( stringData ); +}; +var WEBGL_CONSTANTS = { +FLOAT: 5126, +FLOAT_MAT3: 35675, +FLOAT_MAT4: 35676, +FLOAT_VEC2: 35664, +FLOAT_VEC3: 35665, +FLOAT_VEC4: 35666, +LINEAR: 9729, +REPEAT: 10497, +SAMPLER_2D: 35678, +TRIANGLES: 4, +LINES: 1, +UNSIGNED_BYTE: 5121, +UNSIGNED_SHORT: 5123, +VERTEX_SHADER: 35633, +FRAGMENT_SHADER: 35632 +}; +var WEBGL_TYPE = { +5126: Number, +35675: THREE.Matrix3, +35676: THREE.Matrix4, +35664: THREE.Vector2, +35665: THREE.Vector3, +35666: THREE.Vector4, +35678: THREE.Texture +}; +var WEBGL_COMPONENT_TYPES = { +5120: Int8Array, +5121: Uint8Array, +5122: Int16Array, +5123: Uint16Array, +5125: Uint32Array, +5126: Float32Array +}; +var WEBGL_FILTERS = { +9728: THREE.NearestFilter, +9729: THREE.LinearFilter, +9984: THREE.NearestMipMapNearestFilter, +9985: THREE.LinearMipMapNearestFilter, +9986: THREE.NearestMipMapLinearFilter, +9987: THREE.LinearMipMapLinearFilter +}; +var WEBGL_WRAPPINGS = { +33071: THREE.ClampToEdgeWrapping, +33648: THREE.MirroredRepeatWrapping, +10497: THREE.RepeatWrapping +}; +var WEBGL_TEXTURE_FORMATS = { +6406: THREE.AlphaFormat, +6407: THREE.RGBFormat, +6408: THREE.RGBAFormat, +6409: THREE.LuminanceFormat, +6410: THREE.LuminanceAlphaFormat +}; +var WEBGL_TEXTURE_DATATYPES = { +5121: THREE.UnsignedByteType, +32819: THREE.UnsignedShort4444Type, +32820: THREE.UnsignedShort5551Type, +33635: THREE.UnsignedShort565Type +}; +var WEBGL_SIDES = { +1028: THREE.BackSide, // Culling front +1029: THREE.FrontSide // Culling back +}; +var WEBGL_DEPTH_FUNCS = { +512: THREE.NeverDepth, +513: THREE.LessDepth, +514: THREE.EqualDepth, +515: THREE.LessEqualDepth, +516: THREE.GreaterEqualDepth, +517: THREE.NotEqualDepth, +518: THREE.GreaterEqualDepth, +519: THREE.AlwaysDepth +}; +var WEBGL_BLEND_EQUATIONS = { +32774: THREE.AddEquation, +32778: THREE.SubtractEquation, +32779: THREE.ReverseSubtractEquation +}; +var WEBGL_BLEND_FUNCS = { +0: THREE.ZeroFactor, +1: THREE.OneFactor, +768: THREE.SrcColorFactor, +769: THREE.OneMinusSrcColorFactor, +770: THREE.SrcAlphaFactor, +771: THREE.OneMinusSrcAlphaFactor, +772: THREE.DstAlphaFactor, +773: THREE.OneMinusDstAlphaFactor, +774: THREE.DstColorFactor, +775: THREE.OneMinusDstColorFactor, +776: THREE.SrcAlphaSaturateFactor +}; +var WEBGL_TYPE_SIZES = { +'SCALAR': 1, +'VEC2': 2, +'VEC3': 3, +'VEC4': 4, +'MAT2': 4, +'MAT3': 9, +'MAT4': 16 +}; +var PATH_PROPERTIES = { +scale: 'scale', +translation: 'position', +rotation: 'quaternion' +}; +var INTERPOLATION = { +LINEAR: THREE.InterpolateLinear, +STEP: THREE.InterpolateDiscrete +}; +var STATES_ENABLES = { +2884: 'CULL_FACE', +2929: 'DEPTH_TEST', +3042: 'BLEND', +3089: 'SCISSOR_TEST', +32823: 'POLYGON_OFFSET_FILL', +32926: 'SAMPLE_ALPHA_TO_COVERAGE' +}; +function _each( object, callback, thisObj ) { +if ( !object ) { +return Promise.resolve(); +} +var results; +var fns = []; +if ( Object.prototype.toString.call( object ) === '[object Array]' ) { +results = []; +var length = object.length; +for ( var idx = 0; idx < length; idx ++ ) { +var value = callback.call( thisObj || this, object[ idx ], idx ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, idx )); +} else { +results[ idx ] = value; +} +} +} +} else { +results = {}; +for ( var key in object ) { +if ( object.hasOwnProperty( key ) ) { +var value = callback.call( thisObj || this, object[ key ], key ); +if ( value ) { +fns.push( value ); +if ( value instanceof Promise ) { +value.then( function( key, value ) { +results[ key ] = value; +}.bind( this, key )); +} else { +results[ key ] = value; +} +} +} +} +} +return Promise.all( fns ).then( function() { +return results; +}); +} +function resolveURL( url, path ) { +if ( typeof url !== 'string' || url === '' ) +return ''; +if ( /^(https?:)?\/\//i.test( url ) ) { +return url; +} +if ( /^data:.*,.*$/i.test( url ) ) { +return url; +} +return ( path || '' ) + url; +} +function convertUint8ArrayToString( array ) { +var s = ''; +for ( var i = 0; i < array.length; i ++ ) { +s += String.fromCharCode( array[ i ] ); +} +return s; +} +function replaceTHREEShaderAttributes( shaderText, technique ) { +var attributes = {}; +for ( var attributeId in technique.attributes ) { +var pname = technique.attributes[ attributeId ]; +var param = technique.parameters[ pname ]; +var atype = param.type; +var semantic = param.semantic; +attributes[ attributeId ] = { +type: atype, +semantic: semantic +}; +} +var shaderParams = technique.parameters; +var shaderAttributes = technique.attributes; +var params = {}; +for ( var attributeId in attributes ) { +var pname = shaderAttributes[ attributeId ]; +var shaderParam = shaderParams[ pname ]; +var semantic = shaderParam.semantic; +if ( semantic ) { +params[ attributeId ] = shaderParam; +} +} +for ( var pname in params ) { +var param = params[ pname ]; +var semantic = param.semantic; +var regEx = new RegExp( "\\b" + pname + "\\b", "g" ); +switch ( semantic ) { +case "POSITION": +shaderText = shaderText.replace( regEx, 'position' ); +break; +case "NORMAL": +shaderText = shaderText.replace( regEx, 'normal' ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +shaderText = shaderText.replace( regEx, 'uv' ); +break; +case 'TEXCOORD_1': +shaderText = shaderText.replace( regEx, 'uv2' ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +shaderText = shaderText.replace( regEx, 'color' ); +break; +case "WEIGHT": +shaderText = shaderText.replace( regEx, 'skinWeight' ); +break; +case "JOINT": +shaderText = shaderText.replace( regEx, 'skinIndex' ); +break; +} +} +return shaderText; +} +function createDefaultMaterial() { +return new THREE.MeshPhongMaterial( { +color: 0x00000, +emissive: 0x888888, +specular: 0x000000, +shininess: 0, +transparent: false, +depthTest: true, +side: THREE.FrontSide +} ); +} +function DeferredShaderMaterial( params ) { +this.isDeferredShaderMaterial = true; +this.params = params; +} +DeferredShaderMaterial.prototype.create = function () { +var uniforms = THREE.UniformsUtils.clone( this.params.uniforms ); +for ( var uniformId in this.params.uniforms ) { +var originalUniform = this.params.uniforms[ uniformId ]; +if ( originalUniform.value instanceof THREE.Texture ) { +uniforms[ uniformId ].value = originalUniform.value; +uniforms[ uniformId ].value.needsUpdate = true; +} +uniforms[ uniformId ].semantic = originalUniform.semantic; +uniforms[ uniformId ].node = originalUniform.node; +} +this.params.uniforms = uniforms; +return new THREE.RawShaderMaterial( this.params ); +}; +function GLTFParser( json, extensions, options ) { +this.json = json || {}; +this.extensions = extensions || {}; +this.options = options || {}; +this.cache = new GLTFRegistry(); +} +GLTFParser.prototype._withDependencies = function ( dependencies ) { +var _dependencies = {}; +for ( var i = 0; i < dependencies.length; i ++ ) { +var dependency = dependencies[ i ]; +var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); +var cached = this.cache.get( dependency ); +if ( cached !== undefined ) { +_dependencies[ dependency ] = cached; +} else if ( this[ fnName ] ) { +var fn = this[ fnName ](); +this.cache.add( dependency, fn ); +_dependencies[ dependency ] = fn; +} +} +return _each( _dependencies, function ( dependency ) { +return dependency; +} ); +}; +GLTFParser.prototype.parse = function ( callback ) { +var json = this.json; +this.cache.removeAll(); +this._withDependencies( [ +"scenes", +"cameras", +"animations" +] ).then( function ( dependencies ) { +var scenes = []; +for ( var name in dependencies.scenes ) { +scenes.push( dependencies.scenes[ name ] ); +} +var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; +var cameras = []; +for ( var name in dependencies.cameras ) { +var camera = dependencies.cameras[ name ]; +cameras.push( camera ); +} +var animations = []; +for ( var name in dependencies.animations ) { +animations.push( dependencies.animations[ name ] ); +} +callback( scene, scenes, cameras, animations ); +} ); +}; +GLTFParser.prototype.loadShaders = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.shaders, function ( shader ) { +if ( shader.extensions && shader.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadShader( shader, dependencies.bufferViews ); +} +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'text' ); +loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) { +resolve( shaderText ); +} ); +} ); +} ); +} ); +}; +GLTFParser.prototype.loadBuffers = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return _each( json.buffers, function ( buffer, name ) { +if ( name === BINARY_EXTENSION_BUFFER_NAME ) { +return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body; +} +if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) { +return new Promise( function ( resolve ) { +var loader = new THREE.FileLoader(); +loader.setResponseType( 'arraybuffer' ); +loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) { +resolve( buffer ); +} ); +} ); +} else { +console.warn( 'THREE.GLTFLoader: ' + buffer.type + ' buffer type is not supported' ); +} +} ); +}; +GLTFParser.prototype.loadBufferViews = function () { +var json = this.json; +return this._withDependencies( [ +"buffers" +] ).then( function ( dependencies ) { +return _each( json.bufferViews, function ( bufferView ) { +var arraybuffer = dependencies.buffers[ bufferView.buffer ]; +var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0; +return arraybuffer.slice( bufferView.byteOffset, bufferView.byteOffset + byteLength ); +} ); +} ); +}; +GLTFParser.prototype.loadAccessors = function () { +var json = this.json; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.accessors, function ( accessor ) { +var arraybuffer = dependencies.bufferViews[ accessor.bufferView ]; +var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; +var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; +var elementBytes = TypedArray.BYTES_PER_ELEMENT; +var itemBytes = elementBytes * itemSize; +if ( accessor.byteStride && accessor.byteStride !== itemBytes ) { +var array = new TypedArray( arraybuffer ); +var ib = new THREE.InterleavedBuffer( array, accessor.byteStride / elementBytes ); +return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); +} else { +array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize ); +return new THREE.BufferAttribute( array, itemSize ); +} +} ); +} ); +}; +GLTFParser.prototype.loadTextures = function () { +var json = this.json; +var extensions = this.extensions; +var options = this.options; +return this._withDependencies( [ +"bufferViews" +] ).then( function ( dependencies ) { +return _each( json.textures, function ( texture ) { +if ( texture.source ) { +return new Promise( function ( resolve ) { +var source = json.images[ texture.source ]; +var sourceUri = source.uri; +if ( source.extensions && source.extensions[ EXTENSIONS.KHR_BINARY_GLTF ] ) { +sourceUri = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].loadTextureSourceUri( source, dependencies.bufferViews ); +} +var textureLoader = THREE.Loader.Handlers.get( sourceUri ); +if ( textureLoader === null ) { +textureLoader = new THREE.TextureLoader(); +} +textureLoader.setCrossOrigin( options.crossOrigin ); +textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) { +_texture.flipY = false; +if ( texture.name !== undefined ) _texture.name = texture.name; +_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat; +if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) { +console.warn( 'THREE.GLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + +'internalFormat will be forced to be the same value as format.' ); +} +_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType; +if ( texture.sampler ) { +var sampler = json.samplers[ texture.sampler ]; +_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; +_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter; +_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; +_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; +} +resolve( _texture ); +}, undefined, function () { +resolve(); +} ); +} ); +} +} ); +} ); +}; +GLTFParser.prototype.loadMaterials = function () { +var json = this.json; +return this._withDependencies( [ +"shaders", +"textures" +] ).then( function ( dependencies ) { +return _each( json.materials, function ( material ) { +var materialType; +var materialValues = {}; +var materialParams = {}; +var khr_material; +if ( material.extensions && material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { +khr_material = material.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; +} +if ( khr_material ) { +var keys = [ 'ambient', 'emission', 'transparent', 'transparency', 'doubleSided' ]; +switch ( khr_material.technique ) { +case 'BLINN' : +case 'PHONG' : +materialType = THREE.MeshPhongMaterial; +keys.push( 'diffuse', 'specular', 'shininess' ); +break; +case 'LAMBERT' : +materialType = THREE.MeshLambertMaterial; +keys.push( 'diffuse' ); +break; +case 'CONSTANT' : +default : +materialType = THREE.MeshBasicMaterial; +break; +} +keys.forEach( function( v ) { +if ( khr_material.values[ v ] !== undefined ) materialValues[ v ] = khr_material.values[ v ]; +} ); +if ( khr_material.doubleSided || materialValues.doubleSided ) { +materialParams.side = THREE.DoubleSide; +} +if ( khr_material.transparent || materialValues.transparent ) { +materialParams.transparent = true; +materialParams.opacity = ( materialValues.transparency !== undefined ) ? materialValues.transparency : 1; +} +} else if ( material.technique === undefined ) { +materialType = THREE.MeshPhongMaterial; +Object.assign( materialValues, material.values ); +} else { +materialType = DeferredShaderMaterial; +var technique = json.techniques[ material.technique ]; +materialParams.uniforms = {}; +var program = json.programs[ technique.program ]; +if ( program ) { +materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ]; +if ( ! materialParams.fragmentShader ) { +console.warn( "ERROR: Missing fragment shader definition:", program.fragmentShader ); +materialType = THREE.MeshPhongMaterial; +} +var vertexShader = dependencies.shaders[ program.vertexShader ]; +if ( ! vertexShader ) { +console.warn( "ERROR: Missing vertex shader definition:", program.vertexShader ); +materialType = THREE.MeshPhongMaterial; +} +materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique ); +var uniforms = technique.uniforms; +for ( var uniformId in uniforms ) { +var pname = uniforms[ uniformId ]; +var shaderParam = technique.parameters[ pname ]; +var ptype = shaderParam.type; +if ( WEBGL_TYPE[ ptype ] ) { +var pcount = shaderParam.count; +var value; +if ( material.values !== undefined ) value = material.values[ pname ]; +var uvalue = new WEBGL_TYPE[ ptype ](); +var usemantic = shaderParam.semantic; +var unode = shaderParam.node; +switch ( ptype ) { +case WEBGL_CONSTANTS.FLOAT: +uvalue = shaderParam.value; +if ( pname == "transparency" ) { +materialParams.transparent = true; +} +if ( value !== undefined ) { +uvalue = value; +} +break; +case WEBGL_CONSTANTS.FLOAT_VEC2: +case WEBGL_CONSTANTS.FLOAT_VEC3: +case WEBGL_CONSTANTS.FLOAT_VEC4: +case WEBGL_CONSTANTS.FLOAT_MAT3: +if ( shaderParam && shaderParam.value ) { +uvalue.fromArray( shaderParam.value ); +} +if ( value ) { +uvalue.fromArray( value ); +} +break; +case WEBGL_CONSTANTS.FLOAT_MAT2: +console.warn( "FLOAT_MAT2 is not a supported uniform type" ); +break; +case WEBGL_CONSTANTS.FLOAT_MAT4: +if ( pcount ) { +uvalue = new Array( pcount ); +for ( var mi = 0; mi < pcount; mi ++ ) { +uvalue[ mi ] = new WEBGL_TYPE[ ptype ](); +} +if ( shaderParam && shaderParam.value ) { +var m4v = shaderParam.value; +uvalue.fromArray( m4v ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} else { +if ( shaderParam && shaderParam.value ) { +var m4 = shaderParam.value; +uvalue.fromArray( m4 ); +} +if ( value ) { +uvalue.fromArray( value ); +} +} +break; +case WEBGL_CONSTANTS.SAMPLER_2D: +if ( value !== undefined ) { +uvalue = dependencies.textures[ value ]; +} else if ( shaderParam.value !== undefined ) { +uvalue = dependencies.textures[ shaderParam.value ]; +} else { +uvalue = null; +} +break; +} +materialParams.uniforms[ uniformId ] = { +value: uvalue, +semantic: usemantic, +node: unode +}; +} else { +throw new Error( "Unknown shader uniform param type: " + ptype ); +} +} +var states = technique.states || {}; +var enables = states.enable || []; +var functions = states.functions || {}; +var enableCullFace = false; +var enableDepthTest = false; +var enableBlend = false; +for ( var i = 0, il = enables.length; i < il; i ++ ) { +var enable = enables[ i ]; +switch ( STATES_ENABLES[ enable ] ) { +case 'CULL_FACE': +enableCullFace = true; +break; +case 'DEPTH_TEST': +enableDepthTest = true; +break; +case 'BLEND': +enableBlend = true; +break; +case 'SCISSOR_TEST': +case 'POLYGON_OFFSET_FILL': +case 'SAMPLE_ALPHA_TO_COVERAGE': +break; +default: +throw new Error( "Unknown technique.states.enable: " + enable ); +} +} +if ( enableCullFace ) { +materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide; +} else { +materialParams.side = THREE.DoubleSide; +} +materialParams.depthTest = enableDepthTest; +materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth; +materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true; +materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending; +materialParams.transparent = enableBlend; +var blendEquationSeparate = functions.blendEquationSeparate; +if ( blendEquationSeparate !== undefined ) { +materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ]; +materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ]; +} else { +materialParams.blendEquation = THREE.AddEquation; +materialParams.blendEquationAlpha = THREE.AddEquation; +} +var blendFuncSeparate = functions.blendFuncSeparate; +if ( blendFuncSeparate !== undefined ) { +materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ]; +materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ]; +materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ]; +materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ]; +} else { +materialParams.blendSrc = THREE.OneFactor; +materialParams.blendDst = THREE.ZeroFactor; +materialParams.blendSrcAlpha = THREE.OneFactor; +materialParams.blendDstAlpha = THREE.ZeroFactor; +} +} +} +if ( Array.isArray( materialValues.diffuse ) ) { +materialParams.color = new THREE.Color().fromArray( materialValues.diffuse ); +} else if ( typeof( materialValues.diffuse ) === 'string' ) { +materialParams.map = dependencies.textures[ materialValues.diffuse ]; +} +delete materialParams.diffuse; +if ( typeof( materialValues.reflective ) === 'string' ) { +materialParams.envMap = dependencies.textures[ materialValues.reflective ]; +} +if ( typeof( materialValues.bump ) === 'string' ) { +materialParams.bumpMap = dependencies.textures[ materialValues.bump ]; +} +if ( Array.isArray( materialValues.emission ) ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.color = new THREE.Color().fromArray( materialValues.emission ); +} else { +materialParams.emissive = new THREE.Color().fromArray( materialValues.emission ); +} +} else if ( typeof( materialValues.emission ) === 'string' ) { +if ( materialType === THREE.MeshBasicMaterial ) { +materialParams.map = dependencies.textures[ materialValues.emission ]; +} else { +materialParams.emissiveMap = dependencies.textures[ materialValues.emission ]; +} +} +if ( Array.isArray( materialValues.specular ) ) { +materialParams.specular = new THREE.Color().fromArray( materialValues.specular ); +} else if ( typeof( materialValues.specular ) === 'string' ) { +materialParams.specularMap = dependencies.textures[ materialValues.specular ]; +} +if ( materialValues.shininess !== undefined ) { +materialParams.shininess = materialValues.shininess; +} +var _material = new materialType( materialParams ); +if ( material.name !== undefined ) _material.name = material.name; +return _material; +} ); +} ); +}; +GLTFParser.prototype.loadMeshes = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"materials" +] ).then( function ( dependencies ) { +return _each( json.meshes, function ( mesh ) { +var group = new THREE.Group(); +if ( mesh.name !== undefined ) group.name = mesh.name; +if ( mesh.extras ) group.userData = mesh.extras; +var primitives = mesh.primitives || []; +for ( var name in primitives ) { +var primitive = primitives[ name ]; +if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { +var geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'NORMAL': +geometry.addAttribute( 'normal', bufferAttribute ); +break; +case 'TEXCOORD_0': +case 'TEXCOORD0': +case 'TEXCOORD': +geometry.addAttribute( 'uv', bufferAttribute ); +break; +case 'TEXCOORD_1': +geometry.addAttribute( 'uv2', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +case 'WEIGHT': +geometry.addAttribute( 'skinWeight', bufferAttribute ); +break; +case 'JOINT': +geometry.addAttribute( 'skinIndex', bufferAttribute ); +break; +} +} +if ( primitive.indices ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +} +var material = dependencies.materials !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial(); +var meshNode = new THREE.Mesh( geometry, material ); +meshNode.castShadow = true; +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { +var geometry = new THREE.BufferGeometry(); +var attributes = primitive.attributes; +for ( var attributeId in attributes ) { +var attributeEntry = attributes[ attributeId ]; +if ( ! attributeEntry ) return; +var bufferAttribute = dependencies.accessors[ attributeEntry ]; +switch ( attributeId ) { +case 'POSITION': +geometry.addAttribute( 'position', bufferAttribute ); +break; +case 'COLOR_0': +case 'COLOR0': +case 'COLOR': +geometry.addAttribute( 'color', bufferAttribute ); +break; +} +} +var material = dependencies.materials[ primitive.material ]; +var meshNode; +if ( primitive.indices ) { +geometry.setIndex( dependencies.accessors[ primitive.indices ] ); +meshNode = new THREE.LineSegments( geometry, material ); +} else { +meshNode = new THREE.Line( geometry, material ); +} +meshNode.name = ( name === "0" ? group.name : group.name + name ); +if ( primitive.extras ) meshNode.userData = primitive.extras; +group.add( meshNode ); +} else { +console.warn( "Only triangular and line primitives are supported" ); +} +} +return group; +} ); +} ); +}; +GLTFParser.prototype.loadCameras = function () { +var json = this.json; +return _each( json.cameras, function ( camera ) { +if ( camera.type == "perspective" && camera.perspective ) { +var yfov = camera.perspective.yfov; +var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1; +var xfov = yfov * aspectRatio; +var _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6 ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} else if ( camera.type == "orthographic" && camera.orthographic ) { +var _camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, camera.orthographic.znear, camera.orthographic.zfar ); +if ( camera.name !== undefined ) _camera.name = camera.name; +if ( camera.extras ) _camera.userData = camera.extras; +return _camera; +} +} ); +}; +GLTFParser.prototype.loadSkins = function () { +var json = this.json; +return this._withDependencies( [ +"accessors" +] ).then( function ( dependencies ) { +return _each( json.skins, function ( skin ) { +var bindShapeMatrix = new THREE.Matrix4(); +if ( skin.bindShapeMatrix !== undefined ) bindShapeMatrix.fromArray( skin.bindShapeMatrix ); +var _skin = { +bindShapeMatrix: bindShapeMatrix, +jointNames: skin.jointNames, +inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] +}; +return _skin; +} ); +} ); +}; +GLTFParser.prototype.loadAnimations = function () { +var json = this.json; +return this._withDependencies( [ +"accessors", +"nodes" +] ).then( function ( dependencies ) { +return _each( json.animations, function ( animation, animationId ) { +var tracks = []; +for ( var channelId in animation.channels ) { +var channel = animation.channels[ channelId ]; +var sampler = animation.samplers[ channel.sampler ]; +if ( sampler ) { +var target = channel.target; +var name = target.id; +var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; +var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; +var inputAccessor = dependencies.accessors[ input ]; +var outputAccessor = dependencies.accessors[ output ]; +var node = dependencies.nodes[ name ]; +if ( node ) { +node.updateMatrix(); +node.matrixAutoUpdate = true; +var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation +? THREE.QuaternionKeyframeTrack +: THREE.VectorKeyframeTrack; +var targetName = node.name ? node.name : node.uuid; +var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; +tracks.push( new TypedKeyframeTrack( +targetName + '.' + PATH_PROPERTIES[ target.path ], +THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), +THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), +interpolation +) ); +} +} +} +var name = animation.name !== undefined ? animation.name : "animation_" + animationId; +return new THREE.AnimationClip( name, undefined, tracks ); +} ); +} ); +}; +GLTFParser.prototype.loadNodes = function () { +var json = this.json; +var extensions = this.extensions; +var scope = this; +return _each( json.nodes, function ( node ) { +var matrix = new THREE.Matrix4(); +var _node; +if ( node.jointName ) { +_node = new THREE.Bone(); +_node.name = node.name !== undefined ? node.name : node.jointName; +_node.jointName = node.jointName; +} else { +_node = new THREE.Object3D(); +if ( node.name !== undefined ) _node.name = node.name; +} +if ( node.extras ) _node.userData = node.extras; +if ( node.matrix !== undefined ) { +matrix.fromArray( node.matrix ); +_node.applyMatrix( matrix ); +} else { +if ( node.translation !== undefined ) { +_node.position.fromArray( node.translation ); +} +if ( node.rotation !== undefined ) { +_node.quaternion.fromArray( node.rotation ); +} +if ( node.scale !== undefined ) { +_node.scale.fromArray( node.scale ); +} +} +return _node; +} ).then( function ( __nodes ) { +return scope._withDependencies( [ +"meshes", +"skins", +"cameras" +] ).then( function ( dependencies ) { +return _each( __nodes, function ( _node, nodeId ) { +var node = json.nodes[ nodeId ]; +if ( node.meshes !== undefined ) { +for ( var meshId in node.meshes ) { +var mesh = node.meshes[ meshId ]; +var group = dependencies.meshes[ mesh ]; +if ( group === undefined ) { +console.warn( 'GLTFLoader: Couldn\'t find node "' + mesh + '".' ); +continue; +} +for ( var childrenId in group.children ) { +var child = group.children[ childrenId ]; +var originalMaterial = child.material; +var originalGeometry = child.geometry; +var originalUserData = child.userData; +var originalName = child.name; +var material; +if ( originalMaterial.isDeferredShaderMaterial ) { +originalMaterial = material = originalMaterial.create(); +} else { +material = originalMaterial; +} +switch ( child.type ) { +case 'LineSegments': +child = new THREE.LineSegments( originalGeometry, material ); +break; +case 'LineLoop': +child = new THREE.LineLoop( originalGeometry, material ); +break; +case 'Line': +child = new THREE.Line( originalGeometry, material ); +break; +default: +child = new THREE.Mesh( originalGeometry, material ); +} +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var skinEntry; +if ( node.skin ) { +skinEntry = dependencies.skins[ node.skin ]; +} +if ( skinEntry ) { +var getJointNode = function ( jointId ) { +var keys = Object.keys( __nodes ); +for ( var i = 0, il = keys.length; i < il; i ++ ) { +var n = __nodes[ keys[ i ] ]; +if ( n.jointName === jointId ) return n; +} +return null; +}; +var geometry = originalGeometry; +var material = originalMaterial; +material.skinning = true; +child = new THREE.SkinnedMesh( geometry, material, false ); +child.castShadow = true; +child.userData = originalUserData; +child.name = originalName; +var bones = []; +var boneInverses = []; +for ( var i = 0, l = skinEntry.jointNames.length; i < l; i ++ ) { +var jointId = skinEntry.jointNames[ i ]; +var jointNode = getJointNode( jointId ); +if ( jointNode ) { +bones.push( jointNode ); +var m = skinEntry.inverseBindMatrices.array; +var mat = new THREE.Matrix4().fromArray( m, i * 16 ); +boneInverses.push( mat ); +} else { +console.warn( "WARNING: joint: '" + jointId + "' could not be found" ); +} +} +child.bind( new THREE.Skeleton( bones, boneInverses, false ), skinEntry.bindShapeMatrix ); +var buildBoneGraph = function ( parentJson, parentObject, property ) { +var children = parentJson[ property ]; +if ( children === undefined ) return; +for ( var i = 0, il = children.length; i < il; i ++ ) { +var nodeId = children[ i ]; +var bone = __nodes[ nodeId ]; +var boneJson = json.nodes[ nodeId ]; +if ( bone !== undefined && bone.isBone === true && boneJson !== undefined ) { +parentObject.add( bone ); +buildBoneGraph( boneJson, bone, 'children' ); +} +} +}; +buildBoneGraph( node, child, 'skeletons' ); +} +_node.add( child ); +} +} +} +if ( node.camera !== undefined ) { +var camera = dependencies.cameras[ node.camera ]; +_node.add( camera ); +} +if ( node.extensions +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] +&& node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ) { +var extensionLights = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].lights; +var light = extensionLights[ node.extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].light ]; +_node.add( light ); +} +return _node; +} ); +} ); +} ); +}; +GLTFParser.prototype.loadScenes = function () { +var json = this.json; +function buildNodeHierachy( nodeId, parentObject, allNodes ) { +var _node = allNodes[ nodeId ]; +parentObject.add( _node ); +var node = json.nodes[ nodeId ]; +if ( node.children ) { +var children = node.children; +for ( var i = 0, l = children.length; i < l; i ++ ) { +var child = children[ i ]; +buildNodeHierachy( child, _node, allNodes ); +} +} +} +return this._withDependencies( [ +"nodes" +] ).then( function ( dependencies ) { +return _each( json.scenes, function ( scene ) { +var _scene = new THREE.Scene(); +if ( scene.name !== undefined ) _scene.name = scene.name; +if ( scene.extras ) _scene.userData = scene.extras; +var nodes = scene.nodes || []; +for ( var i = 0, l = nodes.length; i < l; i ++ ) { +var nodeId = nodes[ i ]; +buildNodeHierachy( nodeId, _scene, dependencies.nodes ); +} +_scene.traverse( function ( child ) { +if ( child.material && child.material.isRawShaderMaterial ) { +child.gltfShader = new GLTFShader( child, dependencies.nodes ); +child.onBeforeRender = function(renderer, scene, camera){ +this.gltfShader.update(scene, camera); +}; +} +} ); +return _scene; +} ); +} ); +}; +return GLTFLoader; +} )(); +"use strict"; +var JSONParser = function() +{ +} +JSONParser.prototype.constructor = JSONParser; +JSONParser.prototype.parseJavaScript = function(jsobj) { +var child = this.CreateElement('scene'); +this.ConvertToX3DOM(jsobj, "", child); +console.log(jsobj, child); +return child; +}; +JSONParser.prototype.elementSetAttribute = function(element, key, value) { +if (key === 'SON schema') { +} else if (key === 'ncoding') { +} else { +if (typeof element.setAttribute === 'function') { +element.setAttribute(key, value); +} +} +}; +JSONParser.prototype.ConvertChildren = function(parentkey, object, element) { +var key; +for (key in object) { +if (typeof object[key] === 'object') { +if (isNaN(parseInt(key))) { +this.ConvertObject(key, object, element, parentkey.substr(1)); +} else { +this.ConvertToX3DOM(object[key], key, element, parentkey.substr(1)); +} +} +} +}; +JSONParser.prototype.CreateElement = function(key, containerField) { +var child = document.createElement(key); +if (typeof containerField !== 'undefined') { +this.elementSetAttribute(child, 'containerField', containerField); +} +return child; +}; +JSONParser.prototype.CDATACreateFunction = function(document, element, str) { +var y = str.replace(/\\"/g, "\\\"") +.replace(/</g, "<") +.replace(/>/g, ">") +.replace(/&/g, "&"); +do { +str = y; +y = str.replace(/'([^'\r\n]*)\n([^']*)'/g, "'$1\\n$2'"); +if (str !== y) { +console.log("CDATA Replacing",str,"with",y); +} +} while (y != str); +var domParser = new DOMParser(); +var cdataStr = ''; // has to be wrapped into an element +var scriptDoc = domParser .parseFromString (cdataStr, 'application/xml'); +var cdata = scriptDoc .children[0] .childNodes[1]; // space after script is childNode[0] +element .appendChild(cdata); +}; +JSONParser.prototype.ConvertObject = function(key, object, element, containerField) { +var child; +if (object !== null && typeof object[key] === 'object') { +if (key.substr(0,1) === '@') { +this.ConvertToX3DOM(object[key], key, element); +} else if (key.substr(0,1) === '-') { +this.ConvertChildren(key, object[key], element); +} else if (key === '#comment') { +for (var c in object[key]) { +child = document.createComment(this.CommentStringToXML(object[key][c])); +element.appendChild(child); +} +} else if (key === '#text') { +child = document.createTextNode(object[key].join("")); +element.appendChild(child); +} else if (key === '#sourceText') { +this.CDATACreateFunction(document, element, object[key].join("\r\n")+"\r\n"); +} else { +if (key === 'connect' || key === 'fieldValue' || key === 'field' || key === 'meta' || key === 'component') { +for (var childkey in object[key]) { // for each field +if (typeof object[key][childkey] === 'object') { +child = this.CreateElement(key, containerField); +this.ConvertToX3DOM(object[key][childkey], childkey, child); +element.appendChild(child); +element.appendChild(document.createTextNode("\n")); +} +} +} else { +child = this.CreateElement(key, containerField); +this.ConvertToX3DOM(object[key], key, child); +element.appendChild(child); +element.appendChild(document.createTextNode("\n")); +} +} +} +}; +JSONParser.prototype.CommentStringToXML = function(str) { +var y = str; +str = str.replace(/\\\\/g, '\\'); +if (y !== str) { +console.log("X3DJSONLD replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.SFStringToXML = function(str) { +var y = str; +str = str.replace(/\\/g, '\\\\'); +str = str.replace(/"/g, '\\\"'); +if (y !== str) { +console.log("X3DJSONLD [] replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.JSONStringToXML = function(str) { +var y = str; +str = str.replace(/\\/g, '\\\\'); +str = str.replace(/\n/g, '\\n'); +if (y !== str) { +console.log("X3DJSONLD replacing", y, "with", str); +} +return str; +}; +JSONParser.prototype.ConvertToX3DOM = function(object, parentkey, element, containerField) { +var key; +var localArray = []; +var isArray = false; +var arrayOfStrings = false; +for (key in object) { +if (isNaN(parseInt(key))) { +isArray = false; +} else { +isArray = true; +} +if (isArray) { +if (typeof object[key] === 'number') { +localArray.push(object[key]); +} else if (typeof object[key] === 'string') { +localArray.push(object[key]); +arrayOfStrings = true; +} else if (typeof object[key] === 'boolean') { +localArray.push(object[key]); +} else if (typeof object[key] === 'object') { +this.ConvertToX3DOM(object[key], key, element); +} else if (typeof object[key] === 'undefined') { +} else { +console.error("Unknown type found in array "+typeof object[key]); +} +} else if (typeof object[key] === 'object') { +if (key === 'scene') { +this.ConvertToX3DOM(object[key], key, element); +} else { +this.ConvertObject(key, object, element); +} +} else if (typeof object[key] === 'number') { +this.elementSetAttribute(element, key.substr(1),object[key]); +} else if (typeof object[key] === 'string') { +if (key === '#comment') { +var child = document.createComment(this.CommentStringToXML(object[key])); +element.appendChild(child); +} else if (key === '#text') { +var child = document.createTextNode(object[key]); +element.appendChild(child); +} else { +this.elementSetAttribute(element, key.substr(1), this.JSONStringToXML(object[key])); +} +} else if (typeof object[key] === 'boolean') { +this.elementSetAttribute(element, key.substr(1),object[key]); +} else if (typeof object[key] === 'undefined') { +} else { +console.error("Unknown type found in object "+typeof object[key]); +console.error(object); +} +} +if (isArray) { +if (parentkey.substr(0,1) === '@') { +if (arrayOfStrings) { +arrayOfStrings = false; +for (var str in localArray) { +localArray[str] = this.SFStringToXML(localArray[str]); +} +this.elementSetAttribute(element, parentkey.substr(1),'"'+localArray.join('" "')+'"'); +} else { +this.elementSetAttribute(element, parentkey.substr(1),localArray.join(" ")); +} +} +isArray = false; +} +return element; +}; +function LoadManager () { +this.urlQueue = []; +this.urlNext = -1; +this.MaxRequests = 3; +this.totalRequests = 0; +this.totalResponses = 0; +this.requestCount = 0; +var lmThat = this; +this.load = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadText = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'text', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadHtml = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'html', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadXml = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'xml', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadJson = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'json', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.loadImage = function (url, success, progress, failed, userdata) { +this.urlQueue.push( {'url':url, 'type':'image', 'userdata':userdata, 'success':success, 'failure':failed, 'progress':progress} ); +this.loadNextUrl(); +} +this.success = function (response, string, xhr) { +if (typeof(xhr._loadManager.success) !== undefined) { +xhr._loadManager.success (response, xhr._loadManager.userdata, xhr); +} +} +this.failure = function (xhr, errorCode, errorText) { +if (typeof(xhr._loadManager.failure) !== undefined) { +xhr._loadManager.failure (xhr, xhr._loadManager.userdata, errorCode, errorText); +} +} +this.requestComplete = function (event, xhr, settings) { +lmThat.requestCount --; +lmThat.totalResponses++; +lmThat.loadNextUrl(); +} +this.loadNextUrl = function () { +if (this.requestCount >= this.MaxRequests) {return; } +if (this.urlNext >= this.urlQueue.length || this.urlNext < 0) { +this.urlNext = -1; +for (var i=0; i 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.CODE_SLASH: +slashes[ slashesPointer++ ] = i; +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.CODE_LF: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); +slashesPointer = 0; +bufferPointer = 0; +break; +case Consts.CODE_CR: +break; +default: +word += String.fromCharCode( code ); +break; +} +} +}; +Parser.prototype.parseText = function ( text ) { +var length = text.length; +var buffer = new Array( 32 ); +var bufferPointer = 0; +var slashes = new Array( 32 ); +var slashesPointer = 0; +var reachedFaces = false; +var char; +var word = ''; +for ( var i = 0; i < length; i++ ) { +char = text[ i ]; +switch ( char ) { +case Consts.STRING_SPACE: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.STRING_SLASH: +slashes[ slashesPointer++ ] = i; +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +break; +case Consts.STRING_LF: +if ( word.length > 0 ) buffer[ bufferPointer++ ] = word; +word = ''; +reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ); +slashesPointer = 0; +bufferPointer = 0; +break; +case Consts.STRING_CR: +break; +default: +word += char; +} +} +}; +Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) { +if ( bufferPointer < 1 ) return reachedFaces; +var bufferLength = bufferPointer - 1; +switch ( buffer[ 0 ] ) { +case Consts.LINE_V: +if ( reachedFaces ) { +this.processCompletedObject( null, this.rawObject.groupName ); +reachedFaces = false; +} +this.rawObject.pushVertex( buffer ); +break; +case Consts.LINE_VT: +this.rawObject.pushUv( buffer ); +break; +case Consts.LINE_VN: +this.rawObject.pushNormal( buffer ); +break; +case Consts.LINE_F: +reachedFaces = true; +var haveQuad = bufferLength % 4 === 0; +if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVn( buffer ); +} else { +this.rawObject.buildFaceVVn( buffer ); +} +} else if ( bufferLength === slashesPointer * 2 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVt( buffer ); +} else { +this.rawObject.buildFaceVVt( buffer ); +} +} else if ( bufferLength * 2 === slashesPointer * 3 ) { +if ( haveQuad ) { +this.rawObject.buildQuadVVtVn( buffer ); +} else { +this.rawObject.buildFaceVVtVn( buffer ); +} +} else { +if ( haveQuad ) { +this.rawObject.buildQuadV( buffer ); +} else { +this.rawObject.buildFaceV( buffer ); +} +} +break; +case Consts.LINE_L: +if ( bufferLength === slashesPointer * 2 ) { +this.rawObject.buildLineVvt( buffer ); +} else { +this.rawObject.buildLineV( buffer ); +} +break; +case Consts.LINE_S: +this.rawObject.pushSmoothingGroup( buffer[ 1 ] ); +break; +case Consts.LINE_G: +this.processCompletedGroup( buffer[ 1 ] ); +break; +case Consts.LINE_O: +if ( this.rawObject.vertices.length > 0 ) { +this.processCompletedObject( buffer[ 1 ], null ); +reachedFaces = false; +} else { +this.rawObject.pushObject( buffer[ 1 ] ); +} +break; +case Consts.LINE_MTLLIB: +this.rawObject.pushMtllib( buffer[ 1 ] ); +break; +case Consts.LINE_USEMTL: +this.rawObject.pushUsemtl( buffer[ 1 ] ); +break; +default: +break; +} +return reachedFaces; +}; +Parser.prototype.processCompletedObject = function ( objectName, groupName ) { +this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +this.inputObjectCount++; +this.rawObject = this.rawObject.newInstanceFromObject( objectName, groupName ); +}; +Parser.prototype.processCompletedGroup = function ( groupName ) { +var notEmpty = this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +if ( notEmpty ) { +this.inputObjectCount ++; +this.rawObject = this.rawObject.newInstanceFromGroup( groupName ); +} else { +this.rawObject.pushGroup( groupName ); +} +}; +Parser.prototype.finalize = function () { +this.rawObject.finalize( this.meshCreator, this.inputObjectCount, this.debug ); +this.inputObjectCount++; +}; +return Parser; +})(); +var RawObject = (function () { +function RawObject( objectName, groupName, mtllibName ) { +this.globalVertexOffset = 1; +this.globalUvOffset = 1; +this.globalNormalOffset = 1; +this.vertices = []; +this.normals = []; +this.uvs = []; +this.mtllibName = Validator.verifyInput( mtllibName, 'none' ); +this.objectName = Validator.verifyInput( objectName, 'none' ); +this.groupName = Validator.verifyInput( groupName, 'none' ); +this.activeMtlName = 'none'; +this.activeSmoothingGroup = 1; +this.mtlCount = 0; +this.smoothingGroupCount = 0; +this.rawObjectDescriptions = []; +var index = this.buildIndex( this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; +} +RawObject.prototype.buildIndex = function ( materialName, smoothingGroup) { +return materialName + '|' + smoothingGroup; +}; +RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) { +var newRawObject = new RawObject( objectName, groupName, this.mtllibName ); +newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3; +newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2; +newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3; +return newRawObject; +}; +RawObject.prototype.newInstanceFromGroup = function ( groupName ) { +var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName ); +newRawObject.vertices = this.vertices; +newRawObject.uvs = this.uvs; +newRawObject.normals = this.normals; +newRawObject.globalVertexOffset = this.globalVertexOffset; +newRawObject.globalUvOffset = this.globalUvOffset; +newRawObject.globalNormalOffset = this.globalNormalOffset; +return newRawObject; +}; +RawObject.prototype.pushVertex = function ( buffer ) { +this.vertices.push( parseFloat( buffer[ 1 ] ) ); +this.vertices.push( parseFloat( buffer[ 2 ] ) ); +this.vertices.push( parseFloat( buffer[ 3 ] ) ); +}; +RawObject.prototype.pushUv = function ( buffer ) { +this.uvs.push( parseFloat( buffer[ 1 ] ) ); +this.uvs.push( parseFloat( buffer[ 2 ] ) ); +}; +RawObject.prototype.pushNormal = function ( buffer ) { +this.normals.push( parseFloat( buffer[ 1 ] ) ); +this.normals.push( parseFloat( buffer[ 2 ] ) ); +this.normals.push( parseFloat( buffer[ 3 ] ) ); +}; +RawObject.prototype.pushObject = function ( objectName ) { +this.objectName = objectName; +}; +RawObject.prototype.pushMtllib = function ( mtllibName ) { +this.mtllibName = mtllibName; +}; +RawObject.prototype.pushGroup = function ( groupName ) { +this.groupName = groupName; +this.verifyIndex(); +}; +RawObject.prototype.pushUsemtl = function ( mtlName ) { +if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return; +this.activeMtlName = mtlName; +this.mtlCount++; +this.verifyIndex(); +}; +RawObject.prototype.pushSmoothingGroup = function ( activeSmoothingGroup ) { +var normalized = activeSmoothingGroup === 'off' ? 0 : activeSmoothingGroup; +if ( this.activeSmoothingGroup === normalized ) return; +this.activeSmoothingGroup = normalized; +this.smoothingGroupCount++; +this.verifyIndex(); +}; +RawObject.prototype.verifyIndex = function () { +var index = this.buildIndex( this.activeMtlName, ( this.activeSmoothingGroup === 0 ) ? 0 : 1 ); +this.rawObjectDescriptionInUse = this.rawObjectDescriptions[ index ]; +if ( ! Validator.isValid( this.rawObjectDescriptionInUse ) ) { +this.rawObjectDescriptionInUse = new RawObjectDescription( this.objectName, this.groupName, this.activeMtlName, this.activeSmoothingGroup ); +this.rawObjectDescriptions[ index ] = this.rawObjectDescriptionInUse; +} +}; +RawObject.prototype.buildQuadVVtVn = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] ); +this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] ); +this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] ); +} +}; +RawObject.prototype.buildQuadVVt = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); +this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); +} +}; +RawObject.prototype.buildQuadVVn = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] ); +this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] ); +} +}; +RawObject.prototype.buildQuadV = function ( indexArray ) { +for ( var i = 0; i < 6; i ++ ) { +this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] ); +} +}; +RawObject.prototype.buildFaceVVtVn = function ( indexArray ) { +for ( var i = 1; i < 10; i += 3 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVt( indexArray[ i + 1 ] ); +this.attachFaceVn( indexArray[ i + 2 ] ); +} +}; +RawObject.prototype.buildFaceVVt = function ( indexArray ) { +for ( var i = 1; i < 7; i += 2 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVt( indexArray[ i + 1 ] ); +} +}; +RawObject.prototype.buildFaceVVn = function ( indexArray ) { +for ( var i = 1; i < 7; i += 2 ) { +this.attachFaceV_( indexArray[ i ] ); +this.attachFaceVn( indexArray[ i + 1 ] ); +} +}; +RawObject.prototype.buildFaceV = function ( indexArray ) { +for ( var i = 1; i < 4; i ++ ) { +this.attachFaceV_( indexArray[ i ] ); +} +}; +RawObject.prototype.attachFaceV_ = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalVertexOffset ) * 3; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.vertices.push( this.vertices[ index++ ] ); +rodiu.vertices.push( this.vertices[ index++ ] ); +rodiu.vertices.push( this.vertices[ index ] ); +}; +RawObject.prototype.attachFaceVt = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalUvOffset ) * 2; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.uvs.push( this.uvs[ index++ ] ); +rodiu.uvs.push( this.uvs[ index ] ); +}; +RawObject.prototype.attachFaceVn = function ( faceIndex ) { +var faceIndexInt = parseInt( faceIndex ); +var index = ( faceIndexInt - this.globalNormalOffset ) * 3; +var rodiu = this.rawObjectDescriptionInUse; +rodiu.normals.push( this.normals[ index++ ] ); +rodiu.normals.push( this.normals[ index++ ] ); +rodiu.normals.push( this.normals[ index ] ); +}; +RawObject.prototype.buildLineVvt = function ( lineArray ) { +var length = lineArray.length; +for ( var i = 1; i < length; i ++ ) { +this.vertices.push( parseInt( lineArray[ i ] ) ); +this.uvs.push( parseInt( lineArray[ i ] ) ); +} +}; +RawObject.prototype.buildLineV = function ( lineArray ) { +var length = lineArray.length; +for ( var i = 1; i < length; i++ ) { +this.vertices.push( parseInt( lineArray[ i ] ) ); +} +}; +RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) { +var temp = this.rawObjectDescriptions; +this.rawObjectDescriptions = []; +var rawObjectDescription; +var index = 0; +var absoluteVertexCount = 0; +var absoluteNormalCount = 0; +var absoluteUvCount = 0; +for ( var name in temp ) { +rawObjectDescription = temp[ name ]; +if ( rawObjectDescription.vertices.length > 0 ) { +if ( rawObjectDescription.objectName === 'none' ) rawObjectDescription.objectName = rawObjectDescription.groupName; +this.rawObjectDescriptions[ index++ ] = rawObjectDescription; +absoluteVertexCount += rawObjectDescription.vertices.length; +absoluteUvCount += rawObjectDescription.uvs.length; +absoluteNormalCount += rawObjectDescription.normals.length; +} +} +var notEmpty = false; +if ( index > 0 ) { +if ( debug ) this.createReport( inputObjectCount, true ); +meshCreator.buildMesh( +this.rawObjectDescriptions, +inputObjectCount, +absoluteVertexCount, +absoluteNormalCount, +absoluteUvCount +); +notEmpty = true; +} +return notEmpty; +}; +RawObject.prototype.createReport = function ( inputObjectCount, printDirectly ) { +var report = { +name: this.objectName ? this.objectName : 'groups', +mtllibName: this.mtllibName, +vertexCount: this.vertices.length / 3, +normalCount: this.normals.length / 3, +uvCount: this.uvs.length / 2, +smoothingGroupCount: this.smoothingGroupCount, +mtlCount: this.mtlCount, +rawObjectDescriptions: this.rawObjectDescriptions.length +}; +if ( printDirectly ) { +console.log( 'Input Object number: ' + inputObjectCount + ' Object name: ' + report.name ); +console.log( 'Mtllib name: ' + report.mtllibName ); +console.log( 'Vertex count: ' + report.vertexCount ); +console.log( 'Normal count: ' + report.normalCount ); +console.log( 'UV count: ' + report.uvCount ); +console.log( 'SmoothingGroup count: ' + report.smoothingGroupCount ); +console.log( 'Material count: ' + report.mtlCount ); +console.log( 'Real RawObjectDescription count: ' + report.rawObjectDescriptions ); +console.log( '' ); +} +return report; +}; +return RawObject; +})(); +var RawObjectDescription = (function () { +function RawObjectDescription( objectName, groupName, materialName, smoothingGroup ) { +this.objectName = objectName; +this.groupName = groupName; +this.materialName = materialName; +this.smoothingGroup = smoothingGroup; +this.vertices = []; +this.uvs = []; +this.normals = []; +} +return RawObjectDescription; +})(); +var MeshCreator = (function () { +function MeshCreator() { +this.sceneGraphBaseNode = null; +this.materials = null; +this.debug = false; +this.globalObjectCount = 1; +this.validated = false; +} +MeshCreator.prototype.setSceneGraphBaseNode = function ( sceneGraphBaseNode ) { +this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, this.sceneGraphBaseNode ); +this.sceneGraphBaseNode = Validator.verifyInput( this.sceneGraphBaseNode, new THREE.Group() ); +}; +MeshCreator.prototype.setMaterials = function ( materials ) { +this.materials = Validator.verifyInput( materials, this.materials ); +this.materials = Validator.verifyInput( this.materials, { materials: [] } ); +}; +MeshCreator.prototype.setDebug = function ( debug ) { +if ( debug === true || debug === false ) this.debug = debug; +}; +MeshCreator.prototype.validate = function () { +if ( this.validated ) return; +this.setSceneGraphBaseNode( null ); +this.setMaterials( null ); +this.setDebug( null ); +this.globalObjectCount = 1; +}; +MeshCreator.prototype.finalize = function () { +this.sceneGraphBaseNode = null; +this.materials = null; +this.validated = false; +}; +MeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) { +if ( this.debug ) console.log( 'MeshCreator.buildRawMeshData:\nInput object no.: ' + inputObjectCount ); +var bufferGeometry = new THREE.BufferGeometry(); +var vertexBA = new THREE.BufferAttribute( new Float32Array( absoluteVertexCount ), 3 ); +bufferGeometry.addAttribute( 'position', vertexBA ); +var normalBA; +if ( absoluteNormalCount > 0 ) { +normalBA = new THREE.BufferAttribute( new Float32Array( absoluteNormalCount ), 3 ); +bufferGeometry.addAttribute( 'normal', normalBA ); +} +var uvBA; +if ( absoluteUvCount > 0 ) { +uvBA = new THREE.BufferAttribute( new Float32Array( absoluteUvCount ), 2 ); +bufferGeometry.addAttribute( 'uv', uvBA ); +} +if ( this.debug ) console.log( 'Creating Multi-Material for object no.: ' + this.globalObjectCount ); +var rawObjectDescription; +var material; +var materialName; +var createMultiMaterial = rawObjectDescriptions.length > 1; +var materials = []; +var materialIndex = 0; +var materialIndexMapping = []; +var selectedMaterialIndex; +var vertexBAOffset = 0; +var vertexGroupOffset = 0; +var vertexLength; +var normalOffset = 0; +var uvOffset = 0; +for ( var oodIndex in rawObjectDescriptions ) { +rawObjectDescription = rawObjectDescriptions[ oodIndex ]; +materialName = rawObjectDescription.materialName; +material = this.materials[ materialName ]; +if ( ! material ) { +material = this.materials[ 'defaultMaterial' ]; +if ( ! material ) { +material = new THREE.MeshStandardMaterial( { color: 0xDCF1FF} ); +material.name = 'defaultMaterial'; +this.materials[ 'defaultMaterial' ] = material; +} +console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' ); +} +if ( rawObjectDescription.smoothingGroup === 0 ) { +materialName = material.name + '_flat'; +var materialClone = this.materials[ materialName ]; +if ( ! materialClone ) { +materialClone = material.clone(); +materialClone.name = materialName; +materialClone.shading = THREE.FlatShading; +this.materials[ materialName ] = name; +} +} +vertexLength = rawObjectDescription.vertices.length; +if ( createMultiMaterial ) { +selectedMaterialIndex = materialIndexMapping[ materialName ]; +if ( ! selectedMaterialIndex ) { +selectedMaterialIndex = materialIndex; +materialIndexMapping[ materialName ] = materialIndex; +materials.push( material ); +materialIndex++; +} +bufferGeometry.addGroup( vertexGroupOffset, vertexLength / 3, selectedMaterialIndex ); +vertexGroupOffset += vertexLength / 3; +} +vertexBA.set( rawObjectDescription.vertices, vertexBAOffset ); +vertexBAOffset += vertexLength; +if ( normalBA ) { +normalBA.set( rawObjectDescription.normals, normalOffset ); +normalOffset += rawObjectDescription.normals.length; +} +if ( uvBA ) { +uvBA.set( rawObjectDescription.uvs, uvOffset ); +uvOffset += rawObjectDescription.uvs.length; +} +if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex ); +} +if ( ! normalBA ) bufferGeometry.computeVertexNormals(); +if ( createMultiMaterial ) material = materials; +var mesh = new THREE.Mesh( bufferGeometry, material ); +this.sceneGraphBaseNode.add( mesh ); +this.globalObjectCount++; +}; +MeshCreator.prototype.printReport = function ( rawObjectDescription, selectedMaterialIndex ) { +console.log( +' Output Object no.: ' + this.globalObjectCount + +'\n objectName: ' + rawObjectDescription.objectName + +'\n groupName: ' + rawObjectDescription.groupName + +'\n materialName: ' + rawObjectDescription.materialName + +'\n materialIndex: ' + selectedMaterialIndex + +'\n smoothingGroup: ' + rawObjectDescription.smoothingGroup + +'\n #vertices: ' + rawObjectDescription.vertices.length / 3 + +'\n #uvs: ' + rawObjectDescription.uvs.length / 2 + +'\n #normals: ' + rawObjectDescription.normals.length / 3 +); +}; +return MeshCreator; +})(); +OBJLoader2.prototype._buildWebWorkerCode = function ( funcBuildObject, funcBuildSingelton ) { +var workerCode = ''; +workerCode += funcBuildObject( 'Consts', Consts ); +workerCode += funcBuildObject( 'Validator', Validator ); +workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser ); +workerCode += funcBuildSingelton( 'RawObject', 'RawObject', RawObject ); +workerCode += funcBuildSingelton( 'RawObjectDescription', 'RawObjectDescription', RawObjectDescription ); +return workerCode; +}; +return OBJLoader2; +})(); +var xseen = { +canvases : [], +sceneInfo : [], +nodeDefinitions : {}, +parseTable : {}, +node : {}, +utils : {}, +eventManager : {}, +Events : {}, +Navigation : {}, +loadMgr : {}, +loader : { +'Null' : '', +'ColladaLoader' : '', +'GltfLegacy' : '', +'GltfLoader' : '', +'ObjLoader' : '', +'ImageLoader' : '', +'X3dLoader' : '', +}, +loadProgress : function (xhr) { +if (xhr.total != 0) { +console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); +} +}, +loadError : function (xhr, userdata, code, message) { +console.error('An error happened on '+userdata.e.id+'\n'+code+'\n'+message); +}, +loadMime : { +'' : {name: 'Null', loader: 'Null'}, +'dae' : {name: 'Collada', loader: 'ColladaLoader'}, +'glb' : {name: 'glTF Binary', loader: 'GltfLoader'}, +'glbl' : {name: 'glTF Binary', loader: 'GltfLegacy'}, +'gltf' : {name: 'glTF JSON', loader: 'GltfLoader'}, +'obj' : {name: 'OBJ', loader: 'ObjLoader'}, +'png' : {name: 'PNG', loader: 'ImageLoader'}, +'jpg' : {name: 'JPEG', loader: 'ImageLoader'}, +'jpeg' : {name: 'JPEG', loader: 'ImageLoader'}, +'gif' : {name: 'GIF', loader: 'ImageLoader'}, +'x3d' : {name: 'X3D XML', loader: 'X3dLoader'}, +}, +array_to_object : function (a) { +var o = {}; +for(var i=0;i ' + obj.type + ' (' + obj.name + ')'); +for (var i=0; i +var validParams = xseen.array_to_object([ +'showLog', +'showStat', +'showProgress', +'PrimitiveQuality', +'components', +'loadpath', +'disableDoubleClick', +'backend', +'altImg', +'runtimeEnabled', +'keysEnabled', +'showTouchpoints', +'disableTouch', +'maxActiveDownloads' +]); +var components, prefix; +var showLoggingConsole = false; +for (var i=0; i < xseens.length; i++) { +settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'false'); +settings.setProperty("showLog", xseens[i].getAttribute("showLog") || 'true'); +settings.setProperty("showStat", xseens[i].getAttribute("showStat") || 'false'); +settings.setProperty("showProgress", xseens[i].getAttribute("showProgress") || 'true'); +settings.setProperty("PrimitiveQuality", xseens[i].getAttribute("PrimitiveQuality") || 'High'); +params = xseens[i].getElementsByTagName('PARAM'); +for (var j=0; j < params.length; j++) { +if (params[j].getAttribute('name') in validParams) { +settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value')); +} else { +} +} +if (settings.getProperty('showLog') === 'true') { +showLoggingConsole = true; +} +if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) { +components = settings.getProperty('components', xseens[i].getAttribute("components")); +if (components) { +prefix = settings.getProperty('loadpath', xseens[i].getAttribute("loadpath")); +components = components.trim().split(','); +for (j=0; j < components.length; j++) { +xseen.loadJS(components[j] + ".js", prefix); +} +} +if (xseens[i].getAttribute("src")) { +var _scene = document.createElement("scene"); +var _inl = document.createElement("Inline"); +_inl.setAttribute("url", xseens[i].getAttribute("src")); +_scene.appendChild(_inl); +xseens[i].appendChild(_scene); +} +} +} +if (showLoggingConsole == true) { +xseen.debug.activate(true); +} else { +xseen.debug.activate(false); +} +if (xseen.versionInfo !== undefined) { +xseen.debug.logInfo("XSeen version " + xseen.versionInfo.version + ", " + +"Date " + xseen.versionInfo.date); +xseen.debug.logInfo(xseen.versionInfo.splashText); +} +var x_element; +var x_canvas; +var altDiv, altP, aLnk, altImg; +var t0, t1; +for (var i=0; i < xseens.length; i++) +{ +x_element = xseens[i]; // The XSeen DOM element +x_canvas = new THREE.Scene(); // May need addtl info if multiple: xseen.X3DCanvas(x_element, xseen.canvases.length); +xseen.canvases.push(x_canvas); // TODO: Need to handle failure to initialize? +t0 = new Date().getTime(); +var divWidth = x_element.getAttribute('width'); +var divHeight = x_element.getAttribute('height'); +if (divHeight + divWidth < 100) { +divHeight = 450; +divWidth = 800; +} else if (divHeight < 50) { +divHeight = Math.floor(divWidth/2) + 50; +} else if (divWidth < 50) { +divWidth = divHeight * 2 - 100; +} +var turntable = (x_element.getAttribute('turntable') || '').toLowerCase(); +if (turntable == 'on' || turntable == 'yes' || turntable == 'y' || turntable == '1') { +turntable = true; +} else { +turntable = false; +} +turntable = false; +var x_camera = {}; +var x_renderer = new THREE.WebGLRenderer(); +x_renderer.setSize (divWidth, divHeight); +var x_effect = new THREE.StereoEffect(x_renderer); +x_renderer.controls = {'update' : function() {return;}}; +x_element.addEventListener ('dblclick', xseen.Events.canvasHandler, true); +x_element.addEventListener ('click', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mousedown', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mousemove', xseen.Events.canvasHandler, true); +x_element.addEventListener ('mouseup', xseen.Events.canvasHandler, true); +x_element.addEventListener ('xseen', xseen.Events.XSeenHandler); // Last chance for XSeen handling of event +x_element.addEventListener ('change', xseen.Events.XSeenDebugHandler, true); // Early catch of 'change' event +xseen.sceneInfo.push ({ +'size' : {'width':divWidth, 'height':divHeight}, +'scene' : x_canvas, +'renderer' : x_renderer, +'effect' : x_effect, +'camera' : [x_camera], +'turntable' : turntable, +'mixers' : [], +'clock' : new THREE.Clock(), +'element' : x_element, +'selectable': [], +'stacks' : [], +'tmp' : {activeViewpoint:false}, +'xseen' : xseen, +}); +x_element._xseen = {}; +x_element._xseen.children = []; +x_element._xseen.sceneInfo = xseen.sceneInfo[xseen.sceneInfo.length-1]; +t1 = new Date().getTime() - t0; +xseen.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms."); +} +var ready = (function(eventType) { +var evt = null; +if (document.createEvent) { +evt = document.createEvent("Events"); +evt.initEvent(eventType, true, true); +document.dispatchEvent(evt); +} else if (document.createEventObject) { +evt = document.createEventObject(); +document.body.fireEvent('on' + eventType, evt); +} +})('load'); +var t=[]; +for (var i=0; i xseen.debug.maxLinesToLog) { return; } +var node = document.createElement("p"); +node.style.margin = 0; +switch (logType) { +case xseen.debug.INFO: +node.style.color = "#009900"; +break; +case xseen.debug.WARNING: +node.style.color = "#cd853f"; +break; +case xseen.debug.ERROR: +node.style.color = "#ff4500"; +break; +case xseen.debug.EXCEPTION: +node.style.color = "#ffff00"; +break; +default: +node.style.color = "#009900"; +break; +} +try { +node.innerHTML = logType + ": " + msg; +xseen.debug.logContainer.insertBefore(node, xseen.debug.logContainer.firstChild); +} catch (err) { +if (window.console.firebug !== undefined) { +window.console.warn(msg); +} +} +if (xseen.debug.isFirebugAvailable) { +switch (logType) { +case xseen.debug.INFO: +window.console.info(msg); +break; +case xseen.debug.WARNING: +window.console.warn(msg); +break; +case xseen.debug.ERROR: +window.console.error(msg); +break; +case xseen.debug.EXCEPTION: +window.console.debug(msg); +break; +default: +break; +} +} +xseen.debug.numLinesLogged++; +}, +logInfo: function(msg) { +xseen.debug.doLog(msg, xseen.debug.INFO); +}, +logWarning: function(msg) { +xseen.debug.doLog(msg, xseen.debug.WARNING); +}, +logError: function(msg) { +xseen.debug.doLog(msg, xseen.debug.ERROR); +}, +logException: function(msg) { +xseen.debug.doLog(msg, xseen.debug.EXCEPTION); +}, +assert: function(c, msg) { +if (!c) { +xseen.debug.doLog("Assertion failed in " + xseen.debug.assert.caller.name + ': ' + msg, xseen.debug.ERROR); +} +}, +typeOf: function (obj) { +var type = typeof obj; +return type === "object" && !obj ? "null" : type; +}, +exists: function (obj, name, type) { +type = type || "function"; +return (obj ? this.typeOf(obj[name]) : "null") === type; +}, +dumpFields: function (node) { +var str = ""; +for (var fName in node) { +str += (fName + ", "); +} +str += '\n'; +xseen.debug.logInfo(str); +return str; +} +}; +this.debug.setup(); +}; +xseen.Navigation = { +'TwoPi' : 2 * Math.PI, +'none' : function () {}, // Does not allow user-controlled navigation +'turntable' : function (speed, deltaT, scene, camera) +{ +var T, radians, radius, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +radius = vp.fields._radius0; +camera.position.x = radius * Math.sin(radians) +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.position.z = radius * Math.cos(radians); +camera.lookAt(scene.ORIGIN); +}, +'tilt' : function (speed, deltaT, scene, camera) +{ +var T, radians, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.lookAt(scene.ORIGIN); +}, +'setup' : { +'none' : function () {return null;}, +'orbit' : function (camera, renderer) +{ +var controls; +controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.enableZoom = false; +controls.enableZoom = true; +return controls; +}, +'trackball' : function (camera, renderer) +{ +var controls; +controls = new THREE.TrackballControls(camera, renderer.domElement); +controls.rotateSpeed = 1.0; +controls.zoomSpeed = 1.2; +controls.panSpeed = 0.8; +controls.noZoom = false; +controls.noPan = false; +controls.staticMoving = true; +controls.dynamicDampingFactor = 0.3; +controls.keys = [ 65, 83, 68 ]; +return controls; +}, +}, +}; +xseen.Navigation = { +'TwoPi' : 2 * Math.PI, +'none' : function () {}, // Does not allow user-controlled navigation +'turntable' : function (speed, deltaT, scene, camera) +{ +var T, radians, radius, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +radius = vp.fields._radius0; +camera.position.x = radius * Math.sin(radians) +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.position.z = radius * Math.cos(radians); +camera.lookAt(scene.ORIGIN); +}, +'tilt' : function (speed, deltaT, scene, camera) +{ +var T, radians, vp; +T = (new Date()).getTime() - xseen.timeStart; +radians = T * speed * this.TwoPi; +vp = scene.stacks.Viewpoints.getActive(); // Convienence declaration +camera.position.y = vp.fields.position[1] * Math.cos(1.5*radians); +camera.lookAt(scene.ORIGIN); +}, +'setup' : { +'none' : function () {return null;}, +'orbit' : function (camera, renderer) +{ +var controls; +controls = new THREE.OrbitControls( camera, renderer.domElement ); +controls.enableZoom = false; +controls.enableZoom = true; +return controls; +}, +}, +}; +xseen.nodes = { +'_defineNode' : function(nodeName, nodeComponent, nodeMethod) { +var methodBase = ''; +node = { +'tag' : nodeName, +'taglc' : nodeName.toLowerCase(), +'component' : nodeComponent, +'method' : methodBase + nodeMethod, +'fields' : [], +'fieldIndex': [], +'addField' : function (fieldObj, datatype, defaultValue) { +var fieldName, namelc, enumerated, animatable; +if (typeof(fieldObj) === 'object') { +fieldName = fieldObj.name; +datatype = fieldObj.datatype; +defaultValue = fieldObj.defaultValue; +enumerated = (typeof(fieldObj.enumerated) === 'undefined') ? [] : fieldObj.enumerated; +animatable = (typeof(fieldObj.animatable) === 'undefined') ? false : fieldObj.animatable; +} else { +fieldName = fieldObj; +animatable = false; +if (typeof(defaultValue) == 'array') { +enumerated = defaultValue; +defaultValue = enumerated[0]; +} else { +enumerated = []; +} +} +namelc = fieldName.toLowerCase(); +this.fields.push ({ +'field' : fieldName, +'fieldlc' : namelc, +'type' : datatype, +'default' : defaultValue, +'enumeration' : enumerated, +'animatable' : animatable, +'clone' : this.cloneField, +'setFieldName' : this.setFieldName, +}); +this.fieldIndex[namelc] = this.fields.length-1; +return this; +}, +'addNode' : function () { +xseen.parseTable[this.taglc] = this; +}, +'cloneField' : function () { +var newFieldObject = { +'field' : this.field, +'fieldlc' : this.fieldlc, +'type' : this.type, +'default' : 0, +'enumeration' : [], +'animatable' : this.animatable, +'clone' : this.clone, +'setFieldName' : this.setFieldName, +}; +for (var i=0; i= 0) { +this._internals.activeNode = this._internals.stack[this._internals.active]; +} else { +this._internals.activeNode = this._internals.defaultNode; +} +} +this.init = function() { // Clears existing stack +this._internals.stack = []; +} +this.pushDown = function(node) { // Push new node onto stack and make active +this._internals.stack.push (node); +this._setActiveNode(); +} +this.popOff = function() { // Pop node off stack and make next one active +this._internals.stack.pop(); +this._setActiveNode(); +} +this.getActive = function() { +return this._internals.activeNode; +} +this.setDefault = function(node) { +this._internals.defaultNode = node; +if (Object.keys(this._internals.activeNode).length === 0) { +this._internals.activeNode = this._internals.defaultNode; +} +} +} +xseen.types = { +'Deg2Rad' : Math.PI / 180, +'SFFloat' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return value; +}, +'SFInt' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return Math.round(value); +}, +'SFBool' : function (value, def) +{ +if (value === null) {return def;} +if (value) {return true;} +if (!value) {return false;} +return def; +}, +'SFTime' : function (value, def) +{ +if (value === null) {return def;} +if (Number.isNaN(value)) {return def}; +return value; +}, +'SFVec3f' : function (value, def) +{ +if (value === null) {return def;} +var v3 = value.split(' '); +if (v3.length < 3 || Number.isNaN(v3[0]) || Number.isNaN(v3[1]) || Number.isNaN(v3[2])) { +return def; +} +return [v3[0]-0, v3[1]-0, v3[2]-0]; +}, +'SFVec2f' : function (value, def) +{ +if (value === null) {return def;} +var v2 = value.split(' '); +if (v2.length != 2 || Number.isNaN(v2[0]) || Number.isNaN(v2[1])) { +return def; +} +return [v2[0]-0, v2[1]-0]; +}, +'SFRotation' : function (value, def) +{ +if (value === null) {return def;} +var v4 = value.split(' '); +if (v4.length != 4 || Number.isNaN(v4[0]) || Number.isNaN(v4[1]) || Number.isNaN(v4[2]) || Number.isNaN(v4[3])) { +return def; +} +var result = { +'vector' : [v4[0], v4[1], v4[2], v4[3]], +'axis_angle' : [{'x': v4[0], 'y': v4[1], 'z': v4[2]}, v4[3]], +}; +return result; +}, +'SFColor' : function (value, defaultString) +{ +var v3 = this.SFVec3f(value, defaultString); +v3[0] = Math.min(Math.max(v3[0], 0.0), 1.0); +v3[1] = Math.min(Math.max(v3[1], 0.0), 1.0); +v3[2] = Math.min(Math.max(v3[2], 0.0), 1.0); +return v3; +}, +'SFString' : function (value, def) +{ +if (value === null) {value = def;} +return value; +}, +'MFFloat' : function (value, def) +{ +var defReturn = (def == '') ? [] : def; +if (value === null) {return defReturn;} +var mi = value.split(' '); +var rv = []; +for (var i=0; iDocumentation."]; +var version = { +major : Major, +minor : Minor, +patch : Patch, +preRelease : PreRelease, +release : Release, +version : '', +date : RDate, +splashText : SplashText +}; +version.version = version.major + '.' + version.minor + '.' + version.patch; +version.version += (version.preRelease != '') ? '-' + version.preRelease : ''; +version.version += (version.release != '') ? '+' + version.release : ''; +return version; +} +xseen.node.core_NOOP = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.parsing = function (s, e) { +xseen.debug.logInfo ('Parsing init details stub for ' + s); +} +xseen.node.af_Entity = { +'init' : function (e,p) +{ +xseen.node.parsing('A-Frame Entity'); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Assets = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.af_AssetItem = { +'init' : function (e,p) // Only field is SRC. +{ +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Mixin = { +'init' : function (e,p) // Lots of fields -- all nebelous until used +{ +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Appearance = function (e) { +var parameters = { +'aoMap' : e._xseen.fields['ambient-occlusion-map'], +'aoMapIntensity' : e._xseen.fields['ambient-occlusion-map-intensity'], +'color' : e._xseen.fields['color'], +'displacementMap' : e._xseen.fields['displacement-map'], +'displacementScale' : e._xseen.fields['displacement-scale'], +'displacementBias' : e._xseen.fields['displacement-bias'], +'envMap' : e._xseen.fields['env-map'], +'normalMap' : e._xseen.fields['normal-map'], +'normalScale' : e._xseen.fields['normal-scale'], +'wireframe' : e._xseen.fields['wireframe'], +'wireframeLinewidth' : e._xseen.fields['wireframe-linewidth'], +}; +var material = new THREE.MeshPhongMaterial(parameters); +return material; +} +xseen.node.af_Box = { +'init' : function (e,p) +{ +var geometry = new THREE.BoxGeometry( +e._xseen.fields.width, +e._xseen.fields.height, +e._xseen.fields.depth, +e._xseen.fields['segments-width'], +e._xseen.fields['segments-height'], +e._xseen.fields['segments-depth'] +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Cone = { +'init' : function (e,p) +{ +var geometry = new THREE.ConeGeometry( +e._xseen.fields.radius, +e._xseen.fields.height, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-height'], +e._xseen.fields['open-ended'], +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Cylinder = { +'init' : function (e,p) +{ +var geometry = new THREE.CylinderGeometry( +e._xseen.fields['radius-top'], +e._xseen.fields['radius-bottom'], +e._xseen.fields.height, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-height'], +e._xseen.fields['open-ended'], +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Dodecahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.DodecahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Icosahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.IcosahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Octahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.OctahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Sphere = { +'init' : function (e,p) +{ +var geometry = new THREE.SphereGeometry( +e._xseen.fields.radius, +e._xseen.fields['segments-width'], +e._xseen.fields['segments-height'], +e._xseen.fields['phi-start'] * xseen.types.Deg2Rad, +e._xseen.fields['phi-length'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-start'] * xseen.types.Deg2Rad, +e._xseen.fields['theta-length'] * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Tetrahedron = { +'init' : function (e,p) +{ +var geometry = new THREE.TetrahedronGeometry( +e._xseen.fields.radius, +e._xseen.fields.detail +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.af_Torus = { +'init' : function (e,p) +{ +var geometry = new THREE.TorusGeometry( +e._xseen.fields.radius, +e._xseen.fields.tube, +e._xseen.fields['segments-radial'], +e._xseen.fields['segments-tubular'], +e._xseen.fields.arc * xseen.types.Deg2Rad +); +var appearance = xseen.node.af_Appearance (e); +var mesh = new THREE.Mesh (geometry, appearance); +mesh.userData = e; +p._xseen.sceneInfo.selectable.push(mesh); +var group = new THREE.Group(); +group.add (mesh); +if (typeof(p._xseen.children) == 'undefined') {p._xseen.children = [];} +p._xseen.children.push(group); +}, +'fin' : function (e,p) {} +}; +xseen.node.unk_Viewpoint = { +'init' : function (e,p) +{ // This should really go in a separate push-down list for Viewpoints +e._xseen.fields._radius0 = Math.sqrt( e._xseen.fields.position[0]*e._xseen.fields.position[0] + +e._xseen.fields.position[1]*e._xseen.fields.position[1] + +e._xseen.fields.position[2]*e._xseen.fields.position[2]); +e._xseen.domNode = e; // Back-link to node if needed later on +e._xseen.position = new THREE.Vector3(e._xseen.fields.position[0], e._xseen.fields.position[1], e._xseen.fields.position[2]); +e._xseen.type = e._xseen.fields.cameratype; +e._xseen.motion = e._xseen.fields.motion; +e._xseen.motionspeed = e._xseen.fields.motionspeed * 1000; +if (e._xseen.motion == 'turntable' || e._xseen.motion == 'tilt') {e._xseen.motionspeed = 1.0/e._xseen.motionspeed;} +if (!e._xseen.sceneInfo.tmp.activeViewpoint) { +e._xseen.sceneInfo.stacks.Viewpoints.pushDown(e._xseen); +e._xseen.sceneInfo.tmp.activeViewpoint = true; +} +e._xseen.handlers = {}; +e._xseen.handlers.setactive = this.setactive; +}, +'fin' : function (e,p) {}, +'setactive' : function (ev) +{ +var xseenNode = this.destination._xseen; +xseenNode.sceneInfo.stacks.Viewpoints.pushDown(xseenNode); // TODO: This is probably not the right way to change VP in the stack +xseenNode.sceneInfo.element._xseen.renderer.activeCamera = +xseenNode.sceneInfo.element._xseen.renderer.cameras[xseenNode.fields.type]; +xseenNode.sceneInfo.element._xseen.renderer.activeRender = +xseenNode.sceneInfo.element._xseen.renderer.renderEffects[xseenNode.fields.type]; +if (xseenNode.fields.type != 'stereo') { +xseenNode.sceneInfo.element._xseen.renderer.activeRender.setViewport( 0, 0, xseenNode.sceneInfo.size.width, this.destination._xseen.sceneInfo.size.height); +} +}, +}; +xseen.node.controls_Navigation = { +'init' : function (e,p) +{ // This should really go in a separate push-down list for Viewpoints +e._xseen.domNode = e; // Back-link to node if needed later on +e._xseen.speed = e._xseen.fields.speed; +if (e._xseen.setup == 'examine') {e._xseen.setup == 'trackball';} +e._xseen.type = 'none'; +e._xseen.setup = e._xseen.fields.type; +if (!(e._xseen.setup == 'orbit' || e._xseen.setup == 'trackball')) {e._xseen.setup = 'none';} +if (!e._xseen.sceneInfo.tmp.activeNavigation) { +e._xseen.sceneInfo.stacks.Navigation.pushDown(e._xseen); +e._xseen.sceneInfo.tmp.activeNavigation = true; +} +e._xseen.handlers = {}; +e._xseen.handlers.setactive = this.setactive; +}, +'fin' : function (e,p) {}, +'setactive' : function (ev) +{ +}, +}; +xseen.node.lighting_Light = { +'init' : function (e,p) +{ +var color = xseen.types.Color3toInt (e._xseen.fields.color); +var intensity = e._xseen.fields.intensity - 0; +var lamp, type=e._xseen.fields.type.toLowerCase(); +if (type == 'point') { +lamp = new THREE.PointLight (color, intensity); +lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); +lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); +} else if (type == 'spot') { +lamp = new THREE.SpotLight (color, intensity); +lamp.position.set(0-e._xseen.fields.direction[0], 0-e._xseen.fields.direction[1], 0-e._xseen.fields.direction[2]); +lamp.distance = Math.max(0.0, e._xseen.fields.radius - 0); +lamp.decay = Math.max (.1, e._xseen.fields.attenuation[1]/2 + e._xseen.fields.attenuation[2]); +lamp.angle = Math.max(0.0, Math.min(1.5707963267948966192313216916398, e._xseen.fields.cutoffangle)); +lamp.penumbra = 1 - Math.max(0.0, Math.min(lamp.angle, e._xseen.fields.beamwidth)) / lamp.angle; +} else { // DirectionalLight (by default) +lamp = new THREE.DirectionalLight (color, intensity); +lamp.position.x = 0-e._xseen.fields.direction[0]; +lamp.position.y = 0-e._xseen.fields.direction[1]; +lamp.position.z = 0-e._xseen.fields.direction[2]; +} +p._xseen.children.push(lamp); +lamp = null; +} +, +'fin' : function (e,p) +{ +} +}; +xseen.node.appearance_Material = { +'init' : function (e,p) +{ +var transparency = e._xseen.fields.transparency - 0; +var shininess = e._xseen.fields.shininess - 0; +var colorDiffuse = xseen.types.Color3toInt (e._xseen.fields.diffusecolor); +var colorEmissive = xseen.types.Color3toInt (e._xseen.fields.emissivecolor); +var colorSpecular = xseen.types.Color3toInt (e._xseen.fields.specularcolor); +p._xseen.material = new THREE.MeshPhongMaterial( { +'color' : colorDiffuse, +'emissive' : colorEmissive, +'specular' : colorSpecular, +'shininess' : shininess, +'opacity' : 1.0-transparency, +'transparent' : (transparency > 0.0) ? true : false +} ); +e._xseen.animate['diffusecolor'] = p._xseen.material.color; +e._xseen.animate['emissivecolor'] = p._xseen.material.emissive; +e._xseen.animate['specularcolor'] = p._xseen.material.specular; +e._xseen.animate['transparency'] = p._xseen.material.opacity; +e._xseen.animate['shininess'] = p._xseen.material.shininess; +}, +'fin' : function (e,p) {} +}; +xseen.node.appearance_ImageTexture = { +'init' : function (e,p) +{ +p._xseen.texture = xseen.loader.ImageLoader.load(e._xseen.fields.url); +p._xseen.texture.wrapS = (e._xseen.fields.repeats) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; +p._xseen.texture.wrapT = (e._xseen.fields.repeatt) ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; +}, +'fin' : function (e,p) {} +}; +xseen.node.appearance_Appearance = { +'init' : function (e,p) {}, +'fin' : function (e,p) +{ +if (typeof(e._xseen.texture) !== 'undefined' && e._xseen.texture !== null) { +e._xseen.material.map = e._xseen.texture; +} +p._xseen.appearance = e._xseen.material; +} +}; +xseen.node.geometry_Coordinate = { +'init' : function (e,p) +{ +var vertices = []; +for (var i=0; i 0) ? true : false; +var useColor = (color.length > 0) ? true : false; +var maxIndex = Math.max.apply(null, indices); +var minIndex = Math.min.apply(null, indices); +if (maxIndex >= vertices.length) { +console.log ('Maximum index ('+maxIndex+') exceeds vertex count ('+vertices.length+'). No geometry is created'); +return; +} +if (useNormals && maxIndex >= normals.length) { +console.log ('Maximum index ('+maxIndex+') exceeds normal count ('+normals.length+'). No geometry is created'); +return; +} +if (useColor && maxIndex >= color.length) { +console.log ('Maximum index ('+maxIndex+') exceeds color count ('+color.length+'). No geometry is created'); +return; +} +if (minIndex < 0) { +console.log ('Minimum index ('+minIndex+') less than zero. No geometry is created'); +return; +} +if (indices.length % 3 != 0) { +console.log ('Number of indices ('+indices.length+') not divisible by 3. No geometry is created'); +return; +} +var geometry = new THREE.Geometry(); +var normal_pz = new THREE.Vector3 (0, 0, 1); +var normal_mz = new THREE.Vector3 (0, 0, -1); +for (var i=0; i 0) {clip.duration = e._xseen.fields.duration;} +e._xseen.mixer.clipAction( clip ).play(); +} ); +} else { // Play a specific animation +var clip = THREE.AnimationClip.findByName(response.animations, e._xseen.fields.playonload); +var action = e._xseen.mixer.clipAction (clip); +action.play(); +} +} +} +} +}; +xseen.node.x_Route = { +'init' : function (e,p) +{ +var dest = e._xseen.fields.destination; +var hand = e._xseen.fields.handler; +var externalHandler = false; +if (e._xseen.fields.source == '' || +typeof(window[hand]) !== 'function' && +(dest == '' || e._xseen.fields.event == '' || e._xseen.fields.field == '')) { +xseen.debug.logError ('Route node missing field. No route setup. Source: '+e._xseen.fields.source+'.'+e._xseen.fields.event+'; Destination: '+dest+'.'+e._xseen.fields.field+'; Handler: '+hand); +return; +} else if (typeof(window[hand]) === 'function') { +externalHandler = true; +} +var eSource = document.getElementById (e._xseen.fields.source); +if (! externalHandler) { +var eDestination = document.getElementById (dest); +if (typeof(eSource) === 'undefined' || typeof(eDestination) === 'undefined') { +xseen.debug.logError ('Source or Destination node does not exist. No route setup'); +return; +} +var fField = xseen.nodes._getFieldInfo (eDestination.nodeName, e._xseen.fields.field); +if (typeof(fField) === 'undefined' || !fField.good) { +xseen.debug.logError ('Destination field does not exist or incorrectly specified. No route setup'); +return; +} +xseen.Events.addHandler (e, eSource, e._xseen.fields.event, eDestination, fField); +} else { +var handler = window[hand]; +eSource.addEventListener ('xseen', handler); +} +}, +'fin' : function (e,p) +{ +}, +'evHandler' : function (u) +{ +var de = u.e; +var df = u.f; +return de._xseen.handlers[df.handlerName]; +}, +}; +xseen.node.core_NOOP = { +'init' : function (e,p) {}, +'fin' : function (e,p) {} +}; +xseen.node.core_WorldInfo = { +'init' : function (e,p) {parsing('WorldInfo', e)}, +'fin' : function (e,p) {} +}; +function parsing (s, e) { +xseen.debug.logInfo ('Parsing init details stub for ' + s); +} +xseen.node.unk_Shape = { +'init' : function (e,p) {}, +'fin' : function (e,p) +{ +if (typeof(e._xseen.materialProperty) !== 'undefined') { +e._xseen.appearance.vertexColors = THREE.VertexColors; +e._xseen.appearance._needsUpdate = true; +e._xseen.appearance.needsUpdate = true; +} +var mesh = new THREE.Mesh (e._xseen.geometry, e._xseen.appearance); +mesh.userData = e; +p._xseen.children.push(mesh); +p._xseen.sceneInfo.selectable.push(mesh); +mesh = null; +} +}; +xseen.node.grouping_Transform = { +'init' : function (e,p) +{ +var group = new THREE.Group(); +if (e.nodeName == "TRANSFORM") { +var rotation = xseen.types.Rotation2Quat(e._xseen.fields.rotation); +group.name = 'Transform children [' + e.id + ']'; +group.position.x = e._xseen.fields.translation[0]; +group.position.y = e._xseen.fields.translation[1]; +group.position.z = e._xseen.fields.translation[2]; +group.scale.x = e._xseen.fields.scale[0]; +group.scale.y = e._xseen.fields.scale[1]; +group.scale.z = e._xseen.fields.scale[2]; +group.quaternion.x = rotation.x; +group.quaternion.y = rotation.y; +group.quaternion.z = rotation.z; +group.quaternion.w = rotation.w; +e._xseen.animate['translation'] = group.position; +e._xseen.animate['rotation'] = group.quaternion; +e._xseen.animate['scale'] = group.scale; +} +e._xseen.sceneNode = group; +}, +'fin' : function (e,p) +{ +e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +e._xseen.sceneNode.add(child); +}); +p._xseen.children.push(e._xseen.sceneNode); +} +}; +xseen.node.networking_Inline = { +'init' : function (e,p) +{ +if (typeof(e._xseen.processedUrl) === 'undefined' || !e._xseen.requestedUrl) { +var uri = xseen.parseUrl (e._xseen.fields.url); +var type = uri.extension; +e._xseen.loadGroup = new THREE.Group(); +e._xseen.loadGroup.name = 'Inline content [' + e.id + ']'; +console.log ('Created Inline Group with UUID ' + e._xseen.loadGroup.uuid); +var userdata = {'requestType':'x3d', 'e':e, 'p':p} +if (type.toLowerCase() == 'json') { +userdata.requestType = 'json'; +xseen.loadMgr.loadJson (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); +} else { +xseen.loadMgr.loadXml (e._xseen.fields.url, this.loadSuccess, xseen.loadProgress, xseen.loadError, userdata); +} +e._xseen.requestedUrl = true; +} +p._xseen.children.push(e._xseen.loadGroup); +console.log ('Using Inline Group with UUID ' + e._xseen.loadGroup.uuid); +}, +'fin' : function (e,p) +{ +}, +'loadSuccess' : +function (response, userdata, xhr) { +userdata.e._xseen.processedUrl = true; +userdata.e._xseen.loadResponse = response; +console.log("download successful for "+userdata.e.id); +if (userdata.requestType == 'json') { +var tmp = {'scene': response}; +response = null; +response = (new JSONParser()).parseJavaScript(tmp); +} +var start = {'_xseen':0}; +var findSceneTag = function (fragment) { +if (typeof(fragment._xseen) === 'undefined') {fragment._xseen = {'childCount': -1};} +if (fragment.nodeName.toLowerCase() == 'scene') { +start = fragment; +return; +} else if (fragment.children.length > 0) { +for (fragment._xseen.childCount=0; fragment._xseen.childCount 0) { +userdata.e.appendChild(start.children[0]); +} +xseen.Parse(userdata.e, userdata.p, userdata.p._xseen.sceneInfo); +userdata.e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +userdata.e._xseen.loadGroup.add(child); +console.log ('...Adding ' + child.type + ' (' + child.name + ') to Inline Group? with UUID ' + userdata.e._xseen.loadGroup.uuid + ' (' + userdata.e._xseen.loadGroup.name + ')'); +}); +userdata.p._xseen.sceneInfo.scene.updateMatrixWorld(); +} else { +console.log("Found illegal X3D file -- no 'scene' tag"); +} +} +}; +xseen.node.core_Scene = { +'DEFAULT' : { +'Viewpoint' : { +'Position' : new THREE.Vector3 (0, 0, 10), +'Orientation' : '0 1 0 0', // TODO: fix (and below) when handling orientation +'Type' : 'perpsective', +'Motion' : 'none', +'MotionSpeed' : 1.0, +}, +'Navigation' : { +'Speed' : 1.0, // 16 spr (1 revolution per 16 seconds), in mseconds. +'Type' : 'none', +'Setup' : 'none', +} +}, +'init' : function (e,p) +{ +xseen.sceneInfo[0].stacks.Viewpoints.setDefault( +{ +'position' : this.DEFAULT.Viewpoint.Position, +'type' : this.DEFAULT.Viewpoint.Type, +'motion' : this.DEFAULT.Viewpoint.Motion, +'motionspeed': this.DEFAULT.Viewpoint.MotionSpeed / 1000, +'domNode' : e, +'fields' : {}, +} +); +xseen.sceneInfo[0].stacks.Navigation.setDefault( +{ +'speed' : this.DEFAULT.Navigation.Speed / 1000, +'type' : this.DEFAULT.Navigation.Type, +'setup' : this.DEFAULT.Navigation.Setup, +'domNode' : e, +'fields' : {}, +} +); +var width = e._xseen.sceneInfo.size.width; +var height = e._xseen.sceneInfo.size.height; +var x_renderer = new THREE.WebGLRenderer(); +x_renderer.setSize (width, height); +var perspectiveCamera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 ); +var orthoCamera = new THREE.OrthographicCamera( 75, width / height, 0.1, 1000 ); +perspectiveCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position +perspectiveCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position +perspectiveCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position +orthoCamera.position.x = this.DEFAULT.Viewpoint.Position.x; // Default position +orthoCamera.position.y = this.DEFAULT.Viewpoint.Position.y; // Default position +orthoCamera.position.z = this.DEFAULT.Viewpoint.Position.z; // Default position +var x_effect = new THREE.StereoEffect(x_renderer); +e.appendChild (x_renderer.domElement); +e._xseen.renderer = { +'canvas' : e._xseen.sceneInfo.scene, +'width' : width, +'height' : height, +'cameras' : { +'perspective' : perspectiveCamera, +'ortho' : orthoCamera, +'stereo' : perspectiveCamera, +}, // Removed .sceneInfo camera because this node defines the camera +'effects' : x_effect, +'renderEffects' : { +'normal' : x_renderer, +'perspective' : x_renderer, +'ortho' : x_renderer, +'stereo' : x_effect, +}, +'activeRender' : {}, +'activeCamera' : {}, +'controls' : {}, // Used for navigation +}; +e._xseen.renderer.activeRender = e._xseen.renderer.renderEffects.normal; +e._xseen.renderer.activeCamera = e._xseen.renderer.cameras.perspective; +}, +'fin' : function (e,p) +{ +e._xseen.children.forEach (function (child, ndx, wholeThing) +{ +console.log('Adding child of type ' + child.type + ' (' + child.name + ')'); +e._xseen.renderer.canvas.add(child); +}); +xseen.dumpSceneGraph (); +var vp = xseen.sceneInfo[0].stacks.Viewpoints.getActive(); +var nav = xseen.sceneInfo[0].stacks.Navigation.getActive(); +var currentCamera = e._xseen.renderer.activeCamera; +var currentRenderer = e._xseen.renderer.activeRender; +currentCamera.position.x = vp.position.x; +currentCamera.position.y = vp.position.y; +currentCamera.position.z = vp.position.z; +e._xseen.renderer.controls = xseen.Navigation.setup[nav.setup] (currentCamera, currentRenderer); +xseen.debug.logInfo("Ready to kick off rendering loop"); +xseen.renderFrame(); +}, +}; +xseen.node.env_Background = { +'init' : function (e,p) +{ +var color = new THREE.Color(e._xseen.fields.skycolor[0], e._xseen.fields.skycolor[1], e._xseen.fields.skycolor[2]); +var textureCube = new THREE.CubeTextureLoader() +.load ([e._xseen.fields.srcright, +e._xseen.fields.srcleft, +e._xseen.fields.srctop, +e._xseen.fields.srcbottom, +e._xseen.fields.srcfront, +e._xseen.fields.srcback], +this.loadSuccess({'e':e, 'p':p}) +); +e._xseen.sceneInfo.scene.background = color; +}, +'fin' : function (e,p) +{ +p._xseen.appearance = e._xseen.material; +}, +'loadSuccess' : function (userdata) +{ +var e = userdata.e; +var p = userdata.p; +return function (textureCube) +{ +e._xseen.processedUrl = true; +e._xseen.loadTexture = textureCube; +e._xseen.sceneInfo.scene.background = textureCube; +} +}, +}; +xseen.node.x_Animate = { +'init' : function (e,p) +{ +var delay = e._xseen.fields.delay * 1000; // Convert to milliseconds +var duration = e._xseen.fields.duration * 1000; // Convert to milliseconds +var repeat = (e._xseen.fields.repeat < 0) ? Infinity : e._xseen.fields.repeat; +var interpolator = e._xseen.fields.interpolator; +var easing = e._xseen.fields.easing; +var fields = xseen.parseTable[p.localName.toLowerCase()].fields; +var fieldIndex = xseen.parseTable[p.localName.toLowerCase()].fieldIndex; +var toField = e._xseen.fields.field; +var toFieldIndex = fieldIndex[toField]; +if (typeof(fields[toFieldIndex]) === 'undefined') { +xseen.debug.logInfo("Field '" + toField + "' not found in parent (" + p.localName.toLowerCase() + "). No animation performed."); +return; +} +var fieldObject = fields[toFieldIndex].clone().setFieldName('to'); // Parse table entry for 'toField' +var to = xseen.nodes._parseField(fieldObject, e); // Parsed data -- need to convert to THREE format +var interpolation; +if (fieldObject.type == 'SFVec3f') { +interpolation = TWEEN.Interpolation.Linear; +to = xseen.types.Vector3(to); +xseen.debug.logInfo("Interpolating field '" + toField + "' as 3-space."); +} else if (fieldObject.type == 'SFColor') { +interpolation = this.Interpolator.color; +to = new THREE.Color (xseen.types.Color3toInt(to)); +xseen.debug.logInfo("Interpolation field '" + toField + "' as color."); +} else if (fieldObject.type == 'SFRotation') { +interpolation = this.Interpolator.slerp; +to = xseen.types.Rotation2Quat(to); +xseen.debug.logInfo("Interpolation field '" + toField + "' as rotation."); +} else { +xseen.debug.logInfo("Field '" + toField + "' not converted to THREE format. No animation performed."); +return; +} +var fieldTHREE = p._xseen.animate[toField]; // THREE field for animation +var tween = new TWEEN.Tween(fieldTHREE) +.to(to, duration) +.delay(delay) +.repeat(repeat) +.interpolation(interpolation); +var easingType = e._xseen.fields.easingtype; +easingType = easingType.charAt(0).toUpperCase() + easingType.slice(1); +easing = (easingType != 'Linear' && easing == '') ? 'inout' : easing; +if (easing != '') { +easing = easing.replace('in', 'In').replace('out', 'Out'); +easingType = (easingType == 'Linear') ? 'Quadratic' : easingType; +e._xseen.fields.easing = easing; +e._xseen.fields.easingtype = easingType; +tween.easing(TWEEN.Easing[easingType][easing]); +} +e._xseen.initialValue = fieldTHREE.clone(); +e._xseen.animatingField = fieldTHREE; +e._xseen.handlers = {}; +e._xseen.handlers.setstart = this.setstart; +e._xseen.handlers.setstop = this.setstop; +e._xseen.handlers.setpause = this.setpause; +e._xseen.handlers.setresetstart = this.setresetstart; +e._xseen.animating = tween; +p._xseen.animation.push (tween); +tween.start(); +}, +'fin' : function (e,p) {}, +'setstart' : function (ev) +{ +console.log ('Starting animation'); +this.destination._xseen.animating.start(); +}, +'setstop' : function (ev) +{ +console.log ('Stopping animation'); +this.destination._xseen.animating.stop(); +}, +'setpause' : function (ev) +{ +console.log ('Pausing (really stopping) animation'); +this.destination._xseen.animating.stop(); +}, +'setresetstart' : function (ev) // TODO: Create seperate 'reset' method +{ +console.log ('Reset and start animation'); +this.destination._xseen.animatingField = this.destination._xseen.initialValue; +this.destination._xseen.animating.start(); +}, +'Interpolator' : { +'slerp' : function (v,k) +{ +var m = v.length - 1; +var f = m * k; +var i = Math.floor(f); +if (k < 0) { +return v[0].slerp(v[1], f); +} +if (k > 1) { +return v[m].slerp(v[m-1], m-f); +} +return v[i].slerp (v[i + 1 > m ? m : i + 1], f-i); +}, +'color' : function (v,k) +{ +var m = v.length - 1; +var f = m * k; +var i = Math.floor(f); +var fn = this.slerpCompute; +if (k < 0) { +return v[0].lerp(v[1], f); +} +if (k > 1) { +return v[m].lerp(v[m-1], m-f); +} +return v[i].lerp (v[i + 1 > m ? m : i + 1], f - i); +}, +}, +}; +xseen._addAframeAppearance = function (node) { +node +.addField('ambient-occlusion-map', 'SFString', '') +.addField('ambient-occlusion-map-intensity', 'SFFloat', 1) +.addField('ambient-occlusion-texture-offset', 'SFVec2f', '0 0') +.addField('ambient-occlusion-texture-repeat', 'SFVec2f', '1 1') +.addField('color', 'Color', '#FFF') +.addField('displacement-bias', 'SFFloat', 0.5) +.addField('displacement-map', 'SFString', '') +.addField('displacement-scale', 'SFFloat', 1) +.addField('displacement-texture-offset', 'SFVec2f', '0 0') +.addField('displacement-texture-repeat', 'SFVec2f', '1 1') +.addField('env-map', 'SFString', '') +.addField('fog', 'SFBool', true) +.addField('metalness', 'SFFloat', 0) +.addField('normal-map', 'SFString', '') +.addField('normal-scale', 'SFVec2f', '1 1') +.addField('normal-texture-offset', 'SFVec2f', '0 0') +.addField('normal-texture-repeat', 'SFVec2f', '1 1') +.addField('repeat', 'SFVec2f', '1 1') +.addField('roughness', 'SFFloat', 0.5) +.addField('spherical-env-map', 'SFString', '') +.addField('src', 'SFString', '') +.addField('wireframe', 'SFBool', false) +.addField('wireframe-linewidth', 'SFInt', 2) +.addNode(); +} +xseen.nodes._defineNode('a-entity', 'A-Frame', 'af_Entity') +.addField('geometry', 'SFString', '') +.addField('material', 'SFString', '') +.addField('light', 'SFString', '') +.addNode(); +var node; +node = xseen.nodes._defineNode('a-box', 'A-Frame', 'af_Box') +.addField('depth', 'SFFloat', 1) +.addField('height', 'SFFloat', 1) +.addField('width', 'SFFloat', 512) +.addField('segments-depth', 'SFInt', 1) +.addField('segments-height', 'SFInt', 1) +.addField('segments-width', 'SFInt', 1); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-cone', 'A-Frame', 'af_Cone') +.addField('height', 'SFFloat', 1) +.addField('radius', 'SFFloat', 1) +.addField('open-ended', 'SFBool', false) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 1) +.addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-cylinder', 'A-Frame', 'af_Cylinder') +.addField('height', 'SFFloat', 1) +.addField('open-ended', 'SFBool', false) +.addField('radius-bottom', 'SFFloat', 1) +.addField('radius-top', 'SFFloat', 1) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 1) +.addField('segments-radial', 'SFInt', 8); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-dodecahedron', 'A-Frame', 'af_Dodecahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-icosahedron', 'A-Frame', 'af_Icosahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-octahedron', 'A-Frame', 'af_Octahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-sphere', 'A-Frame', 'af_Sphere') +.addField('radius', 'SFFloat', 1) +.addField('theta-start', 'SFFloat', 0) +.addField('theta-length', 'SFFloat', 180) +.addField('phi-start', 'SFFloat', 0) +.addField('phi-length', 'SFFloat', 360) +.addField('segments-height', 'SFInt', 18) +.addField('segments-width', 'SFInt', 36); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-tetrahedron', 'A-Frame', 'af_Tetrahedron') +.addField('radius', 'SFFloat', 1) +.addField('detail', 'SFFloat', 0); +xseen._addAframeAppearance (node); +node = xseen.nodes._defineNode('a-torus', 'A-Frame', 'af_Torus') +.addField('radius', 'SFFloat', 2) +.addField('tube', 'SFFloat', 1) +.addField('arc', 'SFFloat', 360) +.addField('segments-radial', 'SFInt', 8) +.addField('segments-tubular', 'SFInt', 6); +xseen._addAframeAppearance (node); +xseen.nodes._defineNode('a-assets', 'A-Frame', 'af_Assets') +.addNode(); +xseen.nodes._defineNode('a-asset-item', 'A-Frame', 'af_AssetItem') +.addField('src', 'SFString', '') +.addNode(); +xseen.nodes._defineNode('a-mixin', 'A-Frame', 'af_Mixin') +.addField('*', 'SFString', '') +.addNode(); +xseen.nodes._defineNode('Cone', 'Geometry3D', 'geometry3D_Cone') +.addField('bottomRadius', 'SFFloat', 1) +.addField('height', 'SFFloat', 2) +.addField('bottom', 'SFBool', true) +.addField('side', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode('Box', 'Geometry3D', 'geometry3D_Box') +.addField('size', 'SFVec3f', [1,1,1]) +.addNode(); +xseen.nodes._defineNode('Sphere', 'Geometry3D', 'geometry3D_Sphere') +.addField('radius', 'SFFloat', '1') +.addNode(); +xseen.nodes._defineNode('Cylinder', 'Geometry3D', 'geometry3D_Cylinder') +.addField('radius', 'SFFloat', 1) +.addField('height', 'SFFloat', 2) +.addField('bottom', 'SFBool', true) +.addField('side', 'SFBool', true) +.addField('top', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode ('Material', 'Appearance', 'appearance_Material') +.addField({name:'diffuseColor', datatype:'SFColor', defaultValue:[.8,.8,.8], animatable:true}) +.addField({name:'emissiveColor',datatype: 'SFColor', defaultValue:[0,0,0], animatable:true}) +.addField({name:'specularColor', datatype:'SFColor', defaultValue:[0,0,0], animatable:true}) +.addField({name:'transparency', datatype:'SFFloat', defaultValue:'0', animatable:true}) +.addField({name:'shininess', datatype:'SFFloat', defaultValue:'0', animatable:true}) +.addNode(); +xseen.nodes._defineNode ('Transform', 'Grouping', 'grouping_Transform') +.addField({name:'translation', datatype:'SFVec3f', defaultValue:[0,0,0], animatable:true}) +.addField({name:'scale', datatype:'SFVec3f', defaultValue:[1,1,1], animatable:true}) +.addField({name:'rotation', datatype:'SFRotation', defaultValue:xseen.types.SFRotation('0 1 0 0',''), animatable:true}) +.addNode(); +xseen.nodes._defineNode ('Group', 'Grouping', 'grouping_Transform') +.addNode(); +xseen.nodes._defineNode ('Light', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) // DirectionalLight +.addField('location', 'SFVec3f', [0,0,0]) // PointLight & SpotLight +.addField('radius', 'SFFloat', '100') // PointLight & SpotLight +.addField('attenuation', 'SFVec3f', [1,0,0]) // PointLight & SpotLight +.addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // SpotLight +.addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // SpotLight +.addField('color', 'SFColor', [1,1,1]) // General +.addField('intensity', 'SFFloat', '1') // General +.addField({name:'type', datatype:'EnumerateString', defaultValue:'Directional', enumerated:['Directional', 'Spot', 'Point'], animatable:true}) +.addNode(); +xseen.nodes._defineNode ('DirectionalLight', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Directional') +.addNode(); +xseen.nodes._defineNode ('PointLight', 'Lighting', 'lighting_Light') +.addField('location', 'SFVec3f', [0,0,0]) +.addField('radius', 'SFFloat', '100') +.addField('attenuation', 'SFVec3f', [1,0,0]) +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Point') +.addNode(); +xseen.nodes._defineNode ('SpotLight', 'Lighting', 'lighting_Light') +.addField('direction', 'SFVec3f', [0,0,-1]) +.addField('radius', 'SFFloat', '100') +.addField('attenuation', 'SFVec3f', [1,0,0]) +.addField('beamWidth', 'SFFloat', '0.78539816339744830961566084581988') // pi/4 +.addField('cutOffAngle', 'SFFloat', '1.5707963267948966192313216916398') // pi/2 +.addField('color', 'SFColor', [1,1,1]) +.addField('intensity', 'SFFloat', '1') +.addField('type', 'SFString', 'Spot') +.addNode(); +xseen.nodes._defineNode ('Viewpoint', 'Controls', 'unk_Viewpoint') +.addField('position', 'SFVec3f', '0 0 10') +.addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) +.addField('description', 'SFString', '') +.addField({name:'cameratype', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) +.addField({name:'type', datatype:'EnumerateString', defaultValue:'perspective', enumerated:['perspective', 'stereo', 'orthographic'], animatable:false}) +.addField({name:'motion', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'turntable', 'tilt'], animatable:false}) +.addField('motionspeed', 'SFFloat', 16) +.addField('active', 'SFBool', true) // incoming event +.addNode(); +xseen.nodes._defineNode ('NavigationMode', 'Controls', 'controls_Navigation') +.addField('speed', 'SFFloat', 1.) +.addField({name:'type', datatype:'EnumerateString', defaultValue:'none', enumerated:['none', 'orbit', 'fly', 'examine', 'trackball'], animatable:false}) +.addNode(); +xseen.nodes._defineNode ('Camera', 'Controls', 'unk_Viewpoint') +.addField('position', 'SFVec3f', [0,0,10]) +.addField('orientation', 'SFRotation', xseen.types.SFRotation('0 1 0 0','')) +.addNode(); +xseen.nodes._defineNode ('Inline', 'Networking', 'networking_Inline') +.addField('url', 'SFString', '') +.addNode(); +xseen.nodes._defineNode ('scene', 'Core', 'core_Scene') +.addNode(); +xseen.nodes._defineNode ('canvas', 'Core', 'core_NOOP') +.addNode(); +xseen.nodes._defineNode ('WorldInfo', 'Core', 'core_WorldInfo') +.addNode(); +xseen.nodes._defineNode ('Appearance', 'Appearance', 'appearance_Appearance') +.addNode(); +xseen.nodes._defineNode ('ImageTexture', 'Appearance', 'appearance_ImageTexture') +.addField('url', 'SFString', '') +.addField('repeatS', 'SFBool', true) +.addField('repeatT', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode ('Shape', 'Shape', 'unk_Shape') +.addNode(); +xseen.nodes._defineNode('Background', 'Environmental', 'env_Background') +.addField('skyColor', 'SFColor', [0,0,0]) +.addField('srcFront', 'SFString', '') +.addField('srcBack', 'SFString', '') +.addField('srcTop', 'SFString', '') +.addField('srcBottom', 'SFString', '') +.addField('srcLeft', 'SFString', '') +.addField('srcRight', 'SFString', '') +.addField('backgroundIsCube', 'SFBool', 'true') +.addNode(); +xseen.nodes._defineNode('TriangleSet', 'Geometry', 'geometry_TriangleSet') +.addField('ccw', 'SFBool', 'true') +.addField('colorPerVertex', 'SFBool', 'true') +.addField('solid', 'SFBool', 'true') +.addNode(); +xseen.nodes._defineNode('IndexedTriangleSet', 'Geometry', 'geometry_IndexedTriangleSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('index', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('Coordinate', 'Geometry', 'geometry_Coordinate') +.addField('point', 'MFVec3f', []) +.addNode(); +xseen.nodes._defineNode('Normal', 'Geometry', 'geometry_Normal') +.addField('vector', 'MFVec3f', []) +.addNode(); +xseen.nodes._defineNode('Color', 'Geometry', 'geometry_Color') +.addField('color', 'MFColor', []) +.addNode(); +xseen.nodes._defineNode('IndexedFaceSet', 'Geometry', 'geometry_IndexedFaceSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('coordIndex', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('IndexedQuadSet', 'Geometry', 'geometry_IndexedQuadSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addField('index', 'MFInt', '') +.addNode(); +xseen.nodes._defineNode('QuadSet', 'Geometry', 'geometry_QuadSet') +.addField('ccw', 'SFBool', true) +.addField('colorPerVertex', 'SFBool', true) +.addField('solid', 'SFBool', true) +.addNode(); +xseen.nodes._defineNode('model', 'XSeen', 'x_Model') +.addField('src', 'SFString', '') +.addField('playonload', 'SFString', '') +.addField('duration', 'SFFloat', '-1') +.addNode(); +xseen.nodes._defineNode('animate', 'XSeen', 'x_Animate') +.addField('field', 'SFString', '') +.addField('to', 'MFFloat', '') // Needs to be 'field' datatype. That is not known until node-parse. For now insist on numeric array +.addField('delay', 'SFTime', 0) +.addField('duration', 'SFTime', 0) +.addField('repeat', 'SFInt', 0) +.addField({name:'interpolator', datatype:'EnumerateString', defaultValue:'position', enumerated:['position', 'rotation', 'color'], animatable:false}) +.addField({name:'Easing', datatype:'EnumerateString', defaultValue:'', enumerated:['', 'in', 'out', 'inout'], animatable:false}) +.addField({name:'EasingType', datatype:'EnumerateString', defaultValue:'linear', enumerated:['linear', 'quadratic', 'sinusoidal', 'exponential', 'elastic', 'bounce'], animatable:false}) +.addField('start', 'SFBool', true) // incoming event, need to set timer trigger +.addField('stop', 'SFBool', true) // incoming event, need to set timer trigger +.addField('resetstart', 'SFBool', true) // incoming event, need to set timer trigger +.addField('pause', 'SFBool', true) // incoming event, need to set timer trigger +.addNode(); +xseen.nodes._defineNode('route', 'XSeen', 'x_Route') +.addField('source', 'SFString', '') +.addField('event', 'SFString', '') +.addField('destination', 'SFString', '') +.addField('field', 'SFString', '') +.addField('handler', 'SFString', '') +.addNode(); \ No newline at end of file diff --git a/src/zVersion.js b/src/zVersion.js index 7170b81..34c6a9f 100644 --- a/src/zVersion.js +++ b/src/zVersion.js @@ -14,9 +14,9 @@ xseen.generateVersion = function () { var Major, Minor, Patch, PreRelease, Release, Version, RDate, SplashText; Major = 0; Minor = 4; - Patch = 5; - PreRelease = ''; - Release = 23; + Patch = 6; + PreRelease = 'rc1'; + Release = 25; Version = ''; RDate = '2017-07-10'; SplashText = ["XSeen 3D Language parser.", "XSeen Documentation."]; @@ -36,6 +36,7 @@ xseen.generateVersion = function () { * V0.4.4+22 Fix -- Internal event handling in passing on events of the proper type * V0.4.5+23 Feature -- Navigation (trackball) * V0.4.5+24 Fix -- when there is no navigation + * V0.4.6+25 Update -- JSON Parser (contributed by John Carlson) * * In progress */ diff --git a/test/json-test.html b/test/json-test.html index 2d4f202..798a76a 100644 --- a/test/json-test.html +++ b/test/json-test.html @@ -5,8 +5,7 @@ JSON Test - - +