PageSourceSearch

https://www.mhv-gifhorn.de/wp-content/plugins/vrm360/js/ColladaLoader.js?ver=5.6

js mhv-gifhorn.de collected 2026-10-02 20:37:32 UTC 80,140 bytes, 3,971 lines download raw bytes

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.