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 	@class c3dl.Collada represents the model data from a collada file. To
  8 	load a collada file into a scene, the file must first be called
  9 	with c3dl.addModel('plane.dae'). This makes sure the file
 10 	is parsed before it is used. In the main function, you can create
 11 	instances of the collada file:
 12 	<br />
 13 	<br />
 14 	var collada = new c3dl.Collada();<br />
 15 	collada.init('plane.dae');<br />
 16 
 17 	@augments c3dl.Primitive
 18 */
 19 c3dl.Collada = c3dl.inherit(c3dl.Primitive, function() 
 20 {
 21 	c3dl._superc(this);
 22 
 23 	this.path = null;
 24 	this.sceneGraph = null;
 25 });
 26 
 27 
 28 /**
 29 	Get the path of collada file loaded. This is set when init() 
 30 	is called.
 31 
 32 	@returns {String}
 33 */
 34 c3dl.Collada.prototype.getPath = function()
 35 {
 36 	if( this.isReady())
 37 	{
 38 		return this.path;
 39 	}
 40 }
 41 
 42 /**
 43 	Get the angular velocity of the scenegraph's root node.
 44 
 45 	@returns 
 46 */
 47 c3dl.Collada.prototype.getAngularVel = function()
 48 {
 49 	if( this.isReady())
 50 	{
 51 		return this.sceneGraph.getAngularVel();
 52 	}
 53 }
 54 
 55 /**
 56 	Get the linear velocity of the scenegraph's root node.
 57 
 58 	@returns
 59 */
 60 c3dl.Collada.prototype.getLinearVel = function()
 61 {
 62 	if( this.isReady())
 63 	{
 64 		return this.sceneGraph.getLinearVel();
 65 	}
 66 }
 67 
 68 /**
 69 	Get the position of the scene graph's root.
 70 
 71 	@returns {Array}
 72 */
 73 c3dl.Collada.prototype.getPosition = function()
 74 {
 75 	if( this.isReady())
 76 	{
 77 		return this.sceneGraph.getPosition();
 78 	}
 79 }
 80 
 81 /**
 82 	Set the angular velocity of the scenegraph's root node.
 83 
 84 	@param {Array} vec
 85 */
 86 c3dl.Collada.prototype.setAngularVel = function(vec)
 87 {
 88 	if( this.isReady())
 89 	{
 90 		this.sceneGraph.setAngularVel(vec);
 91 	}
 92 }
 93 
 94 /**
 95 	Get the up vector of the scenegraph's root node.
 96 
 97 	@return {Array}
 98 */
 99 c3dl.Collada.prototype.getUp = function()
100 {
101 	if( this.isReady())
102 	{
103 		return this.sceneGraph.getUp();
104 	}
105 }
106 
107 /**
108 	Get the left vector of the scenegraph's root node.
109 
110 	@return {Array}
111 */
112 c3dl.Collada.prototype.getLeft = function()
113 {
114 	if( this.isReady())
115 	{
116 		return this.sceneGraph.getLeft();
117 	}
118 }
119 
120 /**
121 	Get the direction vector of the scenegraph's root node.
122 
123 	@returns {Array} 
124 */
125 c3dl.Collada.prototype.getDirection = function()
126 {
127 	if( this.isReady())
128 	{
129 		return this.sceneGraph.getDirection();
130 	}
131 }
132 
133 /**
134 	Can this object be picked when the user clicks on it?  In some scripts using
135 	the library, it may not make sense for wall, for example to be picked.  If the
136 	object cannot be picked, it will not tested against the ray which is generated
137 	then the user clicks the canvas, thus increasing performance.
138 
139 	@returns {bool} true if the object can be picked, false otherwise.
140 */
141 c3dl.Collada.prototype.getPickable = function()
142 {
143 	if( this.isReady())
144 	{
145 		return this.sceneGraph.getPickable();
146 	}
147 }
148 
149 /**
150 	Set whether this object should be included in picking tests.  By omitting
151 	objects which should not be interacted with, it can increase performance.
152 
153 	@param {bool} isPickable true if the object should be included in pikcing tests,
154 	false otherwise.
155 */
156 c3dl.Collada.prototype.setPickable = function(isPickable)
157 {
158 	if( this.isReady())
159 	{
160 		this.sceneGraph.setPickable(isPickable);
161 	}
162 }
163 
164 /**
165 	Set the linear velocity of the scenegraph's root node.
166 
167 	@param {Array} vec
168 */
169 c3dl.Collada.prototype.setLinearVel = function(vec)
170 {
171 	if( this.isReady())
172 	{
173 		this.sceneGraph.setLinearVel(vec);
174 	}
175 }
176 
177 /**
178 	This should be called after the collada object is created. It will be 
179 	assigned a structural copy of the collada object which exists in the 
180 	ColladaManager. This object will then be able to update the 
181 	transformations without changing the object in the manager class. The
182 	nodes are copied, however the arrays of vertices, normals, etc are not.
183 	References exsist within this object which will point to the vertex arrays
184 	in the manager object.
185 
186 	@param {string} daepath path of the collada file.
187 */
188 c3dl.Collada.prototype.init = function(daePath)
189 {
190 	this.path = daePath;
191 
192 	// if the file is already in the manager, just get a copy of it now,
193 	// otherwise put it in queue.
194 	
195 	// Before the scene begins, the user must first specify all the collada files
196 	// they will use during the lifetime of the scene.  When the scene begins, all
197 	// the files they specified will be created and initialized.  Either it will 
198 	// be the first time (they won't exist in the manager) or they want a new 
199 	// object, in which case a copy must be created.
200 	if(c3dl.ColladaManager.isFileLoaded(this.path))
201 	{
202 		this.sceneGraph = c3dl.ColladaManager.getSceneGraphCopy(this.path);
203 	}
204 	else
205 	{	
206 		// this will be called if the scene is being initialized and we are 
207 		// placing collada objects in the manager.
208 		c3dl.ColladaQueue.pushBack(this);
209 	}
210 }
211 
212 /**
213 	@private
214 	
215 	Called automatically
216 
217 	Update animations for linear velocity and angular velocity.
218 
219 	@param {float} timeStep
220 */
221 c3dl.Collada.prototype.update = function(timeStep)
222 {
223 	// keep checking to see if the file is done being loaded.
224 	if(this.isReady())
225 	{
226 		this.sceneGraph.update(timeStep);
227 	}
228 	else
229 	{
230 		c3dl.debug.logError('You must call addModel("' + this.path + '"); before canvasMain.');
231 		
232 		if(c3dl.ColladaManager.isFileLoaded(this.path))
233 		{
234 			// get a copy of the scenegraph so we can modify it.
235 			this.sceneGraph = c3dl.ColladaManager.getSceneGraphCopy(this.path);
236 		}
237 	}
238 }
239 
240 /**
241 	@private
242 */
243 c3dl.Collada.prototype.setSceneGraph = function(sg)
244 {
245 	this.sceneGraph = sg;
246 }
247 
248 
249 /**
250 	@private
251 
252 	Called automatically
253 
254 	Render the collada object.
255 
256 	@param {context} glCanvas3D
257 	@param {Scene} scene
258 */
259 c3dl.Collada.prototype.render = function(glCanvas3D, scene)
260 {
261 	if(this.sceneGraph && this.isVisible())
262 	{
263 		// tell the root to render. The render() calls
264 		// will propogate down the graph.
265 		this.sceneGraph.render(glCanvas3D, scene);
266 	}
267 }
268 
269 /**
270 	Scale the the scenegraph's root node.
271 
272 	@param {Array} scaleVec 
273 */
274 c3dl.Collada.prototype.scale = function(scaleVec)
275 {
276 	if( this.isReady())
277 	{
278 		this.sceneGraph.scale(scaleVec);
279 	}
280 }
281 
282 /**
283 	Translate the entire Collada object. This will tell the root of the
284 	Collada scenegraph to translate by 'trans'.
285 
286 	@param {Array} trans
287 */
288 c3dl.Collada.prototype.translate = function(trans)
289 {
290 	if( this.isReady())
291 	{
292 		this.sceneGraph.translate(trans);
293 	}
294 }
295 
296 /**
297 	Place the object to a new location relative to the world origin.
298 	
299 	@param {Array} pos 
300 */
301 c3dl.Collada.prototype.setPosition = function(pos)
302 {
303 	if( this.isReady())
304 	{
305 		this.sceneGraph.setPosition(pos);
306 	}
307 }
308 
309 /**
310 	Get the scenegraph of the Collada object.
311 
312 	@returns {c3dl.SceneNode} Root node of the Collada model's scenegraph.
313 */
314 c3dl.Collada.prototype.getSceneGraph = function()
315 {
316 	return this.sceneGraph;
317 }
318 
319 /**
320 	Set the texture of all the geometry sections (primitive collation elements 
321 	or primitiveSets) to this texture.
322 
323 	@param {string} texturePath Path of the texture.
324 */
325 c3dl.Collada.prototype.setTexture = function(texturePath)
326 {
327 	if(this.isReady())
328 	{
329 		this.sceneGraph.setTexture(texturePath);
330 	}
331 }
332 
333 /**
334 	Sets the material of all the geometry sections (primitive collation elements 
335 	or primitiveSets) to this material. Thus, the entire Collada object will be
336 	rendered using this material.
337 	
338 	@param {c3dl.Material} material
339 */
340 c3dl.Collada.prototype.setMaterial = function(material)
341 {
342 	if( this.isReady())
343 	{
344 		this.sceneGraph.setMaterial(material);
345 	}
346 }
347 
348 /**
349 	Set the way this Collada object should be rendered. The 
350 	Effect will be set to all the nodes within the Collada's
351 	scenegraph. This should only be called once the scene has
352 	been initialized since on initialization, the built-in
353 	effects such as c3dl.effects.GOOCH, c3dl.effects.CARTOON, etc.
354 	are created.
355 
356 	@param {c3dl.Effect} effect
357 */
358 c3dl.Collada.prototype.setEffect = function(effect)
359 {
360   // add type checking?
361   this.sceneGraph.setEffect(effect);
362 }
363 
364 /**
365 	Rotate around the up vector by a hard amount.
366 	
367 	@param {float} angle in radians.
368 */
369 c3dl.Collada.prototype.yaw = function(angle)
370 {
371 	if(this.isReady())
372 	{
373 		this.sceneGraph.yaw(angle);
374 	}
375 }
376 
377 /**
378 	Rotate around the side vector by a hard amount.
379 	
380 	@param {float} angle in radians.
381 */
382 c3dl.Collada.prototype.pitch = function(angle)
383 {
384 	if( this.isReady())
385 	{
386 		this.sceneGraph.pitch(angle);
387 	}
388 }
389 
390 /**
391 	@private
392 */
393 c3dl.Collada.prototype.isReady = function()
394 {
395 	return this.sceneGraph != null ? true: false;
396 }
397 
398 /**
399 	Rotate around the direction vector by a hard amount.
400 	
401 	@param {float} angle in radians.
402 */
403 c3dl.Collada.prototype.roll = function(angle)
404 {
405 	if(this.isReady())
406 	{
407 		this.sceneGraph.roll(angle);
408 	}
409 }
410 
411 /**
412 	@private
413 */
414 c3dl.Collada.prototype.getCopy = function()
415 {
416 	var collada = new Collada();
417 	collada.clone(this);
418 	return collada;
419 }
420 
421 /**
422 	@private
423 */
424 c3dl.Collada.prototype.clone = function(other)
425 {
426 	c3dl._super(this, arguments, "clone");
427 
428 	this.path = other.path;
429 	this.sceneGraph = other.sceneGraph.getCopy();
430 }
431 
432 /**
433 	@private
434 	Does the given ray intersect with this object? This function will
435 	test the ray against all the geometry nodes in the scenegraph and
436 	return true as soon as it finds an intersection.
437 
438 	@param {Array} rayOrigin The ray's origin in view space.
439 	@param {Array} rayDir The ray's direction in view space.
440 
441 	@returns {bool} true if the ray intersects with one of the geometry nodes
442 	in the scenegraph.
443 */
444 c3dl.Collada.prototype.rayIntersectsEnclosures = function(rayOrigin, rayDir)
445 {
446 	// Use the matrix stack, but clear it out first
447 	c3dl.pushMatrix();
448 	c3dl.loadIdentity();
449 
450 	var result = this.sceneGraph.rayIntersectsEnclosures(rayOrigin, rayDir);
451 
452 	// restore the stack to its previous state.
453 	c3dl.popMatrix();
454 	return result;
455 }
456 
457 c3dl.Collada.prototype.getObjectType = function()
458 {
459 	return c3dl.COLLADA;
460 }
461 
462 /**
463 	@private
464 	Does the given ray intersect with any of the triangles in this object?
465 
466 	@param {Array} rayOrigin ray's origin in world space.
467 	@param {Array} rayDir A normalized direction vector.
468 
469 	@returns {bool} true if the ray intersects with any triangle in the object.
470 */
471 c3dl.Collada.prototype.rayIntersectsTriangles = function(rayOrigin, rayDir)
472 {
473 	// Use the matrix stack, but clear it out first
474 	c3dl.pushMatrix();
475 	c3dl.loadIdentity();
476 
477 	var result = this.sceneGraph.rayIntersectsTriangles(rayOrigin, rayDir);
478 
479 	// restore the stack to its previous state.
480 	c3dl.popMatrix();
481 	return result;
482 }
483