1/** 2 * @author mrdoob / http://mrdoob.com/ 3 * @author Mugen87 / https://github.com/Mugen87 4 */ 5 6THREE.ColladaLoader = function ( manager ) { 7 8 this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 9 10}; 11 12THREE.ColladaLoader.prototype = { 13 14 constructor: THREE.ColladaLoader, 15 16 crossOrigin: 'anonymous', 17 18 load: function ( url, onLoad, onProgress, onError ) { 19 20 var scope = this; 21 22 var path = ( scope.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path; 23 24 var loader = new THREE.FileLoader( scope.manager ); 25 loader.setPath( scope.path ); 26 loader.load( url, function ( text ) { 27 28 onLoad( scope.parse( text, path ) ); 29 30 }, onProgress, onError ); 31 32 }, 33 34 setPath: function ( value ) { 35 36 this.path = value; 37 return this; 38 39 }, 40 41 setResourcePath: function ( value ) { 42 43 this.resourcePath = value; 44 return this; 45 46 }, 47 48 options: { 49 50 set convertUpAxis( value ) { 51 52 console.warn( 'THREE.ColladaLoader: options.convertUpAxis() has been removed. Up axis is converted automatically.' ); 53 54 } 55 56 }, 57 58 setCrossOrigin: function ( value ) { 59 60 this.crossOrigin = value; 61 return this; 62 63 }, 64 65 parse: function ( text, path ) { 66 67 function getElementsByTagName( xml, name ) { 68 69 // Non recursive xml.getElementsByTagName() ... 70 71 var array = []; 72 var childNodes = xml.childNodes; 73 74 for ( var i = 0, l = childNodes.length; i < l; i ++ ) { 75 76 var child = childNodes[ i ]; 77 78 if ( child.nodeName === name ) { 79 80 array.push( child ); 81 82 } 83 84 } 85 86 return array; 87 88 } 89 90 function parseStrings( text ) { 91 92 if ( text.length === 0 ) return []; 93 94 var parts = text.trim().split( /\s+/ ); 95 var array = new Array( parts.length ); 96 97 for ( var i = 0, l = parts.length; i < l; i ++ ) { 98 99 array[ i ] = parts[ i ]; 100 101 } 102 103 return array; 104 105 } 106 107 function parseFloats( text ) { 108 109 if ( text.length === 0 ) return []; 110 111 var parts = text.trim().split( /\s+/ ); 112 var array = new Array( parts.length ); 113 114 for ( var i = 0, l = parts.length; i < l; i ++ ) { 115 116 array[ i ] = parseFloat( parts[ i ] ); 117 118 } 119 120 return array; 121 122 } 123 124 function parseInts( text ) { 125 126 if ( text.length === 0 ) return []; 127 128 var parts = text.trim().split( /\s+/ ); 129 var array = new Array( parts.length ); 130 131 for ( var i = 0, l = parts.length; i < l; i ++ ) { 132 133 array[ i ] = parseInt( parts[ i ] ); 134 135 } 136 137 return array; 138 139 } 140 141 function parseId( text ) { 142 143 return text.substring( 1 ); 144 145 } 146 147 function generateId() { 148 149 return 'three_default_' + ( count ++ ); 150 151 } 152 153 function isEmpty( object ) { 154 155 return Object.keys( object ).length === 0; 156 157 } 158 159 // asset 160 161 function parseAsset( xml ) { 162 163 return { 164 unit: parseAssetUnit( getElementsByTagName( xml, 'unit' )[ 0 ] ), 165 upAxis: parseAssetUpAxis( getElementsByTagName( xml, 'up_axis' )[ 0 ] ) 166 }; 167 168 } 169 170 function parseAssetUnit( xml ) { 171 172 if ( ( xml !== undefined ) && ( xml.hasAttribute( 'meter' ) === true ) ) { 173 174 return parseFloat( xml.getAttribute( 'meter' ) ); 175 176 } else { 177 178 return 1; // default 1 meter 179 180 } 181 182 } 183 184 function parseAssetUpAxis( xml ) { 185 186 return xml !== undefined ? xml.textContent : 'Y_UP'; 187 188 } 189 190 // library 191 192 function parseLibrary( xml, libraryName, nodeName, parser ) { 193 194 var library = getElementsByTagName( xml, libraryName )[ 0 ]; 195 196 if ( library !== undefined ) { 197 198 var elements = getElementsByTagName( library, nodeName ); 199 200 for ( var i = 0; i < elements.length; i ++ ) { 201 202 parser( elements[ i ] ); 203 204 } 205 206 } 207 208 } 209 210 function buildLibrary( data, builder ) { 211 212 for ( var name in data ) { 213 214 var object = data[ name ]; 215 object.build = builder( data[ name ] ); 216 217 } 218 219 } 220 221 // get 222 223 function getBuild( data, builder ) { 224 225 if ( data.build !== undefined ) return data.build; 226 227 data.build = builder( data ); 228 229 return data.build; 230 231 } 232 233 // animation 234 235 function parseAnimation( xml ) { 236 237 var data = { 238 sources: {}, 239 samplers: {}, 240 channels: {} 241 }; 242 243 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 244 245 var child = xml.childNodes[ i ]; 246 247 if ( child.nodeType !== 1 ) continue; 248 249 var id; 250 251 switch ( child.nodeName ) { 252 253 case 'source': 254 id = child.getAttribute( 'id' ); 255 data.sources[ id ] = parseSource( child ); 256 break; 257 258 case 'sampler': 259 id = child.getAttribute( 'id' ); 260 data.samplers[ id ] = parseAnimationSampler( child ); 261 break; 262 263 case 'channel': 264 id = child.getAttribute( 'target' );
265 data.channels[ id ] = parseAnimationChannel( child ); 266 break; 267 268 default: 269 console.log( child ); 270 271 } 272 273 } 274 275 library.animations[ xml.getAttribute( 'id' ) ] = data; 276 277 } 278 279 function parseAnimationSampler( xml ) { 280 281 var data = { 282 inputs: {}, 283 }; 284 285 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 286 287 var child = xml.childNodes[ i ]; 288 289 if ( child.nodeType !== 1 ) continue; 290 291 switch ( child.nodeName ) { 292 293 case 'input': 294 var id = parseId( child.getAttribute( 'source' ) ); 295 var semantic = child.getAttribute( 'semantic' ); 296 data.inputs[ semantic ] = id; 297 break; 298 299 } 300 301 } 302 303 return data; 304 305 } 306 307 function parseAnimationChannel( xml ) { 308 309 var data = {}; 310 311 var target = xml.getAttribute( 'target' ); 312 313 // parsing SID Addressing Syntax 314 315 var parts = target.split( '/' ); 316 317 var id = parts.shift(); 318 var sid = parts.shift(); 319 320 // check selection syntax 321 322 var arraySyntax = ( sid.indexOf( '(' ) !== - 1 ); 323 var memberSyntax = ( sid.indexOf( '.' ) !== - 1 ); 324 325 if ( memberSyntax ) { 326 327 // member selection access 328 329 parts = sid.split( '.' ); 330 sid = parts.shift(); 331 data.member = parts.shift(); 332 333 } else if ( arraySyntax ) { 334 335 // array-access syntax. can be used to express fields in one-dimensional vectors or two-dimensional matrices. 336 337 var indices = sid.split( '(' ); 338 sid = indices.shift(); 339 340 for ( var i = 0; i < indices.length; i ++ ) { 341 342 indices[ i ] = parseInt( indices[ i ].replace( /\)/, '' ) ); 343 344 } 345 346 data.indices = indices; 347 348 } 349 350 data.id = id; 351 data.sid = sid; 352 353 data.arraySyntax = arraySyntax; 354 data.memberSyntax = memberSyntax; 355 356 data.sampler = parseId( xml.getAttribute( 'source' ) ); 357 358 return data; 359 360 } 361 362 function buildAnimation( data ) { 363 364 var tracks = []; 365 366 var channels = data.channels; 367 var samplers = data.samplers; 368 var sources = data.sources; 369 370 for ( var target in channels ) { 371 372 if ( channels.hasOwnProperty( target ) ) { 373 374 var channel = channels[ target ]; 375 var sampler = samplers[ channel.sampler ]; 376 377 var inputId = sampler.inputs.INPUT; 378 var outputId = sampler.inputs.OUTPUT; 379 380 var inputSource = sources[ inputId ]; 381 var outputSource = sources[ outputId ]; 382 383 var animation = buildAnimationChannel( channel, inputSource, outputSource ); 384 385 createKeyframeTracks( animation, tracks ); 386 387 } 388 389 } 390 391 return tracks; 392 393 } 394 395 function getAnimation( id ) { 396 397 return getBuild( library.animations[ id ], buildAnimation ); 398 399 } 400 401 function buildAnimationChannel( channel, inputSource, outputSource ) { 402 403 var node = library.nodes[ channel.id ]; 404 var object3D = getNode( node.id ); 405 406 var transform = node.transforms[ channel.sid ]; 407 var defaultMatrix = node.matrix.clone().transpose(); 408 409 var time, stride; 410 var i, il, j, jl; 411 412 var data = {}; 413 414 // the collada spec allows the animation of data in various ways. 415 // depending on the transform type (matrix, translate, rotate, scale), we execute different logic 416 417 switch ( transform ) { 418 419 case 'matrix': 420 421 for ( i = 0, il = inputSource.array.length; i < il; i ++ ) { 422 423 time = inputSource.array[ i ]; 424 stride = i * outputSource.stride; 425 426 if ( data[ time ] === undefined ) data[ time ] = {}; 427 428 if ( channel.arraySyntax === true ) { 429 430 var value = outputSource.array[ stride ]; 431 var index = channel.indices[ 0 ] + 4 * channel.indices[ 1 ]; 432 433 data[ time ][ index ] = value; 434 435 } else { 436 437 for ( j = 0, jl = outputSource.stride; j < jl; j ++ ) { 438 439 data[ time ][ j ] = outputSource.array[ stride + j ]; 440 441 } 442 443 } 444 445 } 446 447 break; 448 449 case 'translate': 450 console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform ); 451 break; 452 453 case 'rotate': 454 console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform ); 455 break; 456 457 case 'scale': 458 console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform ); 459 break; 460 461 } 462 463 var keyframes = prepareAnimationData( data, defaultMatrix ); 464 465 var animation = { 466 name: object3D.uuid, 467 keyframes: keyframes 468 }; 469 470 return animation; 471 472 } 473 474 function prepareAnimationData( data, defaultMatrix ) { 475 476 var keyframes = []; 477 478 // transfer data into a sortable array 479 480 for ( var time in data ) { 481 482 keyframes.push( { time: parseFloat( time ), value: data[ time ] } ); 483 484 } 485 486 // ensure keyframes are sorted by time 487 488 keyframes.sort( ascending ); 489 490 // now we clean up all animation data, so we can use them for keyframe tracks 491 492 for ( var i = 0; i < 16; i ++ ) { 493 494 transformAnimationData( keyframes, i, defaultMatrix.elements[ i ] ); 495 496 } 497 498 return keyframes; 499 500 // array sort function 501 502 function ascending( a, b ) { 503 504 return a.time - b.time; 505 506 } 507 508 } 509 510 var position = new THREE.Vector3(); 511 var scale = new THREE.Vector3(); 512 var quaternion = new THREE.Quaternion(); 513 514 function createKeyframeTracks( animation, tracks ) { 515 516 var keyframes = animation.keyframes; 517 var name = animation.name; 518 519 var times = []; 520 var positionData = []; 521 var quaternionData = []; 522 var scaleData = []; 523
524 for ( var i = 0, l = keyframes.length; i < l; i ++ ) { 525 526 var keyframe = keyframes[ i ]; 527 528 var time = keyframe.time; 529 var value = keyframe.value; 530 531 matrix.fromArray( value ).transpose(); 532 matrix.decompose( position, quaternion, scale ); 533 534 times.push( time ); 535 positionData.push( position.x, position.y, position.z ); 536 quaternionData.push( quaternion.x, quaternion.y, quaternion.z, quaternion.w ); 537 scaleData.push( scale.x, scale.y, scale.z ); 538 539 } 540 541 if ( positionData.length > 0 ) tracks.push( new THREE.VectorKeyframeTrack( name + '.position', times, positionData ) ); 542 if ( quaternionData.length > 0 ) tracks.push( new THREE.QuaternionKeyframeTrack( name + '.quaternion', times, quaternionData ) ); 543 if ( scaleData.length > 0 ) tracks.push( new THREE.VectorKeyframeTrack( name + '.scale', times, scaleData ) ); 544 545 return tracks; 546 547 } 548 549 function transformAnimationData( keyframes, property, defaultValue ) { 550 551 var keyframe; 552 553 var empty = true; 554 var i, l; 555 556 // check, if values of a property are missing in our keyframes 557 558 for ( i = 0, l = keyframes.length; i < l; i ++ ) { 559 560 keyframe = keyframes[ i ]; 561 562 if ( keyframe.value[ property ] === undefined ) { 563 564 keyframe.value[ property ] = null; // mark as missing 565 566 } else { 567 568 empty = false; 569 570 } 571 572 } 573 574 if ( empty === true ) { 575 576 // no values at all, so we set a default value 577 578 for ( i = 0, l = keyframes.length; i < l; i ++ ) { 579 580 keyframe = keyframes[ i ]; 581 582 keyframe.value[ property ] = defaultValue; 583 584 } 585 586 } else { 587 588 // filling gaps 589 590 createMissingKeyframes( keyframes, property ); 591 592 } 593 594 } 595 596 function createMissingKeyframes( keyframes, property ) { 597 598 var prev, next; 599 600 for ( var i = 0, l = keyframes.length; i < l; i ++ ) { 601 602 var keyframe = keyframes[ i ]; 603 604 if ( keyframe.value[ property ] === null ) { 605 606 prev = getPrev( keyframes, i, property ); 607 next = getNext( keyframes, i, property ); 608 609 if ( prev === null ) { 610 611 keyframe.value[ property ] = next.value[ property ]; 612 continue; 613 614 } 615 616 if ( next === null ) { 617 618 keyframe.value[ property ] = prev.value[ property ]; 619 continue; 620 621 } 622 623 interpolate( keyframe, prev, next, property ); 624 625 } 626 627 } 628 629 } 630 631 function getPrev( keyframes, i, property ) { 632 633 while ( i >= 0 ) { 634 635 var keyframe = keyframes[ i ]; 636 637 if ( keyframe.value[ property ] !== null ) return keyframe; 638 639 i --; 640 641 } 642 643 return null; 644 645 } 646 647 function getNext( keyframes, i, property ) { 648 649 while ( i < keyframes.length ) { 650 651 var keyframe = keyframes[ i ]; 652 653 if ( keyframe.value[ property ] !== null ) return keyframe; 654 655 i ++; 656 657 } 658 659 return null; 660 661 } 662 663 function interpolate( key, prev, next, property ) { 664 665 if ( ( next.time - prev.time ) === 0 ) { 666 667 key.value[ property ] = prev.value[ property ]; 668 return; 669 670 } 671 672 key.value[ property ] = ( ( key.time - prev.time ) * ( next.value[ property ] - prev.value[ property ] ) / ( next.time - prev.time ) ) + prev.value[ property ]; 673 674 } 675 676 // animation clips 677 678 function parseAnimationClip( xml ) { 679 680 var data = { 681 name: xml.getAttribute( 'id' ) || 'default', 682 start: parseFloat( xml.getAttribute( 'start' ) || 0 ), 683 end: parseFloat( xml.getAttribute( 'end' ) || 0 ), 684 animations: [] 685 }; 686 687 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 688 689 var child = xml.childNodes[ i ]; 690 691 if ( child.nodeType !== 1 ) continue; 692 693 switch ( child.nodeName ) { 694 695 case 'instance_animation': 696 data.animations.push( parseId( child.getAttribute( 'url' ) ) ); 697 break; 698 699 } 700 701 } 702 703 library.clips[ xml.getAttribute( 'id' ) ] = data; 704 705 } 706 707 function buildAnimationClip( data ) { 708 709 var tracks = []; 710 711 var name = data.name; 712 var duration = ( data.end - data.start ) || - 1; 713 var animations = data.animations; 714 715 for ( var i = 0, il = animations.length; i < il; i ++ ) { 716 717 var animationTracks = getAnimation( animations[ i ] ); 718 719 for ( var j = 0, jl = animationTracks.length; j < jl; j ++ ) { 720 721 tracks.push( animationTracks[ j ] ); 722 723 } 724 725 } 726 727 return new THREE.AnimationClip( name, duration, tracks ); 728 729 } 730 731 function getAnimationClip( id ) { 732 733 return getBuild( library.clips[ id ], buildAnimationClip ); 734 735 } 736 737 // controller 738 739 function parseController( xml ) { 740 741 var data = {}; 742 743 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 744 745 var child = xml.childNodes[ i ]; 746 747 if ( child.nodeType !== 1 ) continue; 748 749 switch ( child.nodeName ) { 750 751 case 'skin': 752 // there is exactly one skin per controller 753 data.id = parseId( child.getAttribute( 'source' ) ); 754 data.skin = parseSkin( child ); 755 break; 756 757 case 'morph': 758 data.id = parseId( child.getAttribute( 'source' ) ); 759 console.warn( 'THREE.ColladaLoader: Morph target animation not supported yet.' ); 760 break; 761 762 } 763 764 } 765 766 library.controllers[ xml.getAttribute( 'id' ) ] = data; 767 768 } 769 770 function parseSkin( xml ) { 771 772 var data = { 773 sources: {} 774 }; 775 776 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 777 778 var child = xml.childNodes[ i ]; 779 780 if ( child.nodeType !== 1 ) continue; 781 782 switch ( child.nodeName ) { 783 784 case 'bind_shape_matrix': 785 data.bindShapeMatrix = parseFloats( child.textContent ); 786 break; 787 788 case 'source': 789 var id = child.getAttribute( 'id' ); 790 data.sources[ id ] = parseSource( child ); 791 break; 792 793 case 'joints': 794 data.joints = parseJoints( child ); 795 break; 796 797 case 'vertex_weights': 798 data.vertexWeights = parseVertexWeights( child ); 799 break; 800 801 } 802 803 } 804 805 return data; 806 807 } 808 809 function parseJoints( xml ) { 810 811 var data = { 812 inputs: {} 813 }; 814 815 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 816 817 var child = xml.childNodes[ i ]; 818 819 if ( child.nodeType !== 1 ) continue; 820 821 switch ( child.nodeName ) { 822 823 case 'input': 824 var semantic = child.getAttribute( 'semantic' ); 825 var id = parseId( child.getAttribute( 'source' ) ); 826 data.inputs[ semantic ] = id; 827 break; 828 829 } 830 831 } 832 833 return data; 834 835 } 836 837 function parseVertexWeights( xml ) { 838 839 var data = { 840 inputs: {} 841 }; 842 843 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 844 845 var child = xml.childNodes[ i ]; 846 847 if ( child.nodeType !== 1 ) continue; 848 849 switch ( child.nodeName ) { 850 851 case 'input': 852 var semantic = child.getAttribute( 'semantic' ); 853 var id = parseId( child.getAttribute( 'source' ) ); 854 var offset = parseInt( child.getAttribute( 'offset' ) ); 855 data.inputs[ semantic ] = { id: id, offset: offset }; 856 break; 857 858 case 'vcount': 859 data.vcount = parseInts( child.textContent ); 860 break; 861 862 case 'v': 863 data.v = parseInts( child.textContent ); 864 break; 865 866 } 867 868 } 869 870 return data; 871 872 } 873 874 function buildController( data ) { 875 876 var build = { 877 id: data.id 878 }; 879 880 var geometry = library.geometries[ build.id ]; 881 882 if ( data.skin !== undefined ) { 883 884 build.skin = buildSkin( data.skin ); 885 886 // we enhance the 'sources' property of the corresponding geometry with our skin data 887 888 geometry.sources.skinIndices = build.skin.indices; 889 geometry.sources.skinWeights = build.skin.weights; 890 891 } 892 893 return build; 894 895 } 896 897 function buildSkin( data ) { 898 899 var BONE_LIMIT = 4; 900 901 var build = { 902 joints: [], // this must be an array to preserve the joint order 903 indices: { 904 array: [], 905 stride: BONE_LIMIT 906 }, 907 weights: { 908 array: [], 909 stride: BONE_LIMIT 910 } 911 }; 912 913 var sources = data.sources; 914 var vertexWeights = data.vertexWeights; 915 916 var vcount = vertexWeights.vcount; 917 var v = vertexWeights.v; 918 var jointOffset = vertexWeights.inputs.JOINT.offset; 919 var weightOffset = vertexWeights.inputs.WEIGHT.offset; 920 921 var jointSource = data.sources[ data.joints.inputs.JOINT ]; 922 var inverseSource = data.sources[ data.joints.inputs.INV_BIND_MATRIX ]; 923 924 var weights = sources[ vertexWeights.inputs.WEIGHT.id ].array; 925 var stride = 0; 926 927 var i, j, l; 928 929 // procces skin data for each vertex 930 931 for ( i = 0, l = vcount.length; i < l; i ++ ) { 932 933 var jointCount = vcount[ i ]; // this is the amount of joints that affect a single vertex 934 var vertexSkinData = []; 935 936 for ( j = 0; j < jointCount; j ++ ) { 937 938 var skinIndex = v[ stride + jointOffset ]; 939 var weightId = v[ stride + weightOffset ]; 940 var skinWeight = weights[ weightId ]; 941 942 vertexSkinData.push( { index: skinIndex, weight: skinWeight } ); 943 944 stride += 2; 945 946 } 947 948 // we sort the joints in descending order based on the weights. 949 // this ensures, we only procced the most important joints of the vertex 950
951 vertexSkinData.sort( descending ); 952 953 // now we provide for each vertex a set of four index and weight values. 954 // the order of the skin data matches the order of vertices 955 956 for ( j = 0; j < BONE_LIMIT; j ++ ) { 957 958 var d = vertexSkinData[ j ]; 959 960 if ( d !== undefined ) { 961 962 build.indices.array.push( d.index ); 963 build.weights.array.push( d.weight ); 964 965 } else { 966 967 build.indices.array.push( 0 ); 968 build.weights.array.push( 0 ); 969 970 } 971 972 } 973 974 } 975 976 // setup bind matrix 977 978 if ( data.bindShapeMatrix ) { 979 980 build.bindMatrix = new THREE.Matrix4().fromArray( data.bindShapeMatrix ).transpose(); 981 982 } else { 983 984 build.bindMatrix = new THREE.Matrix4().identity(); 985 986 } 987 988 // process bones and inverse bind matrix data 989 990 for ( i = 0, l = jointSource.array.length; i < l; i ++ ) { 991 992 var name = jointSource.array[ i ]; 993 var boneInverse = new THREE.Matrix4().fromArray( inverseSource.array, i * inverseSource.stride ).transpose(); 994 995 build.joints.push( { name: name, boneInverse: boneInverse } ); 996 997 } 998 999 return build; 1000 1001 // array sort function 1002 1003 function descending( a, b ) { 1004 1005 return b.weight - a.weight; 1006 1007 } 1008 1009 } 1010 1011 function getController( id ) { 1012 1013 return getBuild( library.controllers[ id ], buildController ); 1014 1015 } 1016 1017 // image 1018 1019 function parseImage( xml ) { 1020 1021 var data = { 1022 init_from: getElementsByTagName( xml, 'init_from' )[ 0 ].textContent 1023 }; 1024 1025 library.images[ xml.getAttribute( 'id' ) ] = data; 1026 1027 } 1028 1029 function buildImage( data ) { 1030 1031 if ( data.build !== undefined ) return data.build; 1032 1033 return data.init_from; 1034 1035 } 1036 1037 function getImage( id ) { 1038 1039 var data = library.images[ id ]; 1040 1041 if ( data !== undefined ) { 1042 1043 return getBuild( data, buildImage ); 1044 1045 } 1046 1047 console.warn( 'THREE.ColladaLoader: Couldn\'t find image with ID:', id ); 1048 1049 return null; 1050 1051 } 1052 1053 // effect 1054 1055 function parseEffect( xml ) { 1056 1057 var data = {}; 1058 1059 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1060 1061 var child = xml.childNodes[ i ]; 1062 1063 if ( child.nodeType !== 1 ) continue; 1064 1065 switch ( child.nodeName ) { 1066 1067 case 'profile_COMMON': 1068 data.profile = parseEffectProfileCOMMON( child ); 1069 break; 1070 1071 } 1072 1073 } 1074 1075 library.effects[ xml.getAttribute( 'id' ) ] = data; 1076 1077 } 1078 1079 function parseEffectProfileCOMMON( xml ) { 1080 1081 var data = { 1082 surfaces: {}, 1083 samplers: {} 1084 }; 1085 1086 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1087 1088 var child = xml.childNodes[ i ]; 1089 1090 if ( child.nodeType !== 1 ) continue; 1091 1092 switch ( child.nodeName ) { 1093 1094 case 'newparam': 1095 parseEffectNewparam( child, data ); 1096 break; 1097 1098 case 'technique': 1099 data.technique = parseEffectTechnique( child ); 1100 break; 1101 1102 case 'extra': 1103 data.extra = parseEffectExtra( child ); 1104 break; 1105 1106 } 1107 1108 } 1109 1110 return data; 1111 1112 } 1113 1114 function parseEffectNewparam( xml, data ) { 1115 1116 var sid = xml.getAttribute( 'sid' ); 1117 1118 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1119 1120 var child = xml.childNodes[ i ]; 1121 1122 if ( child.nodeType !== 1 ) continue; 1123 1124 switch ( child.nodeName ) { 1125 1126 case 'surface': 1127 data.surfaces[ sid ] = parseEffectSurface( child ); 1128 break; 1129 1130 case 'sampler2D': 1131 data.samplers[ sid ] = parseEffectSampler( child ); 1132 break; 1133 1134 } 1135 1136 } 1137 1138 } 1139 1140 function parseEffectSurface( xml ) { 1141 1142 var data = {}; 1143 1144 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1145 1146 var child = xml.childNodes[ i ]; 1147 1148 if ( child.nodeType !== 1 ) continue; 1149 1150 switch ( child.nodeName ) { 1151 1152 case 'init_from': 1153 data.init_from = child.textContent; 1154 break; 1155 1156 } 1157 1158 } 1159 1160 return data; 1161 1162 } 1163 1164 function parseEffectSampler( xml ) { 1165 1166 var data = {}; 1167 1168 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1169 1170 var child = xml.childNodes[ i ]; 1171 1172 if ( child.nodeType !== 1 ) continue; 1173 1174 switch ( child.nodeName ) { 1175 1176 case 'source': 1177 data.source = child.textContent; 1178 break; 1179 1180 } 1181 1182 } 1183 1184 return data; 1185 1186 } 1187 1188 function parseEffectTechnique( xml ) { 1189 1190 var data = {}; 1191 1192 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1193 1194 var child = xml.childNodes[ i ]; 1195 1196 if ( child.nodeType !== 1 ) continue; 1197 1198 switch ( child.nodeName ) { 1199 1200 case 'constant': 1201 case 'lambert': 1202 case 'blinn': 1203 case 'phong': 1204 data.type = child.nodeName; 1205 data.parameters = parseEffectParameters( child ); 1206 break; 1207 1208 } 1209 1210 } 1211 1212 return data; 1213 1214 } 1215 1216 function parseEffectParameters( xml ) { 1217 1218 var data = {}; 1219 1220 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1221 1222 var child = xml.childNodes[ i ]; 1223 1224 if ( child.nodeType !== 1 ) continue; 1225 1226 switch ( child.nodeName ) { 1227 1228 case 'emission': 1229 case 'diffuse': 1230 case 'specular': 1231 case 'bump': 1232 case 'ambient': 1233 case 'shininess': 1234 case 'transparency': 1235 data[ child.nodeName ] = parseEffectParameter( child ); 1236 break; 1237 case 'transparent': 1238 data[ child.nodeName ] = { 1239 opaque: child.getAttribute( 'opaque' ), 1240 data: parseEffectParameter( child ) 1241 }; 1242 break; 1243 1244 } 1245 1246 } 1247 1248 return data; 1249 1250 } 1251 1252 function parseEffectParameter( xml ) { 1253 1254 var data = {}; 1255 1256 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1257 1258 var child = xml.childNodes[ i ]; 1259 1260 if ( child.nodeType !== 1 ) continue; 1261 1262 switch ( child.nodeName ) { 1263 1264 case 'color': 1265 data[ child.nodeName ] = parseFloats( child.textContent ); 1266 break; 1267 1268 case 'float': 1269 data[ child.nodeName ] = parseFloat( child.textContent ); 1270 break; 1271 1272 case 'texture': 1273 data[ child.nodeName ] = { id: child.getAttribute( 'texture' ), extra: parseEffectParameterTexture( child ) }; 1274 break; 1275 1276 } 1277 1278 } 1279 1280 return data; 1281 1282 } 1283 1284 function parseEffectParameterTexture( xml ) { 1285 1286 var data = { 1287 technique: {} 1288 }; 1289 1290 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1291 1292 var child = xml.childNodes[ i ]; 1293 1294 if ( child.nodeType !== 1 ) continue; 1295 1296 switch ( child.nodeName ) { 1297 1298 case 'extra': 1299 parseEffectParameterTextureExtra( child, data ); 1300 break; 1301 1302 } 1303 1304 } 1305 1306 return data; 1307 1308 } 1309 1310 function parseEffectParameterTextureExtra( xml, data ) { 1311 1312 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1313 1314 var child = xml.childNodes[ i ]; 1315 1316 if ( child.nodeType !== 1 ) continue; 1317 1318 switch ( child.nodeName ) { 1319 1320 case 'technique': 1321 parseEffectParameterTextureExtraTechnique( child, data ); 1322 break; 1323 1324 } 1325 1326 } 1327 1328 } 1329 1330 function parseEffectParameterTextureExtraTechnique( xml, data ) { 1331 1332 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1333 1334 var child = xml.childNodes[ i ]; 1335 1336 if ( child.nodeType !== 1 ) continue; 1337 1338 switch ( child.nodeName ) { 1339 1340 case 'repeatU': 1341 case 'repeatV': 1342 case 'offsetU': 1343 case 'offsetV': 1344 data.technique[ child.nodeName ] = parseFloat( child.textContent ); 1345 break; 1346 1347 case 'wrapU': 1348 case 'wrapV': 1349 1350 // some files have values for wrapU/wrapV which become NaN via parseInt 1351 1352 if ( child.textContent.toUpperCase() === 'TRUE' ) { 1353 1354 data.technique[ child.nodeName ] = 1; 1355 1356 } else if ( child.textContent.toUpperCase() === 'FALSE' ) { 1357 1358 data.technique[ child.nodeName ] = 0; 1359 1360 } else { 1361 1362 data.technique[ child.nodeName ] = parseInt( child.textContent ); 1363 1364 } 1365 1366 break; 1367 1368 } 1369 1370 } 1371 1372 } 1373 1374 function parseEffectExtra( xml ) { 1375 1376 var data = {}; 1377 1378 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1379 1380 var child = xml.childNodes[ i ]; 1381 1382 if ( child.nodeType !== 1 ) continue; 1383 1384 switch ( child.nodeName ) { 1385 1386 case 'technique': 1387 data.technique = parseEffectExtraTechnique( child ); 1388 break; 1389 1390 } 1391 1392 } 1393 1394 return data; 1395 1396 } 1397 1398 function parseEffectExtraTechnique( xml ) { 1399 1400 var data = {}; 1401 1402 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1403 1404 var child = xml.childNodes[ i ]; 1405 1406 if ( child.nodeType !== 1 ) continue; 1407 1408 switch ( child.nodeName ) { 1409 1410 case 'double_sided': 1411 data[ child.nodeName ] = parseInt( child.textContent ); 1412 break; 1413 1414 } 1415 1416 } 1417 1418 return data; 1419 1420 } 1421 1422 function buildEffect( data ) { 1423 1424 return data; 1425 1426 } 1427 1428 function getEffect( id ) { 1429 1430 return getBuild( library.effects[ id ], buildEffect ); 1431 1432 } 1433 1434 // material 1435 1436 function parseMaterial( xml ) { 1437 1438 var data = {
1439 name: xml.getAttribute( 'name' ) 1440 }; 1441 1442 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1443 1444 var child = xml.childNodes[ i ]; 1445 1446 if ( child.nodeType !== 1 ) continue; 1447 1448 switch ( child.nodeName ) { 1449 1450 case 'instance_effect': 1451 data.url = parseId( child.getAttribute( 'url' ) ); 1452 break; 1453 1454 } 1455 1456 } 1457 1458 library.materials[ xml.getAttribute( 'id' ) ] = data; 1459 1460 } 1461 1462 function getTextureLoader( image ) { 1463 1464 var loader; 1465 1466 var extension = image.slice( ( image.lastIndexOf( '.' ) - 1 >>> 0 ) + 2 ); // http://www.jstips.co/en/javascript/get-file-extension/ 1467 extension = extension.toLowerCase(); 1468 1469 switch ( extension ) { 1470 1471 case 'tga': 1472 loader = tgaLoader; 1473 break; 1474 1475 default: 1476 loader = textureLoader; 1477 1478 } 1479 1480 return loader; 1481 1482 } 1483 1484 function buildMaterial( data ) { 1485 1486 var effect = getEffect( data.url ); 1487 var technique = effect.profile.technique; 1488 var extra = effect.profile.extra; 1489 1490 var material; 1491 1492 switch ( technique.type ) { 1493 1494 case 'phong': 1495 case 'blinn': 1496 material = new THREE.MeshPhongMaterial(); 1497 break; 1498 1499 case 'lambert': 1500 material = new THREE.MeshLambertMaterial(); 1501 break; 1502 1503 default: 1504 material = new THREE.MeshBasicMaterial(); 1505 break; 1506 1507 } 1508 1509 material.name = data.name || ''; 1510 1511 function getTexture( textureObject ) { 1512 1513 var sampler = effect.profile.samplers[ textureObject.id ]; 1514 var image = null; 1515 1516 // get image 1517 1518 if ( sampler !== undefined ) { 1519 1520 var surface = effect.profile.surfaces[ sampler.source ]; 1521 image = getImage( surface.init_from ); 1522 1523 } else { 1524 1525 console.warn( 'THREE.ColladaLoader: Undefined sampler. Access image directly (see #12530).' ); 1526 image = getImage( textureObject.id ); 1527 1528 } 1529 1530 // create texture if image is avaiable 1531 1532 if ( image !== null ) { 1533 1534 var loader = getTextureLoader( image ); 1535 1536 if ( loader !== undefined ) { 1537 1538 var texture = loader.load( image ); 1539 1540 var extra = textureObject.extra; 1541 1542 if ( extra !== undefined && extra.technique !== undefined && isEmpty( extra.technique ) === false ) { 1543 1544 var technique = extra.technique; 1545 1546 texture.wrapS = technique.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; 1547 texture.wrapT = technique.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; 1548 1549 texture.offset.set( technique.offsetU || 0, technique.offsetV || 0 ); 1550 texture.repeat.set( technique.repeatU || 1, technique.repeatV || 1 ); 1551 1552 } else { 1553 1554 texture.wrapS = THREE.RepeatWrapping; 1555 texture.wrapT = THREE.RepeatWrapping; 1556 1557 } 1558 1559 return texture; 1560 1561 } else { 1562 1563 console.warn( 'THREE.ColladaLoader: Loader for texture %s not found.', image ); 1564 1565 return null; 1566 1567 } 1568 1569 } else { 1570 1571 console.warn( 'THREE.ColladaLoader: Couldn\'t create texture with ID:', textureObject.id ); 1572 1573 return null; 1574 1575 } 1576 1577 } 1578 1579 var parameters = technique.parameters; 1580 1581 for ( var key in parameters ) { 1582 1583 var parameter = parameters[ key ]; 1584 1585 switch ( key ) { 1586 1587 case 'diffuse': 1588 if ( parameter.color ) material.color.fromArray( parameter.color ); 1589 if ( parameter.texture ) material.map = getTexture( parameter.texture ); 1590 break; 1591 case 'specular': 1592 if ( parameter.color && material.specular ) material.specular.fromArray( parameter.color ); 1593 if ( parameter.texture ) material.specularMap = getTexture( parameter.texture ); 1594 break; 1595 case 'bump': 1596 if ( parameter.texture ) material.normalMap = getTexture( parameter.texture ); 1597 break; 1598 case 'ambient': 1599 if ( parameter.texture ) material.lightMap = getTexture( parameter.texture ); 1600 break; 1601 case 'shininess': 1602 if ( parameter.float && material.shininess ) material.shininess = parameter.float; 1603 break; 1604 case 'emission': 1605 if ( parameter.color && material.emissive ) material.emissive.fromArray( parameter.color ); 1606 if ( parameter.texture ) material.emissiveMap = getTexture( parameter.texture ); 1607 break; 1608 1609 } 1610 1611 } 1612 1613 // 1614 1615 var transparent = parameters[ 'transparent' ]; 1616 var transparency = parameters[ 'transparency' ]; 1617 1618 // <transparency> does not exist but <transparent> 1619 1620 if ( transparency === undefined && transparent ) { 1621 1622 transparency = { 1623 float: 1 1624 }; 1625 1626 } 1627 1628 // <transparent> does not exist but <transparency> 1629 1630 if ( transparent === undefined && transparency ) { 1631 1632 transparent = { 1633 opaque: 'A_ONE', 1634 data: { 1635 color: [ 1, 1, 1, 1 ] 1636 } }; 1637 1638 } 1639 1640 if ( transparent && transparency ) { 1641 1642 // handle case if a texture exists but no color 1643 1644 if ( transparent.data.texture ) { 1645 1646 // we do not set an alpha map (see #13792) 1647 1648 material.transparent = true; 1649 1650 } else { 1651 1652 var color = transparent.data.color; 1653 1654 switch ( transparent.opaque ) { 1655 1656 case 'A_ONE': 1657 material.opacity = color[ 3 ] * transparency.float; 1658 break; 1659 case 'RGB_ZERO': 1660 material.opacity = 1 - ( color[ 0 ] * transparency.float ); 1661 break; 1662 case 'A_ZERO': 1663 material.opacity = 1 - ( color[ 3 ] * transparency.float ); 1664 break; 1665 case 'RGB_ONE': 1666 material.opacity = color[ 0 ] * transparency.float; 1667 break; 1668 default: 1669 console.warn( 'THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque ); 1670 1671 } 1672 1673 if ( material.opacity < 1 ) material.transparent = true; 1674 1675 } 1676 1677 } 1678 1679 // 1680 1681 if ( extra !== undefined && extra.technique !== undefined && extra.technique.double_sided === 1 ) { 1682 1683 material.side = THREE.DoubleSide; 1684 1685 } 1686 1687 return material; 1688 1689 } 1690 1691 function getMaterial( id ) { 1692 1693 return getBuild( library.materials[ id ], buildMaterial ); 1694 1695 } 1696 1697 // camera 1698 1699 function parseCamera( xml ) { 1700 1701 var data = {
1702 name: xml.getAttribute( 'name' ) 1703 }; 1704 1705 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1706 1707 var child = xml.childNodes[ i ]; 1708 1709 if ( child.nodeType !== 1 ) continue; 1710 1711 switch ( child.nodeName ) { 1712 1713 case 'optics': 1714 data.optics = parseCameraOptics( child ); 1715 break; 1716 1717 } 1718 1719 } 1720 1721 library.cameras[ xml.getAttribute( 'id' ) ] = data; 1722 1723 } 1724 1725 function parseCameraOptics( xml ) { 1726 1727 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 1728 1729 var child = xml.childNodes[ i ]; 1730 1731 switch ( child.nodeName ) { 1732 1733 case 'technique_common': 1734 return parseCameraTechnique( child ); 1735 1736 } 1737 1738 } 1739 1740 return {}; 1741 1742 } 1743 1744 function parseCameraTechnique( xml ) { 1745 1746 var data = {}; 1747 1748 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 1749 1750 var child = xml.childNodes[ i ]; 1751 1752 switch ( child.nodeName ) { 1753 1754 case 'perspective': 1755 case 'orthographic': 1756 1757 data.technique = child.nodeName; 1758 data.parameters = parseCameraParameters( child ); 1759 1760 break; 1761 1762 } 1763 1764 } 1765 1766 return data; 1767 1768 } 1769 1770 function parseCameraParameters( xml ) { 1771 1772 var data = {}; 1773 1774 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 1775 1776 var child = xml.childNodes[ i ]; 1777 1778 switch ( child.nodeName ) { 1779 1780 case 'xfov': 1781 case 'yfov': 1782 case 'xmag': 1783 case 'ymag': 1784 case 'znear': 1785 case 'zfar': 1786 case 'aspect_ratio': 1787 data[ child.nodeName ] = parseFloat( child.textContent ); 1788 break; 1789 1790 } 1791 1792 } 1793 1794 return data; 1795 1796 } 1797 1798 function buildCamera( data ) { 1799 1800 var camera; 1801 1802 switch ( data.optics.technique ) { 1803 1804 case 'perspective': 1805 camera = new THREE.PerspectiveCamera( 1806 data.optics.parameters.yfov, 1807 data.optics.parameters.aspect_ratio, 1808 data.optics.parameters.znear, 1809 data.optics.parameters.zfar 1810 ); 1811 break; 1812 1813 case 'orthographic': 1814 var ymag = data.optics.parameters.ymag; 1815 var xmag = data.optics.parameters.xmag; 1816 var aspectRatio = data.optics.parameters.aspect_ratio; 1817 1818 xmag = ( xmag === undefined ) ? ( ymag * aspectRatio ) : xmag; 1819 ymag = ( ymag === undefined ) ? ( xmag / aspectRatio ) : ymag; 1820 1821 xmag *= 0.5; 1822 ymag *= 0.5; 1823 1824 camera = new THREE.OrthographicCamera( 1825 - xmag, xmag, ymag, - ymag, // left, right, top, bottom 1826 data.optics.parameters.znear, 1827 data.optics.parameters.zfar 1828 ); 1829 break; 1830 1831 default: 1832 camera = new THREE.PerspectiveCamera(); 1833 break; 1834 1835 } 1836 1837 camera.name = data.name || ''; 1838 1839 return camera; 1840 1841 } 1842 1843 function getCamera( id ) { 1844 1845 var data = library.cameras[ id ]; 1846 1847 if ( data !== undefined ) { 1848 1849 return getBuild( data, buildCamera ); 1850 1851 } 1852 1853 console.warn( 'THREE.ColladaLoader: Couldn\'t find camera with ID:', id ); 1854 1855 return null; 1856 1857 } 1858 1859 // light 1860 1861 function parseLight( xml ) { 1862 1863 var data = {}; 1864 1865 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1866 1867 var child = xml.childNodes[ i ]; 1868 1869 if ( child.nodeType !== 1 ) continue; 1870 1871 switch ( child.nodeName ) { 1872 1873 case 'technique_common': 1874 data = parseLightTechnique( child ); 1875 break; 1876 1877 } 1878 1879 } 1880 1881 library.lights[ xml.getAttribute( 'id' ) ] = data; 1882 1883 } 1884 1885 function parseLightTechnique( xml ) { 1886 1887 var data = {}; 1888 1889 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1890 1891 var child = xml.childNodes[ i ]; 1892 1893 if ( child.nodeType !== 1 ) continue; 1894 1895 switch ( child.nodeName ) { 1896 1897 case 'directional': 1898 case 'point': 1899 case 'spot': 1900 case 'ambient': 1901 1902 data.technique = child.nodeName; 1903 data.parameters = parseLightParameters( child ); 1904 1905 } 1906 1907 } 1908 1909 return data; 1910 1911 } 1912 1913 function parseLightParameters( xml ) { 1914 1915 var data = {}; 1916 1917 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 1918 1919 var child = xml.childNodes[ i ]; 1920 1921 if ( child.nodeType !== 1 ) continue; 1922 1923 switch ( child.nodeName ) { 1924 1925 case 'color': 1926 var array = parseFloats( child.textContent ); 1927 data.color = new THREE.Color().fromArray( array ); 1928 break; 1929 1930 case 'falloff_angle': 1931 data.falloffAngle = parseFloat( child.textContent ); 1932 break; 1933 1934 case 'quadratic_attenuation': 1935 var f = parseFloat( child.textContent ); 1936 data.distance = f ? Math.sqrt( 1 / f ) : 0; 1937 break; 1938 1939 } 1940 1941 } 1942 1943 return data; 1944 1945 } 1946 1947 function buildLight( data ) { 1948 1949 var light; 1950 1951 switch ( data.technique ) { 1952 1953 case 'directional': 1954 light = new THREE.DirectionalLight(); 1955 break; 1956
1957 case 'point': 1958 light = new THREE.PointLight(); 1959 break; 1960 1961 case 'spot': 1962 light = new THREE.SpotLight(); 1963 break; 1964 1965 case 'ambient': 1966 light = new THREE.AmbientLight(); 1967 break; 1968 1969 } 1970 1971 if ( data.parameters.color ) light.color.copy( data.parameters.color ); 1972 if ( data.parameters.distance ) light.distance = data.parameters.distance; 1973 1974 return light; 1975 1976 } 1977 1978 function getLight( id ) { 1979 1980 var data = library.lights[ id ]; 1981 1982 if ( data !== undefined ) { 1983 1984 return getBuild( data, buildLight ); 1985 1986 } 1987 1988 console.warn( 'THREE.ColladaLoader: Couldn\'t find light with ID:', id ); 1989 1990 return null; 1991 1992 } 1993 1994 // geometry 1995 1996 function parseGeometry( xml ) { 1997 1998 var data = { 1999 name: xml.getAttribute( 'name' ), 2000 sources: {}, 2001 vertices: {}, 2002 primitives: [] 2003 }; 2004 2005 var mesh = getElementsByTagName( xml, 'mesh' )[ 0 ]; 2006 2007 // the following tags inside geometry are not supported yet (see https://github.com/mrdoob/three.js/pull/12606): convex_mesh, spline, brep 2008 if ( mesh === undefined ) return; 2009 2010 for ( var i = 0; i < mesh.childNodes.length; i ++ ) { 2011 2012 var child = mesh.childNodes[ i ]; 2013 2014 if ( child.nodeType !== 1 ) continue; 2015 2016 var id = child.getAttribute( 'id' ); 2017 2018 switch ( child.nodeName ) { 2019 2020 case 'source': 2021 data.sources[ id ] = parseSource( child ); 2022 break; 2023 2024 case 'vertices': 2025 // data.sources[ id ] = data.sources[ parseId( getElementsByTagName( child, 'input' )[ 0 ].getAttribute( 'source' ) ) ]; 2026 data.vertices = parseGeometryVertices( child ); 2027 break; 2028 2029 case 'polygons': 2030 console.warn( 'THREE.ColladaLoader: Unsupported primitive type: ', child.nodeName ); 2031 break; 2032 2033 case 'lines': 2034 case 'linestrips': 2035 case 'polylist': 2036 case 'triangles': 2037 data.primitives.push( parseGeometryPrimitive( child ) ); 2038 break; 2039 2040 default: 2041 console.log( child ); 2042 2043 } 2044 2045 } 2046 2047 library.geometries[ xml.getAttribute( 'id' ) ] = data; 2048 2049 } 2050 2051 function parseSource( xml ) { 2052 2053 var data = { 2054 array: [], 2055 stride: 3 2056 }; 2057 2058 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2059 2060 var child = xml.childNodes[ i ]; 2061 2062 if ( child.nodeType !== 1 ) continue; 2063 2064 switch ( child.nodeName ) { 2065 2066 case 'float_array': 2067 data.array = parseFloats( child.textContent ); 2068 break; 2069 2070 case 'Name_array': 2071 data.array = parseStrings( child.textContent ); 2072 break; 2073 2074 case 'technique_common': 2075 var accessor = getElementsByTagName( child, 'accessor' )[ 0 ]; 2076 2077 if ( accessor !== undefined ) { 2078 2079 data.stride = parseInt( accessor.getAttribute( 'stride' ) ); 2080 2081 } 2082 break; 2083 2084 } 2085 2086 } 2087 2088 return data; 2089 2090 } 2091 2092 function parseGeometryVertices( xml ) { 2093 2094 var data = {}; 2095 2096 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2097 2098 var child = xml.childNodes[ i ]; 2099 2100 if ( child.nodeType !== 1 ) continue; 2101 2102 data[ child.getAttribute( 'semantic' ) ] = parseId( child.getAttribute( 'source' ) ); 2103 2104 } 2105 2106 return data; 2107 2108 } 2109 2110 function parseGeometryPrimitive( xml ) { 2111 2112 var primitive = { 2113 type: xml.nodeName, 2114 material: xml.getAttribute( 'material' ), 2115 count: parseInt( xml.getAttribute( 'count' ) ), 2116 inputs: {}, 2117 stride: 0, 2118 hasUV: false 2119 }; 2120 2121 for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) { 2122 2123 var child = xml.childNodes[ i ]; 2124 2125 if ( child.nodeType !== 1 ) continue; 2126 2127 switch ( child.nodeName ) { 2128 2129 case 'input': 2130 var id = parseId( child.getAttribute( 'source' ) ); 2131 var semantic = child.getAttribute( 'semantic' ); 2132 var offset = parseInt( child.getAttribute( 'offset' ) ); 2133 var set = parseInt( child.getAttribute( 'set' ) ); 2134 var inputname = ( set > 0 ? semantic + set : semantic ); 2135 primitive.inputs[ inputname ] = { id: id, offset: offset }; 2136 primitive.stride = Math.max( primitive.stride, offset + 1 ); 2137 if ( semantic === 'TEXCOORD' ) primitive.hasUV = true; 2138 break; 2139 2140 case 'vcount': 2141 primitive.vcount = parseInts( child.textContent ); 2142 break; 2143 2144 case 'p': 2145 primitive.p = parseInts( child.textContent ); 2146 break; 2147 2148 } 2149 2150 } 2151 2152 return primitive; 2153 2154 } 2155 2156 function groupPrimitives( primitives ) { 2157 2158 var build = {}; 2159 2160 for ( var i = 0; i < primitives.length; i ++ ) { 2161 2162 var primitive = primitives[ i ]; 2163 2164 if ( build[ primitive.type ] === undefined ) build[ primitive.type ] = []; 2165 2166 build[ primitive.type ].push( primitive ); 2167 2168 } 2169 2170 return build; 2171 2172 } 2173 2174 function checkUVCoordinates( primitives ) { 2175 2176 var count = 0; 2177 2178 for ( var i = 0, l = primitives.length; i < l; i ++ ) { 2179 2180 var primitive = primitives[ i ]; 2181 2182 if ( primitive.hasUV === true ) { 2183 2184 count ++; 2185 2186 } 2187 2188 } 2189 2190 if ( count > 0 && count < primitives.length ) { 2191 2192 primitives.uvsNeedsFix = true; 2193 2194 } 2195 2196 } 2197 2198 function buildGeometry( data ) { 2199 2200 var build = {}; 2201 2202 var sources = data.sources; 2203 var vertices = data.vertices; 2204 var primitives = data.primitives; 2205 2206 if ( primitives.length === 0 ) return {}; 2207 2208 // our goal is to create one buffer geometry for a single type of primitives 2209 // first, we group all primitives by their type 2210 2211 var groupedPrimitives = groupPrimitives( primitives ); 2212 2213 for ( var type in groupedPrimitives ) { 2214 2215 var primitiveType = groupedPrimitives[ type ]; 2216 2217 // second, ensure consistent uv coordinates for each type of primitives (polylist,triangles or lines) 2218 2219 checkUVCoordinates( primitiveType ); 2220 2221 // third, create a buffer geometry for each type of primitives 2222 2223 build[ type ] = buildGeometryType( primitiveType, sources, vertices ); 2224 2225 } 2226 2227 return build; 2228 2229 } 2230 2231 function buildGeometryType( primitives, sources, vertices ) { 2232 2233 var build = {}; 2234 2235 var position = { array: [], stride: 0 }; 2236 var normal = { array: [], stride: 0 }; 2237 var uv = { array: [], stride: 0 }; 2238 var uv2 = { array: [], stride: 0 }; 2239 var color = { array: [], stride: 0 }; 2240 2241 var skinIndex = { array: [], stride: 4 }; 2242 var skinWeight = { array: [], stride: 4 }; 2243 2244 var geometry = new THREE.BufferGeometry(); 2245 2246 var materialKeys = []; 2247 2248 var start = 0; 2249 2250 for ( var p = 0; p < primitives.length; p ++ ) { 2251 2252 var primitive = primitives[ p ]; 2253 var inputs = primitive.inputs; 2254 2255 // groups 2256 2257 var count = 0; 2258 2259 switch ( primitive.type ) { 2260 2261 case 'lines': 2262 case 'linestrips': 2263 count = primitive.count * 2; 2264 break; 2265 2266 case 'triangles': 2267 count = primitive.count * 3; 2268 break; 2269 2270 case 'polylist': 2271 2272 for ( var g = 0; g < primitive.count; g ++ ) { 2273 2274 var vc = primitive.vcount[ g ]; 2275 2276 switch ( vc ) { 2277 2278 case 3: 2279 count += 3; // single triangle 2280 break; 2281 2282 case 4: 2283 count += 6; // quad, subdivided into two triangles 2284 break; 2285 2286 default: 2287 count += ( vc - 2 ) * 3; // polylist with more than four vertices 2288 break; 2289 2290 } 2291 2292 } 2293 2294 break; 2295 2296 default: 2297 console.warn( 'THREE.ColladaLoader: Unknow primitive type:', primitive.type ); 2298 2299 } 2300 2301 geometry.addGroup( start, count, p ); 2302 start += count; 2303 2304 // material 2305 2306 if ( primitive.material ) { 2307 2308 materialKeys.push( primitive.material ); 2309 2310 } 2311 2312 // geometry data 2313 2314 for ( var name in inputs ) { 2315 2316 var input = inputs[ name ]; 2317 2318 switch ( name ) { 2319 2320 case 'VERTEX': 2321 for ( var key in vertices ) { 2322 2323 var id = vertices[ key ]; 2324 2325 switch ( key ) { 2326 2327 case 'POSITION': 2328 var prevLength = position.array.length; 2329 buildGeometryData( primitive, sources[ id ], input.offset, position.array ); 2330 position.stride = sources[ id ].stride; 2331 2332 if ( sources.skinWeights && sources.skinIndices ) { 2333 2334 buildGeometryData( primitive, sources.skinIndices, input.offset, skinIndex.array ); 2335 buildGeometryData( primitive, sources.skinWeights, input.offset, skinWeight.array ); 2336 2337 } 2338 2339 // see #3803 2340 2341 if ( primitive.hasUV === false && primitives.uvsNeedsFix === true ) { 2342 2343 var count = ( position.array.length - prevLength ) / position.stride; 2344 2345 for ( var i = 0; i < count; i ++ ) { 2346 2347 // fill missing uv coordinates 2348 2349 uv.array.push( 0, 0 ); 2350 2351 } 2352 2353 } 2354 break; 2355 2356 case 'NORMAL': 2357 buildGeometryData( primitive, sources[ id ], input.offset, normal.array ); 2358 normal.stride = sources[ id ].stride; 2359 break; 2360 2361 case 'COLOR': 2362 buildGeometryData( primitive, sources[ id ], input.offset, color.array ); 2363 color.stride = sources[ id ].stride; 2364 break; 2365 2366 case 'TEXCOORD': 2367 buildGeometryData( primitive, sources[ id ], input.offset, uv.array ); 2368 uv.stride = sources[ id ].stride; 2369 break; 2370 2371 case 'TEXCOORD1': 2372 buildGeometryData( primitive, sources[ id ], input.offset, uv2.array ); 2373 uv.stride = sources[ id ].stride; 2374 break; 2375 2376 default: 2377 console.warn( 'THREE.ColladaLoader: Semantic "%s" not handled in geometry build process.', key ); 2378 2379 } 2380 2381 } 2382 break; 2383 2384 case 'NORMAL': 2385 buildGeometryData( primitive, sources[ input.id ], input.offset, normal.array ); 2386 normal.stride = sources[ input.id ].stride; 2387 break; 2388 2389 case 'COLOR': 2390 buildGeometryData( primitive, sources[ input.id ], input.offset, color.array ); 2391 color.stride = sources[ input.id ].stride; 2392 break; 2393 2394 case 'TEXCOORD': 2395 buildGeometryData( primitive, sources[ input.id ], input.offset, uv.array ); 2396 uv.stride = sources[ input.id ].stride; 2397 break; 2398 2399 case 'TEXCOORD1': 2400 buildGeometryData( primitive, sources[ input.id ], input.offset, uv2.array ); 2401 uv2.stride = sources[ input.id ].stride; 2402 break; 2403 2404 } 2405 2406 } 2407 2408 } 2409 2410 // build geometry 2411 2412 if ( position.array.length > 0 ) geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( position.array, position.stride ) ); 2413 if ( normal.array.length >
2413 0 ) geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normal.array, normal.stride ) ); 2414 if ( color.array.length > 0 ) geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( color.array, color.stride ) ); 2415 if ( uv.array.length > 0 ) geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uv.array, uv.stride ) ); 2416 if ( uv2.array.length > 0 ) geometry.addAttribute( 'uv2', new THREE.Float32BufferAttribute( uv2.array, uv2.stride ) ); 2417 2418 if ( skinIndex.array.length > 0 ) geometry.addAttribute( 'skinIndex', new THREE.Float32BufferAttribute( skinIndex.array, skinIndex.stride ) ); 2419 if ( skinWeight.array.length > 0 ) geometry.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( skinWeight.array, skinWeight.stride ) ); 2420 2421 build.data = geometry; 2422 build.type = primitives[ 0 ].type; 2423 build.materialKeys = materialKeys; 2424 2425 return build; 2426 2427 } 2428 2429 function buildGeometryData( primitive, source, offset, array ) { 2430 2431 var indices = primitive.p; 2432 var stride = primitive.stride; 2433 var vcount = primitive.vcount; 2434 2435 function pushVector( i ) { 2436 2437 var index = indices[ i + offset ] * sourceStride; 2438 var length = index + sourceStride; 2439 2440 for ( ; index < length; index ++ ) { 2441 2442 array.push( sourceArray[ index ] ); 2443 2444 } 2445 2446 } 2447 2448 var sourceArray = source.array; 2449 var sourceStride = source.stride; 2450 2451 if ( primitive.vcount !== undefined ) { 2452 2453 var index = 0; 2454 2455 for ( var i = 0, l = vcount.length; i < l; i ++ ) { 2456 2457 var count = vcount[ i ]; 2458 2459 if ( count === 4 ) { 2460 2461 var a = index + stride * 0; 2462 var b = index + stride * 1; 2463 var c = index + stride * 2; 2464 var d = index + stride * 3; 2465 2466 pushVector( a ); pushVector( b ); pushVector( d ); 2467 pushVector( b ); pushVector( c ); pushVector( d ); 2468 2469 } else if ( count === 3 ) { 2470 2471 var a = index + stride * 0; 2472 var b = index + stride * 1; 2473 var c = index + stride * 2; 2474 2475 pushVector( a ); pushVector( b ); pushVector( c ); 2476 2477 } else if ( count > 4 ) { 2478 2479 for ( var k = 1, kl = ( count - 2 ); k <= kl; k ++ ) { 2480 2481 var a = index + stride * 0; 2482 var b = index + stride * k; 2483 var c = index + stride * ( k + 1 ); 2484 2485 pushVector( a ); pushVector( b ); pushVector( c ); 2486 2487 } 2488 2489 } 2490 2491 index += stride * count; 2492 2493 } 2494 2495 } else { 2496 2497 for ( var i = 0, l = indices.length; i < l; i += stride ) { 2498 2499 pushVector( i ); 2500 2501 } 2502 2503 } 2504 2505 } 2506
2507 function getGeometry( id ) { 2508 2509 return getBuild( library.geometries[ id ], buildGeometry ); 2510 2511 } 2512 2513 // kinematics 2514 2515 function parseKinematicsModel( xml ) { 2516 2517 var data = { 2518 name: xml.getAttribute( 'name' ) || '', 2519 joints: {}, 2520 links: [] 2521 }; 2522 2523 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2524 2525 var child = xml.childNodes[ i ]; 2526 2527 if ( child.nodeType !== 1 ) continue; 2528 2529 switch ( child.nodeName ) { 2530 2531 case 'technique_common': 2532 parseKinematicsTechniqueCommon( child, data ); 2533 break; 2534 2535 } 2536 2537 } 2538 2539 library.kinematicsModels[ xml.getAttribute( 'id' ) ] = data; 2540 2541 } 2542 2543 function buildKinematicsModel( data ) { 2544 2545 if ( data.build !== undefined ) return data.build; 2546 2547 return data; 2548 2549 } 2550 2551 function getKinematicsModel( id ) { 2552 2553 return getBuild( library.kinematicsModels[ id ], buildKinematicsModel ); 2554 2555 } 2556 2557 function parseKinematicsTechniqueCommon( xml, data ) { 2558 2559 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2560 2561 var child = xml.childNodes[ i ]; 2562 2563 if ( child.nodeType !== 1 ) continue; 2564 2565 switch ( child.nodeName ) { 2566 2567 case 'joint': 2568 data.joints[ child.getAttribute( 'sid' ) ] = parseKinematicsJoint( child ); 2569 break; 2570 2571 case 'link': 2572 data.links.push( parseKinematicsLink( child ) ); 2573 break; 2574 2575 } 2576 2577 } 2578 2579 } 2580 2581 function parseKinematicsJoint( xml ) { 2582 2583 var data; 2584 2585 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2586 2587 var child = xml.childNodes[ i ]; 2588 2589 if ( child.nodeType !== 1 ) continue; 2590 2591 switch ( child.nodeName ) { 2592 2593 case 'prismatic': 2594 case 'revolute': 2595 data = parseKinematicsJointParameter( child ); 2596 break; 2597 2598 } 2599 2600 } 2601 2602 return data; 2603 2604 } 2605 2606 function parseKinematicsJointParameter( xml, data ) { 2607 2608 var data = { 2609 sid: xml.getAttribute( 'sid' ), 2610 name: xml.getAttribute( 'name' ) || '', 2611 axis: new THREE.Vector3(), 2612 limits: { 2613 min: 0, 2614 max: 0 2615 }, 2616 type: xml.nodeName, 2617 static: false, 2618 zeroPosition: 0, 2619 middlePosition: 0 2620 }; 2621 2622 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2623 2624 var child = xml.childNodes[ i ]; 2625 2626 if ( child.nodeType !== 1 ) continue; 2627 2628 switch ( child.nodeName ) { 2629 2630 case 'axis': 2631 var array = parseFloats( child.textContent ); 2632 data.axis.fromArray( array ); 2633 break; 2634 case 'limits': 2635 var max = child.getElementsByTagName( 'max' )[ 0 ]; 2636 var min = child.getElementsByTagName( 'min' )[ 0 ]; 2637 2638 data.limits.max = parseFloat( max.textContent ); 2639 data.limits.min = parseFloat( min.textContent ); 2640 break; 2641 2642 } 2643 2644 } 2645 2646 // if min is equal to or greater than max, consider the joint static 2647 2648 if ( data.limits.min >= data.limits.max ) { 2649 2650 data.static = true; 2651 2652 } 2653 2654 // calculate middle position 2655 2656 data.middlePosition = ( data.limits.min + data.limits.max ) / 2.0; 2657 2658 return data; 2659 2660 } 2661 2662 function parseKinematicsLink( xml ) { 2663 2664 var data = { 2665 sid: xml.getAttribute( 'sid' ), 2666 name: xml.getAttribute( 'name' ) || '', 2667 attachments: [], 2668 transforms: [] 2669 }; 2670 2671 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2672 2673 var child = xml.childNodes[ i ]; 2674 2675 if ( child.nodeType !== 1 ) continue; 2676 2677 switch ( child.nodeName ) { 2678 2679 case 'attachment_full': 2680 data.attachments.push( parseKinematicsAttachment( child ) ); 2681 break; 2682 2683 case 'matrix': 2684 case 'translate': 2685 case 'rotate': 2686 data.transforms.push( parseKinematicsTransform( child ) ); 2687 break; 2688 2689 } 2690 2691 } 2692 2693 return data; 2694 2695 } 2696 2697 function parseKinematicsAttachment( xml ) { 2698 2699 var data = { 2700 joint: xml.getAttribute( 'joint' ).split( '/' ).pop(), 2701 transforms: [], 2702 links: [] 2703 }; 2704 2705 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2706 2707 var child = xml.childNodes[ i ]; 2708 2709 if ( child.nodeType !== 1 ) continue; 2710 2711 switch ( child.nodeName ) { 2712 2713 case 'link': 2714 data.links.push( parseKinematicsLink( child ) ); 2715 break; 2716 2717 case 'matrix': 2718 case 'translate': 2719 case 'rotate': 2720 data.transforms.push( parseKinematicsTransform( child ) ); 2721 break; 2722 2723 } 2724 2725 } 2726 2727 return data; 2728 2729 } 2730 2731 function parseKinematicsTransform( xml ) { 2732 2733 var data = { 2734 type: xml.nodeName 2735 }; 2736 2737 var array = parseFloats( xml.textContent ); 2738 2739 switch ( data.type ) { 2740 2741 case 'matrix': 2742 data.obj = new THREE.Matrix4(); 2743 data.obj.fromArray( array ).transpose(); 2744 break; 2745
2746 case 'translate': 2747 data.obj = new THREE.Vector3(); 2748 data.obj.fromArray( array ); 2749 break; 2750 2751 case 'rotate': 2752 data.obj = new THREE.Vector3(); 2753 data.obj.fromArray( array ); 2754 data.angle = THREE.Math.degToRad( array[ 3 ] ); 2755 break; 2756 2757 } 2758 2759 return data; 2760 2761 } 2762 2763 // physics 2764 2765 function parsePhysicsModel( xml ) { 2766 2767 var data = { 2768 name: xml.getAttribute( 'name' ) || '', 2769 rigidBodies: {} 2770 }; 2771 2772 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2773 2774 var child = xml.childNodes[ i ]; 2775 2776 if ( child.nodeType !== 1 ) continue; 2777 2778 switch ( child.nodeName ) { 2779 2780 case 'rigid_body': 2781 data.rigidBodies[ child.getAttribute( 'name' ) ] = {}; 2782 parsePhysicsRigidBody( child, data.rigidBodies[ child.getAttribute( 'name' ) ] ); 2783 break; 2784 2785 } 2786 2787 } 2788 2789 library.physicsModels[ xml.getAttribute( 'id' ) ] = data; 2790 2791 } 2792 2793 function parsePhysicsRigidBody( xml, data ) { 2794 2795 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2796 2797 var child = xml.childNodes[ i ]; 2798 2799 if ( child.nodeType !== 1 ) continue; 2800 2801 switch ( child.nodeName ) { 2802 2803 case 'technique_common': 2804 parsePhysicsTechniqueCommon( child, data ); 2805 break; 2806 2807 } 2808 2809 } 2810 2811 } 2812 2813 function parsePhysicsTechniqueCommon( xml, data ) { 2814 2815 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2816 2817 var child = xml.childNodes[ i ]; 2818 2819 if ( child.nodeType !== 1 ) continue; 2820 2821 switch ( child.nodeName ) { 2822 2823 case 'inertia': 2824 data.inertia = parseFloats( child.textContent ); 2825 break; 2826 2827 case 'mass': 2828 data.mass = parseFloats( child.textContent )[ 0 ]; 2829 break; 2830 2831 } 2832 2833 } 2834 2835 } 2836 2837 // scene 2838 2839 function parseKinematicsScene( xml ) { 2840 2841 var data = { 2842 bindJointAxis: [] 2843 }; 2844 2845 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2846 2847 var child = xml.childNodes[ i ]; 2848 2849 if ( child.nodeType !== 1 ) continue; 2850 2851 switch ( child.nodeName ) { 2852 2853 case 'bind_joint_axis': 2854 data.bindJointAxis.push( parseKinematicsBindJointAxis( child ) ); 2855 break; 2856 2857 } 2858 2859 } 2860 2861 library.kinematicsScenes[ parseId( xml.getAttribute( 'url' ) ) ] = data; 2862 2863 } 2864 2865 function parseKinematicsBindJointAxis( xml ) { 2866 2867 var data = { 2868 target: xml.getAttribute( 'target' ).split( '/' ).pop() 2869 }; 2870 2871 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 2872 2873 var child = xml.childNodes[ i ]; 2874 2875 if ( child.nodeType !== 1 ) continue; 2876 2877 switch ( child.nodeName ) { 2878 2879 case 'axis': 2880 var param = child.getElementsByTagName( 'param' )[ 0 ]; 2881 data.axis = param.textContent; 2882 var tmpJointIndex = data.axis.split( 'inst_' ).pop().split( 'axis' )[ 0 ]; 2883 data.jointIndex = tmpJointIndex.substr( 0, tmpJointIndex.length - 1 ); 2884 break; 2885 2886 } 2887 2888 } 2889 2890 return data; 2891 2892 } 2893 2894 function buildKinematicsScene( data ) { 2895 2896 if ( data.build !== undefined ) return data.build; 2897 2898 return data; 2899 2900 } 2901 2902 function getKinematicsScene( id ) { 2903 2904 return getBuild( library.kinematicsScenes[ id ], buildKinematicsScene ); 2905 2906 } 2907 2908 function setupKinematics() { 2909 2910 var kinematicsModelId = Object.keys( library.kinematicsModels )[ 0 ]; 2911 var kinematicsSceneId = Object.keys( library.kinematicsScenes )[ 0 ]; 2912 var visualSceneId = Object.keys( library.visualScenes )[ 0 ]; 2913 2914 if ( kinematicsModelId === undefined || kinematicsSceneId === undefined ) return; 2915 2916 var kinematicsModel = getKinematicsModel( kinematicsModelId ); 2917 var kinematicsScene = getKinematicsScene( kinematicsSceneId ); 2918 var visualScene = getVisualScene( visualSceneId ); 2919 2920 var bindJointAxis = kinematicsScene.bindJointAxis; 2921 var jointMap = {}; 2922 2923 for ( var i = 0, l = bindJointAxis.length; i < l; i ++ ) { 2924 2925 var axis = bindJointAxis[ i ]; 2926 2927 // the result of the following query is an element of type 'translate', 'rotate','scale' or 'matrix' 2928 2929 var targetElement = collada.querySelector( '[sid="' + axis.target + '"]' ); 2930 2931 if ( targetElement ) { 2932 2933 // get the parent of the transfrom element 2934 2935 var parentVisualElement = targetElement.parentElement; 2936 2937 // connect the joint of the kinematics model with the element in the visual scene 2938 2939 connect( axis.jointIndex, parentVisualElement ); 2940 2941 } 2942 2943 } 2944 2945 function connect( jointIndex, visualElement ) { 2946
2947 var visualElementName = visualElement.getAttribute( 'name' ); 2948 var joint = kinematicsModel.joints[ jointIndex ]; 2949 2950 visualScene.traverse( function ( object ) { 2951 2952 if ( object.name === visualElementName ) { 2953 2954 jointMap[ jointIndex ] = { 2955 object: object, 2956 transforms: buildTransformList( visualElement ), 2957 joint: joint, 2958 position: joint.zeroPosition 2959 }; 2960 2961 } 2962 2963 } ); 2964 2965 } 2966 2967 var m0 = new THREE.Matrix4(); 2968 2969 kinematics = { 2970 2971 joints: kinematicsModel && kinematicsModel.joints, 2972 2973 getJointValue: function ( jointIndex ) { 2974 2975 var jointData = jointMap[ jointIndex ]; 2976 2977 if ( jointData ) { 2978 2979 return jointData.position; 2980 2981 } else { 2982 2983 console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' doesn\'t exist.' ); 2984 2985 } 2986 2987 }, 2988 2989 setJointValue: function ( jointIndex, value ) { 2990 2991 var jointData = jointMap[ jointIndex ]; 2992 2993 if ( jointData ) { 2994 2995 var joint = jointData.joint; 2996 2997 if ( value > joint.limits.max || value < joint.limits.min ) { 2998 2999 console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ').' ); 3000 3001 } else if ( joint.static ) { 3002 3003 console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' is static.' ); 3004 3005 } else { 3006 3007 var object = jointData.object; 3008 var axis = joint.axis; 3009 var transforms = jointData.transforms; 3010 3011 matrix.identity(); 3012 3013 // each update, we have to apply all transforms in the correct order 3014 3015 for ( var i = 0; i < transforms.length; i ++ ) { 3016 3017 var transform = transforms[ i ]; 3018 3019 // if there is a connection of the transform node with a joint, apply the joint value 3020 3021 if ( transform.sid && transform.sid.indexOf( jointIndex ) !== - 1 ) { 3022 3023 switch ( joint.type ) { 3024 3025 case 'revolute':
3026 matrix.multiply( m0.makeRotationAxis( axis, THREE.Math.degToRad( value ) ) ); 3027 break; 3028 3029 case 'prismatic': 3030 matrix.multiply( m0.makeTranslation( axis.x * value, axis.y * value, axis.z * value ) ); 3031 break; 3032 3033 default: 3034 console.warn( 'THREE.ColladaLoader: Unknown joint type: ' + joint.type ); 3035 break; 3036 3037 } 3038 3039 } else { 3040 3041 switch ( transform.type ) { 3042 3043 case 'matrix': 3044 matrix.multiply( transform.obj ); 3045 break; 3046 3047 case 'translate': 3048 matrix.multiply( m0.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); 3049 break; 3050 3051 case 'scale': 3052 matrix.scale( transform.obj ); 3053 break; 3054 3055 case 'rotate': 3056 matrix.multiply( m0.makeRotationAxis( transform.obj, transform.angle ) ); 3057 break; 3058 3059 } 3060 3061 } 3062 3063 } 3064 3065 object.matrix.copy( matrix ); 3066 object.matrix.decompose( object.position, object.quaternion, object.scale ); 3067 3068 jointMap[ jointIndex ].position = value; 3069 3070 } 3071 3072 } else { 3073 3074 console.log( 'THREE.ColladaLoader: ' + jointIndex + ' does not exist.' ); 3075 3076 } 3077 3078 } 3079 3080 }; 3081 3082 } 3083 3084 function buildTransformList( node ) { 3085 3086 var transforms = []; 3087 3088 var xml = collada.querySelector( '[id="' + node.id + '"]' ); 3089 3090 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 3091 3092 var child = xml.childNodes[ i ]; 3093 3094 if ( child.nodeType !== 1 ) continue; 3095 3096 switch ( child.nodeName ) { 3097 3098 case 'matrix': 3099 var array = parseFloats( child.textContent ); 3100 var matrix = new THREE.Matrix4().fromArray( array ).transpose(); 3101 transforms.push( { 3102 sid: child.getAttribute( 'sid' ), 3103 type: child.nodeName, 3104 obj: matrix 3105 } ); 3106 break; 3107 3108 case 'translate': 3109 case 'scale': 3110 var array = parseFloats( child.textContent ); 3111 var vector = new THREE.Vector3().fromArray( array ); 3112 transforms.push( { 3113 sid: child.getAttribute( 'sid' ), 3114 type: child.nodeName, 3115 obj: vector 3116 } ); 3117 break; 3118 3119 case 'rotate': 3120 var array = parseFloats( child.textContent ); 3121 var vector = new THREE.Vector3().fromArray( array ); 3122 var angle = THREE.Math.degToRad( array[ 3 ] ); 3123 transforms.push( { 3124 sid: child.getAttribute( 'sid' ), 3125 type: child.nodeName, 3126 obj: vector, 3127 angle: angle 3128 } ); 3129 break; 3130 3131 } 3132 3133 } 3134 3135 return transforms; 3136 3137 } 3138 3139 // nodes 3140 3141 function prepareNodes( xml ) { 3142 3143 var elements = xml.getElementsByTagName( 'node' ); 3144 3145 // ensure all node elements have id attributes 3146 3147 for ( var i = 0; i < elements.length; i ++ ) { 3148 3149 var element = elements[ i ]; 3150 3151 if ( element.hasAttribute( 'id' ) === false ) { 3152 3153 element.setAttribute( 'id', generateId() ); 3154 3155 } 3156 3157 } 3158 3159 } 3160 3161 var matrix = new THREE.Matrix4(); 3162 var vector = new THREE.Vector3(); 3163 3164 function parseNode( xml ) { 3165 3166 var data = { 3167 name: xml.getAttribute( 'name' ) || '', 3168 type: xml.getAttribute( 'type' ), 3169 id: xml.getAttribute( 'id' ), 3170 sid: xml.getAttribute( 'sid' ), 3171 matrix: new THREE.Matrix4(), 3172 nodes: [], 3173 instanceCameras: [], 3174 instanceControllers: [], 3175 instanceLights: [], 3176 instanceGeometries: [], 3177 instanceNodes: [], 3178 transforms: {} 3179 }; 3180 3181 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 3182 3183 var child = xml.childNodes[ i ]; 3184 3185 if ( child.nodeType !== 1 ) continue; 3186 3187 switch ( child.nodeName ) { 3188 3189 case 'node': 3190 data.nodes.push( child.getAttribute( 'id' ) ); 3191 parseNode( child ); 3192 break; 3193 3194 case 'instance_camera': 3195 data.instanceCameras.push( parseId( child.getAttribute( 'url' ) ) ); 3196 break; 3197 3198 case 'instance_controller': 3199 data.instanceControllers.push( parseNodeInstance( child ) ); 3200 break; 3201 3202 case 'instance_light': 3203 data.instanceLights.push( parseId( child.getAttribute( 'url' ) ) ); 3204 break; 3205 3206 case 'instance_geometry': 3207 data.instanceGeometries.push( parseNodeInstance( child ) ); 3208 break; 3209 3210 case 'instance_node': 3211 data.instanceNodes.push( parseId( child.getAttribute( 'url' ) ) ); 3212 break; 3213 3214 case 'matrix': 3215 var array = parseFloats( child.textContent ); 3216 data.matrix.multiply( matrix.fromArray( array ).transpose() ); 3217 data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; 3218 break; 3219 3220 case 'translate': 3221 var array = parseFloats( child.textContent ); 3222 vector.fromArray( array ); 3223 data.matrix.multiply( matrix.makeTranslation( vector.x, vector.y, vector.z ) ); 3224 data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; 3225 break; 3226 3227 case 'rotate': 3228 var array = parseFloats( child.textContent ); 3229 var angle = THREE.Math.degToRad( array[ 3 ] ); 3230 data.matrix.multiply( matrix.makeRotationAxis( vector.fromArray( array ), angle ) ); 3231 data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; 3232 break; 3233 3234 case 'scale': 3235 var array = parseFloats( child.textContent ); 3236 data.matrix.scale( vector.fromArray( array ) ); 3237 data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName; 3238 break; 3239 3240 case 'extra': 3241 break; 3242 3243 default: 3244 console.log( child ); 3245 3246 } 3247 3248 } 3249 3250 if ( hasNode( data.id ) ) { 3251 3252 console.warn( 'THREE.ColladaLoader: There is already a node with ID %s. Exclude current node from further processing.', data.id ); 3253 3254 } else { 3255 3256 library.nodes[ data.id ] = data; 3257 3258 } 3259 3260 return data; 3261 3262 } 3263 3264 function parseNodeInstance( xml ) { 3265 3266 var data = { 3267 id: parseId( xml.getAttribute( 'url' ) ), 3268 materials: {}, 3269 skeletons: [] 3270 }; 3271 3272 for ( var i = 0; i < xml.childNodes.length; i ++ ) { 3273 3274 var child = xml.childNodes[ i ]; 3275 3276 switch ( child.nodeName ) { 3277 3278 case 'bind_material': 3279 var instances = child.getElementsByTagName( 'instance_material' ); 3280 3281 for ( var j = 0; j < instances.length; j ++ ) { 3282 3283 var instance = instances[ j ]; 3284 var symbol = instance.getAttribute( 'symbol' ); 3285 var target = instance.getAttribute( 'target' ); 3286 3287 data.materials[ symbol ] = parseId( target ); 3288 3289 } 3290 3291 break; 3292 3293 case 'skeleton': 3294 data.skeletons.push( parseId( child.textContent ) ); 3295 break; 3296 3297 default: 3298 break; 3299 3300 } 3301 3302 } 3303 3304 return data; 3305 3306 } 3307 3308 function buildSkeleton( skeletons, joints ) { 3309 3310 var boneData = []; 3311 var sortedBoneData = []; 3312 3313 var i, j, data; 3314 3315 // a skeleton can have multiple root bones. collada expresses this 3316 // situtation with multiple "skeleton" tags per controller instance 3317 3318 for ( i = 0; i < skeletons.length; i ++ ) { 3319 3320 var skeleton = skeletons[ i ]; 3321 3322 var root; 3323 3324 if ( hasNode( skeleton ) ) { 3325 3326 root = getNode( skeleton ); 3327 buildBoneHierarchy( root, joints, boneData ); 3328 3329 } else if ( hasVisualScene( skeleton ) ) { 3330 3331 // handle case where the skeleton refers to the visual scene (#13335) 3332 3333 var visualScene = library.visualScenes[ skeleton ]; 3334 var children = visualScene.children; 3335 3336 for ( var j = 0; j < children.length; j ++ ) { 3337 3338 var child = children[ j ]; 3339 3340 if ( child.type === 'JOINT' ) { 3341 3342 var root = getNode( child.id ); 3343 buildBoneHierarchy( root, joints, boneData ); 3344 3345 } 3346 3347 } 3348 3349 } else { 3350 3351 console.error( 'THREE.ColladaLoader: Unable to find root bone of skeleton with ID:', skeleton ); 3352 3353 } 3354 3355 } 3356 3357 // sort bone data (the order is defined in the corresponding controller) 3358 3359 for ( i = 0; i < joints.length; i ++ ) { 3360 3361 for ( j = 0; j < boneData.length; j ++ ) { 3362 3363 data = boneData[ j ]; 3364 3365 if ( data.bone.name === joints[ i ].name ) { 3366 3367 sortedBoneData[ i ] = data; 3368 data.processed = true; 3369 break; 3370 3371 } 3372 3373 } 3374 3375 } 3376 3377 // add unprocessed bone data at the end of the list 3378 3379 for ( i = 0; i < boneData.length; i ++ ) { 3380 3381 data = boneData[ i ]; 3382 3383 if ( data.processed === false ) { 3384 3385 sortedBoneData.push( data ); 3386 data.processed = true; 3387 3388 } 3389 3390 } 3391 3392 // setup arrays for skeleton creation 3393 3394 var bones = []; 3395 var boneInverses = []; 3396 3397 for ( i = 0; i < sortedBoneData.length; i ++ ) { 3398 3399 data = sortedBoneData[ i ]; 3400 3401 bones.push( data.bone ); 3402 boneInverses.push( data.boneInverse ); 3403 3404 } 3405 3406 return new THREE.Skeleton( bones, boneInverses ); 3407 3408 } 3409 3410 function buildBoneHierarchy( root, joints, boneData ) { 3411 3412 // setup bone data from visual scene 3413 3414 root.traverse( function ( object ) { 3415 3416 if ( object.isBone === true ) { 3417 3418 var boneInverse; 3419 3420 // retrieve the boneInverse from the controller data 3421 3422 for ( var i = 0; i < joints.length; i ++ ) { 3423 3424 var joint = joints[ i ]; 3425 3426 if ( joint.name === object.name ) { 3427 3428 boneInverse = joint.boneInverse; 3429 break; 3430 3431 } 3432 3433 } 3434 3435 if ( boneInverse === undefined ) { 3436 3437 // Unfortunately, there can be joints in the visual scene that are not part of the 3438 // corresponding controller. In this case, we have to create a dummy boneInverse matrix 3439 // for the respective bone. This bone won't affect any vertices, because there are no skin indices 3440 // and weights defined for it. But we still have to add the bone to the sorted bone list in order to 3441 // ensure a correct animation of the model. 3442 3443 boneInverse = new THREE.Matrix4(); 3444 3445 } 3446 3447 boneData.push( { bone: object, boneInverse: boneInverse, processed: fal
3447se } ); 3448 3449 } 3450 3451 } ); 3452 3453 } 3454 3455 function buildNode( data ) { 3456 3457 var objects = []; 3458 3459 var matrix = data.matrix; 3460 var nodes = data.nodes; 3461 var type = data.type; 3462 var instanceCameras = data.instanceCameras; 3463 var instanceControllers = data.instanceControllers; 3464 var instanceLights = data.instanceLights; 3465 var instanceGeometries = data.instanceGeometries; 3466 var instanceNodes = data.instanceNodes; 3467 3468 // nodes 3469 3470 for ( var i = 0, l = nodes.length; i < l; i ++ ) { 3471 3472 objects.push( getNode( nodes[ i ] ) ); 3473 3474 } 3475 3476 // instance cameras 3477 3478 for ( var i = 0, l = instanceCameras.length; i < l; i ++ ) { 3479 3480 var instanceCamera = getCamera( instanceCameras[ i ] ); 3481 3482 if ( instanceCamera !== null ) { 3483 3484 objects.push( instanceCamera.clone() ); 3485 3486 } 3487 3488 } 3489 3490 // instance controllers 3491 3492 for ( var i = 0, l = instanceControllers.length; i < l; i ++ ) { 3493 3494 var instance = instanceControllers[ i ]; 3495 var controller = getController( instance.id ); 3496 var geometries = getGeometry( controller.id ); 3497 var newObjects = buildObjects( geometries, instance.materials ); 3498 3499 var skeletons = instance.skeletons; 3500 var joints = controller.skin.joints; 3501 3502 var skeleton = buildSkeleton( skeletons, joints ); 3503 3504 for ( var j = 0, jl = newObjects.length; j < jl; j ++ ) { 3505 3506 var object = newObjects[ j ]; 3507 3508 if ( object.isSkinnedMesh ) { 3509 3510 object.bind( skeleton, controller.skin.bindMatrix ); 3511 object.normalizeSkinWeights(); 3512 3513 } 3514 3515 objects.push( object ); 3516 3517 } 3518 3519 } 3520 3521 // instance lights 3522 3523 for ( var i = 0, l = instanceLights.length; i < l; i ++ ) { 3524 3525 var instanceLight = getLight( instanceLights[ i ] ); 3526 3527 if ( instanceLight !== null ) { 3528 3529 objects.push( instanceLight.clone() ); 3530 3531 } 3532 3533 } 3534 3535 // instance geometries 3536 3537 for ( var i = 0, l = instanceGeometries.length; i < l; i ++ ) { 3538 3539 var instance = instanceGeometries[ i ]; 3540 3541 // a single geometry instance in collada can lead to multiple object3Ds. 3542 // this is the case when primitives are combined like triangles and lines 3543 3544 var geometries = getGeometry( instance.id ); 3545 var newObjects = buildObjects( geometries, instance.materials ); 3546 3547 for ( var j = 0, jl = newObjects.length; j < jl; j ++ ) { 3548 3549 objects.push( newObjects[ j ] ); 3550 3551 } 3552 3553 } 3554 3555 // instance nodes 3556 3557 for ( var i = 0, l = instanceNodes.length; i < l; i ++ ) { 3558 3559 objects.push( getNode( instanceNodes[ i ] ).clone() ); 3560 3561 } 3562 3563 var object; 3564 3565 if ( nodes.length === 0 && objects.length === 1 ) { 3566 3567 object = objects[ 0 ]; 3568 3569 } else { 3570 3571 object = ( type === 'JOINT' ) ? new THREE.Bone() : new THREE.Group(); 3572 3573 for ( var i = 0; i < objects.length; i ++ ) { 3574 3575 object.add( objects[ i ] ); 3576 3577 } 3578 3579 } 3580 3581 if ( object.name === '' ) { 3582 3583 object.name = ( type === 'JOINT' ) ? data.sid : data.name; 3584 3585 } 3586 3587 object.matrix.copy( matrix ); 3588 object.matrix.decompose( object.position, object.quaternion, object.scale ); 3589 3590 return object; 3591 3592 } 3593 3594 var fallbackMaterial = new THREE.MeshBasicMaterial( { color: 0xff00ff } ); 3595 3596 function resolveMaterialBinding( keys, instanceMaterials ) { 3597 3598 var materials = []; 3599 3600 for ( var i = 0, l = keys.length; i < l; i ++ ) { 3601 3602 var id = instanceMaterials[ keys[ i ] ]; 3603 3604 if ( id === undefined ) { 3605 3606 console.warn( 'THREE.ColladaLoader: Material with key %s not found. Apply fallback material.', keys[ i ] ); 3607 materials.push( fallbackMaterial ); 3608 3609 } else { 3610 3611 materials.push( getMaterial( id ) ); 3612 3613 } 3614 3615 } 3616 3617 return materials; 3618 3619 } 3620 3621 function buildObjects( geometries, instanceMaterials ) { 3622 3623 var objects = []; 3624 3625 for ( var type in geometries ) { 3626 3627 var geometry = geometries[ type ]; 3628 3629 var materials = resolveMaterialBinding( geometry.materialKeys, instanceMaterials ); 3630 3631 // handle case if no materials are defined 3632 3633 if ( materials.length === 0 ) { 3634 3635 if ( type === 'lines' || type === 'linestrips' ) { 3636 3637 materials.push( new THREE.LineBasicMaterial() ); 3638 3639 } else { 3640 3641 materials.push( new THREE.MeshPhongMaterial() ); 3642 3643 } 3644 3645 } 3646 3647 // regard skinning 3648 3649 var skinning = ( geometry.data.attributes.skinIndex !== undefined ); 3650 3651 if ( skinning ) { 3652 3653 for ( var i = 0, l = materials.length; i < l; i ++ ) { 3654 3655 materials[ i ].skinning = true; 3656 3657 } 3658 3659 } 3660 3661 // choose between a single or multi materials (material array) 3662 3663 var material = ( materials.length === 1 ) ? materials[ 0 ] : materials; 3664 3665 // now create a specific 3D object 3666 3667 var object; 3668 3669 switch ( type ) { 3670 3671 case 'lines': 3672 object = new THREE.LineSegments( geometry.data, material ); 3673 break; 3674 3675 case 'linestrips': 3676 object = new THREE.Line( geometry.data, material ); 3677 break; 3678 3679 case 'triangles': 3680 case 'polylist': 3681 if ( skinning ) { 3682 3683 object = new THREE.SkinnedMesh( geometry.data, material ); 3684 3685 } else { 3686 3687 object = new THREE.Mesh( geometry.data, material ); 3688 3689 } 3690 break; 3691 3692 } 3693 3694 objects.push( object ); 3695 3696 } 3697 3698 return objects; 3699 3700 } 3701 3702 function hasNode( id ) { 3703 3704 return library.nodes[ id ] !== undefined; 3705 3706 } 3707 3708 function getNode( id ) { 3709 3710 return getBuild( library.nodes[ id ], buildNode ); 3711 3712 } 3713 3714 // visual scenes 3715 3716 function parseVisualScene( xml ) { 3717 3718 var data = {
3719 name: xml.getAttribute( 'name' ), 3720 children: [] 3721 }; 3722 3723 prepareNodes( xml ); 3724 3725 var elements = getElementsByTagName( xml, 'node' ); 3726 3727 for ( var i = 0; i < elements.length; i ++ ) { 3728 3729 data.children.push( parseNode( elements[ i ] ) ); 3730 3731 } 3732 3733 library.visualScenes[ xml.getAttribute( 'id' ) ] = data; 3734 3735 } 3736 3737 function buildVisualScene( data ) { 3738 3739 var group = new THREE.Group(); 3740 group.name = data.name; 3741 3742 var children = data.children; 3743 3744 for ( var i = 0; i < children.length; i ++ ) { 3745 3746 var child = children[ i ]; 3747 3748 group.add( getNode( child.id ) ); 3749 3750 } 3751 3752 return group; 3753 3754 } 3755 3756 function hasVisualScene( id ) { 3757 3758 return library.visualScenes[ id ] !== undefined; 3759 3760 } 3761 3762 function getVisualScene( id ) { 3763 3764 return getBuild( library.visualScenes[ id ], buildVisualScene ); 3765 3766 } 3767 3768 // scenes 3769 3770 function parseScene( xml ) { 3771 3772 var instance = getElementsByTagName( xml, 'instance_visual_scene' )[ 0 ]; 3773 return getVisualScene( parseId( instance.getAttribute( 'url' ) ) ); 3774 3775 } 3776 3777 function setupAnimations() { 3778 3779 var clips = library.clips; 3780 3781 if ( isEmpty( clips ) === true ) { 3782 3783 if ( isEmpty( library.animations ) === false ) { 3784 3785 // if there are animations but no clips, we create a default clip for playback 3786 3787 var tracks = []; 3788 3789 for ( var id in library.animations ) { 3790 3791 var animationTracks = getAnimation( id ); 3792 3793 for ( var i = 0, l = animationTracks.length; i < l; i ++ ) { 3794 3795 tracks.push( animationTracks[ i ] ); 3796 3797 } 3798 3799 } 3800 3801 animations.push( new THREE.AnimationClip( 'default', - 1, tracks ) ); 3802 3803 } 3804 3805 } else { 3806 3807 for ( var id in clips ) { 3808 3809 animations.push( getAnimationClip( id ) ); 3810 3811 } 3812 3813 } 3814 3815 } 3816 3817 // convert the parser error element into text with each child elements text 3818 // separated by new lines. 3819 3820 function parserErrorToText( parserError ) { 3821 3822 var result = ''; 3823 var stack = [ parserError ]; 3824 3825 while ( stack.length ) { 3826 3827 var node = stack.shift(); 3828 3829 if ( node.nodeType === Node.TEXT_NODE ) { 3830 3831 result += node.textContent; 3832 3833 } else { 3834 3835 result += '\n'; 3836 stack.push.apply( stack, node.childNodes ); 3837 3838 } 3839 3840 } 3841 3842 return result.trim(); 3843 3844 } 3845 3846 if ( text.length === 0 ) { 3847 3848 return { scene: new THREE.Scene() }; 3849 3850 } 3851 3852 var xml = new DOMParser().parseFromString( text, 'application/xml' ); 3853 3854 var collada = getElementsByTagName( xml, 'COLLADA' )[ 0 ]; 3855 3856 var parserError = xml.getElementsByTagName( 'parsererror' )[ 0 ]; 3857 if ( parserError !== undefined ) { 3858 3859 // Chrome will return parser error with a div in it 3860 3861 var errorElement = getElementsByTagName( parserError, 'div' )[ 0 ]; 3862 var errorText; 3863 3864 if ( errorElement ) { 3865 3866 errorText = errorElement.textContent; 3867 3868 } else { 3869 3870 errorText = parserErrorToText( parserError ); 3871 3872 } 3873 3874 console.error( 'THREE.ColladaLoader: Failed to parse collada file.\n', errorText ); 3875 3876 return null; 3877 3878 } 3879 3880 // metadata 3881 3882 var version = collada.getAttribute( 'version' ); 3883 console.log( 'THREE.ColladaLoader: File version', version ); 3884 3885 var asset = parseAsset( getElementsByTagName( collada, 'asset' )[ 0 ] ); 3886 var textureLoader = new THREE.TextureLoader( this.manager ); 3887 textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); 3888 3889 var tgaLoader; 3890 3891 if ( THREE.TGALoader ) { 3892 3893 tgaLoader = new THREE.TGALoader( this.manager ); 3894 tgaLoader.setPath( this.resourcePath || path ); 3895 3896 } 3897 3898 // 3899 3900 var animations = []; 3901 var kinematics = {}; 3902 var count = 0; 3903 3904 // 3905 3906 var library = { 3907 animations: {}, 3908 clips: {}, 3909 controllers: {}, 3910 images: {}, 3911 effects: {}, 3912 materials: {}, 3913 cameras: {}, 3914 lights: {}, 3915 geometries: {}, 3916 nodes: {}, 3917 visualScenes: {}, 3918 kinematicsModels: {}, 3919 physicsModels: {}, 3920 kinematicsScenes: {} 3921 }; 3922 3923 parseLibrary( collada, 'library_animations', 'animation', parseAnimation ); 3924 parseLibrary( collada, 'library_animation_clips', 'animation_clip', parseAnimationClip ); 3925 parseLibrary( collada, 'library_controllers', 'controller', parseController ); 3926 parseLibrary( collada, 'library_images', 'image', parseImage ); 3927 parseLibrary( collada, 'library_effects', 'effect', parseEffect ); 3928 parseLibrary( collada, 'library_materials', 'material', parseMaterial ); 3929 parseLibrary( collada, 'library_cameras', 'camera', parseCamera ); 3930 parseLibrary( collada, 'library_lights', 'light', parseLight ); 3931 parseLibrary( collada, 'library_geometries', 'geometry', parseGeometry ); 3932 parseLibrary( collada, 'library_nodes', 'node', parseNode ); 3933 parseLibrary( collada, 'library_visual_scenes', 'visual_scene', parseVisualScene ); 3934 parseLibrary( collada, 'library_kinematics_models', 'kinematics_model', parseKinematicsModel ); 3935 parseLibrary( collada, 'library_physics_models', 'physics_model', parsePhysicsModel ); 3936 parseLibrary( collada, 'scene', 'instance_kinematics_scene', parseKinematicsScene ); 3937 3938 buildLibrary( library.animations, buildAnimation ); 3939 buildLibrary( library.clips, buildAnimationClip ); 3940 buildLibrary( library.controllers, buildController ); 3941 buildLibrary( library.images, buildImage ); 3942 buildLibrary( library.effects, buildEffect ); 3943 buildLibrary( library.materials, buildMaterial ); 3944 buildLibrary( library.cameras, buildCamera ); 3945 buildLibrary( library.lights, buildLight ); 3946 buildLibrary( library.geometries, buildGeometry ); 3947 buildLibrary( library.visualScenes, buildVisualScene ); 3948 3949 setupAnimations(); 3950 setupKinematics(); 3951 3952 var scene = parseScene( getElementsByTagName( collada, 'scene' )[ 0 ] ); 3953 3954 if ( asset.upAxis === 'Z_UP' ) { 3955 3956 scene.quaternion.setFromEuler( new THREE.Euler( - Math.PI / 2, 0, 0 ) ); 3957 3958 } 3959 3960 scene.scale.multiplyScalar( asset.unit ); 3961 3962 return { 3963 animations: animations, 3964 kinematics: kinematics, 3965 library: library, 3966 scene: scene 3967 }; 3968 3969 } 3970 3971};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.