1 /*
  2   Copyright (c) 2008 Seneca College
  3   Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/)
  4 */
  5 
  6 /**
  7 	@private
  8 	
  9 	class ColladaLoader is used by the ModelManager to load .DAE (COLLADA) files.
 10 */
 11 c3dl.ColladaLoader = function()
 12 {
 13 	var XHR_STATE_COMPLETED = 4;
 14 
 15 	var xmlhttp = null;
 16 	this.done = false;
 17 	this.name = "";
 18 	this.rootNode = new c3dl.SceneNode();
 19 
 20 	/**
 21 		@private
 22 		Opens the DAE file, reads the vertex, normal and uv data and stores 
 23 		all the data in 'expanded' form into members.
 24 		
 25 		@param {String} relativePath
 26 		@param {c3dl.SceneNode} rootNode
 27 	*/
 28 	this.load = function(relativePath, rootNode)
 29 	{
 30 		//
 31 		this.rootNode = rootNode;
 32 
 33 		xmlhttp = new XMLHttpRequest();
 34 		
 35 		//
 36 		xmlhttp.parent = this;
 37 
 38 		// call the parse function when XMLHttpRequest is ready.
 39 		xmlhttp.callbackFunc = this.parse;
 40 		xmlhttp.open("GET", relativePath, true);
 41 		xmlhttp.overrideMimeType('text/xml');
 42 
 43 		// this may throw an exception if the file isn't found, so 
 44 		// catch the exception and give the user a helpful warning message.
 45 		try
 46 		{
 47 			// send the request
 48 			xmlhttp.send(null);
 49 		}
 50 		catch(err)
 51 		{
 52 			c3dl.debug.logWarning("Could not find file '" + relativePath +"'. Check the path.");
 53 		}
 54 
 55 		/**
 56 			@private
 57 		*/
 58 		xmlhttp.onreadystatechange = function()
 59 		{
 60 			// when the state has changed to being finished, 
 61 			if(xmlhttp.readyState == XHR_STATE_COMPLETED)
 62 			{
 63 				// this line may not be totally nnecessary.
 64 				if(xmlhttp.responseXML)
 65 				{
 66 					xmlhttp.responseXML.colladaPath = relativePath;
 67 
 68 					// we can now parse by calling the callback which 
 69 					// was set the the parse function.
 70 					this.callbackFunc(xmlhttp.responseXML);
 71 				}
 72 			}
 73 		}
 74 	}
 75 
 76 	
 77 	/**
 78 		@private
 79 
 80 		Parse a node from the DAE file.  The part of the DAE file
 81 		we are interested with is the scenegraph which is a hierarchical
 82 		structure of nodes.  Nodes therefore can contain other nodes, thus
 83 		we use a recursive function to parse each one.
 84 
 85 		// clarify nodes in sg nodes in _DOM_
 86 		@param {xmlDocument} xmlObject The DAE DOM.
 87 		@param {} node The node element to parse. the DOM
 88 		@param {c3dl.SceneNode} sgNode The SceneGraph node, not part of the XML DOM.
 89 	*/
 90 	this.parseNodeRecursive = function(xmlObject, node, sgNode)
 91 	{
 92 		// set this node's transform
 93 		var translateTag = c3dl.ColladaLoader.getChildNodesByNodeName(node, "translate");
 94 
 95 		// node may not have one, so check first
 96 		if(translateTag)
 97 		{
 98 			// string representation of data between <translate> tags.
 99 			var floatValues = c3dl.ColladaLoader.stringsToFloats(translateTag[0].childNodes[0].nodeValue, ' ');
100 			sgNode.translate(floatValues);
101 		}
102 
103 		// rotations
104 		var rotationTags = c3dl.ColladaLoader.getChildNodesByNodeName(node, "rotate");
105 		
106 		if(rotationTags)
107 		{
108 			// example
109 			// <rotate sid="rotateZ">0 0 1 15</rotate>
110 			// <rotate sid="rotateY">0 1 0 0</rotate>
111 			// <rotate sid="rotateX">1 0 0 0</rotate>
112 			for(var i=0; i < rotationTags.length; i++)
113 			{
114 				var floatValues = c3dl.ColladaLoader.stringsToFloats(rotationTags[i].childNodes[0].nodeValue, ' ');
115 
116 				var vec = [floatValues[0], floatValues[1],floatValues[2]];
117 				var angle = c3dl.degreesToRadians(floatValues[3]);
118 
119 				sgNode.rotateOnAxis(vec, angle);
120 			}
121 		}
122 
123 		// <scale> tag
124 		var scaleTag = c3dl.ColladaLoader.getChildNodesByNodeName(node, "scale");
125 		if(scaleTag)
126 		{
127 			var floatValues = c3dl.ColladaLoader.stringsToFloats(scaleTag[0].childNodes[0].nodeValue, ' ');
128 			sgNode.scale(floatValues);
129 		}
130 		
131 		// <matrix> tag specifies the matrix of the node instead of
132 		// a scale, translate and rotate.  
133 		//
134 		//  The set of 16 numbers can have values with leading or trailing spaces, so we have to 
135 		// 
136 		var matrixTag = c3dl.ColladaLoader.getChildNodesByNodeName(node, "matrix");
137 		if(matrixTag)
138 		{
139 			var floatValues = c3dl.ColladaLoader.stringsToFloats(matrixTag[0].childNodes[0].nodeValue, ' ');
140 			sgNode.setTransform(c3dl.transposeMatrix(floatValues));
141 		}
142 		
143 
144 		// At this point, this node's child nodes have been parsed or it
145 		// did not contain subnodes.
146 		
147 		// get the geometryNodes of this node.
148 		var geometries = c3dl.ColladaLoader.getChildNodesByNodeName(node,"instance_geometry");
149 
150 		// base case: we found a geometry leaf node, now instantiate it.
151 		if(geometries)
152 		{
153 			// a node may contain many geometryNodes, so we have to create each one.
154 			for (var currGeo = 0; currGeo < geometries.length; currGeo++)
155 			{
156 				// the url references a <geometry> element within the <library_geometry>
157 				var url = geometries[currGeo].getAttribute("url").split('#')[1];
158 			
159 				// A separate function exists to actually create the geometry which can
160 				// be pretty messy going through the DOM and constructing the geometry.
161 				sgNode.addChild(this.instantiateGeometry(xmlObject, url, geometries[currGeo]));
162 			}
163 		}
164 		
165 		//
166 		// <instance_node> appears after <instance_geometry>
167 		//
168 		var instance_nodes = c3dl.ColladaLoader.getChildNodesByNodeName(node, "instance_node");
169 		if(instance_nodes)
170 		{	
171 			// a <node> can contain 0..N <instance_node> so iterate over each one
172 			for(var currNode = 0; currNode < instance_nodes.length; currNode++)
173 			{
174 				// remove the '#' from the url
175 				var url = instance_nodes[currNode].getAttribute("url").split('#')[1];
176 				sgNode.addChild(this.instantiateNode(xmlObject, url));
177 			}
178 		}
179 				
180 		
181 		// get the DIRECT child nodes of this node.  We can't use
182 		// getElementsByTagName since its recursive and will
183 		// return this node's grandchildren, we don't want that. 
184 		var nodes = c3dl.ColladaLoader.getChildNodesByNodeName(node, "node");
185 		
186 		// recursive case: the node has one or many nodes, therefore
187 		// we have to parse all of its nodes.
188 		if(nodes)
189 		{
190 			// for each of subnodes, call this function.
191 			for(var i=0; i < nodes.length; i++)
192 			{
193 				var scenenode = new c3dl.SceneNode();
194 				scenenode.setName(nodes[i].getAttribute("name"));
195 				sgNode.addChild(scenenode);
196 
197 				// call this function for each of the nodes found.
198 				this.parseNodeRecursive(xmlObject,nodes[i],scenenode);
199 			}
200 		}
201 	}
202 	
203 	/**
204 		@private
205 
206 		Get the child element of a parent element in the scenario when a schema can 
207 		have only one of set of children. For example, the technique element in the 
208 		common profile can have either constant, lambert, phong or blinn child elements
209 		which are mutually exclusive.
210 
211 		@returns {object Element}
212 			
213 		@param {Array} choiceTagNames Array of strings which contain the tags of
214 		choices an element can have.
215 	*/
216 	this.getChoice = function(parentTag, choiceTagNames)
217 	{		
218 		var choice = null;
219 		var i = 0;
220 		
221 		// use an iterator just in case the file does not conform to the spec
222 		// and we can just out of the loop
223 		while( choice == null && i < choiceTagNames.length)
224 		{
225 			choice = parentTag.getElementsByTagName(choiceTagNames[i])[0];
226 			i++;
227 		}
228 
229 		return choice;
230 	}
231 
232 	/**
233 		@private
234 		Parse is responsible for traversing different parts of the xmlObject
235 		and constructing the geometries from the data provided.
236 		
237 		This function is not called directly, it is called once the xml is 
238 		finished downloading.
239 		
240 		@param {xmlDocument} xmlObject
241 	*/
242 	this.parse = function(xmlObject)
243 	{
244 		// since the xmlhttp object is calling this function, any reference
245 		// to 'this' refers to the xmlhttp object, but we want to access the members in
246 		// the object that owns this function, the DAELoader instance.  To
247 		// do this, simply make a reference to the this.parent which was set
248 		// earlier, now we can access the members we need.
249 		var loader = this.parent;
250 		
251 		// get the root element of the xml document, to keep the naming short.
252 		var root = xmlObject.documentElement;
253 
254 		// load the images listed in the dae file under the
255 		// library_images into the texture manager.
256 		var library_images = root.getElementsByTagName("library_images");
257 		
258 		// <collada> may have 0 or many <library_images>
259 		for(var libraryImagesIter = 0; libraryImagesIter < library_images.length; libraryImagesIter++)
260 		{
261 			// one <library_images> has <images>. cardinality isn't mentioned in the spec.
262 			var imageElements = library_images[libraryImagesIter].getElementsByTagName("image");
263 		
264 			// 
265 			for(var imageElementIter = 0; imageElementIter < imageElements.length; imageElementIter++)
266 			{
267 				// an <image> has exactly one <init_from> which have the uri or the
268 				// texture.
269 				//	<image id="file2" name="file2" depth="1">
270 				//		<init_from>./duckCM.tga</init_from>
271 				//	</image>
272 				//
273 				var init_from = imageElements[imageElementIter].getElementsByTagName("init_from")[0];
274 			}
275 		}
276 
277 		// we start at the scene tag. A document instance 
278 		// can contain zero or 1 <scene> tags, therefore we
279 		// can address the 0th element.  If the
280 		// scene tag is not present, this is an error, as 
281 		// we are concerned with some sort of visual scene.
282 		// TODO: handle no scene elements.
283 		var sceneElement = root.getElementsByTagName("scene")[0];
284 
285 		// A scene element can contain ZERO OR ONE of each of the following 
286 		// elements:
287 		// <instance_visual_scene>
288 		// <instance_physics_scene>
289 		// again, we are concerned only with visual stuff right now
290 		// so if the element <instance_visual_scene> is not present, 
291 		// its another error. the <instance_visual_scene> has an attribute that acts as
292 		// a foreign key into an element in the library_visual_scenes
293 		// tag.
294 		// TODO: handle no <instance_visual_scene> element. This can occur if the
295 		// user tries to load a 1.3 Collada instance.
296 		var instanceVisualSceneElem = sceneElement.getElementsByTagName("instance_visual_scene")[0];
297 
298 		// get the url attribute of the <instance_visual_scene>
299 		// this will tell us which scene to load. (we will be loading one scene from the 
300 		// library of scenes).
301 		// this has a # in front of it so we have to remove it.
302 		var visualSceneToLoad = instanceVisualSceneElem.getAttribute("url").split('#')[1];
303 
304 		// a collada document has ZERO OR MANY <library_visual_scenes>
305 		// which in turn have ONE OR MANY <visual_scene>'s.
306 		// a <visual_scene> is the base of a scenegraph, which is what we 
307 		// want.  Note that even if <library_visual_scenes> has
308 		// more than one <visual_scene>'s, only one will be loaded.
309 		// The one to load is the the value found earlier in the 
310 		// <instance_visual_scene>'s url attribute.
311 		// so first, go the the collada's/root's <library_visual_scenes>
312 		// TODO: can't access the first node since there may be many library_visual_scenes,
313 		// we have to iterate over the libraries until we find the visualScene we want.
314 		var libraryVisualScenes = root.getElementsByTagName("library_visual_scenes")[0];
315 
316 		// added 'List' to avoid confusion with 'visualScene', which is the
317 		// scene we will load.
318 		// This is the list of visual scenes.  Only one of which we will actually load.
319 		var visualSceneList = libraryVisualScenes.getElementsByTagName("visual_scene");
320 
321 		// Find the <visual_scene> element which we want, buy
322 		// using the 'foreign key' we got from the <instance_visual_scene>.
323 		var visualScene = null;
324 
325 		// go over all the visual scenes trying to identify the one we want.
326 		for(var i = 0; i < visualSceneList.length; i++)
327 		{
328 			if( visualSceneList[i].getAttribute("id") == visualSceneToLoad)
329 			{
330 				visualScene = visualSceneList[i];
331 			}
332 		}
333 
334 		// we are at the start of the visual_scene tag, this may have many
335 		// nodes
336 
337 		// we now should have the visual_scene to load.
338 		// A visual scene has ONE OR MANY <node> elements which compose the scenegraph.
339 		var nodes = c3dl.ColladaLoader.getChildNodesByNodeName(visualScene,"node");
340 
341 		// there is a change nodes is null if the dae file was edited manually
342 		// and the nodes were removed. 
343 		if(nodes)
344 		{
345 			// parse each of the 'root' nodes.
346 			for(var currNode = 0; currNode < nodes.length; currNode++)
347 			{
348 				var scenenode = new c3dl.SceneNode();
349 				
350 				//
351 				loader.rootNode.addChild(scenenode);	
352 				scenenode.setName(nodes[currNode].getAttribute("name"));
353 
354 				loader.parseNodeRecursive(xmlObject, nodes[currNode], scenenode);
355 			}
356 		}
357 
358 		// TODO: comment
359 		c3dl.ColladaQueue.popFront();
360 		delete xmlObject;
361 		delete xmlhttp;
362 	}
363 
364 	/**
365 	*/
366 	
367 
368 	/**
369 		@private
370 
371 		@param {XMLDocument} xmlObject
372 		@param {String} target
373 	*/
374 	this.instantiateMaterial = function(xmlObject, target)
375 	{
376 		var tempTexture = null;
377 		
378 		// we now have the material ID which we can look up in the library materials.
379 		var material = this.findElementInLibrary(xmlObject, "library_materials", "material", target);
380 		var tempName = target;
381 		
382 		// a <material> has exactly 1 <instance_effect>, so just get the first.
383 		//
384 		//<library_materials>
385 		//	<material id="shine" name="shine">
386 		//		<instance_effect url="#shine-fx"/>
387 		//	</material>
388 		//	<material id="matte" name="matte">
389 		//		<instance_effect url="#matte-fx"/>
390 		//	</material>
391 		//</library_materials>
392 		var instanceEffect = material.getElementsByTagName("instance_effect")[0];
393 		var instanceEffectURL = instanceEffect.getAttribute("url").split('#')[1];
394 
395 		// go to the <library_effects> since we have the <instance_effect>
396 		// and it points to an entry in the library.
397 		//
398 		//
399 		var effect = this.findElementInLibrary(xmlObject, "library_effects", "effect", instanceEffectURL);
400 
401 		// An effect has 1..N profiles. Each profile is designed for a specific platform,
402 		// usage scenario, etc.
403 		//
404 		// profile types include:
405 		// profile_COMMON - used for basic interchange between DCCs.
406 		// profile_CG - opengl and NVIDIA's Cg shading language.
407 		// profile_GLSL - opengl & glslang
408 		// profile_GLES - opengl 1.0 and 1.1
409 		//
410 		// Collada book states support for GLES 2.0 and HLSL are in development,
411 		// has this already been released?
412 		//
413 		// Right now we will focus on the fallback, profile_COMMON since it seems to be 
414 		// the safest profile and will likely be available. This should be understood 
415 		// by every application. We use it now as a default, later other profiles can 
416 		// be supported, namely GLSL/GLES.
417 		var profile_COMMON = effect.getElementsByTagName("profile_COMMON")[0];
418 		
419 		// In each technique in the profile_COMMON profile is one of the  shader 
420 		// algorithms: blinn, phong, constant or lambert. These shading algorithms
421 		// are used in DCC tools which do not have adopted a shading language, therefore
422 		// are using fixed functionality. Blinn shading algorithm is used by most DCC 
423 		// tools and therefore most common.
424 		var technique = profile_COMMON.getElementsByTagName("technique")[0];
425 		
426 		// get the texture
427 		var newparam = profile_COMMON.getElementsByTagName("newparam")[0];
428 		
429 		if( newparam)
430 		{
431 			// go to the surface type
432 			var surface = newparam.getElementsByTagName("surface")[0];
433 
434 			// get the value between the <init_from> tags
435 			// the plane would have:
436 			// <init_from>file1</init_from>
437 			// it also has a format, but we ignore this for now.
438 			var init_from = surface.getElementsByTagName("init_from")[0];
439 
440 			// got the file id.
441 			var fileID = init_from.childNodes[0].nodeValue;
442 
443 			// file1 is an id of an image in the <library_images> library
444 			var texture = this.findElementInLibrary(xmlObject, "library_images", "image", fileID);
445 
446 			// finally, get the image name
447 			//<image id="file2" name="file2" depth="1">
448 			//	<init_from>./duckCM.tga</init_from>
449 			//</image>
450 			var textureName = texture.getElementsByTagName("init_from")[0].childNodes[0].nodeValue;
451 
452 			var resolvedTexture;
453 
454 			// if the texture is an abosolute path, use it.
455 			if(c3dl.isPathAbsolute(textureName))
456 			{
457 				resolvedTexture = textureName;
458 			}
459 			// otherwise, we need to place the path of dae file before the texture.
460 			else
461 			{
462 				resolvedTexture = c3dl.getPathWithoutFilename(xmlObject.colladaPath) + textureName;
463 			}
464 			tempTexture = resolvedTexture;
465 		}
466 		
467 		// get the shading algorithm used.
468 		// as of right now, we aren't concerned with the algorithm itself, (we use our
469 		// own custom shading algorithm) but what we want to extract are the properties
470 		// of the shading method which include diffuse, ambient, specular, etc. components.
471 		var shadingAlgorithm = this.getChoice(technique, ["blinn", "constant", "phong", "lambert"]);
472 
473 		var mat = new c3dl.Material();
474 		//mat.texture = tempTexture;
475 		mat.setName(tempName);
476 		mat.setAmbient(this.getColor(shadingAlgorithm, "ambient"));
477 		mat.setDiffuse(this.getColor(shadingAlgorithm, "diffuse"));
478 		mat.setEmission(this.getColor(shadingAlgorithm, "emission"));
479 		mat.setSpecular(this.getColor(shadingAlgorithm, "specular"));
480 		mat.setShininess(this.getColor(shadingAlgorithm, "shininess"));
481 		
482 		return [mat, tempTexture];
483 	}
484 
485 	/**
486 		@private
487 
488 		The root COLLADA can contain ZERO OR MANY library_geometries node.
489 		However in our case, since we are importing model data, should
490 		have at least one library_geometries.  If absent this will cause
491 		an error.
492 		
493 		The library geometries node contains 1 or Many <geometry>
494 		nodes.
495 		If library_geometries describes a car,
496 		there may be several <geometries> which described the chairs,
497 		seats, body, etc.
498 		
499 		@param {XMLDocument} xmlObject
500 		@param url
501 		@param instanceGeometryElement
502 	*/
503 	this.instantiateGeometry = function(xmlObject, url, instanceGeometryElement)
504 	{
505 		var root = xmlObject.documentElement;
506 		var libraryGeometries = root.getElementsByTagName("library_geometries");
507 
508 		// once not null, we can stop searching.
509 		var geoToCreate = null;
510 		var geometry = new c3dl.Geometry();		
511 
512 		// the url provided points to a geometry in a geometry library. We'll need to
513 		// go through the libraries and find which library it is in.
514 
515 		// TODO: add breakout when found
516 		for (var currLib = 0; currLib < libraryGeometries.length; currLib++)
517 		{
518 			var geometries = libraryGeometries[currLib].getElementsByTagName("geometry");
519 			// for each geometry
520 			for(var currGeo =0; currGeo < geometries.length; currGeo++)
521 			{
522 				if( geometries[currGeo].getAttribute("id") == url)
523 				{
524 					// found it
525 					geoToCreate = geometries[currGeo];
526 				}
527 			}
528 		}
529 
530 		var verticesArray = null;
531 		var vertexStride;
532 
533 		var normalsArray = null;
534 		var normalsStride;
535 
536 		var texCoordsArray = null;
537 		var texCoordsStride;
538 		
539 		var faces = null;
540 		var rawFaces;
541 
542 		// the library geometry will have a <mesh> which will have one or many <triangles>.
543 		// we have to go over each <triangle> element and construct it.
544 
545 		// <geometry> contains all the data which describes a geometric object.
546 		// <geometry> contains <mesh>, which in turn contains collation elements.
547 		// these collation elements describe parts of the mesh differently either
548 		// using polygons or lines.
549 		//
550 		// a geometry contains only one mesh node, so just get the first index.
551 		// there are other kinds of meshes a geometry can have, but for now
552 		// we aren't supporting them (convex_mesh, brep, spline, ...)
553 		var mesh = geoToCreate.getElementsByTagName("mesh")[0];
554 
555 		// we'll need to iterate over all the collation elements.
556 		var collations = [];
557 		
558 		// A mesh is composed of a set of collation elements.
559 		// Types of collation elements are 
560 		// <lines>, <linestrips>,
561 		// <polygons>, <polylist>,
562 		// <triangles>, <tristrips>, <trifans>
563 		//
564 		// polygons can be a set of 3 or more vertices, therefore, we'll need to divide the polygons
565 		// into triangles since GLES does not support quads or polygon rendering.
566 		//
567 		// The library does have means to render lines, but reading this primitive has not been added yet.
568 		//
569 		for(var i = 0; i < mesh.childNodes.length; i++)
570 		{
571 			if(	mesh.childNodes[i].nodeName == "triangles" || 
572 				mesh.childNodes[i].nodeName == "polygons" ||
573 				mesh.childNodes[i].nodeName == "polylist")
574 			{
575 				collations.push(mesh.childNodes[i]);
576 			}
577 		}
578 		
579 		// the collation elements have many primitives.
580 		// <p> element represents a primitive. 
581 		//
582 		// currColl = current collation
583 		for(var currColl = 0; currColl < collations.length; currColl++)
584 		{
585 			// Depending on the type of collation element, the data will be layed out slighly differently.			
586 			//
587 			// <triangles> and <polylist> are similar in that they both only have one <p> tag.
588 			// polylist has an additional <vcount> child element which has a list of integers
589 			// which states the number of vertices per polygon (which can vary).
590 			//
591 			// triangles are always composed of 3 vertices so this element does not require <vcount>
592 			//
593 			if( collations[currColl].nodeName =="triangles" || collations[currColl].nodeName =="polylist")
594 			{	
595 				var p = this.getFirstChildByNodeName(collations[currColl],"p");
596 				rawFaces = this.mergeChildData(p.childNodes).split(" ");	
597 			}
598 
599 			// <polygon>s are broken up like this:
600 			// <p> 0 0 1 1 2 2 3 3 </p>
601 			// <p> ... </p>
602 			// each <p> element describes one polygon which can vary in length from others
603 			//
604 			else if( collations[currColl].nodeName == "polygons")
605 			{
606 				var p_tags = collations[currColl].getElementsByTagName("p");
607 				rawFaces = [];
608 				for(var i = 0; i < p_tags.length; i++)
609 				{
610 					// need to get rid of the spaces
611 					var p_line = p_tags[i].childNodes[0].nodeValue.split(" ");
612 					for(var j=0; j < p_line.length;j++)
613 					{
614 						rawFaces.push(parseInt(p_line[j]));
615 					}
616 				}
617 			}
618 			// If this message is ever seen, that means I have to write the case for it.
619 			else
620 			{
621 				c3dl.debug.logError(collations[currColl].nodeName + " collation element is not yet supported");
622 			}
623 
624 			// At this point, we finished getting the faces for one collation element, the integers which will index
625 			// into data steams. Now we move onto getting the input data streams.
626 
627 			//
628 			// get the inputs which contain the verts, normals, uvs.
629 			//
630 			// A collation element first contains the <input> tags, then <vcount> (in the case of
631 			// <polylist>) and then <p> tags. The <input> tags point to the raw data streams which 
632 			// 
633 			// The <input> tags associate a semantic to a data stream. This explains how the raw data
634 			// stream will be used for this particular context. <input> has a source attribute which
635 			// points to a source element which contains the raw data.
636 			//
637 			var inputs = collations[currColl].getElementsByTagName("input");
638 			
639 			collationElement = new c3dl.PrimitiveSet();
640 			
641 			// The order of the <input> tags can vary, so we can't rely on how they are arranged
642 			// in most documents.
643 			for (var i = 0; i < inputs.length; i++)
644 			{
645 				/**************/
646 				/*  VERTICES  */
647 				/**************/
648 				if(inputs[i].getAttribute("semantic") == "VERTEX")
649 				{				
650 					this.vertexOffset = inputs[i].getAttribute("offset");
651 					// need to remove the leading # from the value
652 					this.vertexSource = inputs[i].getAttribute("source").split('#')[1];
653 
654 					var vertices = c3dl.ColladaLoader.getNodeWithAttribute(xmlObject,"vertices", "id", this.vertexSource);
655 
656 					// get the child
657 					var input = vertices.getElementsByTagName("input")[0];
658 
659 					// get the <input>s source				
660 					var posSource = input.getAttribute("source").split('#')[1];
661 
662 					// the raw data in a long list of floats, we have to group this so the face indices
663 					// can index into it.
664 					var data = this.getData(xmlObject,"source", "id", posSource);
665 					vertexStride = parseInt(data.stride);
666 					
667 					verticesArray = this.groupScalarsIntoArray(data.values, 3,vertexStride);
668 				}
669 
670 				/**************/
671 				/*   NORMAL   */
672 				/**************/
673 				else if(inputs[i].getAttribute("semantic") == "NORMAL")
674 				{
675 					this.normalOffset = inputs[i].getAttribute("offset");
676 					// need to remove the leading # from the value
677 					this.normalSource = inputs[i].getAttribute("source").split('#')[1];
678 					var data = this.getData(xmlObject, "source", "id", this.normalSource);
679 					normalsStride = parseInt(data.stride);
680 					// length * stride instead of literal?
681 					normalsArray = this.groupScalarsIntoArray(data.values, 3,normalsStride);
682 				}
683 
684 				/**************/
685 				/*  TEXCOORD  */
686 				/**************/
687 				else if(inputs[i].getAttribute("semantic") == "TEXCOORD")
688 				{
689 					this.texCoordOffset = inputs[i].getAttribute("offset");
690 					// need to remove the leading # from the value
691 					
692 					var uvSource  = inputs[i].getAttribute("source").split('#')[1];			
693 					var data = this.getData(xmlObject,"source", "id", uvSource);
694 					texCoordsStride = parseInt(data.stride);
695 					
696 					// OpenGL ES seems to expect the bottom left corner of the
697 					// texture in the top left, so we need to flip the texture coordinates
698 					// start at 1, for V.
699 					for (var currUV = 1; currUV < data.values.length; currUV+=texCoordsStride)
700 					{
701 						data.values[currUV] = 1 - data.values[currUV];
702 					}
703 
704 					// we only want 2 values stored, but we may have to stride either 2 or
705 					// 3 depending if a zero was added
706 					// 1.0 1.0 0.0
707 					//         ^ don't need this.
708 					texCoordsArray = this.groupScalarsIntoArray(data.values, 2,texCoordsStride);
709 				}
710 			}
711 			
712 			
713 			// We now have the faces for a collation element and its input data streams,
714 			// however if the collation element is a <polylist> or <polygon>, we'll need to
715 			// re-arrange some of the faces since OpenGLES does not support quads.
716 			//  The next two conditionals handle these two cases.
717 			
718 			
719 			
720 			// <polylist> contains a list of polygons, each which can contains a different number 
721 			// of vertices (one poly has 3 another has 5).  Since we can't render polygons or even
722 			// quads in GLES, the polygons need to be broken down into triangles. Note, it is assumed
723 			// only simple convex polygons are used. (no intersections are present and no polygons are
724 			// concave). If there are intersections or the polygons are concave, they will be represented
725 			// incorrectly.
726 			if(collations[currColl].nodeName == "polylist")
727 			{
728 				rawFaces = this.splitPolylist(collations[currColl], inputs.length, rawFaces);
729 			}
730 						
731 			// before we group the individual values in the faces array into
732 			// arrays so we can easily address values in the arrays for vertices,
733 			// textures, etc, we have to convert the quads into triangles since
734 			// OpenGLES does not support the QUADS primitive mode.
735 			else if( collations[currColl].nodeName == "polygons")
736 			{
737 				// example looks like this:
738 				// 0,0,1,1,2,2,3,3,4,4,....
739 				// here is a quad and collada states its winding
740 				// order is counter clockwise, so our goal
741 				// to convert it to:
742 				// 0,0, 1,1, 3,3 3,3 1,1 2,2
743 				// which is 2 triangles.
744 				
745 				// first thing is to seperate the individual numbers into 'parts'
746 				// 0,0 is one part,  2,2 is another part.  If there are more
747 				// inputs, we have to account for that and our parts will be
748 				// larger
749 				var partSize = inputs.length;
750 				
751 				// make a new list which uses triangles and which will overrite the quads list.
752 				var trianglesList = []; 
753 				
754 				// knowing the partSize, we can use it as a stride to create a new face list
755 				
756 				// count is an attribute of polygons which lists how many primitives the
757 				// polygon has. we can use this data to find out how many times we have to
758 				// change the parts.
759 				for (var currPrim = 0; currPrim < collations[currColl].getAttribute("count"); currPrim++)
760 				{
761 					var partsArray = [];
762 					
763 					// make an array of array so we can easily index parts
764 					// use four since a polygon primitive is defined as having 4 points
765 					for (var currPart = 0; currPart < 4; currPart++)
766 					{
767 						var part = [];
768 						for(currScalar = 0; currScalar < inputs.length; currScalar++)
769 						{
770 							part.push(rawFaces[(currPrim * inputs.length * 4) + (currPart * partSize) + currScalar]);
771 						}
772 						partsArray.push(part);
773 					}
774 
775 					// need to push on the RAW values, don't push on arrays.
776 					// TODO: write a function for this.
777 					for (var s=0; s < partsArray[0].length; s++){trianglesList.push(partsArray[0][s]);}
778 					for (var s=0; s < partsArray[1].length; s++){trianglesList.push(partsArray[1][s]);}
779 					for (var s=0; s < partsArray[3].length; s++){trianglesList.push(partsArray[3][s]);}
780 					for (var s=0; s < partsArray[3].length; s++){trianglesList.push(partsArray[3][s]);}
781 					for (var s=0; s < partsArray[1].length; s++){trianglesList.push(partsArray[1][s]);}
782 					for (var s=0; s < partsArray[2].length; s++){trianglesList.push(partsArray[2][s]);}
783 				}
784 				// now we can overrite what rawFaces had in it
785 				rawFaces = trianglesList;	
786 			}// if polygons
787 			
788 			// we don't need a case for triangles since
789 			
790 			// now that we know how many inputs there were, we can group the faces.
791 			faces = this.groupScalarsIntoArray(rawFaces,inputs.length, inputs.length);
792 		
793 			// each primitive collation element can have a material name. this name matches to the
794 			// <instance_material>'s symbol attribute value.					
795 			collationElement.tempMaterial = collations[currColl].getAttribute("material");
796 			collationElement.init(	this.expandFaces(faces,verticesArray,this.vertexOffset, vertexStride),
797 									this.expandFaces(faces,normalsArray,this.normalOffset, normalsStride),
798 									this.expandFaces(faces,texCoordsArray,this.texCoordOffset,2));
799 			
800 			geometry.addPrimitiveSet(collationElement);
801 		} // end iterating over collations
802 		
803 
804 
805 		// get the texture for the geometry
806 		// go back to the instance_geometry
807 		
808 		// <instance_geometry> has either 0 or 1 <bind_material>, so just get the first if it exsits.
809 		var bind_material = instanceGeometryElement.getElementsByTagName("bind_material")[0];
810 		if( bind_material)
811 		{
812 			// <bind_material> has exactly 1 <technique_common>, so only get the first.
813 			var technique_common = bind_material.getElementsByTagName("technique_common")[0];
814 
815 			// technique_common within instance geometry may have many materials.
816 			var instance_materials = technique_common.getElementsByTagName("instance_material");
817 
818 			// iterate over all the <instance_material>'s <technique_common> has.
819 			for(var im = 0;im < instance_materials.length;im++)
820 			{
821 				// target is the target material to instantiate
822 				var target = instance_materials[im].getAttribute("target").split('#')[1];
823 				
824 				// symbol links to the primitive collation's material attribute.
825 				var symbol = instance_materials[im].getAttribute("symbol");
826 						
827 				// we now have the material ID which we can look up in the library materials.
828 				var material = this.findElementInLibrary(xmlObject, "library_materials", "material", target);
829 
830 				//
831 				var matAndTex = this.instantiateMaterial(xmlObject, target);
832 				var instanceMaterial = matAndTex[0];
833 				var tex = matAndTex[1];
834 				
835 				var GeoCollations = geometry.getPrimitiveSets();
836 				
837 				// The material was instantiated and now we have to iterate over the collations
838 				// and find the collation which has a material which matches the material name.
839 				for(var ic = 0; ic < GeoCollations.length; ic++)
840 				{
841 					if( GeoCollations[ic].tempMaterial == symbol)
842 					{
843 						GeoCollations[ic].setMaterial(instanceMaterial);
844 						GeoCollations[ic].setTexture(tex);
845 					}
846 				}
847 			}// instance_materials loop
848 		}// bind_material conditional
849 		
850 		return geometry;
851 	}
852 
853 	/**
854 		@private
855 		@param {XMLDocument} xmlObject
856 		@param {String} url
857 
858 		@returns {c3dl.SceneNode}
859 	*/
860 	this.instantiateNode = function(xmlObject, url)
861 	{
862 		var root = xmlObject.documentElement;
863 		var libraryNodes = root.getElementsByTagName("library_nodes");
864 		
865 		// domNode
866 		var nodeToCreate = null;
867 			
868 		// <collada> may have many <library_node>. We'll need to go through each to find
869 		// the node with the name we are looking for.
870 		for (var currLib = 0; currLib < libraryNodes.length /*&& nodeToCreate == null*/; currLib++)
871 		{
872 			// get all the nodes in this library.
873 			var nodes = libraryNodes[currLib].getElementsByTagName("node");
874 
875 			// find the node in the list.
876 			for(var currNode = 0; currNode < nodes.length /*&& nodeToCreate == null*/; currNode++)
877 			{
878 				if( nodes[currNode].getAttribute("id") == url)
879 				{
880 					// found it
881 					nodeToCreate = nodes[currNode];
882 				}
883 			}
884 		}
885 
886 		var inode = new c3dl.SceneNode();
887 		inode.setName(nodeToCreate.getAttribute("name"));		
888 		
889 		this.parseNodeRecursive(xmlObject, nodeToCreate, inode);
890 
891 		return inode;
892 	}	
893 
894 	/**
895 		@private
896 		This function takes a list of scalar values and 
897 		groups them into elements inside an array.  This 
898 		must be done since in the DAE file, the values are
899 		stored one after another in a linear format. Since
900 		the triangles/faces use indices to access these scalars,
901 		we have to group them together so indexing will work 
902 		properly.
903 		
904 		@param {Array} rawScalarValues
905 		@param {int} numComponentsPerElement
906 		@param {int} stride
907 		
908 		@returns {Array}
909 	*/
910 	this.groupScalarsIntoArray = function(rawScalarValues,numComponentsPerElement, stride )
911 	{	
912 		// start off with an empty list
913 		var listOfArrays = [];
914 		
915 		//
916 		for (var i = 0; i < rawScalarValues.length; i+= stride )
917 		{
918 			var element = [];
919 			
920 			//
921 			for (var j = i; j < i+numComponentsPerElement; j++)
922 			{
923 				element.push(rawScalarValues[j]);
924 			}
925 
926 			listOfArrays.push(element);
927 		}
928 
929 		return listOfArrays;
930 	}
931 	
932 
933 	/**
934 		@private
935 
936 		@param {} collation
937 		@param {int} numInputs
938 		@param {Array} rawFaces
939 	*/
940 	this.splitPolylist = function(collation, numInputs, rawFaces)
941 	{
942 		// rawFaces = this.splitPolylist(collations[currColl], inputs.length);
943 	
944 		// <vcount> has the count of vertices for each polygon.
945 		// <vcount> 4 4 4 3 4 5 3 4 .. </vcount>
946 		var vcountNode = this.getFirstChildByNodeName(collation,"vcount");
947 		var vcountList = this.mergeChildData(vcountNode.childNodes).split(" ");
948 
949 		// this counter keeps track of where we are in the vcount list
950 		// <vcount>4 4 3 4 4 4 5 ... 4 4 3 4</vcount>
951 		var vcountIndex = 0;
952 		
953 		// 
954 		var primOffset = 0;
955 	
956 		// since we can only support triangles, we have to make a triangle list
957 		// and convert any quads to triangles.
958 		var trianglesList = [];
959 		
960 		//
961 		var partSize = numInputs;
962 		
963 		// iterate from the 0 to vcount, this is the number of primitives
964 		// there are in this mesh.  Primitives in a polylist may have
965 		// more than 4 vertices, we will solve this by making the 
966 		// triangle fans.
967 		// Vertices, according to the spec, will be counter clockwise.  Because
968 		// of this, we should be able to make fans without problems.
969 		for (var currPrim = 0; currPrim < collation.getAttribute("count"); currPrim++, vcountIndex++)
970 		{
971 			var partsArray = [];
972 			
973 			// the current number in the vcount list may have different values depending, may have 3, 4 or more
974 			// so we have to iterate for the amount of values in the primitive.
975 			for (var currPart = 0; currPart < vcountList[vcountIndex]; currPart++)
976 			{
977 				var part = [];
978 
979 				for(currScalar = 0; currScalar < numInputs; currScalar++)
980 				{
981 					// first part (primOffset * inputs.length) indexes into the primitive
982 					// part.  Second part will index into the specific part we want.
983 					part.push(rawFaces[ (primOffset * numInputs) + (currPart * numInputs) + currScalar]);
984 				}
985 				partsArray.push(part);
986 			}
987 			// set the new value for the primitive offset
988 			// since the number of vertices vary in a list of primitives,
989 			// we just keep track how many we have to skip here.
990 			// 4 3 4 3 3
991 			// we can't just calculate how many values we have to skip, we acutally
992 			// have to keep the total count.
993 			primOffset += parseInt(vcountList[vcountIndex]);
994 
995 			// the way we will create a triangle fan is by creating a pivot
996 			// vertex and keep track of the last vertex added.
997 			// everytime a new vertex is added we will create a triangle from
998 			// the pivot, the last vertex and the new one.
999 
1000 			// start this as index 1. the first time this main loop runs, 
1001 			// last will actually be the middle vertex, from then on, it 
1002 			// will act as the last one added since before the iteration ends
1003 			// we set last to be the fanIndex
1004 			var last = 1;
1005 			var firstTriangle = true;
1006 			// create a triangle fan
1007 			for (var fanIndex = 0; fanIndex < vcountList[vcountIndex]-1; )
1008 			{
1009 				// s iterator is for scalar, we are dealing with single values.
1010 				// push on the first 
1011 				for (var s=0; s < partsArray[0].length; s++)
1012 				{
1013 					// first
1014 					trianglesList.push(partsArray[0][s]);
1015 				}
1016 				fanIndex++;
1017 
1018 				// last
1019 				for (var s=0; s < partsArray[0].length; s++)
1020 				{
1021 					trianglesList.push(partsArray[last][s]);
1022 				}
1023 
1024 				// if this is the first iteration, increase the fanIndex.
1025 				if( firstTriangle)
1026 				{
1027 					fanIndex++;
1028 					firstTriangle = false;
1029 				}
1030 
1031 				for (var s=0; s < partsArray[0].length; s++)
1032 				{
1033 					trianglesList.push(partsArray[fanIndex][s]);
1034 				}
1035 
1036 				last = fanIndex;
1037 			}
1038 		}
1039 		// overwrite rawFaces with the triangle faces, as if quads never existed.
1040 		//rawFaces = trianglesList;				
1041 		return trianglesList;
1042 	}
1043 	
1044 	
1045 	/**
1046 		@private
1047 		@param {XMLDocument} xmlObject
1048 		@param {String} libraryName
1049 		@param {String} elementName
1050 		@param {String} elementAttributeId
1051 	
1052 		@returns
1053 	*/
1054 	this.findElementInLibrary = function(xmlObject, libraryName, elementName, elementAttributeId)
1055 	{
1056 		// collada may have many listings of the same library.
1057 		var libraries = xmlObject.getElementsByTagName(libraryName);
1058 		
1059 		// for all the libraries in the <collada> element
1060 		for(libraryIter = 0;libraryIter < libraries.length; libraryIter++)
1061 		{
1062 			var elements = libraries[libraryIter].getElementsByTagName(elementName);
1063 
1064 			// for all the elements in each library.
1065 			for(elementIter = 0; elementIter < elements.length; elementIter++)
1066 			{
1067 				// found it
1068 				if(elementAttributeId == elements[elementIter].getAttribute("id"))
1069 				{
1070 					return elements[elementIter];
1071 				}
1072 			}
1073 		}
1074 		// return null if it could not be found.
1075 		return null;
1076 	}
1077 
1078 
1079 	/**
1080 		@private
1081 		@param {} xmlObject	
1082 		@param {String} nodeName
1083 		@param {String} attributeKey
1084 		@param {String} attributeValue
1085 		
1086 		@return 
1087 	*/
1088 	this.getData = function(xmlObject, nodeName, attributeKey, attributeValue)
1089 	{
1090 		var data = new Object();
1091 
1092 		// find node that has 'src' value as id
1093 		var nsrc = c3dl.ColladaLoader.getNodeWithAttribute(xmlObject,"source", "id", attributeValue);
1094 		// go to the node's technique common
1095 		var tech_common = nsrc.getElementsByTagName("technique_common")[0];
1096 
1097 		// go to its accessor
1098 		var accessor = tech_common.getElementsByTagName("accessor")[0];
1099 		data.stride = accessor.getAttribute("stride");
1100 		
1101 		// get the source
1102 		var accessorSrc = accessor.getAttribute("source").split("#")[1];
1103 		
1104 		//
1105 		var float_array = c3dl.ColladaLoader.getNodeWithAttribute(xmlObject,"float_array", "id", accessorSrc);
1106 
1107 		//values in the DAE file are seperated with a space
1108 		// don't use nodeValue since it will be broken up in 4096 chunks
1109 		data.values = this.mergeChildData(float_array.childNodes).split(" ");
1110 		return data;
1111 	}
1112 
1113 	/**
1114 		@private
1115 		@param {Array} faces A 2D array which has indices.  Each index points
1116 		to a set of vertex, normal or texture coordinates.
1117 
1118 		<pre>
1119 		// faces can look like this:
1120 		[
1121 			[0,0,0] , 
1122 			[1,0,1] , 
1123 			[2,0,2] 
1124 		]
1125 		// first value in each element is vertex index.
1126 		// second value is normal index.
1127 		// third value is uv index.
1128 		</pre>
1129 
1130 		@param {Array} array The array to expand. For a cube, the array of vertices
1131 		can look like:
1132 		
1133 		<pre>
1134 		[1.00000,1.00000,-1.00000] , [1.00000,-1.00000,-1.00000] , [-1.00000,-1.00000,-1.00000] 
1135 		</pre>
1136 
1137 
1138 		@param offset Refers to the component we are interested in within an array
1139 		in the faces array.  Since the faces array has indices for verts, normals and texcoords,
1140 		using a different index gets us a index which is a 0 based index into a list of 
1141 		coordinates.
1142 		
1143 		<pre>
1144 		here is a faces array.  If we used offset=2, we would get the following indices:
1145 		[0,0,0] , [1,0,1] , [2,0,2] 
1146            ^         ^         ^
1147 		</pre>
1148 		
1149 		@param {int} numComponentsToExpand 
1150 	*/
1151 	this.expandFaces = function(faces, array, offset, numComponentsToExpand)
1152 	{
1153 		// this is a single dimensional array which hold expanded values.
1154 		var expandedArray = [];
1155 		
1156 		// in the nested loop, we divide the instructions into parts to
1157 		// make it easier to read, but don't allocate these varialbes everytime
1158 		// the loop is executed, so declare them here. This speeds up the parser
1159 		// quite a bit.
1160 		var face;
1161 		var coordIndex;
1162 		var coord;
1163 		var floatValue;
1164 
1165 		// for all the faces
1166 		//  [0,0,0] ,   [1,0,1] ,   [2,0,2] 
1167 		//  \    /      \    /      \    /  
1168 		//    \/          \/          \/
1169 		// currFace=0   currFace=1    ...
1170 		for(var currFace = 0; currFace < faces.length; currFace++)
1171 		{
1172 			// have to be scalar, no arrays
1173 			
1174 			// each element in the faces array is another array. That second array
1175 			// hold components.  We iterate 
1176 			for (var currComp = 0; currComp < numComponentsToExpand; currComp++)
1177 			{
1178 				// go to a particular face:
1179 				// [3, 1, 4]
1180 				face = faces[currFace];
1181 			
1182 				
1183 				// we might be dealing with verts, norms or textures, each has a different offset.
1184 				// then go to a particular value inside denoted by the offset.
1185 				// [3, 1, 4]
1186 				//        ^
1187 				// offset = 2 for tex
1188 				// 4 is an index which refers to the 4th set of tex coords.
1189 				// something like:
1190 				// ["1.0", "0.0"] 
1191 				coordIndex = face[offset];
1192 				
1193 				// coord is a vertex coord, normal or uv coord retrieved from the
1194 				// 'array' array.
1195 				//
1196 				// ["29.4787", "0", "50.5349"]  -> array[0]
1197 				// ["29.4787", "0", "50.5349"]  -> array[1]
1198 				// ...
1199 				// this is done for each component, for textures, we have 2 components.
1200 				// for normals, we have 3.
1201 				if(coordIndex)
1202 				{
1203 					coord = array[coordIndex][currComp];
1204 				}
1205 				
1206 				// we have the value, but its a string so we have to convert it to a number.
1207 				floatVal = parseFloat(coord);
1208 			
1209 				// insert that value into the 1d array.
1210 				expandedArray.push(floatVal);
1211 			}
1212 		}
1213 		return expandedArray;
1214 	}
1215 
1216 	/**
1217 		@private
1218 		Loading is done once all the nodes in the .DAE file have been created and placed
1219 		into the ModelManager.
1220 		
1221 		@returns {bool} true if loading is done, false otherwise. 
1222 	*/
1223 	this.doneLoading = function()
1224 	{
1225 		return this.done;
1226 	}
1227 	
1228 	/**
1229 		@private
1230 
1231 		get data from blinn, phong, lambert node
1232 		
1233 		@param {}
1234 		@param {String} str
1235 	*/
1236 	this.getColor = function(node, str)
1237 	{
1238 		// ambient, diffuse, specular, etc.
1239 		var component = node != null ? node.getElementsByTagName(str)[0] : null;
1240 		
1241 		// if the component has a reference parameter, ignore it for now.
1242 		// that component will not be used in the calculations.
1243 		var returnValue = [0,0,0];
1244 		
1245 		// if the component is present (ambient, diffuse, specular)
1246 		if(component)
1247 		{			
1248 			var value = this.getChoice(component,["color", "float", "texture"]);
1249 				
1250 			if(value.nodeName == "color" )
1251 			{
1252 				returnValue = [];
1253 				for (var currNode = 0; currNode < value.childNodes.length; currNode++)
1254 				{
1255 					returnValue += value.childNodes[currNode].nodeValue;	
1256 				}
1257 				returnValue = returnValue.split(" ");
1258                 returnValue = [parseFloat(returnValue[0]),parseFloat(returnValue[1]),parseFloat(returnValue[2])];			
1259 				returnValue = returnValue.slice(0,3);
1260 			}
1261 			//
1262 			else if(value.nodeName == "float" )
1263 			{
1264 				returnValue = parseFloat(value.childNodes[0].nodeValue);
1265 			}
1266 			// 
1267 			else if(value.nodeName == "texture")
1268 			{
1269 				returnValue = [1,1,1];
1270 			}
1271 		}
1272 
1273 		return returnValue;
1274 	}
1275 	
1276 	
1277 	
1278 	/**
1279 		@private
1280 		When reading data in between tags, 
1281 		
1282 		There tends to be a lot of data between tags such as <float_array>
1283 		<float_array id="Teapot-mesh-positions-array" count="1590">29.4787 0 50.5349 ..... 34.093 </float_array>
1284 		
1285 		We can't just read the node value contents because it is broken up into 4096 byte chunks. So 
1286 		we use this function to merge all the chunks together.
1287 
1288 		@param {String} childNodes
1289 
1290 		@returns
1291 	*/
1292 	this.mergeChildData = function(childNodes)
1293 	{
1294 		var values = [];
1295 		for (var currNode = 0; currNode < childNodes.length; currNode++)
1296 		{
1297 			values += childNodes[currNode].nodeValue;
1298 		}
1299 		
1300 		// there are some 3d authoring tools which place a trailing space after a long set of data.
1301 		// for example:
1302 		//
1303 		// <float_array id="Teapot-mesh-positions-array" count="1590">29.4787 0 50.5349 ..... 34.093 </float_array>
1304 		//
1305 		// the trailing space causes problems as when the string is split, an undefined value
1306 		// is placed at the end of the array.  So we will get rid of any trailing spaces here.
1307 		//
1308 		return values.replace(/\s+$/,'');
1309 	}
1310 	
1311 	
1312 	
1313 	/**
1314 		@private
1315 		Get the first child of type 'nodeName' of a element 'searchNode'.
1316 
1317 		@param {Object Element} searchNode - the element to search.
1318 		@param {String} nodeName - the node to search for.
1319 		
1320 		@returns 
1321 	*/
1322 	this.getFirstChildByNodeName = function(searchNode, nodeName)
1323 	{
1324 		for (var i=0; i < searchNode.childNodes.length; i++ )
1325 		{
1326 			// found it!
1327 			if( searchNode.childNodes[ i ].nodeName == nodeName )
1328 			{
1329 				return searchNode.childNodes[ i ];
1330 			}
1331 		}
1332 		return null;
1333 	}
1334 
1335 }
1336 
1337 
1338 /**
1339 	@private	
1340 	
1341 	static method of collada loader. 
1342 	
1343 	@param {} xmlObject
1344 	@param {String} nodeName
1345 	@param {String} attributeKey
1346 	@param {String} attributeValue
1347 	
1348 	@returns 
1349 */
1350 c3dl.ColladaLoader.getNodeWithAttribute = function(xmlObject, nodeName, attributeKey, attributeValue)
1351 {
1352 	var nodeFound;
1353 	
1354 	// go to the root of the XML
1355 	var root = xmlObject.documentElement;
1356 
1357 	// get all the tags names 'nodeName'
1358 	var elements = root.getElementsByTagName(nodeName);
1359 	
1360 	// we might have a few tags, we need to find the one with the id specified
1361 	for (var i = 0; i < elements.length; i++)
1362 	{
1363 		if( elements[i].getAttribute(attributeKey) == attributeValue)
1364 		{
1365 			nodeFound = elements[i]; 
1366 		}
1367 	}
1368 	return nodeFound;
1369 }
1370 
1371 
1372 /**
1373 	@private
1374 	Get the child nodes of searchNode which have the name 'nodeName'.
1375 	This funciton is not recursive.  It only returns the <b>direct</b> children.
1376 	
1377 	@param {String} searchNode - the node to search.
1378 	@param {String} nodeName - the nodes to search for.
1379 
1380 	@returns {Array} array of node elements.
1381 */
1382 c3dl.ColladaLoader.getChildNodesByNodeName = function(searchNode, nodeName)
1383 {
1384 	var children = [];
1385 	var foundOne = false;
1386 
1387 	if(searchNode.childNodes.length > 0)
1388 	{
1389 		for (var i = 0; i < searchNode.childNodes.length; i++)
1390 		{
1391 			if(searchNode.childNodes[i].nodeName == nodeName)
1392 			{
1393 				children.push( searchNode.childNodes[i]);
1394 				foundOne = true;
1395 			}
1396 		}
1397 	}
1398 
1399 	// somehow need to specify if non were found, so 
1400 	// send back a null if that's the case.
1401 	if(foundOne == false)
1402 	{
1403 		children = null;
1404 	}
1405 
1406 	return children;
1407 }
1408 
1409 
1410 
1411 /**
1412 	@private
1413 	static method of c3dl.ColladaLoader
1414 	
1415 	Turn a series of strings seperated by 'delimiter' into float values.
1416 	used when we need to convert <translate>0.0 0.0 0.0</translate> into float
1417 	values.
1418 	
1419 	@param {String} numbers A string which contains numbers such as "1.0 0.0 1.0 0.0       0.0 1.0 ..." note the spaces.
1420 	@param {String} delimeter A string which is being used to separate the numbers.
1421 */
1422 c3dl.ColladaLoader.stringsToFloats = function(numbers, delimeter)
1423 {
1424 	var floatValues = [];
1425 	
1426 	// Get rid of leading and trailing spaces so they don't interfere with out split().
1427 		
1428 	// remove leading whitespace
1429 	var trimmedNumbers = numbers.replace(/^\s+/, '');
1430 	
1431 	// remove trailing whitespace
1432 	trimmedNumbers = trimmedNumbers.replace(/\s+$/, '');
1433 	
1434 	// remove superfluous whitespace between numbers
1435 	// find one or more instances of whitespace and replace with a single space.
1436 	trimmedNumbers = trimmedNumbers.replace(/\s+/g,' ' );
1437 	
1438 	// split each number into an element of an array, but they are still
1439 	// strings, so convert them to float.
1440 	var strValues = trimmedNumbers.split(delimeter);
1441 
1442 	for (var i = 0; i < strValues.length; i++)
1443 	{
1444 		floatValues.push(parseFloat(strValues[i]));
1445 	}
1446 
1447 	return floatValues;
1448 }
1449