├── README.md ├── index.html └── js ├── Detector.js ├── 3d.js └── ColladaLoader.js /README.md: -------------------------------------------------------------------------------- 1 | A simple WebGL viewer for Collada files (.dae) based on Three.js. 2 | 3 | This project was started by Herbert Braun and featured in c't Magazin 13/2013. More information about the project: www.woerter.de/textverzeichnis/#filter/id=148 (in German). 4 | 5 | Comments and variable names are in German. I'll gladly translate them if there is demand. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3D-Modell-Test 5 | 6 | 7 | 8 | 9 | 29 | 30 | 31 |
32 |
33 | Steuerung der Animation per Tastatur: 34 |
35 |
Leertaste
36 |
Pause
37 |
Pfeil links/rechts
38 |
Objekt an der X-Achse drehen
39 |
Pfeil rauf/runter
40 |
Objekt an der Y-Achse drehen
41 |
Bild rauf/runter
42 |
Objekt an der Z-Achse drehen
43 |
Plus/Minus
44 |
Größer/kleiner
45 |
46 |
47 | 48 | -------------------------------------------------------------------------------- /js/Detector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | * @author mr.doob / http://mrdoob.com/ 4 | */ 5 | 6 | var Detector = { 7 | 8 | canvas: !! window.CanvasRenderingContext2D, 9 | webgl: ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(), 10 | workers: !! window.Worker, 11 | fileapi: window.File && window.FileReader && window.FileList && window.Blob, 12 | 13 | getWebGLErrorMessage: function () { 14 | 15 | var element = document.createElement( 'div' ); 16 | element.id = 'webgl-error-message'; 17 | element.style.fontFamily = 'monospace'; 18 | element.style.fontSize = '13px'; 19 | element.style.fontWeight = 'normal'; 20 | element.style.textAlign = 'center'; 21 | element.style.background = '#fff'; 22 | element.style.color = '#000'; 23 | element.style.padding = '1.5em'; 24 | element.style.width = '400px'; 25 | element.style.margin = '5em auto 0'; 26 | 27 | if ( ! this.webgl ) { 28 | 29 | element.innerHTML = window.WebGLRenderingContext ? [ 30 | 'Your graphics card does not seem to support WebGL.
', 31 | 'Find out how to get it here.' 32 | ].join( '\n' ) : [ 33 | 'Your browser does not seem to support WebGL.
', 34 | 'Find out how to get it here.' 35 | ].join( '\n' ); 36 | 37 | } 38 | 39 | return element; 40 | 41 | }, 42 | 43 | addGetWebGLMessage: function ( parameters ) { 44 | 45 | var parent, id, element; 46 | 47 | parameters = parameters || {}; 48 | 49 | parent = parameters.parent !== undefined ? parameters.parent : document.body; 50 | id = parameters.id !== undefined ? parameters.id : 'oldie'; 51 | 52 | element = Detector.getWebGLErrorMessage(); 53 | element.id = id; 54 | 55 | parent.appendChild( element ); 56 | 57 | } 58 | 59 | }; 60 | -------------------------------------------------------------------------------- /js/3d.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var file = './collada/getriebe.dae'; // Pfad zum Collada-Modell 4 | var tilted = true; // Modell um 90 Grad drehen? 5 | var modelScale = 10; // abhängig von der Größe des Modells 6 | var cameraPositionZ = 1500; // Abstand der Kamera 7 | var cameraInitialVector = 30; // je kleiner der Vektor, desto größer erscheint das Modell 8 | var colorLight = [0xffffaa, 0xffffaa]; // Farben der beiden Lichter 9 | var colorBackground = 0x000000; // Hintergrundfarbe 10 | var dimensions = [window.innerWidth, window.innerHeight]; // Größe der Darstellung 11 | var canvasid = '3dmodell'; // Name des Canvas-Containers 12 | var rotate = [0.0005, 0.01, 0.0005]; // Geschwindigkeit der Animation (X-, Y-, Z-Achse) 13 | var rotateManual = 0.1; // manuelle Drehung per Tastatur 14 | var cameraZoom = 50; // manuelle Änderung der Zoomstufe 15 | var play = true; // nach dem Laden sofort animieren? 16 | // ab hier nichts ändern 17 | 18 | var camera, scene, renderer, dae, skin, lastFrame; 19 | window.addEventListener('load', function() { 20 | if (!Detector.webgl) Detector.addGetWebGLMessage(); // Browser kann kein WebGL 21 | 22 | // Collada-Modell 23 | var loader = new THREE.ColladaLoader(); 24 | if (tilted) loader.options.upAxis = 'X'; // Drehung um 90 Grad 25 | loader.options.convertUpAxis = true; // an der Y-Achse ausrichten 26 | loader.load(file, function (collada) { 27 | dae = collada.scene; 28 | dae.scale.x = dae.scale.y = dae.scale.z = modelScale; 29 | scene = new THREE.Scene(); // initiiert die Szene 30 | scene.add(dae); 31 | 32 | // Kamera 33 | camera = new THREE.PerspectiveCamera(cameraInitialVector, dimensions[0]/dimensions[1], 1, 10000); 34 | camera.position.z = cameraPositionZ; 35 | 36 | // Lichter 37 | var directionalLight1 = new THREE.DirectionalLight(colorLight[0], 1.0); 38 | directionalLight1.position.set(1, 0, 0); 39 | var directionalLight2 = new THREE.DirectionalLight(colorLight[1], 2.0); 40 | directionalLight2.position.set(-1, 0, 0); 41 | scene.add(directionalLight1); 42 | scene.add(directionalLight2); 43 | 44 | // Renderer 45 | renderer = new THREE.WebGLRenderer({antialias: true}); 46 | renderer.setClearColor(colorBackground); 47 | renderer.setSize(dimensions[0], dimensions[1]); 48 | // verankere Darstellung im HTML 49 | document.getElementById(canvasid).appendChild(renderer.domElement); 50 | animate(); 51 | }); 52 | 53 | var animate = function() { 54 | requestAnimationFrame(animate); // Animationsschleife 55 | if (play) { // Drehen, wenn Play-Status == true 56 | dae.rotation.x += rotate[0]; 57 | dae.rotation.y += rotate[1]; 58 | dae.rotation.z += rotate[2]; 59 | } 60 | renderer.render(scene, camera); 61 | }; 62 | 63 | // Tastenkürzel abfragen 64 | window.addEventListener('keydown', function(e) { 65 | var key = e.keyCode; 66 | console.log("Key " + key); 67 | switch (key) { 68 | case 37: // left 69 | dae.rotation.y -= rotateManual; 70 | e.preventDefault(); 71 | break; 72 | case 39: // right 73 | dae.rotation.y += rotateManual; 74 | e.preventDefault(); 75 | break; 76 | case 38: // up 77 | dae.rotation.x -= rotateManual; 78 | e.preventDefault(); 79 | break; 80 | case 40: // down 81 | dae.rotation.x += rotateManual; 82 | e.preventDefault(); 83 | break; 84 | case 33: // pageup 85 | dae.rotation.z += rotateManual; 86 | e.preventDefault(); 87 | break; 88 | case 34: // pagedown 89 | dae.rotation.z -= rotateManual; 90 | e.preventDefault(); 91 | break; 92 | case 32: // space 93 | play = play? false : true; 94 | e.preventDefault(); 95 | break; 96 | case 187: // plus 97 | camera.position.z -= cameraZoom; 98 | e.preventDefault(); 99 | break; 100 | case 189: // minus 101 | camera.position.z += cameraZoom; 102 | e.preventDefault(); 103 | break; 104 | } 105 | renderer.render(scene, camera); 106 | }, false); 107 | }, false); 108 | -------------------------------------------------------------------------------- /js/ColladaLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com 3 | */ 4 | 5 | THREE.ColladaLoader = function () { 6 | 7 | var COLLADA = null; 8 | var scene = null; 9 | var daeScene; 10 | 11 | var readyCallbackFunc = null; 12 | 13 | var sources = {}; 14 | var images = {}; 15 | var animations = {}; 16 | var controllers = {}; 17 | var geometries = {}; 18 | var materials = {}; 19 | var effects = {}; 20 | var cameras = {}; 21 | var lights = {}; 22 | 23 | var animData; 24 | var visualScenes; 25 | var baseUrl; 26 | var morphs; 27 | var skins; 28 | 29 | var flip_uv = true; 30 | var preferredShading = THREE.SmoothShading; 31 | 32 | var options = { 33 | // Force Geometry to always be centered at the local origin of the 34 | // containing Mesh. 35 | centerGeometry: false, 36 | 37 | // Axis conversion is done for geometries, animations, and controllers. 38 | // If we ever pull cameras or lights out of the COLLADA file, they'll 39 | // need extra work. 40 | convertUpAxis: false, 41 | 42 | subdivideFaces: true, 43 | 44 | upAxis: 'Y', 45 | 46 | // For reflective or refractive materials we'll use this cubemap 47 | defaultEnvMap: null 48 | 49 | }; 50 | 51 | var colladaUnit = 1.0; 52 | var colladaUp = 'Y'; 53 | var upConversion = null; 54 | 55 | function load ( url, readyCallback, progressCallback ) { 56 | 57 | var length = 0; 58 | 59 | if ( document.implementation && document.implementation.createDocument ) { 60 | 61 | var request = new XMLHttpRequest(); 62 | 63 | request.onreadystatechange = function() { 64 | 65 | if( request.readyState == 4 ) { 66 | 67 | if( request.status == 0 || request.status == 200 ) { 68 | 69 | 70 | if ( request.responseXML ) { 71 | 72 | readyCallbackFunc = readyCallback; 73 | parse( request.responseXML, undefined, url ); 74 | 75 | } else if ( request.responseText ) { 76 | 77 | readyCallbackFunc = readyCallback; 78 | var xmlParser = new DOMParser(); 79 | var responseXML = xmlParser.parseFromString( request.responseText, "application/xml" ); 80 | parse( responseXML, undefined, url ); 81 | 82 | } else { 83 | 84 | console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); 85 | 86 | } 87 | 88 | } 89 | 90 | } else if ( request.readyState == 3 ) { 91 | 92 | if ( progressCallback ) { 93 | 94 | if ( length == 0 ) { 95 | 96 | length = request.getResponseHeader( "Content-Length" ); 97 | 98 | } 99 | 100 | progressCallback( { total: length, loaded: request.responseText.length } ); 101 | 102 | } 103 | 104 | } 105 | 106 | } 107 | 108 | request.open( "GET", url, true ); 109 | request.send( null ); 110 | 111 | } else { 112 | 113 | alert( "Don't know how to parse XML!" ); 114 | 115 | } 116 | 117 | }; 118 | 119 | function parse( doc, callBack, url ) { 120 | 121 | COLLADA = doc; 122 | callBack = callBack || readyCallbackFunc; 123 | 124 | if ( url !== undefined ) { 125 | 126 | var parts = url.split( '/' ); 127 | parts.pop(); 128 | baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; 129 | 130 | } 131 | 132 | parseAsset(); 133 | setUpConversion(); 134 | images = parseLib( "//dae:library_images/dae:image", _Image, "image" ); 135 | materials = parseLib( "//dae:library_materials/dae:material", Material, "material" ); 136 | effects = parseLib( "//dae:library_effects/dae:effect", Effect, "effect" ); 137 | geometries = parseLib( "//dae:library_geometries/dae:geometry", Geometry, "geometry" ); 138 | cameras = parseLib( ".//dae:library_cameras/dae:camera", Camera, "camera" ); 139 | lights = parseLib( ".//dae:library_lights/dae:light", Light, "light" ); 140 | controllers = parseLib( "//dae:library_controllers/dae:controller", Controller, "controller" ); 141 | animations = parseLib( "//dae:library_animations/dae:animation", Animation, "animation" ); 142 | visualScenes = parseLib( ".//dae:library_visual_scenes/dae:visual_scene", VisualScene, "visual_scene" ); 143 | 144 | morphs = []; 145 | skins = []; 146 | 147 | daeScene = parseScene(); 148 | scene = new THREE.Object3D(); 149 | 150 | for ( var i = 0; i < daeScene.nodes.length; i ++ ) { 151 | 152 | scene.add( createSceneGraph( daeScene.nodes[ i ] ) ); 153 | 154 | } 155 | 156 | // unit conversion 157 | scene.scale.multiplyScalar( colladaUnit ); 158 | 159 | createAnimations(); 160 | 161 | var result = { 162 | 163 | scene: scene, 164 | morphs: morphs, 165 | skins: skins, 166 | animations: animData, 167 | dae: { 168 | images: images, 169 | materials: materials, 170 | cameras: cameras, 171 | lights: lights, 172 | effects: effects, 173 | geometries: geometries, 174 | controllers: controllers, 175 | animations: animations, 176 | visualScenes: visualScenes, 177 | scene: daeScene 178 | } 179 | 180 | }; 181 | 182 | if ( callBack ) { 183 | 184 | callBack( result ); 185 | 186 | } 187 | 188 | return result; 189 | 190 | }; 191 | 192 | function setPreferredShading ( shading ) { 193 | 194 | preferredShading = shading; 195 | 196 | }; 197 | 198 | function parseAsset () { 199 | 200 | var elements = COLLADA.evaluate( '//dae:asset', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); 201 | 202 | var element = elements.iterateNext(); 203 | 204 | if ( element && element.childNodes ) { 205 | 206 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 207 | 208 | var child = element.childNodes[ i ]; 209 | 210 | switch ( child.nodeName ) { 211 | 212 | case 'unit': 213 | 214 | var meter = child.getAttribute( 'meter' ); 215 | 216 | if ( meter ) { 217 | 218 | colladaUnit = parseFloat( meter ); 219 | 220 | } 221 | 222 | break; 223 | 224 | case 'up_axis': 225 | 226 | colladaUp = child.textContent.charAt(0); 227 | break; 228 | 229 | } 230 | 231 | } 232 | 233 | } 234 | 235 | }; 236 | 237 | function parseLib ( q, classSpec, prefix ) { 238 | 239 | var elements = COLLADA.evaluate(q, COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null) ; 240 | 241 | var lib = {}; 242 | var element = elements.iterateNext(); 243 | var i = 0; 244 | 245 | while ( element ) { 246 | 247 | var daeElement = ( new classSpec() ).parse( element ); 248 | if ( !daeElement.id || daeElement.id.length == 0 ) daeElement.id = prefix + ( i ++ ); 249 | lib[ daeElement.id ] = daeElement; 250 | 251 | element = elements.iterateNext(); 252 | 253 | } 254 | 255 | return lib; 256 | 257 | }; 258 | 259 | function parseScene() { 260 | 261 | var sceneElement = COLLADA.evaluate( './/dae:scene/dae:instance_visual_scene', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ).iterateNext(); 262 | 263 | if ( sceneElement ) { 264 | 265 | var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); 266 | return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; 267 | 268 | } else { 269 | 270 | return null; 271 | 272 | } 273 | 274 | }; 275 | 276 | function createAnimations() { 277 | 278 | animData = []; 279 | 280 | // fill in the keys 281 | recurseHierarchy( scene ); 282 | 283 | }; 284 | 285 | function recurseHierarchy( node ) { 286 | 287 | var n = daeScene.getChildById( node.name, true ), 288 | newData = null; 289 | 290 | if ( n && n.keys ) { 291 | 292 | newData = { 293 | fps: 60, 294 | hierarchy: [ { 295 | node: n, 296 | keys: n.keys, 297 | sids: n.sids 298 | } ], 299 | node: node, 300 | name: 'animation_' + node.name, 301 | length: 0 302 | }; 303 | 304 | animData.push(newData); 305 | 306 | for ( var i = 0, il = n.keys.length; i < il; i++ ) { 307 | 308 | newData.length = Math.max( newData.length, n.keys[i].time ); 309 | 310 | } 311 | 312 | } else { 313 | 314 | newData = { 315 | hierarchy: [ { 316 | keys: [], 317 | sids: [] 318 | } ] 319 | } 320 | 321 | } 322 | 323 | for ( var i = 0, il = node.children.length; i < il; i++ ) { 324 | 325 | var d = recurseHierarchy( node.children[i] ); 326 | 327 | for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { 328 | 329 | newData.hierarchy.push( { 330 | keys: [], 331 | sids: [] 332 | } ); 333 | 334 | } 335 | 336 | } 337 | 338 | return newData; 339 | 340 | }; 341 | 342 | function calcAnimationBounds () { 343 | 344 | var start = 1000000; 345 | var end = -start; 346 | var frames = 0; 347 | 348 | for ( var id in animations ) { 349 | 350 | var animation = animations[ id ]; 351 | 352 | for ( var i = 0; i < animation.sampler.length; i ++ ) { 353 | 354 | var sampler = animation.sampler[ i ]; 355 | sampler.create(); 356 | 357 | start = Math.min( start, sampler.startTime ); 358 | end = Math.max( end, sampler.endTime ); 359 | frames = Math.max( frames, sampler.input.length ); 360 | 361 | } 362 | 363 | } 364 | 365 | return { start:start, end:end, frames:frames }; 366 | 367 | }; 368 | 369 | function createMorph ( geometry, ctrl ) { 370 | 371 | var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; 372 | 373 | if ( !morphCtrl || !morphCtrl.morph ) { 374 | 375 | console.log("could not find morph controller!"); 376 | return; 377 | 378 | } 379 | 380 | var morph = morphCtrl.morph; 381 | 382 | for ( var i = 0; i < morph.targets.length; i ++ ) { 383 | 384 | var target_id = morph.targets[ i ]; 385 | var daeGeometry = geometries[ target_id ]; 386 | 387 | if ( !daeGeometry.mesh || 388 | !daeGeometry.mesh.primitives || 389 | !daeGeometry.mesh.primitives.length ) { 390 | continue; 391 | } 392 | 393 | var target = daeGeometry.mesh.primitives[ 0 ].geometry; 394 | 395 | if ( target.vertices.length === geometry.vertices.length ) { 396 | 397 | geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); 398 | 399 | } 400 | 401 | } 402 | 403 | geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); 404 | 405 | }; 406 | 407 | function createSkin ( geometry, ctrl, applyBindShape ) { 408 | 409 | var skinCtrl = controllers[ ctrl.url ]; 410 | 411 | if ( !skinCtrl || !skinCtrl.skin ) { 412 | 413 | console.log( "could not find skin controller!" ); 414 | return; 415 | 416 | } 417 | 418 | if ( !ctrl.skeleton || !ctrl.skeleton.length ) { 419 | 420 | console.log( "could not find the skeleton for the skin!" ); 421 | return; 422 | 423 | } 424 | 425 | var skin = skinCtrl.skin; 426 | var skeleton = daeScene.getChildById( ctrl.skeleton[ 0 ] ); 427 | var hierarchy = []; 428 | 429 | applyBindShape = applyBindShape !== undefined ? applyBindShape : true; 430 | 431 | var bones = []; 432 | geometry.skinWeights = []; 433 | geometry.skinIndices = []; 434 | 435 | //createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 ); 436 | //createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights ); 437 | 438 | /* 439 | geometry.animation = { 440 | name: 'take_001', 441 | fps: 30, 442 | length: 2, 443 | JIT: true, 444 | hierarchy: hierarchy 445 | }; 446 | */ 447 | 448 | if ( applyBindShape ) { 449 | 450 | for ( var i = 0; i < geometry.vertices.length; i ++ ) { 451 | 452 | geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); 453 | 454 | } 455 | 456 | } 457 | 458 | }; 459 | 460 | function setupSkeleton ( node, bones, frame, parent ) { 461 | 462 | node.world = node.world || new THREE.Matrix4(); 463 | node.world.copy( node.matrix ); 464 | 465 | if ( node.channels && node.channels.length ) { 466 | 467 | var channel = node.channels[ 0 ]; 468 | var m = channel.sampler.output[ frame ]; 469 | 470 | if ( m instanceof THREE.Matrix4 ) { 471 | 472 | node.world.copy( m ); 473 | 474 | } 475 | 476 | } 477 | 478 | if ( parent ) { 479 | 480 | node.world.multiplyMatrices( parent, node.world ); 481 | 482 | } 483 | 484 | bones.push( node ); 485 | 486 | for ( var i = 0; i < node.nodes.length; i ++ ) { 487 | 488 | setupSkeleton( node.nodes[ i ], bones, frame, node.world ); 489 | 490 | } 491 | 492 | }; 493 | 494 | function setupSkinningMatrices ( bones, skin ) { 495 | 496 | // FIXME: this is dumb... 497 | 498 | for ( var i = 0; i < bones.length; i ++ ) { 499 | 500 | var bone = bones[ i ]; 501 | var found = -1; 502 | 503 | if ( bone.type != 'JOINT' ) continue; 504 | 505 | for ( var j = 0; j < skin.joints.length; j ++ ) { 506 | 507 | if ( bone.sid == skin.joints[ j ] ) { 508 | 509 | found = j; 510 | break; 511 | 512 | } 513 | 514 | } 515 | 516 | if ( found >= 0 ) { 517 | 518 | var inv = skin.invBindMatrices[ found ]; 519 | 520 | bone.invBindMatrix = inv; 521 | bone.skinningMatrix = new THREE.Matrix4(); 522 | bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) 523 | 524 | bone.weights = []; 525 | 526 | for ( var j = 0; j < skin.weights.length; j ++ ) { 527 | 528 | for (var k = 0; k < skin.weights[ j ].length; k ++) { 529 | 530 | var w = skin.weights[ j ][ k ]; 531 | 532 | if ( w.joint == found ) { 533 | 534 | bone.weights.push( w ); 535 | 536 | } 537 | 538 | } 539 | 540 | } 541 | 542 | } else { 543 | 544 | throw 'ColladaLoader: Could not find joint \'' + bone.sid + '\'.'; 545 | 546 | } 547 | 548 | } 549 | 550 | }; 551 | 552 | function applySkin ( geometry, instanceCtrl, frame ) { 553 | 554 | var skinController = controllers[ instanceCtrl.url ]; 555 | 556 | frame = frame !== undefined ? frame : 40; 557 | 558 | if ( !skinController || !skinController.skin ) { 559 | 560 | console.log( 'ColladaLoader: Could not find skin controller.' ); 561 | return; 562 | 563 | } 564 | 565 | if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { 566 | 567 | console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); 568 | return; 569 | 570 | } 571 | 572 | var animationBounds = calcAnimationBounds(); 573 | var skeleton = daeScene.getChildById( instanceCtrl.skeleton[0], true ) || 574 | daeScene.getChildBySid( instanceCtrl.skeleton[0], true ); 575 | 576 | var i, j, w, vidx, weight; 577 | var v = new THREE.Vector3(), o, s; 578 | 579 | // move vertices to bind shape 580 | 581 | for ( i = 0; i < geometry.vertices.length; i ++ ) { 582 | 583 | geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); 584 | 585 | } 586 | 587 | // process animation, or simply pose the rig if no animation 588 | 589 | for ( frame = 0; frame < animationBounds.frames; frame ++ ) { 590 | 591 | var bones = []; 592 | var skinned = []; 593 | 594 | // zero skinned vertices 595 | 596 | for ( i = 0; i < geometry.vertices.length; i++ ) { 597 | 598 | skinned.push( new THREE.Vector3() ); 599 | 600 | } 601 | 602 | // process the frame and setup the rig with a fresh 603 | // transform, possibly from the bone's animation channel(s) 604 | 605 | setupSkeleton( skeleton, bones, frame ); 606 | setupSkinningMatrices( bones, skinController.skin ); 607 | 608 | // skin 'm 609 | 610 | for ( i = 0; i < bones.length; i ++ ) { 611 | 612 | if ( bones[ i ].type != 'JOINT' ) continue; 613 | 614 | for ( j = 0; j < bones[ i ].weights.length; j ++ ) { 615 | 616 | w = bones[ i ].weights[ j ]; 617 | vidx = w.index; 618 | weight = w.weight; 619 | 620 | o = geometry.vertices[vidx]; 621 | s = skinned[vidx]; 622 | 623 | v.x = o.x; 624 | v.y = o.y; 625 | v.z = o.z; 626 | 627 | v.applyMatrix4( bones[i].skinningMatrix ); 628 | 629 | s.x += (v.x * weight); 630 | s.y += (v.y * weight); 631 | s.z += (v.z * weight); 632 | 633 | } 634 | 635 | } 636 | 637 | geometry.morphTargets.push( { name: "target_" + frame, vertices: skinned } ); 638 | 639 | } 640 | 641 | }; 642 | 643 | function createSceneGraph ( node, parent ) { 644 | 645 | var obj = new THREE.Object3D(); 646 | var skinned = false; 647 | var skinController; 648 | var morphController; 649 | var i, j; 650 | 651 | // FIXME: controllers 652 | 653 | for ( i = 0; i < node.controllers.length; i ++ ) { 654 | 655 | var controller = controllers[ node.controllers[ i ].url ]; 656 | 657 | switch ( controller.type ) { 658 | 659 | case 'skin': 660 | 661 | if ( geometries[ controller.skin.source ] ) { 662 | 663 | var inst_geom = new InstanceGeometry(); 664 | 665 | inst_geom.url = controller.skin.source; 666 | inst_geom.instance_material = node.controllers[ i ].instance_material; 667 | 668 | node.geometries.push( inst_geom ); 669 | skinned = true; 670 | skinController = node.controllers[ i ]; 671 | 672 | } else if ( controllers[ controller.skin.source ] ) { 673 | 674 | // urgh: controller can be chained 675 | // handle the most basic case... 676 | 677 | var second = controllers[ controller.skin.source ]; 678 | morphController = second; 679 | // skinController = node.controllers[i]; 680 | 681 | if ( second.morph && geometries[ second.morph.source ] ) { 682 | 683 | var inst_geom = new InstanceGeometry(); 684 | 685 | inst_geom.url = second.morph.source; 686 | inst_geom.instance_material = node.controllers[ i ].instance_material; 687 | 688 | node.geometries.push( inst_geom ); 689 | 690 | } 691 | 692 | } 693 | 694 | break; 695 | 696 | case 'morph': 697 | 698 | if ( geometries[ controller.morph.source ] ) { 699 | 700 | var inst_geom = new InstanceGeometry(); 701 | 702 | inst_geom.url = controller.morph.source; 703 | inst_geom.instance_material = node.controllers[ i ].instance_material; 704 | 705 | node.geometries.push( inst_geom ); 706 | morphController = node.controllers[ i ]; 707 | 708 | } 709 | 710 | console.log( 'ColladaLoader: Morph-controller partially supported.' ); 711 | 712 | default: 713 | break; 714 | 715 | } 716 | 717 | } 718 | 719 | // FIXME: multi-material mesh? 720 | // geometries 721 | 722 | var double_sided_materials = {}; 723 | 724 | for ( i = 0; i < node.geometries.length; i ++ ) { 725 | 726 | var instance_geometry = node.geometries[i]; 727 | var instance_materials = instance_geometry.instance_material; 728 | var geometry = geometries[ instance_geometry.url ]; 729 | var used_materials = {}; 730 | var used_materials_array = []; 731 | var num_materials = 0; 732 | var first_material; 733 | 734 | if ( geometry ) { 735 | 736 | if ( !geometry.mesh || !geometry.mesh.primitives ) 737 | continue; 738 | 739 | if ( obj.name.length == 0 ) { 740 | 741 | obj.name = geometry.id; 742 | 743 | } 744 | 745 | // collect used fx for this geometry-instance 746 | 747 | if ( instance_materials ) { 748 | 749 | for ( j = 0; j < instance_materials.length; j ++ ) { 750 | 751 | var instance_material = instance_materials[ j ]; 752 | var mat = materials[ instance_material.target ]; 753 | var effect_id = mat.instance_effect.url; 754 | var shader = effects[ effect_id ].shader; 755 | var material3js = shader.material; 756 | 757 | if ( geometry.doubleSided ) { 758 | 759 | if ( !( material3js in double_sided_materials ) ) { 760 | 761 | var _copied_material = material3js.clone(); 762 | _copied_material.side = THREE.DoubleSide; 763 | double_sided_materials[ material3js ] = _copied_material; 764 | 765 | } 766 | 767 | material3js = double_sided_materials[ material3js ]; 768 | 769 | } 770 | 771 | material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; 772 | used_materials[ instance_material.symbol ] = num_materials; 773 | used_materials_array.push( material3js ); 774 | first_material = material3js; 775 | first_material.name = mat.name == null || mat.name === '' ? mat.id : mat.name; 776 | num_materials ++; 777 | 778 | } 779 | 780 | } 781 | 782 | var mesh; 783 | var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, shading: THREE.FlatShading, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); 784 | var geom = geometry.mesh.geometry3js; 785 | 786 | if ( num_materials > 1 ) { 787 | 788 | material = new THREE.MeshFaceMaterial( used_materials_array ); 789 | 790 | for ( j = 0; j < geom.faces.length; j ++ ) { 791 | 792 | var face = geom.faces[ j ]; 793 | face.materialIndex = used_materials[ face.daeMaterial ] 794 | 795 | } 796 | 797 | } 798 | 799 | if ( skinController !== undefined ) { 800 | 801 | applySkin( geom, skinController ); 802 | 803 | material.morphTargets = true; 804 | 805 | mesh = new THREE.SkinnedMesh( geom, material, false ); 806 | mesh.skeleton = skinController.skeleton; 807 | mesh.skinController = controllers[ skinController.url ]; 808 | mesh.skinInstanceController = skinController; 809 | mesh.name = 'skin_' + skins.length; 810 | 811 | skins.push( mesh ); 812 | 813 | } else if ( morphController !== undefined ) { 814 | 815 | createMorph( geom, morphController ); 816 | 817 | material.morphTargets = true; 818 | 819 | mesh = new THREE.Mesh( geom, material ); 820 | mesh.name = 'morph_' + morphs.length; 821 | 822 | morphs.push( mesh ); 823 | 824 | } else { 825 | 826 | mesh = new THREE.Mesh( geom, material ); 827 | // mesh.geom.name = geometry.id; 828 | 829 | } 830 | 831 | node.geometries.length > 1 ? obj.add( mesh ) : obj = mesh; 832 | 833 | } 834 | 835 | } 836 | 837 | for ( i = 0; i < node.cameras.length; i ++ ) { 838 | 839 | var params = cameras[node.cameras[i].url]; 840 | obj = new THREE.PerspectiveCamera(params.fov, params.aspect_ratio, params.znear, params.zfar); 841 | 842 | } 843 | 844 | for ( i = 0; i < node.lights.length; i ++ ) { 845 | 846 | var params = lights[node.lights[i].url]; 847 | 848 | switch ( params.technique ) { 849 | 850 | case 'ambient': 851 | obj = new THREE.AmbientLight(params.color); 852 | break; 853 | 854 | case 'point': 855 | obj = new THREE.PointLight(params.color); 856 | break; 857 | 858 | case 'directional': 859 | obj = new THREE.DirectionalLight(params.color); 860 | break; 861 | 862 | } 863 | 864 | } 865 | 866 | obj.name = node.name || node.id || ""; 867 | obj.matrix = node.matrix; 868 | 869 | var props = node.matrix.decompose(); 870 | obj.position = props[ 0 ]; 871 | obj.quaternion = props[ 1 ]; 872 | obj.useQuaternion = true; 873 | obj.scale = props[ 2 ]; 874 | 875 | if ( options.centerGeometry && obj.geometry ) { 876 | 877 | var delta = THREE.GeometryUtils.center( obj.geometry ); 878 | delta.multiply( obj.scale ); 879 | delta.applyQuaternion( obj.quaternion ); 880 | 881 | obj.position.sub( delta ); 882 | 883 | } 884 | 885 | for ( i = 0; i < node.nodes.length; i ++ ) { 886 | 887 | obj.add( createSceneGraph( node.nodes[i], node ) ); 888 | 889 | } 890 | 891 | return obj; 892 | 893 | }; 894 | 895 | function getJointId( skin, id ) { 896 | 897 | for ( var i = 0; i < skin.joints.length; i ++ ) { 898 | 899 | if ( skin.joints[ i ] == id ) { 900 | 901 | return i; 902 | 903 | } 904 | 905 | } 906 | 907 | }; 908 | 909 | function getLibraryNode( id ) { 910 | 911 | return COLLADA.evaluate( './/dae:library_nodes//dae:node[@id=\'' + id + '\']', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ).iterateNext(); 912 | 913 | }; 914 | 915 | function getChannelsForNode (node ) { 916 | 917 | var channels = []; 918 | var startTime = 1000000; 919 | var endTime = -1000000; 920 | 921 | for ( var id in animations ) { 922 | 923 | var animation = animations[id]; 924 | 925 | for ( var i = 0; i < animation.channel.length; i ++ ) { 926 | 927 | var channel = animation.channel[i]; 928 | var sampler = animation.sampler[i]; 929 | var id = channel.target.split('/')[0]; 930 | 931 | if ( id == node.id ) { 932 | 933 | sampler.create(); 934 | channel.sampler = sampler; 935 | startTime = Math.min(startTime, sampler.startTime); 936 | endTime = Math.max(endTime, sampler.endTime); 937 | channels.push(channel); 938 | 939 | } 940 | 941 | } 942 | 943 | } 944 | 945 | if ( channels.length ) { 946 | 947 | node.startTime = startTime; 948 | node.endTime = endTime; 949 | 950 | } 951 | 952 | return channels; 953 | 954 | }; 955 | 956 | function calcFrameDuration( node ) { 957 | 958 | var minT = 10000000; 959 | 960 | for ( var i = 0; i < node.channels.length; i ++ ) { 961 | 962 | var sampler = node.channels[i].sampler; 963 | 964 | for ( var j = 0; j < sampler.input.length - 1; j ++ ) { 965 | 966 | var t0 = sampler.input[ j ]; 967 | var t1 = sampler.input[ j + 1 ]; 968 | minT = Math.min( minT, t1 - t0 ); 969 | 970 | } 971 | } 972 | 973 | return minT; 974 | 975 | }; 976 | 977 | function calcMatrixAt( node, t ) { 978 | 979 | var animated = {}; 980 | 981 | var i, j; 982 | 983 | for ( i = 0; i < node.channels.length; i ++ ) { 984 | 985 | var channel = node.channels[ i ]; 986 | animated[ channel.sid ] = channel; 987 | 988 | } 989 | 990 | var matrix = new THREE.Matrix4(); 991 | 992 | for ( i = 0; i < node.transforms.length; i ++ ) { 993 | 994 | var transform = node.transforms[ i ]; 995 | var channel = animated[ transform.sid ]; 996 | 997 | if ( channel !== undefined ) { 998 | 999 | var sampler = channel.sampler; 1000 | var value; 1001 | 1002 | for ( j = 0; j < sampler.input.length - 1; j ++ ) { 1003 | 1004 | if ( sampler.input[ j + 1 ] > t ) { 1005 | 1006 | value = sampler.output[ j ]; 1007 | //console.log(value.flatten) 1008 | break; 1009 | 1010 | } 1011 | 1012 | } 1013 | 1014 | if ( value !== undefined ) { 1015 | 1016 | if ( value instanceof THREE.Matrix4 ) { 1017 | 1018 | matrix.multiplyMatrices( matrix, value ); 1019 | 1020 | } else { 1021 | 1022 | // FIXME: handle other types 1023 | 1024 | matrix.multiplyMatrices( matrix, transform.matrix ); 1025 | 1026 | } 1027 | 1028 | } else { 1029 | 1030 | matrix.multiplyMatrices( matrix, transform.matrix ); 1031 | 1032 | } 1033 | 1034 | } else { 1035 | 1036 | matrix.multiplyMatrices( matrix, transform.matrix ); 1037 | 1038 | } 1039 | 1040 | } 1041 | 1042 | return matrix; 1043 | 1044 | }; 1045 | 1046 | function bakeAnimations ( node ) { 1047 | 1048 | if ( node.channels && node.channels.length ) { 1049 | 1050 | var keys = [], 1051 | sids = []; 1052 | 1053 | for ( var i = 0, il = node.channels.length; i < il; i++ ) { 1054 | 1055 | var channel = node.channels[i], 1056 | fullSid = channel.fullSid, 1057 | sampler = channel.sampler, 1058 | input = sampler.input, 1059 | transform = node.getTransformBySid( channel.sid ), 1060 | member; 1061 | 1062 | if ( channel.arrIndices ) { 1063 | 1064 | member = []; 1065 | 1066 | for ( var j = 0, jl = channel.arrIndices.length; j < jl; j++ ) { 1067 | 1068 | member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); 1069 | 1070 | } 1071 | 1072 | } else { 1073 | 1074 | member = getConvertedMember( channel.member ); 1075 | 1076 | } 1077 | 1078 | if ( transform ) { 1079 | 1080 | if ( sids.indexOf( fullSid ) === -1 ) { 1081 | 1082 | sids.push( fullSid ); 1083 | 1084 | } 1085 | 1086 | for ( var j = 0, jl = input.length; j < jl; j++ ) { 1087 | 1088 | var time = input[j], 1089 | data = sampler.getData( transform.type, j ), 1090 | key = findKey( keys, time ); 1091 | 1092 | if ( !key ) { 1093 | 1094 | key = new Key( time ); 1095 | var timeNdx = findTimeNdx( keys, time ); 1096 | keys.splice( timeNdx == -1 ? keys.length : timeNdx, 0, key ); 1097 | 1098 | } 1099 | 1100 | key.addTarget( fullSid, transform, member, data ); 1101 | 1102 | } 1103 | 1104 | } else { 1105 | 1106 | console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); 1107 | 1108 | } 1109 | 1110 | } 1111 | 1112 | // post process 1113 | for ( var i = 0; i < sids.length; i++ ) { 1114 | 1115 | var sid = sids[ i ]; 1116 | 1117 | for ( var j = 0; j < keys.length; j++ ) { 1118 | 1119 | var key = keys[ j ]; 1120 | 1121 | if ( !key.hasTarget( sid ) ) { 1122 | 1123 | interpolateKeys( keys, key, j, sid ); 1124 | 1125 | } 1126 | 1127 | } 1128 | 1129 | } 1130 | 1131 | node.keys = keys; 1132 | node.sids = sids; 1133 | 1134 | } 1135 | 1136 | }; 1137 | 1138 | function findKey ( keys, time) { 1139 | 1140 | var retVal = null; 1141 | 1142 | for ( var i = 0, il = keys.length; i < il && retVal == null; i++ ) { 1143 | 1144 | var key = keys[i]; 1145 | 1146 | if ( key.time === time ) { 1147 | 1148 | retVal = key; 1149 | 1150 | } else if ( key.time > time ) { 1151 | 1152 | break; 1153 | 1154 | } 1155 | 1156 | } 1157 | 1158 | return retVal; 1159 | 1160 | }; 1161 | 1162 | function findTimeNdx ( keys, time) { 1163 | 1164 | var ndx = -1; 1165 | 1166 | for ( var i = 0, il = keys.length; i < il && ndx == -1; i++ ) { 1167 | 1168 | var key = keys[i]; 1169 | 1170 | if ( key.time >= time ) { 1171 | 1172 | ndx = i; 1173 | 1174 | } 1175 | 1176 | } 1177 | 1178 | return ndx; 1179 | 1180 | }; 1181 | 1182 | function interpolateKeys ( keys, key, ndx, fullSid ) { 1183 | 1184 | var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx-1 : 0 ), 1185 | nextKey = getNextKeyWith( keys, fullSid, ndx+1 ); 1186 | 1187 | if ( prevKey && nextKey ) { 1188 | 1189 | var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), 1190 | prevTarget = prevKey.getTarget( fullSid ), 1191 | nextData = nextKey.getTarget( fullSid ).data, 1192 | prevData = prevTarget.data, 1193 | data; 1194 | 1195 | if ( prevTarget.type === 'matrix' ) { 1196 | 1197 | data = prevData; 1198 | 1199 | } else if ( prevData.length ) { 1200 | 1201 | data = []; 1202 | 1203 | for ( var i = 0; i < prevData.length; ++i ) { 1204 | 1205 | data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; 1206 | 1207 | } 1208 | 1209 | } else { 1210 | 1211 | data = prevData + ( nextData - prevData ) * scale; 1212 | 1213 | } 1214 | 1215 | key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); 1216 | 1217 | } 1218 | 1219 | }; 1220 | 1221 | // Get next key with given sid 1222 | 1223 | function getNextKeyWith( keys, fullSid, ndx ) { 1224 | 1225 | for ( ; ndx < keys.length; ndx++ ) { 1226 | 1227 | var key = keys[ ndx ]; 1228 | 1229 | if ( key.hasTarget( fullSid ) ) { 1230 | 1231 | return key; 1232 | 1233 | } 1234 | 1235 | } 1236 | 1237 | return null; 1238 | 1239 | }; 1240 | 1241 | // Get previous key with given sid 1242 | 1243 | function getPrevKeyWith( keys, fullSid, ndx ) { 1244 | 1245 | ndx = ndx >= 0 ? ndx : ndx + keys.length; 1246 | 1247 | for ( ; ndx >= 0; ndx-- ) { 1248 | 1249 | var key = keys[ ndx ]; 1250 | 1251 | if ( key.hasTarget( fullSid ) ) { 1252 | 1253 | return key; 1254 | 1255 | } 1256 | 1257 | } 1258 | 1259 | return null; 1260 | 1261 | }; 1262 | 1263 | function _Image() { 1264 | 1265 | this.id = ""; 1266 | this.init_from = ""; 1267 | 1268 | }; 1269 | 1270 | _Image.prototype.parse = function(element) { 1271 | 1272 | this.id = element.getAttribute('id'); 1273 | 1274 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1275 | 1276 | var child = element.childNodes[ i ]; 1277 | 1278 | if ( child.nodeName == 'init_from' ) { 1279 | 1280 | this.init_from = child.textContent; 1281 | 1282 | } 1283 | 1284 | } 1285 | 1286 | return this; 1287 | 1288 | }; 1289 | 1290 | function Controller() { 1291 | 1292 | this.id = ""; 1293 | this.name = ""; 1294 | this.type = ""; 1295 | this.skin = null; 1296 | this.morph = null; 1297 | 1298 | }; 1299 | 1300 | Controller.prototype.parse = function( element ) { 1301 | 1302 | this.id = element.getAttribute('id'); 1303 | this.name = element.getAttribute('name'); 1304 | this.type = "none"; 1305 | 1306 | for ( var i = 0; i < element.childNodes.length; i++ ) { 1307 | 1308 | var child = element.childNodes[ i ]; 1309 | 1310 | switch ( child.nodeName ) { 1311 | 1312 | case 'skin': 1313 | 1314 | this.skin = (new Skin()).parse(child); 1315 | this.type = child.nodeName; 1316 | break; 1317 | 1318 | case 'morph': 1319 | 1320 | this.morph = (new Morph()).parse(child); 1321 | this.type = child.nodeName; 1322 | break; 1323 | 1324 | default: 1325 | break; 1326 | 1327 | } 1328 | } 1329 | 1330 | return this; 1331 | 1332 | }; 1333 | 1334 | function Morph() { 1335 | 1336 | this.method = null; 1337 | this.source = null; 1338 | this.targets = null; 1339 | this.weights = null; 1340 | 1341 | }; 1342 | 1343 | Morph.prototype.parse = function( element ) { 1344 | 1345 | var sources = {}; 1346 | var inputs = []; 1347 | var i; 1348 | 1349 | this.method = element.getAttribute( 'method' ); 1350 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); 1351 | 1352 | for ( i = 0; i < element.childNodes.length; i ++ ) { 1353 | 1354 | var child = element.childNodes[ i ]; 1355 | if ( child.nodeType != 1 ) continue; 1356 | 1357 | switch ( child.nodeName ) { 1358 | 1359 | case 'source': 1360 | 1361 | var source = ( new Source() ).parse( child ); 1362 | sources[ source.id ] = source; 1363 | break; 1364 | 1365 | case 'targets': 1366 | 1367 | inputs = this.parseInputs( child ); 1368 | break; 1369 | 1370 | default: 1371 | 1372 | console.log( child.nodeName ); 1373 | break; 1374 | 1375 | } 1376 | 1377 | } 1378 | 1379 | for ( i = 0; i < inputs.length; i ++ ) { 1380 | 1381 | var input = inputs[ i ]; 1382 | var source = sources[ input.source ]; 1383 | 1384 | switch ( input.semantic ) { 1385 | 1386 | case 'MORPH_TARGET': 1387 | 1388 | this.targets = source.read(); 1389 | break; 1390 | 1391 | case 'MORPH_WEIGHT': 1392 | 1393 | this.weights = source.read(); 1394 | break; 1395 | 1396 | default: 1397 | break; 1398 | 1399 | } 1400 | } 1401 | 1402 | return this; 1403 | 1404 | }; 1405 | 1406 | Morph.prototype.parseInputs = function(element) { 1407 | 1408 | var inputs = []; 1409 | 1410 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1411 | 1412 | var child = element.childNodes[i]; 1413 | if ( child.nodeType != 1) continue; 1414 | 1415 | switch ( child.nodeName ) { 1416 | 1417 | case 'input': 1418 | 1419 | inputs.push( (new Input()).parse(child) ); 1420 | break; 1421 | 1422 | default: 1423 | break; 1424 | } 1425 | } 1426 | 1427 | return inputs; 1428 | 1429 | }; 1430 | 1431 | function Skin() { 1432 | 1433 | this.source = ""; 1434 | this.bindShapeMatrix = null; 1435 | this.invBindMatrices = []; 1436 | this.joints = []; 1437 | this.weights = []; 1438 | 1439 | }; 1440 | 1441 | Skin.prototype.parse = function( element ) { 1442 | 1443 | var sources = {}; 1444 | var joints, weights; 1445 | 1446 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); 1447 | this.invBindMatrices = []; 1448 | this.joints = []; 1449 | this.weights = []; 1450 | 1451 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1452 | 1453 | var child = element.childNodes[i]; 1454 | if ( child.nodeType != 1 ) continue; 1455 | 1456 | switch ( child.nodeName ) { 1457 | 1458 | case 'bind_shape_matrix': 1459 | 1460 | var f = _floats(child.textContent); 1461 | this.bindShapeMatrix = getConvertedMat4( f ); 1462 | break; 1463 | 1464 | case 'source': 1465 | 1466 | var src = new Source().parse(child); 1467 | sources[ src.id ] = src; 1468 | break; 1469 | 1470 | case 'joints': 1471 | 1472 | joints = child; 1473 | break; 1474 | 1475 | case 'vertex_weights': 1476 | 1477 | weights = child; 1478 | break; 1479 | 1480 | default: 1481 | 1482 | console.log( child.nodeName ); 1483 | break; 1484 | 1485 | } 1486 | } 1487 | 1488 | this.parseJoints( joints, sources ); 1489 | this.parseWeights( weights, sources ); 1490 | 1491 | return this; 1492 | 1493 | }; 1494 | 1495 | Skin.prototype.parseJoints = function ( element, sources ) { 1496 | 1497 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1498 | 1499 | var child = element.childNodes[ i ]; 1500 | if ( child.nodeType != 1 ) continue; 1501 | 1502 | switch ( child.nodeName ) { 1503 | 1504 | case 'input': 1505 | 1506 | var input = ( new Input() ).parse( child ); 1507 | var source = sources[ input.source ]; 1508 | 1509 | if ( input.semantic == 'JOINT' ) { 1510 | 1511 | this.joints = source.read(); 1512 | 1513 | } else if ( input.semantic == 'INV_BIND_MATRIX' ) { 1514 | 1515 | this.invBindMatrices = source.read(); 1516 | 1517 | } 1518 | 1519 | break; 1520 | 1521 | default: 1522 | break; 1523 | } 1524 | 1525 | } 1526 | 1527 | }; 1528 | 1529 | Skin.prototype.parseWeights = function ( element, sources ) { 1530 | 1531 | var v, vcount, inputs = []; 1532 | 1533 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1534 | 1535 | var child = element.childNodes[ i ]; 1536 | if ( child.nodeType != 1 ) continue; 1537 | 1538 | switch ( child.nodeName ) { 1539 | 1540 | case 'input': 1541 | 1542 | inputs.push( ( new Input() ).parse( child ) ); 1543 | break; 1544 | 1545 | case 'v': 1546 | 1547 | v = _ints( child.textContent ); 1548 | break; 1549 | 1550 | case 'vcount': 1551 | 1552 | vcount = _ints( child.textContent ); 1553 | break; 1554 | 1555 | default: 1556 | break; 1557 | 1558 | } 1559 | 1560 | } 1561 | 1562 | var index = 0; 1563 | 1564 | for ( var i = 0; i < vcount.length; i ++ ) { 1565 | 1566 | var numBones = vcount[i]; 1567 | var vertex_weights = []; 1568 | 1569 | for ( var j = 0; j < numBones; j++ ) { 1570 | 1571 | var influence = {}; 1572 | 1573 | for ( var k = 0; k < inputs.length; k ++ ) { 1574 | 1575 | var input = inputs[ k ]; 1576 | var value = v[ index + input.offset ]; 1577 | 1578 | switch ( input.semantic ) { 1579 | 1580 | case 'JOINT': 1581 | 1582 | influence.joint = value;//this.joints[value]; 1583 | break; 1584 | 1585 | case 'WEIGHT': 1586 | 1587 | influence.weight = sources[ input.source ].data[ value ]; 1588 | break; 1589 | 1590 | default: 1591 | break; 1592 | 1593 | } 1594 | 1595 | } 1596 | 1597 | vertex_weights.push( influence ); 1598 | index += inputs.length; 1599 | } 1600 | 1601 | for ( var j = 0; j < vertex_weights.length; j ++ ) { 1602 | 1603 | vertex_weights[ j ].index = i; 1604 | 1605 | } 1606 | 1607 | this.weights.push( vertex_weights ); 1608 | 1609 | } 1610 | 1611 | }; 1612 | 1613 | function VisualScene () { 1614 | 1615 | this.id = ""; 1616 | this.name = ""; 1617 | this.nodes = []; 1618 | this.scene = new THREE.Object3D(); 1619 | 1620 | }; 1621 | 1622 | VisualScene.prototype.getChildById = function( id, recursive ) { 1623 | 1624 | for ( var i = 0; i < this.nodes.length; i ++ ) { 1625 | 1626 | var node = this.nodes[ i ].getChildById( id, recursive ); 1627 | 1628 | if ( node ) { 1629 | 1630 | return node; 1631 | 1632 | } 1633 | 1634 | } 1635 | 1636 | return null; 1637 | 1638 | }; 1639 | 1640 | VisualScene.prototype.getChildBySid = function( sid, recursive ) { 1641 | 1642 | for ( var i = 0; i < this.nodes.length; i ++ ) { 1643 | 1644 | var node = this.nodes[ i ].getChildBySid( sid, recursive ); 1645 | 1646 | if ( node ) { 1647 | 1648 | return node; 1649 | 1650 | } 1651 | 1652 | } 1653 | 1654 | return null; 1655 | 1656 | }; 1657 | 1658 | VisualScene.prototype.parse = function( element ) { 1659 | 1660 | this.id = element.getAttribute( 'id' ); 1661 | this.name = element.getAttribute( 'name' ); 1662 | this.nodes = []; 1663 | 1664 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1665 | 1666 | var child = element.childNodes[ i ]; 1667 | if ( child.nodeType != 1 ) continue; 1668 | 1669 | switch ( child.nodeName ) { 1670 | 1671 | case 'node': 1672 | 1673 | this.nodes.push( ( new Node() ).parse( child ) ); 1674 | break; 1675 | 1676 | default: 1677 | break; 1678 | 1679 | } 1680 | 1681 | } 1682 | 1683 | return this; 1684 | 1685 | }; 1686 | 1687 | function Node() { 1688 | 1689 | this.id = ""; 1690 | this.name = ""; 1691 | this.sid = ""; 1692 | this.nodes = []; 1693 | this.controllers = []; 1694 | this.transforms = []; 1695 | this.geometries = []; 1696 | this.channels = []; 1697 | this.matrix = new THREE.Matrix4(); 1698 | 1699 | }; 1700 | 1701 | Node.prototype.getChannelForTransform = function( transformSid ) { 1702 | 1703 | for ( var i = 0; i < this.channels.length; i ++ ) { 1704 | 1705 | var channel = this.channels[i]; 1706 | var parts = channel.target.split('/'); 1707 | var id = parts.shift(); 1708 | var sid = parts.shift(); 1709 | var dotSyntax = (sid.indexOf(".") >= 0); 1710 | var arrSyntax = (sid.indexOf("(") >= 0); 1711 | var arrIndices; 1712 | var member; 1713 | 1714 | if ( dotSyntax ) { 1715 | 1716 | parts = sid.split("."); 1717 | sid = parts.shift(); 1718 | member = parts.shift(); 1719 | 1720 | } else if ( arrSyntax ) { 1721 | 1722 | arrIndices = sid.split("("); 1723 | sid = arrIndices.shift(); 1724 | 1725 | for ( var j = 0; j < arrIndices.length; j ++ ) { 1726 | 1727 | arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); 1728 | 1729 | } 1730 | 1731 | } 1732 | 1733 | if ( sid == transformSid ) { 1734 | 1735 | channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; 1736 | return channel; 1737 | 1738 | } 1739 | 1740 | } 1741 | 1742 | return null; 1743 | 1744 | }; 1745 | 1746 | Node.prototype.getChildById = function ( id, recursive ) { 1747 | 1748 | if ( this.id == id ) { 1749 | 1750 | return this; 1751 | 1752 | } 1753 | 1754 | if ( recursive ) { 1755 | 1756 | for ( var i = 0; i < this.nodes.length; i ++ ) { 1757 | 1758 | var n = this.nodes[ i ].getChildById( id, recursive ); 1759 | 1760 | if ( n ) { 1761 | 1762 | return n; 1763 | 1764 | } 1765 | 1766 | } 1767 | 1768 | } 1769 | 1770 | return null; 1771 | 1772 | }; 1773 | 1774 | Node.prototype.getChildBySid = function ( sid, recursive ) { 1775 | 1776 | if ( this.sid == sid ) { 1777 | 1778 | return this; 1779 | 1780 | } 1781 | 1782 | if ( recursive ) { 1783 | 1784 | for ( var i = 0; i < this.nodes.length; i ++ ) { 1785 | 1786 | var n = this.nodes[ i ].getChildBySid( sid, recursive ); 1787 | 1788 | if ( n ) { 1789 | 1790 | return n; 1791 | 1792 | } 1793 | 1794 | } 1795 | } 1796 | 1797 | return null; 1798 | 1799 | }; 1800 | 1801 | Node.prototype.getTransformBySid = function ( sid ) { 1802 | 1803 | for ( var i = 0; i < this.transforms.length; i ++ ) { 1804 | 1805 | if ( this.transforms[ i ].sid == sid ) return this.transforms[ i ]; 1806 | 1807 | } 1808 | 1809 | return null; 1810 | 1811 | }; 1812 | 1813 | Node.prototype.parse = function( element ) { 1814 | 1815 | var url; 1816 | 1817 | this.id = element.getAttribute('id'); 1818 | this.sid = element.getAttribute('sid'); 1819 | this.name = element.getAttribute('name'); 1820 | this.type = element.getAttribute('type'); 1821 | 1822 | this.type = this.type == 'JOINT' ? this.type : 'NODE'; 1823 | 1824 | this.nodes = []; 1825 | this.transforms = []; 1826 | this.geometries = []; 1827 | this.cameras = []; 1828 | this.lights = []; 1829 | this.controllers = []; 1830 | this.matrix = new THREE.Matrix4(); 1831 | 1832 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1833 | 1834 | var child = element.childNodes[ i ]; 1835 | if ( child.nodeType != 1 ) continue; 1836 | 1837 | switch ( child.nodeName ) { 1838 | 1839 | case 'node': 1840 | 1841 | this.nodes.push( ( new Node() ).parse( child ) ); 1842 | break; 1843 | 1844 | case 'instance_camera': 1845 | 1846 | this.cameras.push( ( new InstanceCamera() ).parse( child ) ); 1847 | break; 1848 | 1849 | case 'instance_light': 1850 | 1851 | this.lights.push( ( new InstanceLight() ).parse( child ) ); 1852 | break; 1853 | 1854 | case 'instance_controller': 1855 | 1856 | this.controllers.push( ( new InstanceController() ).parse( child ) ); 1857 | break; 1858 | 1859 | case 'instance_geometry': 1860 | 1861 | this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); 1862 | break; 1863 | 1864 | case 'instance_node': 1865 | 1866 | url = child.getAttribute( 'url' ).replace( /^#/, '' ); 1867 | var iNode = getLibraryNode( url ); 1868 | 1869 | if ( iNode ) { 1870 | 1871 | this.nodes.push( ( new Node() ).parse( iNode )) ; 1872 | 1873 | } 1874 | 1875 | break; 1876 | 1877 | case 'rotate': 1878 | case 'translate': 1879 | case 'scale': 1880 | case 'matrix': 1881 | case 'lookat': 1882 | case 'skew': 1883 | 1884 | this.transforms.push( ( new Transform() ).parse( child ) ); 1885 | break; 1886 | 1887 | case 'extra': 1888 | break; 1889 | 1890 | default: 1891 | 1892 | console.log( child.nodeName ); 1893 | break; 1894 | 1895 | } 1896 | 1897 | } 1898 | 1899 | this.channels = getChannelsForNode( this ); 1900 | bakeAnimations( this ); 1901 | 1902 | this.updateMatrix(); 1903 | 1904 | return this; 1905 | 1906 | }; 1907 | 1908 | Node.prototype.updateMatrix = function () { 1909 | 1910 | this.matrix.identity(); 1911 | 1912 | for ( var i = 0; i < this.transforms.length; i ++ ) { 1913 | 1914 | this.transforms[ i ].apply( this.matrix ); 1915 | 1916 | } 1917 | 1918 | }; 1919 | 1920 | function Transform () { 1921 | 1922 | this.sid = ""; 1923 | this.type = ""; 1924 | this.data = []; 1925 | this.obj = null; 1926 | 1927 | }; 1928 | 1929 | Transform.prototype.parse = function ( element ) { 1930 | 1931 | this.sid = element.getAttribute( 'sid' ); 1932 | this.type = element.nodeName; 1933 | this.data = _floats( element.textContent ); 1934 | this.convert(); 1935 | 1936 | return this; 1937 | 1938 | }; 1939 | 1940 | Transform.prototype.convert = function () { 1941 | 1942 | switch ( this.type ) { 1943 | 1944 | case 'matrix': 1945 | 1946 | this.obj = getConvertedMat4( this.data ); 1947 | break; 1948 | 1949 | case 'rotate': 1950 | 1951 | this.angle = THREE.Math.degToRad( this.data[3] ); 1952 | 1953 | case 'translate': 1954 | 1955 | fixCoords( this.data, -1 ); 1956 | this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); 1957 | break; 1958 | 1959 | case 'scale': 1960 | 1961 | fixCoords( this.data, 1 ); 1962 | this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); 1963 | break; 1964 | 1965 | default: 1966 | console.log( 'Can not convert Transform of type ' + this.type ); 1967 | break; 1968 | 1969 | } 1970 | 1971 | }; 1972 | 1973 | Transform.prototype.apply = function () { 1974 | 1975 | var m1 = new THREE.Matrix4(); 1976 | 1977 | return function ( matrix ) { 1978 | 1979 | switch ( this.type ) { 1980 | 1981 | case 'matrix': 1982 | 1983 | matrix.multiply( this.obj ); 1984 | 1985 | break; 1986 | 1987 | case 'translate': 1988 | 1989 | matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); 1990 | 1991 | break; 1992 | 1993 | case 'rotate': 1994 | 1995 | matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); 1996 | 1997 | break; 1998 | 1999 | case 'scale': 2000 | 2001 | matrix.scale( this.obj ); 2002 | 2003 | break; 2004 | 2005 | } 2006 | 2007 | }; 2008 | 2009 | }(); 2010 | 2011 | Transform.prototype.update = function ( data, member ) { 2012 | 2013 | var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; 2014 | 2015 | switch ( this.type ) { 2016 | 2017 | case 'matrix': 2018 | 2019 | if ( ! member ) { 2020 | 2021 | this.obj.copy( data ); 2022 | 2023 | } else if ( member.length === 1 ) { 2024 | 2025 | switch ( member[ 0 ] ) { 2026 | 2027 | case 0: 2028 | 2029 | this.obj.n11 = data[ 0 ]; 2030 | this.obj.n21 = data[ 1 ]; 2031 | this.obj.n31 = data[ 2 ]; 2032 | this.obj.n41 = data[ 3 ]; 2033 | 2034 | break; 2035 | 2036 | case 1: 2037 | 2038 | this.obj.n12 = data[ 0 ]; 2039 | this.obj.n22 = data[ 1 ]; 2040 | this.obj.n32 = data[ 2 ]; 2041 | this.obj.n42 = data[ 3 ]; 2042 | 2043 | break; 2044 | 2045 | case 2: 2046 | 2047 | this.obj.n13 = data[ 0 ]; 2048 | this.obj.n23 = data[ 1 ]; 2049 | this.obj.n33 = data[ 2 ]; 2050 | this.obj.n43 = data[ 3 ]; 2051 | 2052 | break; 2053 | 2054 | case 3: 2055 | 2056 | this.obj.n14 = data[ 0 ]; 2057 | this.obj.n24 = data[ 1 ]; 2058 | this.obj.n34 = data[ 2 ]; 2059 | this.obj.n44 = data[ 3 ]; 2060 | 2061 | break; 2062 | 2063 | } 2064 | 2065 | } else if ( member.length === 2 ) { 2066 | 2067 | var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); 2068 | this.obj[ propName ] = data; 2069 | 2070 | } else { 2071 | 2072 | console.log('Incorrect addressing of matrix in transform.'); 2073 | 2074 | } 2075 | 2076 | break; 2077 | 2078 | case 'translate': 2079 | case 'scale': 2080 | 2081 | if ( Object.prototype.toString.call( member ) === '[object Array]' ) { 2082 | 2083 | member = members[ member[ 0 ] ]; 2084 | 2085 | } 2086 | 2087 | switch ( member ) { 2088 | 2089 | case 'X': 2090 | 2091 | this.obj.x = data; 2092 | break; 2093 | 2094 | case 'Y': 2095 | 2096 | this.obj.y = data; 2097 | break; 2098 | 2099 | case 'Z': 2100 | 2101 | this.obj.z = data; 2102 | break; 2103 | 2104 | default: 2105 | 2106 | this.obj.x = data[ 0 ]; 2107 | this.obj.y = data[ 1 ]; 2108 | this.obj.z = data[ 2 ]; 2109 | break; 2110 | 2111 | } 2112 | 2113 | break; 2114 | 2115 | case 'rotate': 2116 | 2117 | if ( Object.prototype.toString.call( member ) === '[object Array]' ) { 2118 | 2119 | member = members[ member[ 0 ] ]; 2120 | 2121 | } 2122 | 2123 | switch ( member ) { 2124 | 2125 | case 'X': 2126 | 2127 | this.obj.x = data; 2128 | break; 2129 | 2130 | case 'Y': 2131 | 2132 | this.obj.y = data; 2133 | break; 2134 | 2135 | case 'Z': 2136 | 2137 | this.obj.z = data; 2138 | break; 2139 | 2140 | case 'ANGLE': 2141 | 2142 | this.angle = THREE.Math.degToRad( data ); 2143 | break; 2144 | 2145 | default: 2146 | 2147 | this.obj.x = data[ 0 ]; 2148 | this.obj.y = data[ 1 ]; 2149 | this.obj.z = data[ 2 ]; 2150 | this.angle = THREE.Math.degToRad( data[ 3 ] ); 2151 | break; 2152 | 2153 | } 2154 | break; 2155 | 2156 | } 2157 | 2158 | }; 2159 | 2160 | function InstanceController() { 2161 | 2162 | this.url = ""; 2163 | this.skeleton = []; 2164 | this.instance_material = []; 2165 | 2166 | }; 2167 | 2168 | InstanceController.prototype.parse = function ( element ) { 2169 | 2170 | this.url = element.getAttribute('url').replace(/^#/, ''); 2171 | this.skeleton = []; 2172 | this.instance_material = []; 2173 | 2174 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2175 | 2176 | var child = element.childNodes[ i ]; 2177 | if ( child.nodeType !== 1 ) continue; 2178 | 2179 | switch ( child.nodeName ) { 2180 | 2181 | case 'skeleton': 2182 | 2183 | this.skeleton.push( child.textContent.replace(/^#/, '') ); 2184 | break; 2185 | 2186 | case 'bind_material': 2187 | 2188 | var instances = COLLADA.evaluate( './/dae:instance_material', child, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); 2189 | 2190 | if ( instances ) { 2191 | 2192 | var instance = instances.iterateNext(); 2193 | 2194 | while ( instance ) { 2195 | 2196 | this.instance_material.push( (new InstanceMaterial()).parse(instance) ); 2197 | instance = instances.iterateNext(); 2198 | 2199 | } 2200 | 2201 | } 2202 | 2203 | break; 2204 | 2205 | case 'extra': 2206 | break; 2207 | 2208 | default: 2209 | break; 2210 | 2211 | } 2212 | } 2213 | 2214 | return this; 2215 | 2216 | }; 2217 | 2218 | function InstanceMaterial () { 2219 | 2220 | this.symbol = ""; 2221 | this.target = ""; 2222 | 2223 | }; 2224 | 2225 | InstanceMaterial.prototype.parse = function ( element ) { 2226 | 2227 | this.symbol = element.getAttribute('symbol'); 2228 | this.target = element.getAttribute('target').replace(/^#/, ''); 2229 | return this; 2230 | 2231 | }; 2232 | 2233 | function InstanceGeometry() { 2234 | 2235 | this.url = ""; 2236 | this.instance_material = []; 2237 | 2238 | }; 2239 | 2240 | InstanceGeometry.prototype.parse = function ( element ) { 2241 | 2242 | this.url = element.getAttribute('url').replace(/^#/, ''); 2243 | this.instance_material = []; 2244 | 2245 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2246 | 2247 | var child = element.childNodes[i]; 2248 | if ( child.nodeType != 1 ) continue; 2249 | 2250 | if ( child.nodeName == 'bind_material' ) { 2251 | 2252 | var instances = COLLADA.evaluate( './/dae:instance_material', child, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); 2253 | 2254 | if ( instances ) { 2255 | 2256 | var instance = instances.iterateNext(); 2257 | 2258 | while ( instance ) { 2259 | 2260 | this.instance_material.push( (new InstanceMaterial()).parse(instance) ); 2261 | instance = instances.iterateNext(); 2262 | 2263 | } 2264 | 2265 | } 2266 | 2267 | break; 2268 | 2269 | } 2270 | 2271 | } 2272 | 2273 | return this; 2274 | 2275 | }; 2276 | 2277 | function Geometry() { 2278 | 2279 | this.id = ""; 2280 | this.mesh = null; 2281 | 2282 | }; 2283 | 2284 | Geometry.prototype.parse = function ( element ) { 2285 | 2286 | this.id = element.getAttribute('id'); 2287 | 2288 | extractDoubleSided( this, element ); 2289 | 2290 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2291 | 2292 | var child = element.childNodes[i]; 2293 | 2294 | switch ( child.nodeName ) { 2295 | 2296 | case 'mesh': 2297 | 2298 | this.mesh = (new Mesh(this)).parse(child); 2299 | break; 2300 | 2301 | case 'extra': 2302 | 2303 | // console.log( child ); 2304 | break; 2305 | 2306 | default: 2307 | break; 2308 | } 2309 | } 2310 | 2311 | return this; 2312 | 2313 | }; 2314 | 2315 | function Mesh( geometry ) { 2316 | 2317 | this.geometry = geometry.id; 2318 | this.primitives = []; 2319 | this.vertices = null; 2320 | this.geometry3js = null; 2321 | 2322 | }; 2323 | 2324 | Mesh.prototype.parse = function( element ) { 2325 | 2326 | this.primitives = []; 2327 | 2328 | var i, j; 2329 | 2330 | for ( i = 0; i < element.childNodes.length; i ++ ) { 2331 | 2332 | var child = element.childNodes[ i ]; 2333 | 2334 | switch ( child.nodeName ) { 2335 | 2336 | case 'source': 2337 | 2338 | _source( child ); 2339 | break; 2340 | 2341 | case 'vertices': 2342 | 2343 | this.vertices = ( new Vertices() ).parse( child ); 2344 | break; 2345 | 2346 | case 'triangles': 2347 | 2348 | this.primitives.push( ( new Triangles().parse( child ) ) ); 2349 | break; 2350 | 2351 | case 'polygons': 2352 | 2353 | this.primitives.push( ( new Polygons().parse( child ) ) ); 2354 | break; 2355 | 2356 | case 'polylist': 2357 | 2358 | this.primitives.push( ( new Polylist().parse( child ) ) ); 2359 | break; 2360 | 2361 | default: 2362 | break; 2363 | 2364 | } 2365 | 2366 | } 2367 | 2368 | this.geometry3js = new THREE.Geometry(); 2369 | 2370 | var vertexData = sources[ this.vertices.input['POSITION'].source ].data; 2371 | 2372 | for ( i = 0; i < vertexData.length; i += 3 ) { 2373 | 2374 | this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); 2375 | 2376 | } 2377 | 2378 | for ( i = 0; i < this.primitives.length; i ++ ) { 2379 | 2380 | var primitive = this.primitives[ i ]; 2381 | primitive.setVertices( this.vertices ); 2382 | this.handlePrimitive( primitive, this.geometry3js ); 2383 | 2384 | } 2385 | 2386 | this.geometry3js.computeCentroids(); 2387 | this.geometry3js.computeFaceNormals(); 2388 | 2389 | if ( this.geometry3js.calcNormals ) { 2390 | 2391 | this.geometry3js.computeVertexNormals(); 2392 | delete this.geometry3js.calcNormals; 2393 | 2394 | } 2395 | 2396 | this.geometry3js.computeBoundingBox(); 2397 | 2398 | return this; 2399 | 2400 | }; 2401 | 2402 | Mesh.prototype.handlePrimitive = function( primitive, geom ) { 2403 | 2404 | var j, k, pList = primitive.p, inputs = primitive.inputs; 2405 | var input, index, idx32; 2406 | var source, numParams; 2407 | var vcIndex = 0, vcount = 3, maxOffset = 0; 2408 | var texture_sets = []; 2409 | 2410 | for ( j = 0; j < inputs.length; j ++ ) { 2411 | 2412 | input = inputs[ j ]; 2413 | var offset = input.offset + 1; 2414 | maxOffset = (maxOffset < offset)? offset : maxOffset; 2415 | 2416 | switch ( input.semantic ) { 2417 | 2418 | case 'TEXCOORD': 2419 | texture_sets.push( input.set ); 2420 | break; 2421 | 2422 | } 2423 | 2424 | } 2425 | 2426 | for ( var pCount = 0; pCount < pList.length; ++pCount ) { 2427 | 2428 | var p = pList[ pCount ], i = 0; 2429 | 2430 | while ( i < p.length ) { 2431 | 2432 | var vs = []; 2433 | var ns = []; 2434 | var ts = null; 2435 | var cs = []; 2436 | 2437 | if ( primitive.vcount ) { 2438 | 2439 | vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; 2440 | 2441 | } else { 2442 | 2443 | vcount = p.length / maxOffset; 2444 | 2445 | } 2446 | 2447 | 2448 | for ( j = 0; j < vcount; j ++ ) { 2449 | 2450 | for ( k = 0; k < inputs.length; k ++ ) { 2451 | 2452 | input = inputs[ k ]; 2453 | source = sources[ input.source ]; 2454 | 2455 | index = p[ i + ( j * maxOffset ) + input.offset ]; 2456 | numParams = source.accessor.params.length; 2457 | idx32 = index * numParams; 2458 | 2459 | switch ( input.semantic ) { 2460 | 2461 | case 'VERTEX': 2462 | 2463 | vs.push( index ); 2464 | 2465 | break; 2466 | 2467 | case 'NORMAL': 2468 | 2469 | ns.push( getConvertedVec3( source.data, idx32 ) ); 2470 | 2471 | break; 2472 | 2473 | case 'TEXCOORD': 2474 | 2475 | ts = ts || { }; 2476 | if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; 2477 | // invert the V 2478 | ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); 2479 | 2480 | break; 2481 | 2482 | case 'COLOR': 2483 | 2484 | cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); 2485 | 2486 | break; 2487 | 2488 | default: 2489 | 2490 | break; 2491 | 2492 | } 2493 | 2494 | } 2495 | 2496 | } 2497 | 2498 | if ( ns.length == 0 ) { 2499 | 2500 | // check the vertices inputs 2501 | input = this.vertices.input.NORMAL; 2502 | 2503 | if ( input ) { 2504 | 2505 | source = sources[ input.source ]; 2506 | numParams = source.accessor.params.length; 2507 | 2508 | for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) { 2509 | 2510 | ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); 2511 | 2512 | } 2513 | 2514 | } else { 2515 | 2516 | geom.calcNormals = true; 2517 | 2518 | } 2519 | 2520 | } 2521 | 2522 | if ( !ts ) { 2523 | 2524 | ts = { }; 2525 | // check the vertices inputs 2526 | input = this.vertices.input.TEXCOORD; 2527 | 2528 | if ( input ) { 2529 | 2530 | texture_sets.push( input.set ); 2531 | source = sources[ input.source ]; 2532 | numParams = source.accessor.params.length; 2533 | 2534 | for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) { 2535 | 2536 | idx32 = vs[ ndx ] * numParams; 2537 | if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; 2538 | // invert the V 2539 | ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); 2540 | 2541 | } 2542 | 2543 | } 2544 | 2545 | } 2546 | 2547 | if ( cs.length == 0 ) { 2548 | 2549 | // check the vertices inputs 2550 | input = this.vertices.input.COLOR; 2551 | 2552 | if ( input ) { 2553 | 2554 | source = sources[ input.source ]; 2555 | numParams = source.accessor.params.length; 2556 | 2557 | for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) { 2558 | 2559 | idx32 = vs[ ndx ] * numParams; 2560 | cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); 2561 | 2562 | } 2563 | 2564 | } 2565 | 2566 | } 2567 | 2568 | var face = null, faces = [], uv, uvArr; 2569 | 2570 | if ( vcount === 3 ) { 2571 | 2572 | faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); 2573 | 2574 | } else if ( vcount === 4 ) { 2575 | faces.push( new THREE.Face4( vs[0], vs[1], vs[2], vs[3], ns, cs.length ? cs : new THREE.Color() ) ); 2576 | 2577 | } else if ( vcount > 4 && options.subdivideFaces ) { 2578 | 2579 | var clr = cs.length ? cs : new THREE.Color(), 2580 | vec1, vec2, vec3, v1, v2, norm; 2581 | 2582 | // subdivide into multiple Face3s 2583 | 2584 | for ( k = 1; k < vcount - 1; ) { 2585 | 2586 | // FIXME: normals don't seem to be quite right 2587 | 2588 | faces.push( new THREE.Face3( vs[0], vs[k], vs[k+1], [ ns[0], ns[k++], ns[k] ], clr ) ); 2589 | 2590 | } 2591 | 2592 | } 2593 | 2594 | if ( faces.length ) { 2595 | 2596 | for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { 2597 | 2598 | face = faces[ndx]; 2599 | face.daeMaterial = primitive.material; 2600 | geom.faces.push( face ); 2601 | 2602 | for ( k = 0; k < texture_sets.length; k++ ) { 2603 | 2604 | uv = ts[ texture_sets[k] ]; 2605 | 2606 | if ( vcount > 4 ) { 2607 | 2608 | // Grab the right UVs for the vertices in this face 2609 | uvArr = [ uv[0], uv[ndx+1], uv[ndx+2] ]; 2610 | 2611 | } else if ( vcount === 4 ) { 2612 | 2613 | uvArr = [ uv[0], uv[1], uv[2], uv[3] ]; 2614 | 2615 | } else { 2616 | 2617 | uvArr = [ uv[0], uv[1], uv[2] ]; 2618 | 2619 | } 2620 | 2621 | if ( !geom.faceVertexUvs[k] ) { 2622 | 2623 | geom.faceVertexUvs[k] = []; 2624 | 2625 | } 2626 | 2627 | geom.faceVertexUvs[k].push( uvArr ); 2628 | 2629 | } 2630 | 2631 | } 2632 | 2633 | } else { 2634 | 2635 | console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); 2636 | 2637 | } 2638 | 2639 | i += maxOffset * vcount; 2640 | 2641 | } 2642 | } 2643 | 2644 | }; 2645 | 2646 | function Polygons () { 2647 | 2648 | this.material = ""; 2649 | this.count = 0; 2650 | this.inputs = []; 2651 | this.vcount = null; 2652 | this.p = []; 2653 | this.geometry = new THREE.Geometry(); 2654 | 2655 | }; 2656 | 2657 | Polygons.prototype.setVertices = function ( vertices ) { 2658 | 2659 | for ( var i = 0; i < this.inputs.length; i ++ ) { 2660 | 2661 | if ( this.inputs[ i ].source == vertices.id ) { 2662 | 2663 | this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; 2664 | 2665 | } 2666 | 2667 | } 2668 | 2669 | }; 2670 | 2671 | Polygons.prototype.parse = function ( element ) { 2672 | 2673 | this.material = element.getAttribute( 'material' ); 2674 | this.count = _attr_as_int( element, 'count', 0 ); 2675 | 2676 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2677 | 2678 | var child = element.childNodes[ i ]; 2679 | 2680 | switch ( child.nodeName ) { 2681 | 2682 | case 'input': 2683 | 2684 | this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); 2685 | break; 2686 | 2687 | case 'vcount': 2688 | 2689 | this.vcount = _ints( child.textContent ); 2690 | break; 2691 | 2692 | case 'p': 2693 | 2694 | this.p.push( _ints( child.textContent ) ); 2695 | break; 2696 | 2697 | case 'ph': 2698 | 2699 | console.warn( 'polygon holes not yet supported!' ); 2700 | break; 2701 | 2702 | default: 2703 | break; 2704 | 2705 | } 2706 | 2707 | } 2708 | 2709 | return this; 2710 | 2711 | }; 2712 | 2713 | function Polylist () { 2714 | 2715 | Polygons.call( this ); 2716 | 2717 | this.vcount = []; 2718 | 2719 | }; 2720 | 2721 | Polylist.prototype = Object.create( Polygons.prototype ); 2722 | 2723 | function Triangles () { 2724 | 2725 | Polygons.call( this ); 2726 | 2727 | this.vcount = 3; 2728 | 2729 | }; 2730 | 2731 | Triangles.prototype = Object.create( Polygons.prototype ); 2732 | 2733 | function Accessor() { 2734 | 2735 | this.source = ""; 2736 | this.count = 0; 2737 | this.stride = 0; 2738 | this.params = []; 2739 | 2740 | }; 2741 | 2742 | Accessor.prototype.parse = function ( element ) { 2743 | 2744 | this.params = []; 2745 | this.source = element.getAttribute( 'source' ); 2746 | this.count = _attr_as_int( element, 'count', 0 ); 2747 | this.stride = _attr_as_int( element, 'stride', 0 ); 2748 | 2749 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2750 | 2751 | var child = element.childNodes[ i ]; 2752 | 2753 | if ( child.nodeName == 'param' ) { 2754 | 2755 | var param = {}; 2756 | param[ 'name' ] = child.getAttribute( 'name' ); 2757 | param[ 'type' ] = child.getAttribute( 'type' ); 2758 | this.params.push( param ); 2759 | 2760 | } 2761 | 2762 | } 2763 | 2764 | return this; 2765 | 2766 | }; 2767 | 2768 | function Vertices() { 2769 | 2770 | this.input = {}; 2771 | 2772 | }; 2773 | 2774 | Vertices.prototype.parse = function ( element ) { 2775 | 2776 | this.id = element.getAttribute('id'); 2777 | 2778 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2779 | 2780 | if ( element.childNodes[i].nodeName == 'input' ) { 2781 | 2782 | var input = ( new Input() ).parse( element.childNodes[ i ] ); 2783 | this.input[ input.semantic ] = input; 2784 | 2785 | } 2786 | 2787 | } 2788 | 2789 | return this; 2790 | 2791 | }; 2792 | 2793 | function Input () { 2794 | 2795 | this.semantic = ""; 2796 | this.offset = 0; 2797 | this.source = ""; 2798 | this.set = 0; 2799 | 2800 | }; 2801 | 2802 | Input.prototype.parse = function ( element ) { 2803 | 2804 | this.semantic = element.getAttribute('semantic'); 2805 | this.source = element.getAttribute('source').replace(/^#/, ''); 2806 | this.set = _attr_as_int(element, 'set', -1); 2807 | this.offset = _attr_as_int(element, 'offset', 0); 2808 | 2809 | if ( this.semantic == 'TEXCOORD' && this.set < 0 ) { 2810 | 2811 | this.set = 0; 2812 | 2813 | } 2814 | 2815 | return this; 2816 | 2817 | }; 2818 | 2819 | function Source ( id ) { 2820 | 2821 | this.id = id; 2822 | this.type = null; 2823 | 2824 | }; 2825 | 2826 | Source.prototype.parse = function ( element ) { 2827 | 2828 | this.id = element.getAttribute( 'id' ); 2829 | 2830 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2831 | 2832 | var child = element.childNodes[i]; 2833 | 2834 | switch ( child.nodeName ) { 2835 | 2836 | case 'bool_array': 2837 | 2838 | this.data = _bools( child.textContent ); 2839 | this.type = child.nodeName; 2840 | break; 2841 | 2842 | case 'float_array': 2843 | 2844 | this.data = _floats( child.textContent ); 2845 | this.type = child.nodeName; 2846 | break; 2847 | 2848 | case 'int_array': 2849 | 2850 | this.data = _ints( child.textContent ); 2851 | this.type = child.nodeName; 2852 | break; 2853 | 2854 | case 'IDREF_array': 2855 | case 'Name_array': 2856 | 2857 | this.data = _strings( child.textContent ); 2858 | this.type = child.nodeName; 2859 | break; 2860 | 2861 | case 'technique_common': 2862 | 2863 | for ( var j = 0; j < child.childNodes.length; j ++ ) { 2864 | 2865 | if ( child.childNodes[ j ].nodeName == 'accessor' ) { 2866 | 2867 | this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); 2868 | break; 2869 | 2870 | } 2871 | } 2872 | break; 2873 | 2874 | default: 2875 | // console.log(child.nodeName); 2876 | break; 2877 | 2878 | } 2879 | 2880 | } 2881 | 2882 | return this; 2883 | 2884 | }; 2885 | 2886 | Source.prototype.read = function () { 2887 | 2888 | var result = []; 2889 | 2890 | //for (var i = 0; i < this.accessor.params.length; i++) { 2891 | 2892 | var param = this.accessor.params[ 0 ]; 2893 | 2894 | //console.log(param.name + " " + param.type); 2895 | 2896 | switch ( param.type ) { 2897 | 2898 | case 'IDREF': 2899 | case 'Name': case 'name': 2900 | case 'float': 2901 | 2902 | return this.data; 2903 | 2904 | case 'float4x4': 2905 | 2906 | for ( var j = 0; j < this.data.length; j += 16 ) { 2907 | 2908 | var s = this.data.slice( j, j + 16 ); 2909 | var m = getConvertedMat4( s ); 2910 | result.push( m ); 2911 | } 2912 | 2913 | break; 2914 | 2915 | default: 2916 | 2917 | console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); 2918 | break; 2919 | 2920 | } 2921 | 2922 | //} 2923 | 2924 | return result; 2925 | 2926 | }; 2927 | 2928 | function Material () { 2929 | 2930 | this.id = ""; 2931 | this.name = ""; 2932 | this.instance_effect = null; 2933 | 2934 | }; 2935 | 2936 | Material.prototype.parse = function ( element ) { 2937 | 2938 | this.id = element.getAttribute( 'id' ); 2939 | this.name = element.getAttribute( 'name' ); 2940 | 2941 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2942 | 2943 | if ( element.childNodes[ i ].nodeName == 'instance_effect' ) { 2944 | 2945 | this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); 2946 | break; 2947 | 2948 | } 2949 | 2950 | } 2951 | 2952 | return this; 2953 | 2954 | }; 2955 | 2956 | function ColorOrTexture () { 2957 | 2958 | this.color = new THREE.Color(); 2959 | this.color.setRGB( Math.random(), Math.random(), Math.random() ); 2960 | this.color.a = 1.0; 2961 | 2962 | this.texture = null; 2963 | this.texcoord = null; 2964 | this.texOpts = null; 2965 | 2966 | }; 2967 | 2968 | ColorOrTexture.prototype.isColor = function () { 2969 | 2970 | return ( this.texture == null ); 2971 | 2972 | }; 2973 | 2974 | ColorOrTexture.prototype.isTexture = function () { 2975 | 2976 | return ( this.texture != null ); 2977 | 2978 | }; 2979 | 2980 | ColorOrTexture.prototype.parse = function ( element ) { 2981 | 2982 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2983 | 2984 | var child = element.childNodes[ i ]; 2985 | if ( child.nodeType != 1 ) continue; 2986 | 2987 | switch ( child.nodeName ) { 2988 | 2989 | case 'color': 2990 | 2991 | var rgba = _floats( child.textContent ); 2992 | this.color = new THREE.Color(); 2993 | this.color.setRGB( rgba[0], rgba[1], rgba[2] ); 2994 | this.color.a = rgba[3]; 2995 | break; 2996 | 2997 | case 'texture': 2998 | 2999 | this.texture = child.getAttribute('texture'); 3000 | this.texcoord = child.getAttribute('texcoord'); 3001 | // Defaults from: 3002 | // https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension 3003 | this.texOpts = { 3004 | offsetU: 0, 3005 | offsetV: 0, 3006 | repeatU: 1, 3007 | repeatV: 1, 3008 | wrapU: 1, 3009 | wrapV: 1, 3010 | }; 3011 | this.parseTexture( child ); 3012 | break; 3013 | 3014 | default: 3015 | break; 3016 | 3017 | } 3018 | 3019 | } 3020 | 3021 | return this; 3022 | 3023 | }; 3024 | 3025 | ColorOrTexture.prototype.parseTexture = function ( element ) { 3026 | 3027 | if ( ! element.childNodes ) return this; 3028 | 3029 | // This should be supported by Maya, 3dsMax, and MotionBuilder 3030 | 3031 | if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { 3032 | 3033 | element = element.childNodes[1]; 3034 | 3035 | if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { 3036 | 3037 | element = element.childNodes[1]; 3038 | 3039 | } 3040 | 3041 | } 3042 | 3043 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3044 | 3045 | var child = element.childNodes[ i ]; 3046 | 3047 | switch ( child.nodeName ) { 3048 | 3049 | case 'offsetU': 3050 | case 'offsetV': 3051 | case 'repeatU': 3052 | case 'repeatV': 3053 | 3054 | this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); 3055 | break; 3056 | 3057 | case 'wrapU': 3058 | case 'wrapV': 3059 | 3060 | this.texOpts[ child.nodeName ] = parseInt( child.textContent ); 3061 | break; 3062 | 3063 | default: 3064 | this.texOpts[ child.nodeName ] = child.textContent; 3065 | break; 3066 | 3067 | } 3068 | 3069 | } 3070 | 3071 | return this; 3072 | 3073 | }; 3074 | 3075 | function Shader ( type, effect ) { 3076 | 3077 | this.type = type; 3078 | this.effect = effect; 3079 | this.material = null; 3080 | 3081 | }; 3082 | 3083 | Shader.prototype.parse = function ( element ) { 3084 | 3085 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3086 | 3087 | var child = element.childNodes[ i ]; 3088 | if ( child.nodeType != 1 ) continue; 3089 | 3090 | switch ( child.nodeName ) { 3091 | 3092 | case 'ambient': 3093 | case 'emission': 3094 | case 'diffuse': 3095 | case 'specular': 3096 | case 'transparent': 3097 | 3098 | this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); 3099 | break; 3100 | 3101 | case 'shininess': 3102 | case 'reflectivity': 3103 | case 'index_of_refraction': 3104 | case 'transparency': 3105 | 3106 | var f = evaluateXPath( child, './/dae:float' ); 3107 | 3108 | if ( f.length > 0 ) 3109 | this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); 3110 | 3111 | break; 3112 | 3113 | default: 3114 | break; 3115 | 3116 | } 3117 | 3118 | } 3119 | 3120 | this.create(); 3121 | return this; 3122 | 3123 | }; 3124 | 3125 | Shader.prototype.create = function() { 3126 | 3127 | var props = {}; 3128 | var transparent = ( this['transparency'] !== undefined && this['transparency'] < 1.0 ); 3129 | 3130 | for ( var prop in this ) { 3131 | 3132 | switch ( prop ) { 3133 | 3134 | case 'ambient': 3135 | case 'emission': 3136 | case 'diffuse': 3137 | case 'specular': 3138 | 3139 | var cot = this[ prop ]; 3140 | 3141 | if ( cot instanceof ColorOrTexture ) { 3142 | 3143 | if ( cot.isTexture() ) { 3144 | 3145 | var samplerId = cot.texture; 3146 | var surfaceId = this.effect.sampler[samplerId].source; 3147 | 3148 | if ( surfaceId ) { 3149 | 3150 | var surface = this.effect.surface[surfaceId]; 3151 | var image = images[surface.init_from]; 3152 | 3153 | if (image) { 3154 | 3155 | var texture = THREE.ImageUtils.loadTexture(baseUrl + image.init_from); 3156 | texture.wrapS = cot.texOpts.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; 3157 | texture.wrapT = cot.texOpts.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; 3158 | texture.offset.x = cot.texOpts.offsetU; 3159 | texture.offset.y = cot.texOpts.offsetV; 3160 | texture.repeat.x = cot.texOpts.repeatU; 3161 | texture.repeat.y = cot.texOpts.repeatV; 3162 | props['map'] = texture; 3163 | 3164 | // Texture with baked lighting? 3165 | if (prop === 'emission') props['emissive'] = 0xffffff; 3166 | 3167 | } 3168 | 3169 | } 3170 | 3171 | } else if ( prop === 'diffuse' || !transparent ) { 3172 | 3173 | if ( prop === 'emission' ) { 3174 | 3175 | props[ 'emissive' ] = cot.color.getHex(); 3176 | 3177 | } else { 3178 | 3179 | props[ prop ] = cot.color.getHex(); 3180 | 3181 | } 3182 | 3183 | } 3184 | 3185 | } 3186 | 3187 | break; 3188 | 3189 | case 'shininess': 3190 | 3191 | props[ prop ] = this[ prop ]; 3192 | break; 3193 | 3194 | case 'reflectivity': 3195 | 3196 | props[ prop ] = this[ prop ]; 3197 | if( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; 3198 | props['combine'] = THREE.MixOperation; //mix regular shading with reflective component 3199 | break; 3200 | 3201 | case 'index_of_refraction': 3202 | 3203 | props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable 3204 | if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; 3205 | break; 3206 | 3207 | case 'transparency': 3208 | 3209 | if ( transparent ) { 3210 | 3211 | props[ 'transparent' ] = true; 3212 | props[ 'opacity' ] = this[ prop ]; 3213 | transparent = true; 3214 | 3215 | } 3216 | 3217 | break; 3218 | 3219 | default: 3220 | break; 3221 | 3222 | } 3223 | 3224 | } 3225 | 3226 | props[ 'shading' ] = preferredShading; 3227 | props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; 3228 | 3229 | switch ( this.type ) { 3230 | 3231 | case 'constant': 3232 | 3233 | if (props.emissive != undefined) props.color = props.emissive; 3234 | this.material = new THREE.MeshBasicMaterial( props ); 3235 | break; 3236 | 3237 | case 'phong': 3238 | case 'blinn': 3239 | 3240 | if (props.diffuse != undefined) props.color = props.diffuse; 3241 | this.material = new THREE.MeshPhongMaterial( props ); 3242 | break; 3243 | 3244 | case 'lambert': 3245 | default: 3246 | 3247 | if (props.diffuse != undefined) props.color = props.diffuse; 3248 | this.material = new THREE.MeshLambertMaterial( props ); 3249 | break; 3250 | 3251 | } 3252 | 3253 | return this.material; 3254 | 3255 | }; 3256 | 3257 | function Surface ( effect ) { 3258 | 3259 | this.effect = effect; 3260 | this.init_from = null; 3261 | this.format = null; 3262 | 3263 | }; 3264 | 3265 | Surface.prototype.parse = function ( element ) { 3266 | 3267 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3268 | 3269 | var child = element.childNodes[ i ]; 3270 | if ( child.nodeType != 1 ) continue; 3271 | 3272 | switch ( child.nodeName ) { 3273 | 3274 | case 'init_from': 3275 | 3276 | this.init_from = child.textContent; 3277 | break; 3278 | 3279 | case 'format': 3280 | 3281 | this.format = child.textContent; 3282 | break; 3283 | 3284 | default: 3285 | 3286 | console.log( "unhandled Surface prop: " + child.nodeName ); 3287 | break; 3288 | 3289 | } 3290 | 3291 | } 3292 | 3293 | return this; 3294 | 3295 | }; 3296 | 3297 | function Sampler2D ( effect ) { 3298 | 3299 | this.effect = effect; 3300 | this.source = null; 3301 | this.wrap_s = null; 3302 | this.wrap_t = null; 3303 | this.minfilter = null; 3304 | this.magfilter = null; 3305 | this.mipfilter = null; 3306 | 3307 | }; 3308 | 3309 | Sampler2D.prototype.parse = function ( element ) { 3310 | 3311 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3312 | 3313 | var child = element.childNodes[ i ]; 3314 | if ( child.nodeType != 1 ) continue; 3315 | 3316 | switch ( child.nodeName ) { 3317 | 3318 | case 'source': 3319 | 3320 | this.source = child.textContent; 3321 | break; 3322 | 3323 | case 'minfilter': 3324 | 3325 | this.minfilter = child.textContent; 3326 | break; 3327 | 3328 | case 'magfilter': 3329 | 3330 | this.magfilter = child.textContent; 3331 | break; 3332 | 3333 | case 'mipfilter': 3334 | 3335 | this.mipfilter = child.textContent; 3336 | break; 3337 | 3338 | case 'wrap_s': 3339 | 3340 | this.wrap_s = child.textContent; 3341 | break; 3342 | 3343 | case 'wrap_t': 3344 | 3345 | this.wrap_t = child.textContent; 3346 | break; 3347 | 3348 | default: 3349 | 3350 | console.log( "unhandled Sampler2D prop: " + child.nodeName ); 3351 | break; 3352 | 3353 | } 3354 | 3355 | } 3356 | 3357 | return this; 3358 | 3359 | }; 3360 | 3361 | function Effect () { 3362 | 3363 | this.id = ""; 3364 | this.name = ""; 3365 | this.shader = null; 3366 | this.surface = {}; 3367 | this.sampler = {}; 3368 | 3369 | }; 3370 | 3371 | Effect.prototype.create = function () { 3372 | 3373 | if ( this.shader == null ) { 3374 | 3375 | return null; 3376 | 3377 | } 3378 | 3379 | }; 3380 | 3381 | Effect.prototype.parse = function ( element ) { 3382 | 3383 | this.id = element.getAttribute( 'id' ); 3384 | this.name = element.getAttribute( 'name' ); 3385 | 3386 | extractDoubleSided( this, element ); 3387 | 3388 | this.shader = null; 3389 | 3390 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3391 | 3392 | var child = element.childNodes[ i ]; 3393 | if ( child.nodeType != 1 ) continue; 3394 | 3395 | switch ( child.nodeName ) { 3396 | 3397 | case 'profile_COMMON': 3398 | 3399 | this.parseTechnique( this.parseProfileCOMMON( child ) ); 3400 | break; 3401 | 3402 | default: 3403 | break; 3404 | 3405 | } 3406 | 3407 | } 3408 | 3409 | return this; 3410 | 3411 | }; 3412 | 3413 | Effect.prototype.parseNewparam = function ( element ) { 3414 | 3415 | var sid = element.getAttribute( 'sid' ); 3416 | 3417 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3418 | 3419 | var child = element.childNodes[ i ]; 3420 | if ( child.nodeType != 1 ) continue; 3421 | 3422 | switch ( child.nodeName ) { 3423 | 3424 | case 'surface': 3425 | 3426 | this.surface[sid] = ( new Surface( this ) ).parse( child ); 3427 | break; 3428 | 3429 | case 'sampler2D': 3430 | 3431 | this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); 3432 | break; 3433 | 3434 | case 'extra': 3435 | 3436 | break; 3437 | 3438 | default: 3439 | 3440 | console.log( child.nodeName ); 3441 | break; 3442 | 3443 | } 3444 | 3445 | } 3446 | 3447 | }; 3448 | 3449 | Effect.prototype.parseProfileCOMMON = function ( element ) { 3450 | 3451 | var technique; 3452 | 3453 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3454 | 3455 | var child = element.childNodes[ i ]; 3456 | 3457 | if ( child.nodeType != 1 ) continue; 3458 | 3459 | switch ( child.nodeName ) { 3460 | 3461 | case 'profile_COMMON': 3462 | 3463 | this.parseProfileCOMMON( child ); 3464 | break; 3465 | 3466 | case 'technique': 3467 | 3468 | technique = child; 3469 | break; 3470 | 3471 | case 'newparam': 3472 | 3473 | this.parseNewparam( child ); 3474 | break; 3475 | 3476 | case 'image': 3477 | 3478 | var _image = ( new _Image() ).parse( child ); 3479 | images[ _image.id ] = _image; 3480 | break; 3481 | 3482 | case 'extra': 3483 | break; 3484 | 3485 | default: 3486 | 3487 | console.log( child.nodeName ); 3488 | break; 3489 | 3490 | } 3491 | 3492 | } 3493 | 3494 | return technique; 3495 | 3496 | }; 3497 | 3498 | Effect.prototype.parseTechnique= function ( element ) { 3499 | 3500 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3501 | 3502 | var child = element.childNodes[i]; 3503 | if ( child.nodeType != 1 ) continue; 3504 | 3505 | switch ( child.nodeName ) { 3506 | 3507 | case 'constant': 3508 | case 'lambert': 3509 | case 'blinn': 3510 | case 'phong': 3511 | 3512 | this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); 3513 | break; 3514 | 3515 | default: 3516 | break; 3517 | 3518 | } 3519 | 3520 | } 3521 | 3522 | }; 3523 | 3524 | function InstanceEffect () { 3525 | 3526 | this.url = ""; 3527 | 3528 | }; 3529 | 3530 | InstanceEffect.prototype.parse = function ( element ) { 3531 | 3532 | this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); 3533 | return this; 3534 | 3535 | }; 3536 | 3537 | function Animation() { 3538 | 3539 | this.id = ""; 3540 | this.name = ""; 3541 | this.source = {}; 3542 | this.sampler = []; 3543 | this.channel = []; 3544 | 3545 | }; 3546 | 3547 | Animation.prototype.parse = function ( element ) { 3548 | 3549 | this.id = element.getAttribute( 'id' ); 3550 | this.name = element.getAttribute( 'name' ); 3551 | this.source = {}; 3552 | 3553 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3554 | 3555 | var child = element.childNodes[ i ]; 3556 | 3557 | if ( child.nodeType != 1 ) continue; 3558 | 3559 | switch ( child.nodeName ) { 3560 | 3561 | case 'animation': 3562 | 3563 | var anim = ( new Animation() ).parse( child ); 3564 | 3565 | for ( var src in anim.source ) { 3566 | 3567 | this.source[ src ] = anim.source[ src ]; 3568 | 3569 | } 3570 | 3571 | for ( var j = 0; j < anim.channel.length; j ++ ) { 3572 | 3573 | this.channel.push( anim.channel[ j ] ); 3574 | this.sampler.push( anim.sampler[ j ] ); 3575 | 3576 | } 3577 | 3578 | break; 3579 | 3580 | case 'source': 3581 | 3582 | var src = ( new Source() ).parse( child ); 3583 | this.source[ src.id ] = src; 3584 | break; 3585 | 3586 | case 'sampler': 3587 | 3588 | this.sampler.push( ( new Sampler( this ) ).parse( child ) ); 3589 | break; 3590 | 3591 | case 'channel': 3592 | 3593 | this.channel.push( ( new Channel( this ) ).parse( child ) ); 3594 | break; 3595 | 3596 | default: 3597 | break; 3598 | 3599 | } 3600 | 3601 | } 3602 | 3603 | return this; 3604 | 3605 | }; 3606 | 3607 | function Channel( animation ) { 3608 | 3609 | this.animation = animation; 3610 | this.source = ""; 3611 | this.target = ""; 3612 | this.fullSid = null; 3613 | this.sid = null; 3614 | this.dotSyntax = null; 3615 | this.arrSyntax = null; 3616 | this.arrIndices = null; 3617 | this.member = null; 3618 | 3619 | }; 3620 | 3621 | Channel.prototype.parse = function ( element ) { 3622 | 3623 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); 3624 | this.target = element.getAttribute( 'target' ); 3625 | 3626 | var parts = this.target.split( '/' ); 3627 | 3628 | var id = parts.shift(); 3629 | var sid = parts.shift(); 3630 | 3631 | var dotSyntax = ( sid.indexOf(".") >= 0 ); 3632 | var arrSyntax = ( sid.indexOf("(") >= 0 ); 3633 | 3634 | if ( dotSyntax ) { 3635 | 3636 | parts = sid.split("."); 3637 | this.sid = parts.shift(); 3638 | this.member = parts.shift(); 3639 | 3640 | } else if ( arrSyntax ) { 3641 | 3642 | var arrIndices = sid.split("("); 3643 | this.sid = arrIndices.shift(); 3644 | 3645 | for (var j = 0; j < arrIndices.length; j ++ ) { 3646 | 3647 | arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); 3648 | 3649 | } 3650 | 3651 | this.arrIndices = arrIndices; 3652 | 3653 | } else { 3654 | 3655 | this.sid = sid; 3656 | 3657 | } 3658 | 3659 | this.fullSid = sid; 3660 | this.dotSyntax = dotSyntax; 3661 | this.arrSyntax = arrSyntax; 3662 | 3663 | return this; 3664 | 3665 | }; 3666 | 3667 | function Sampler ( animation ) { 3668 | 3669 | this.id = ""; 3670 | this.animation = animation; 3671 | this.inputs = []; 3672 | this.input = null; 3673 | this.output = null; 3674 | this.strideOut = null; 3675 | this.interpolation = null; 3676 | this.startTime = null; 3677 | this.endTime = null; 3678 | this.duration = 0; 3679 | 3680 | }; 3681 | 3682 | Sampler.prototype.parse = function ( element ) { 3683 | 3684 | this.id = element.getAttribute( 'id' ); 3685 | this.inputs = []; 3686 | 3687 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3688 | 3689 | var child = element.childNodes[ i ]; 3690 | if ( child.nodeType != 1 ) continue; 3691 | 3692 | switch ( child.nodeName ) { 3693 | 3694 | case 'input': 3695 | 3696 | this.inputs.push( (new Input()).parse( child ) ); 3697 | break; 3698 | 3699 | default: 3700 | break; 3701 | 3702 | } 3703 | 3704 | } 3705 | 3706 | return this; 3707 | 3708 | }; 3709 | 3710 | Sampler.prototype.create = function () { 3711 | 3712 | for ( var i = 0; i < this.inputs.length; i ++ ) { 3713 | 3714 | var input = this.inputs[ i ]; 3715 | var source = this.animation.source[ input.source ]; 3716 | 3717 | switch ( input.semantic ) { 3718 | 3719 | case 'INPUT': 3720 | 3721 | this.input = source.read(); 3722 | break; 3723 | 3724 | case 'OUTPUT': 3725 | 3726 | this.output = source.read(); 3727 | this.strideOut = source.accessor.stride; 3728 | break; 3729 | 3730 | case 'INTERPOLATION': 3731 | 3732 | this.interpolation = source.read(); 3733 | break; 3734 | 3735 | case 'IN_TANGENT': 3736 | 3737 | break; 3738 | 3739 | case 'OUT_TANGENT': 3740 | 3741 | break; 3742 | 3743 | default: 3744 | 3745 | console.log(input.semantic); 3746 | break; 3747 | 3748 | } 3749 | 3750 | } 3751 | 3752 | this.startTime = 0; 3753 | this.endTime = 0; 3754 | this.duration = 0; 3755 | 3756 | if ( this.input.length ) { 3757 | 3758 | this.startTime = 100000000; 3759 | this.endTime = -100000000; 3760 | 3761 | for ( var i = 0; i < this.input.length; i ++ ) { 3762 | 3763 | this.startTime = Math.min( this.startTime, this.input[ i ] ); 3764 | this.endTime = Math.max( this.endTime, this.input[ i ] ); 3765 | 3766 | } 3767 | 3768 | this.duration = this.endTime - this.startTime; 3769 | 3770 | } 3771 | 3772 | }; 3773 | 3774 | Sampler.prototype.getData = function ( type, ndx ) { 3775 | 3776 | var data; 3777 | 3778 | if ( type === 'matrix' && this.strideOut === 16 ) { 3779 | 3780 | data = this.output[ ndx ]; 3781 | 3782 | } else if ( this.strideOut > 1 ) { 3783 | 3784 | data = []; 3785 | ndx *= this.strideOut; 3786 | 3787 | for ( var i = 0; i < this.strideOut; ++i ) { 3788 | 3789 | data[ i ] = this.output[ ndx + i ]; 3790 | 3791 | } 3792 | 3793 | if ( this.strideOut === 3 ) { 3794 | 3795 | switch ( type ) { 3796 | 3797 | case 'rotate': 3798 | case 'translate': 3799 | 3800 | fixCoords( data, -1 ); 3801 | break; 3802 | 3803 | case 'scale': 3804 | 3805 | fixCoords( data, 1 ); 3806 | break; 3807 | 3808 | } 3809 | 3810 | } else if ( this.strideOut === 4 && type === 'matrix' ) { 3811 | 3812 | fixCoords( data, -1 ); 3813 | 3814 | } 3815 | 3816 | } else { 3817 | 3818 | data = this.output[ ndx ]; 3819 | 3820 | } 3821 | 3822 | return data; 3823 | 3824 | }; 3825 | 3826 | function Key ( time ) { 3827 | 3828 | this.targets = []; 3829 | this.time = time; 3830 | 3831 | }; 3832 | 3833 | Key.prototype.addTarget = function ( fullSid, transform, member, data ) { 3834 | 3835 | this.targets.push( { 3836 | sid: fullSid, 3837 | member: member, 3838 | transform: transform, 3839 | data: data 3840 | } ); 3841 | 3842 | }; 3843 | 3844 | Key.prototype.apply = function ( opt_sid ) { 3845 | 3846 | for ( var i = 0; i < this.targets.length; ++i ) { 3847 | 3848 | var target = this.targets[ i ]; 3849 | 3850 | if ( !opt_sid || target.sid === opt_sid ) { 3851 | 3852 | target.transform.update( target.data, target.member ); 3853 | 3854 | } 3855 | 3856 | } 3857 | 3858 | }; 3859 | 3860 | Key.prototype.getTarget = function ( fullSid ) { 3861 | 3862 | for ( var i = 0; i < this.targets.length; ++i ) { 3863 | 3864 | if ( this.targets[ i ].sid === fullSid ) { 3865 | 3866 | return this.targets[ i ]; 3867 | 3868 | } 3869 | 3870 | } 3871 | 3872 | return null; 3873 | 3874 | }; 3875 | 3876 | Key.prototype.hasTarget = function ( fullSid ) { 3877 | 3878 | for ( var i = 0; i < this.targets.length; ++i ) { 3879 | 3880 | if ( this.targets[ i ].sid === fullSid ) { 3881 | 3882 | return true; 3883 | 3884 | } 3885 | 3886 | } 3887 | 3888 | return false; 3889 | 3890 | }; 3891 | 3892 | // TODO: Currently only doing linear interpolation. Should support full COLLADA spec. 3893 | Key.prototype.interpolate = function ( nextKey, time ) { 3894 | 3895 | for ( var i = 0; i < this.targets.length; ++i ) { 3896 | 3897 | var target = this.targets[ i ], 3898 | nextTarget = nextKey.getTarget( target.sid ), 3899 | data; 3900 | 3901 | if ( target.transform.type !== 'matrix' && nextTarget ) { 3902 | 3903 | var scale = ( time - this.time ) / ( nextKey.time - this.time ), 3904 | nextData = nextTarget.data, 3905 | prevData = target.data; 3906 | 3907 | // check scale error 3908 | 3909 | if ( scale < 0 || scale > 1 ) { 3910 | 3911 | console.log( "Key.interpolate: Warning! Scale out of bounds:" + scale ); 3912 | scale = scale < 0 ? 0 : 1; 3913 | 3914 | } 3915 | 3916 | if ( prevData.length ) { 3917 | 3918 | data = []; 3919 | 3920 | for ( var j = 0; j < prevData.length; ++j ) { 3921 | 3922 | data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; 3923 | 3924 | } 3925 | 3926 | } else { 3927 | 3928 | data = prevData + ( nextData - prevData ) * scale; 3929 | 3930 | } 3931 | 3932 | } else { 3933 | 3934 | data = target.data; 3935 | 3936 | } 3937 | 3938 | target.transform.update( data, target.member ); 3939 | 3940 | } 3941 | 3942 | }; 3943 | 3944 | // Camera 3945 | 3946 | function Camera() { 3947 | 3948 | this.id = ""; 3949 | this.name = ""; 3950 | this.technique = ""; 3951 | 3952 | }; 3953 | 3954 | Camera.prototype.parse = function ( element ) { 3955 | 3956 | this.id = element.getAttribute( 'id' ); 3957 | this.name = element.getAttribute( 'name' ); 3958 | 3959 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3960 | 3961 | var child = element.childNodes[ i ]; 3962 | if ( child.nodeType != 1 ) continue; 3963 | 3964 | switch ( child.nodeName ) { 3965 | 3966 | case 'optics': 3967 | this.parseOptics( child ); 3968 | break; 3969 | 3970 | } 3971 | 3972 | } 3973 | 3974 | return this; 3975 | 3976 | }; 3977 | 3978 | Camera.prototype.parseOptics = function ( element ) { 3979 | 3980 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3981 | 3982 | if ( element.childNodes[ i ].nodeName == 'technique_common' ) { 3983 | 3984 | var technique = element.childNodes[ i ]; 3985 | 3986 | for ( var j = 0; j < technique.childNodes.length; j ++ ) { 3987 | 3988 | this.technique = technique.childNodes[ j ].nodeName; 3989 | 3990 | if ( this.technique == 'perspective' ) { 3991 | 3992 | var perspective = technique.childNodes[ j ]; 3993 | 3994 | for ( var k = 0; k < perspective.childNodes.length; k ++ ) { 3995 | 3996 | var param = perspective.childNodes[ k ]; 3997 | 3998 | switch ( param.nodeName ) { 3999 | 4000 | case 'yfov': 4001 | this.yfov = param.textContent; 4002 | break; 4003 | case 'xfov': 4004 | this.xfov = param.textContent; 4005 | break; 4006 | case 'znear': 4007 | this.znear = param.textContent; 4008 | break; 4009 | case 'zfar': 4010 | this.zfar = param.textContent; 4011 | break; 4012 | case 'aspect_ratio': 4013 | this.aspect_ratio = param.textContent; 4014 | break; 4015 | 4016 | } 4017 | 4018 | } 4019 | 4020 | } else if ( this.technique == 'orthographic' ) { 4021 | 4022 | var orthographic = technique.childNodes[ j ]; 4023 | 4024 | for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { 4025 | 4026 | var param = orthographic.childNodes[ k ]; 4027 | 4028 | switch ( param.nodeName ) { 4029 | 4030 | case 'xmag': 4031 | this.xmag = param.textContent; 4032 | break; 4033 | case 'ymag': 4034 | this.ymag = param.textContent; 4035 | break; 4036 | case 'znear': 4037 | this.znear = param.textContent; 4038 | break; 4039 | case 'zfar': 4040 | this.zfar = param.textContent; 4041 | break; 4042 | case 'aspect_ratio': 4043 | this.aspect_ratio = param.textContent; 4044 | break; 4045 | 4046 | } 4047 | 4048 | } 4049 | 4050 | } 4051 | 4052 | } 4053 | 4054 | } 4055 | 4056 | } 4057 | 4058 | return this; 4059 | 4060 | }; 4061 | 4062 | function InstanceCamera() { 4063 | 4064 | this.url = ""; 4065 | 4066 | }; 4067 | 4068 | InstanceCamera.prototype.parse = function ( element ) { 4069 | 4070 | this.url = element.getAttribute('url').replace(/^#/, ''); 4071 | 4072 | return this; 4073 | 4074 | }; 4075 | 4076 | // Light 4077 | 4078 | function Light() { 4079 | 4080 | this.id = ""; 4081 | this.name = ""; 4082 | this.technique = ""; 4083 | 4084 | }; 4085 | 4086 | Light.prototype.parse = function ( element ) { 4087 | 4088 | this.id = element.getAttribute( 'id' ); 4089 | this.name = element.getAttribute( 'name' ); 4090 | 4091 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4092 | 4093 | var child = element.childNodes[ i ]; 4094 | if ( child.nodeType != 1 ) continue; 4095 | 4096 | switch ( child.nodeName ) { 4097 | 4098 | case 'technique_common': 4099 | this.parseTechnique( child ); 4100 | break; 4101 | 4102 | } 4103 | 4104 | } 4105 | 4106 | return this; 4107 | 4108 | }; 4109 | 4110 | Light.prototype.parseTechnique = function ( element ) { 4111 | 4112 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4113 | 4114 | var child = element.childNodes[ i ]; 4115 | 4116 | switch ( child.nodeName ) { 4117 | 4118 | case 'ambient': 4119 | case 'point': 4120 | case 'directional': 4121 | 4122 | this.technique = child.nodeName; 4123 | 4124 | for ( var k = 0; k < child.childNodes.length; k ++ ) { 4125 | 4126 | var param = child.childNodes[ k ]; 4127 | 4128 | switch ( param.nodeName ) { 4129 | 4130 | case 'color': 4131 | var vector = new THREE.Vector3().fromArray( _floats( param.textContent ) ); 4132 | this.color = new THREE.Color().setRGB( vector.x, vector.y, vector.z ); 4133 | break; 4134 | 4135 | } 4136 | 4137 | } 4138 | 4139 | break; 4140 | 4141 | } 4142 | 4143 | } 4144 | 4145 | }; 4146 | 4147 | function InstanceLight() { 4148 | 4149 | this.url = ""; 4150 | 4151 | }; 4152 | 4153 | InstanceLight.prototype.parse = function ( element ) { 4154 | 4155 | this.url = element.getAttribute('url').replace(/^#/, ''); 4156 | 4157 | return this; 4158 | 4159 | }; 4160 | 4161 | function _source( element ) { 4162 | 4163 | var id = element.getAttribute( 'id' ); 4164 | 4165 | if ( sources[ id ] != undefined ) { 4166 | 4167 | return sources[ id ]; 4168 | 4169 | } 4170 | 4171 | sources[ id ] = ( new Source(id )).parse( element ); 4172 | return sources[ id ]; 4173 | 4174 | }; 4175 | 4176 | function _nsResolver( nsPrefix ) { 4177 | 4178 | if ( nsPrefix == "dae" ) { 4179 | 4180 | return "http://www.collada.org/2005/11/COLLADASchema"; 4181 | 4182 | } 4183 | 4184 | return null; 4185 | 4186 | }; 4187 | 4188 | function _bools( str ) { 4189 | 4190 | var raw = _strings( str ); 4191 | var data = []; 4192 | 4193 | for ( var i = 0, l = raw.length; i < l; i ++ ) { 4194 | 4195 | data.push( (raw[i] == 'true' || raw[i] == '1') ? true : false ); 4196 | 4197 | } 4198 | 4199 | return data; 4200 | 4201 | }; 4202 | 4203 | function _floats( str ) { 4204 | 4205 | var raw = _strings(str); 4206 | var data = []; 4207 | 4208 | for ( var i = 0, l = raw.length; i < l; i ++ ) { 4209 | 4210 | data.push( parseFloat( raw[ i ] ) ); 4211 | 4212 | } 4213 | 4214 | return data; 4215 | 4216 | }; 4217 | 4218 | function _ints( str ) { 4219 | 4220 | var raw = _strings( str ); 4221 | var data = []; 4222 | 4223 | for ( var i = 0, l = raw.length; i < l; i ++ ) { 4224 | 4225 | data.push( parseInt( raw[ i ], 10 ) ); 4226 | 4227 | } 4228 | 4229 | return data; 4230 | 4231 | }; 4232 | 4233 | function _strings( str ) { 4234 | 4235 | return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; 4236 | 4237 | }; 4238 | 4239 | function _trimString( str ) { 4240 | 4241 | return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); 4242 | 4243 | }; 4244 | 4245 | function _attr_as_float( element, name, defaultValue ) { 4246 | 4247 | if ( element.hasAttribute( name ) ) { 4248 | 4249 | return parseFloat( element.getAttribute( name ) ); 4250 | 4251 | } else { 4252 | 4253 | return defaultValue; 4254 | 4255 | } 4256 | 4257 | }; 4258 | 4259 | function _attr_as_int( element, name, defaultValue ) { 4260 | 4261 | if ( element.hasAttribute( name ) ) { 4262 | 4263 | return parseInt( element.getAttribute( name ), 10) ; 4264 | 4265 | } else { 4266 | 4267 | return defaultValue; 4268 | 4269 | } 4270 | 4271 | }; 4272 | 4273 | function _attr_as_string( element, name, defaultValue ) { 4274 | 4275 | if ( element.hasAttribute( name ) ) { 4276 | 4277 | return element.getAttribute( name ); 4278 | 4279 | } else { 4280 | 4281 | return defaultValue; 4282 | 4283 | } 4284 | 4285 | }; 4286 | 4287 | function _format_float( f, num ) { 4288 | 4289 | if ( f === undefined ) { 4290 | 4291 | var s = '0.'; 4292 | 4293 | while ( s.length < num + 2 ) { 4294 | 4295 | s += '0'; 4296 | 4297 | } 4298 | 4299 | return s; 4300 | 4301 | } 4302 | 4303 | num = num || 2; 4304 | 4305 | var parts = f.toString().split( '.' ); 4306 | parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; 4307 | 4308 | while( parts[ 1 ].length < num ) { 4309 | 4310 | parts[ 1 ] += '0'; 4311 | 4312 | } 4313 | 4314 | return parts.join( '.' ); 4315 | 4316 | }; 4317 | 4318 | function evaluateXPath( node, query ) { 4319 | 4320 | var instances = COLLADA.evaluate( query, node, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); 4321 | 4322 | var inst = instances.iterateNext(); 4323 | var result = []; 4324 | 4325 | while ( inst ) { 4326 | 4327 | result.push( inst ); 4328 | inst = instances.iterateNext(); 4329 | 4330 | } 4331 | 4332 | return result; 4333 | 4334 | }; 4335 | 4336 | function extractDoubleSided( obj, element ) { 4337 | 4338 | obj.doubleSided = false; 4339 | 4340 | var node = COLLADA.evaluate( './/dae:extra//dae:double_sided', element, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); 4341 | 4342 | if ( node ) { 4343 | 4344 | node = node.iterateNext(); 4345 | 4346 | if ( node && parseInt( node.textContent, 10 ) === 1 ) { 4347 | 4348 | obj.doubleSided = true; 4349 | 4350 | } 4351 | 4352 | } 4353 | 4354 | }; 4355 | 4356 | // Up axis conversion 4357 | 4358 | function setUpConversion() { 4359 | 4360 | if ( !options.convertUpAxis || colladaUp === options.upAxis ) { 4361 | 4362 | upConversion = null; 4363 | 4364 | } else { 4365 | 4366 | switch ( colladaUp ) { 4367 | 4368 | case 'X': 4369 | 4370 | upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; 4371 | break; 4372 | 4373 | case 'Y': 4374 | 4375 | upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; 4376 | break; 4377 | 4378 | case 'Z': 4379 | 4380 | upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; 4381 | break; 4382 | 4383 | } 4384 | 4385 | } 4386 | 4387 | }; 4388 | 4389 | function fixCoords( data, sign ) { 4390 | 4391 | if ( !options.convertUpAxis || colladaUp === options.upAxis ) { 4392 | 4393 | return; 4394 | 4395 | } 4396 | 4397 | switch ( upConversion ) { 4398 | 4399 | case 'XtoY': 4400 | 4401 | var tmp = data[ 0 ]; 4402 | data[ 0 ] = sign * data[ 1 ]; 4403 | data[ 1 ] = tmp; 4404 | break; 4405 | 4406 | case 'XtoZ': 4407 | 4408 | var tmp = data[ 2 ]; 4409 | data[ 2 ] = data[ 1 ]; 4410 | data[ 1 ] = data[ 0 ]; 4411 | data[ 0 ] = tmp; 4412 | break; 4413 | 4414 | case 'YtoX': 4415 | 4416 | var tmp = data[ 0 ]; 4417 | data[ 0 ] = data[ 1 ]; 4418 | data[ 1 ] = sign * tmp; 4419 | break; 4420 | 4421 | case 'YtoZ': 4422 | 4423 | var tmp = data[ 1 ]; 4424 | data[ 1 ] = sign * data[ 2 ]; 4425 | data[ 2 ] = tmp; 4426 | break; 4427 | 4428 | case 'ZtoX': 4429 | 4430 | var tmp = data[ 0 ]; 4431 | data[ 0 ] = data[ 1 ]; 4432 | data[ 1 ] = data[ 2 ]; 4433 | data[ 2 ] = tmp; 4434 | break; 4435 | 4436 | case 'ZtoY': 4437 | 4438 | var tmp = data[ 1 ]; 4439 | data[ 1 ] = data[ 2 ]; 4440 | data[ 2 ] = sign * tmp; 4441 | break; 4442 | 4443 | } 4444 | 4445 | }; 4446 | 4447 | function getConvertedVec3( data, offset ) { 4448 | 4449 | var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; 4450 | fixCoords( arr, -1 ); 4451 | return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); 4452 | 4453 | }; 4454 | 4455 | function getConvertedMat4( data ) { 4456 | 4457 | if ( options.convertUpAxis ) { 4458 | 4459 | // First fix rotation and scale 4460 | 4461 | // Columns first 4462 | var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; 4463 | fixCoords( arr, -1 ); 4464 | data[ 0 ] = arr[ 0 ]; 4465 | data[ 4 ] = arr[ 1 ]; 4466 | data[ 8 ] = arr[ 2 ]; 4467 | arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; 4468 | fixCoords( arr, -1 ); 4469 | data[ 1 ] = arr[ 0 ]; 4470 | data[ 5 ] = arr[ 1 ]; 4471 | data[ 9 ] = arr[ 2 ]; 4472 | arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; 4473 | fixCoords( arr, -1 ); 4474 | data[ 2 ] = arr[ 0 ]; 4475 | data[ 6 ] = arr[ 1 ]; 4476 | data[ 10 ] = arr[ 2 ]; 4477 | // Rows second 4478 | arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; 4479 | fixCoords( arr, -1 ); 4480 | data[ 0 ] = arr[ 0 ]; 4481 | data[ 1 ] = arr[ 1 ]; 4482 | data[ 2 ] = arr[ 2 ]; 4483 | arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; 4484 | fixCoords( arr, -1 ); 4485 | data[ 4 ] = arr[ 0 ]; 4486 | data[ 5 ] = arr[ 1 ]; 4487 | data[ 6 ] = arr[ 2 ]; 4488 | arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; 4489 | fixCoords( arr, -1 ); 4490 | data[ 8 ] = arr[ 0 ]; 4491 | data[ 9 ] = arr[ 1 ]; 4492 | data[ 10 ] = arr[ 2 ]; 4493 | 4494 | // Now fix translation 4495 | arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; 4496 | fixCoords( arr, -1 ); 4497 | data[ 3 ] = arr[ 0 ]; 4498 | data[ 7 ] = arr[ 1 ]; 4499 | data[ 11 ] = arr[ 2 ]; 4500 | 4501 | } 4502 | 4503 | return new THREE.Matrix4( 4504 | data[0], data[1], data[2], data[3], 4505 | data[4], data[5], data[6], data[7], 4506 | data[8], data[9], data[10], data[11], 4507 | data[12], data[13], data[14], data[15] 4508 | ); 4509 | 4510 | }; 4511 | 4512 | function getConvertedIndex( index ) { 4513 | 4514 | if ( index > -1 && index < 3 ) { 4515 | 4516 | var members = ['X', 'Y', 'Z'], 4517 | indices = { X: 0, Y: 1, Z: 2 }; 4518 | 4519 | index = getConvertedMember( members[ index ] ); 4520 | index = indices[ index ]; 4521 | 4522 | } 4523 | 4524 | return index; 4525 | 4526 | }; 4527 | 4528 | function getConvertedMember( member ) { 4529 | 4530 | if ( options.convertUpAxis ) { 4531 | 4532 | switch ( member ) { 4533 | 4534 | case 'X': 4535 | 4536 | switch ( upConversion ) { 4537 | 4538 | case 'XtoY': 4539 | case 'XtoZ': 4540 | case 'YtoX': 4541 | 4542 | member = 'Y'; 4543 | break; 4544 | 4545 | case 'ZtoX': 4546 | 4547 | member = 'Z'; 4548 | break; 4549 | 4550 | } 4551 | 4552 | break; 4553 | 4554 | case 'Y': 4555 | 4556 | switch ( upConversion ) { 4557 | 4558 | case 'XtoY': 4559 | case 'YtoX': 4560 | case 'ZtoX': 4561 | 4562 | member = 'X'; 4563 | break; 4564 | 4565 | case 'XtoZ': 4566 | case 'YtoZ': 4567 | case 'ZtoY': 4568 | 4569 | member = 'Z'; 4570 | break; 4571 | 4572 | } 4573 | 4574 | break; 4575 | 4576 | case 'Z': 4577 | 4578 | switch ( upConversion ) { 4579 | 4580 | case 'XtoZ': 4581 | 4582 | member = 'X'; 4583 | break; 4584 | 4585 | case 'YtoZ': 4586 | case 'ZtoX': 4587 | case 'ZtoY': 4588 | 4589 | member = 'Y'; 4590 | break; 4591 | 4592 | } 4593 | 4594 | break; 4595 | 4596 | } 4597 | 4598 | } 4599 | 4600 | return member; 4601 | 4602 | }; 4603 | 4604 | return { 4605 | 4606 | load: load, 4607 | parse: parse, 4608 | setPreferredShading: setPreferredShading, 4609 | applySkin: applySkin, 4610 | geometries : geometries, 4611 | options: options 4612 | 4613 | }; 4614 | 4615 | }; 4616 | --------------------------------------------------------------------------------