1 /*
2     Copyright (c) 2008-2009 NetAllied Systems GmbH
3 
4 	This file is part of COLLADAMax.
5 
6     Portions of the code are:
7     Copyright (c) 2005-2007 Feeling Software Inc.
8     Copyright (c) 2005-2007 Sony Computer Entertainment America
9 
10     Based on the 3dsMax COLLADASW Tools:
11     Copyright (c) 2005-2006 Autodesk Media Entertainment
12 
13     Licensed under the MIT Open Source License,
14     for details please see LICENSE file or the website
15     http://www.opensource.org/licenses/mit-license.php
16 */
17 
18 #include "COLLADAMaxStableHeaders.h"
19 
20 #include "COLLADASWStreamWriter.h"
21 #include "COLLADASWSource.h"
22 #include "Math/COLLADABUMathUtils.h"
23 
24 #include "COLLADAMaxAnimationExporter.h"
25 #include "COLLADAMaxExportSceneGraph.h"
26 
27 #include <max.h>
28 #include <istdplug.h>
29 #include <control.h>
30 #include <units.h>
31 
32 
33 namespace COLLADAMax
34 {
35 
36 
37     //---------------------------------------------------------------
Animation(Control * controller,const String & id,const String & sid,const String * parameter,int type,ConversionFunctor * conversionFunctor)38     Animation::Animation ( Control * controller, const String & id, const String & sid, const String * parameter, int type, ConversionFunctor* conversionFunctor )
39             : mController ( controller ),
40 			mINode(0),
41             mId ( id ),
42             mSid ( sid ),
43             mParameters ( parameter ),
44 			mMatrixIndex(-1),
45             mType ( type ),
46 			mConversionFunctor( conversionFunctor ? conversionFunctor->clone() : 0 ),
47 			mInputTypeFlags(NONE)
48     {}
49 
50 
51 	//---------------------------------------------------------------
Animation(Control * controller,const String & id,const String & sid,int matrixIndex,int type,ConversionFunctor * conversionFunctor)52 	Animation::Animation ( Control * controller, const String & id, const String & sid, int matrixIndex, int type, ConversionFunctor* conversionFunctor )
53 		: mController ( controller ),
54 		mINode(0),
55 		mId ( id ),
56 		mSid ( sid ),
57 		mParameters ( 0 ),
58 		mMatrixIndex(matrixIndex),
59 		mType ( type ),
60 		mConversionFunctor( conversionFunctor ? conversionFunctor->clone() : 0 ),
61 		mInputTypeFlags(NONE)
62 	{}
63 
64 
65 
66 	//---------------------------------------------------------------
Animation(INode * iNode,const String & id,const String & sid,const String * parameter,int type,ConversionFunctor * conversionFunctor)67 	Animation::Animation ( INode * iNode, const String & id, const String & sid, const String * parameter, int type, ConversionFunctor* conversionFunctor )
68 		:
69 		mController(0),
70 		mINode ( iNode ),
71 		mId ( id ),
72 		mSid ( sid ),
73 		mParameters ( parameter ),
74 		mMatrixIndex(-1),
75 		mType ( type ),
76 		mConversionFunctor( conversionFunctor ? conversionFunctor->clone() : 0 ),
77 		mInputTypeFlags(NONE)
78 	{}
79 
80 
Animation(const Animation & other)81 	Animation::Animation( const Animation& other ):
82 		mController (other.mController),
83 		mINode (other.mINode),
84 		mId( other.mId),
85 		mSid(other.mSid),
86 		mParameters(other.mParameters),
87 		mMatrixIndex(other.mMatrixIndex),
88 		mType(other.mType),
89 		mInputTypeFlags(other.mInputTypeFlags),
90 		mConversionFunctor ( other.mConversionFunctor ? other.mConversionFunctor->clone() : 0)
91 	{}
92 
operator =(const Animation & other)93 	const Animation& Animation::operator=( const Animation& other )
94 	{
95 		mController  = other.mController;
96 		mINode  = other.mINode;
97 		mId =  other.mId;
98 		mSid = other.mSid;
99 		mParameters = other.mParameters;
100 		mMatrixIndex = other.mMatrixIndex;
101 		mType = other.mType;
102 		mInputTypeFlags = other.mInputTypeFlags;
103 		mConversionFunctor  = other.mConversionFunctor ? other.mConversionFunctor->clone() : 0;
104 		return *this;
105 	}
106 
107 	//---------------------------------------------------------------
getDimension() const108     int Animation::getDimension() const
109     {
110         switch ( mType )
111         {
112 
113         case FLOAT:
114         case POSITION_X:
115         case POSITION_Y:
116         case POSITION_Z:
117         case ROTATION_X:
118         case ROTATION_Y:
119         case ROTATION_Z:
120             return 1;
121 
122 		case FLOAT3:
123             return 3;
124 
125 		case SCALE_ROT_AXIS_R:
126 		case SCALE_ROT_AXIS:
127 		case FLOAT4:
128 			return 4;
129 
130 		case FLOAT4x4:
131 			return 16;
132 
133         default:
134             return 0;
135         }
136     }
137 
~Animation()138 	Animation::~Animation()
139 	{
140 		delete mConversionFunctor;
141 	}
142 
143 	const float AnimationExporter::DEFAULT_INLENGHT = 0.333f;
144 	const float AnimationExporter::DEFAULT_OUTLENGHT = 0.333f;
145     const float AnimationExporter::DEFAULT_INLENGHT_FLOAT = AnimationExporter::DEFAULT_INLENGHT;
146 
147 	float AnimationExporter::DEFAULT_INLENGHT_POINTX_FLOAT_ARRAY[4] = {AnimationExporter::DEFAULT_INLENGHT, AnimationExporter::DEFAULT_INLENGHT, AnimationExporter::DEFAULT_INLENGHT, AnimationExporter::DEFAULT_INLENGHT};
148 
149 	const float AnimationExporter::DEFAULT_OUTLENGHT_FLOAT = AnimationExporter::DEFAULT_OUTLENGHT;
150 	float AnimationExporter::DEFAULT_OUTLENGHT_POINTX_FLOAT_ARRAY[4] = {AnimationExporter::DEFAULT_OUTLENGHT, AnimationExporter::DEFAULT_OUTLENGHT, AnimationExporter::DEFAULT_OUTLENGHT, AnimationExporter::DEFAULT_OUTLENGHT};
151 
152 
153     const float AnimationExporter::mTimeFactor = 1.0f / ( GetTicksPerFrame() * GetFrameRate() );
154 
155 
156 
157     //---------------------------------------------------------------
AnimationExporter(COLLADASW::StreamWriter * streamWriter,DocumentExporter * documentExporter)158     AnimationExporter::AnimationExporter ( COLLADASW::StreamWriter * streamWriter, DocumentExporter * documentExporter )
159             : COLLADASW::LibraryAnimations ( streamWriter ),
160             mDocumentExporter ( documentExporter ),
161 			mClipExporter( streamWriter)
162     {}
163 
164     //---------------------------------------------------------------
getPreviousTime(const int & i,Control * controller) const165     TimeValue AnimationExporter::getPreviousTime ( const int & i, Control * controller ) const
166     {
167         return ( i > 0 ) ? controller->GetKeyTime ( i - 1 ) : controller->GetKeyTime ( i ) - TimeValue ( 1.0f / mTimeFactor );
168     }
169 
170     //---------------------------------------------------------------
getNextTime(const int & i,const int & keyCount,Control * controller) const171     TimeValue AnimationExporter::getNextTime ( const int & i, const int & keyCount, Control * controller ) const
172     {
173         return ( i < keyCount - 1 ) ? controller->GetKeyTime ( i + 1 ) : controller->GetKeyTime ( i ) + TimeValue ( 1.0f / mTimeFactor );
174     }
175 
176 	//---------------------------------------------------------------
forceSampleMatrices(INode * iNode)177 	bool AnimationExporter::forceSampleMatrices(INode* iNode)
178 	{
179 		if ( iNode )
180 		{
181 			Control* controller = iNode->GetTMController();
182 			// The -only- class we can hope to export without sampling is the PRS controller
183 			if ( controller )
184 				return (controller->ClassID() != Class_ID(PRS_CONTROL_CLASS_ID, 0) || findConstraint(controller));
185 		}
186 		return false;
187 	}
188 
189 	// Function searches controller and all subanims for presence of a Constraint.
190 	// This is bit of a cheap fix, a slightly better option may be to include
191 	// parent offset calculation in rotation/position sampling (like Matrix sampling).
192 	//---------------------------------------------------------------
findConstraint(Animatable * controller)193 	bool AnimationExporter::findConstraint(Animatable* controller)
194 	{
195 		if (controller->SuperClassID() == CTRL_FLOAT_CLASS_ID)
196 			return false;
197 		Class_ID classId = controller->ClassID();
198 		if (classId.PartB() == 0)
199 		{
200 			unsigned long classIdPartA = classId.PartA();
201 			if (classIdPartA == POSITION_CONSTRAINT_CLASS_ID ||
202 				classIdPartA == ORIENTATION_CONSTRAINT_CLASS_ID ||
203 				classIdPartA == LOOKAT_CONSTRAINT_CLASS_ID)
204 			{
205 				return true;
206 			}
207 		}
208 
209 		int SubcontrollerCount = controller->NumSubs();
210 		for (int i = 0; i < SubcontrollerCount; i++)
211 		{
212 			Animatable* subController = controller->SubAnim(i);
213 			if (subController )
214 			{
215 				if (findConstraint(subController))
216 					return true;
217 			}
218 		}
219 		return false;
220 	}
221 
222 
223     //---------------------------------------------------------------
doExport()224     void AnimationExporter::doExport()
225     {
226 		if (!mAnimationList.empty() || !mAnimationMap.empty())
227 		{
228 			if (!mAnimationList.empty()) {
229 				openAnimation();
230 
231 				for (AnimationList::iterator it = mAnimationList.begin(); it != mAnimationList.end(); ++it)
232 					exportSources(*it);
233 
234 				for (AnimationList::iterator it = mAnimationList.begin(); it != mAnimationList.end(); ++it)
235 					exportSampler(*it);
236 
237 				for (AnimationList::iterator it = mAnimationList.begin(); it != mAnimationList.end(); ++it)
238 					exportChannel(*it);
239 
240 
241 				closeAnimation();
242 			}
243 #if defined(MAX_RELEASE_R17) && (MAX_RELEASE >= MAX_RELEASE_R17)
244 			for (auto& animPair : mAnimationMap) {
245 				String animName = animPair.first;
246 				openAnimation(animName + "_animation", animName);
247 				for (auto& anim : animPair.second) {
248 					exportSources(anim);
249 				}
250 				for (auto& anim : animPair.second) {
251 					exportSampler(anim);
252 				}
253 				for (auto& anim : animPair.second) {
254 					exportChannel(anim);
255 				}
256 				closeAnimation();
257 			}
258 #endif
259 
260             closeLibrary();
261 
262 #if defined(MAX_RELEASE_R17) && (MAX_RELEASE >= MAX_RELEASE_R17)
263 			// Export the animation clips to go with the named animations
264 			if (mAnimationMap.size() != 0) {
265 				mClipExporter.open();
266 				for (auto& animPair : mAnimationMap) {
267 					String animName = animPair.first;
268 					COLLADASW::ColladaAnimationClip clip(animName + "_clip", animName);
269 					clip.setInstancedAnimation(animName + "_animation");
270 					mClipExporter.addClip(clip);
271 				}
272 				mClipExporter.close();
273 			}
274 #endif
275 		}
276     }
277 
278 
279 	//---------------------------------------------------------------
addAnimatedFloat(Control * controller,const String & id,const String & sid,const String parameters[],bool forceFullCheck,ConversionFunctor * conversionFunctor)280 	bool AnimationExporter::addAnimatedFloat ( Control * controller, const String & id, const String & sid, const String parameters[], bool forceFullCheck, ConversionFunctor* conversionFunctor)
281 	{
282 		if ( !isAnimated(controller) )
283 			return false;
284 
285 		Animation animation(controller, id, sid, parameters, Animation::FLOAT, conversionFunctor);
286 
287 		if ( !forceFullCheck || isAnimated(animation, true) )
288 		{
289 			addAnimation(animation);
290 			return true;
291 		}
292 		return false;
293 	}
294 
295 
296 	//---------------------------------------------------------------
addAnimatedFloat(Control * controller,const String & id,const String & sid,int matrixIndex,bool forceFullCheck,ConversionFunctor * conversionFunctor)297 	bool AnimationExporter::addAnimatedFloat ( Control * controller, const String & id, const String & sid, int matrixIndex, bool forceFullCheck, ConversionFunctor* conversionFunctor)
298 	{
299 		if ( !isAnimated(controller) )
300 			return false;
301 
302 		Animation animation(controller, id, sid, matrixIndex, Animation::FLOAT, conversionFunctor);
303 
304 		if ( !forceFullCheck || isAnimated(animation, true) )
305 		{
306 			addAnimation(animation);
307 			return true;
308 		}
309 		return false;
310 	}
311 
312 
313 
314     //---------------------------------------------------------------
addAnimatedPoint3(Control * controller,const String & layerName,const String & id,const String & sid,const String parameters[],bool forceFullCheck,ConversionFunctor * conversionFunctor)315     bool AnimationExporter::addAnimatedPoint3 ( Control * controller, const String& layerName, const String & id, const String & sid, const String parameters[], bool forceFullCheck, ConversionFunctor* conversionFunctor )
316     {
317 		if ( !isAnimated(controller))
318 			return false;
319 
320 		bool animated = false;
321 
322         Control * subControllers[ 3 ] = {controller->GetXController(), controller->GetYController(), controller->GetZController() };
323 
324         // First, Try to extract animations from the component controllers
325 
326         if ( subControllers[ 0 ] && subControllers[ 1 ]  && subControllers[ 2 ]  )
327         {
328             for ( int i = 0; i < 3; ++i )
329             {
330                 if ( isAnimated ( subControllers[ i ] ) )
331                 {
332                     Animation animation ( subControllers[ i ], id, sid, parameters + i, Animation::FLOAT, conversionFunctor );
333 					if ( !forceFullCheck || isAnimated(animation, true) )
334 					{
335 						if (layerName == "")
336 							addAnimation(animation);
337 						else
338 							addNamedAnimation(animation, layerName);
339 
340 						animated = true;
341 					}
342                 }
343             }
344         }
345         else if ( isAnimated ( controller ) )
346         {
347             // Else, with no subs, try and export ourselves as keyframes
348             Animation animation ( controller, id, sid, parameters, Animation::FLOAT3, conversionFunctor );
349 			if ( !forceFullCheck || isAnimated(animation, true) )
350 			{
351 				if (layerName == "")
352 					addAnimation(animation);
353 				else
354 					addNamedAnimation(animation, layerName);
355 
356 				animated = true;
357 			}
358         }
359 		return animated;
360     }
361 
362 
363 	//---------------------------------------------------------------
addAnimatedPoint4(Control * controller,const String & id,const String & sid,const String parameters[],bool forceFullCheck,ConversionFunctor * conversionFunctor)364 	bool AnimationExporter::addAnimatedPoint4 ( Control * controller, const String & id, const String & sid, const String parameters[], bool forceFullCheck, ConversionFunctor* conversionFunctor )
365 	{
366 		bool animated = false;
367 
368 		Control * subControllers[ 4 ] = {controller->GetXController(), controller->GetYController(), controller->GetZController(), controller->GetWController() };
369 
370 		// First, Try to extract animations from the component controllers
371 
372 		if ( subControllers[ 0 ] || subControllers[ 1 ]  || subControllers[ 2 ]  || subControllers[ 3 ] )
373 		{
374 			for ( int i = 0; i < 4; ++i )
375 			{
376 				if ( isAnimated ( subControllers[ i ] ) )
377 				{
378 					Animation animation ( subControllers[ i ], id, sid, parameters + i, Animation::FLOAT, conversionFunctor );
379 					if ( !forceFullCheck || isAnimated(animation, true) )
380 					{
381 						addAnimation ( animation );
382 						animated = true;
383 					}
384 				}
385 			}
386 		}
387 
388 		else if ( isAnimated ( controller ) )
389 		{
390 			// Else, with no subs, try and export ourselves as keyframes
391 			Animation animation ( controller, id, sid, parameters, Animation::FLOAT4, conversionFunctor );
392 			if ( !forceFullCheck || isAnimated(animation, true) )
393 			{
394 				addAnimation ( animation );
395 				animated = true;
396 			}
397 		}
398 
399 		return animated;
400 	}
401 
402 
403 	//---------------------------------------------------------------
addAnimatedAngle(Control * controller,const String & layerName,const String & id,const String & sid,const String parameters[],int animatedAngle,bool forceFullCheck)404     bool AnimationExporter::addAnimatedAngle ( Control * controller, const String& layerName, const String & id, const String & sid, const String parameters[], int animatedAngle, bool forceFullCheck  )
405     {
406         if ( !isAnimated ( controller ) )
407 			return false;
408 
409 		// maybe this rotation controller doesn't have XYZ components
410 		Control * xyzController = NULL;
411 
412 		switch ( animatedAngle )
413 		{
414 
415 		case Animation::ROTATION_X:
416 			xyzController = controller->GetXController();
417 			break;
418 
419 		case Animation::ROTATION_Y:
420 			xyzController = controller->GetYController();
421 			break;
422 
423 		case Animation::ROTATION_Z:
424 			xyzController = controller->GetZController();
425 			break;
426 		}
427 
428 		if ( xyzController != NULL )
429 			controller = xyzController;
430 
431 		Animation animation ( controller, id, sid, parameters, animatedAngle, &ConversionFunctors::radToDeg );
432 
433 		if ( !forceFullCheck || isAnimated(animation, true) )
434 		{
435 			if (layerName == "")
436 				addAnimation(animation);
437 			else
438 				addNamedAnimation(animation, layerName);
439 
440 			return true;
441 		}
442 
443 		return false;
444     }
445 
446 
447 	//---------------------------------------------------------------
addAnimatedAxisAngle(Control * controller,const String & id,const String & sid,const String parameters[],int type,bool forceFullCheck)448 	bool AnimationExporter::addAnimatedAxisAngle ( Control * controller, const String & id, const String & sid, const String parameters[], int type, bool forceFullCheck )
449 	{
450 		if ( !isAnimated ( controller ) )
451 			return false;
452 
453 		Animation animation ( controller, id, sid, parameters, type);
454 		if ( !forceFullCheck || isAnimated(animation, true) )
455 		{
456 			addAnimation ( animation );
457 			return true;
458 		}
459 		return false;
460 	}
461 
462 
463 	//---------------------------------------------------------------
addAnimatedFloat4x4(INode * node,const String & id,const String & sid,const String parameters[],bool forceFullCheck)464 	bool AnimationExporter::addAnimatedFloat4x4 ( INode * node, const String & id, const String & sid, const String parameters[], bool forceFullCheck )
465 	{
466 		Animation animation ( node, id, sid, parameters, Animation::FLOAT4x4);
467 
468 		if ( !forceFullCheck || isAnimated(animation, true) )
469 		{
470 			addAnimation ( animation );
471 			return true;
472 		}
473 
474 		return false;
475 	}
476 
477 
478 	//---------------------------------------------------------------
addAnimatedParameter(IParamBlock * parameterBlock,int parameterId,const String & id,const String & sid,const String parameters[],bool forceFullCheck,ConversionFunctor * conversionFunctor)479 	bool AnimationExporter::addAnimatedParameter( IParamBlock * parameterBlock, int parameterId, const String & id, const String & sid, const String parameters[], bool forceFullCheck, ConversionFunctor* conversionFunctor  )
480 	{
481 		ParamType type = parameterBlock->GetParameterType(parameterId);
482 		Control* controller = parameterBlock->GetController(parameterId);
483 
484 		if ( !isAnimated(controller) )
485 			return false;
486 
487 		switch ( type )
488 		{
489 		case TYPE_FLOAT:
490 			return addAnimatedFloat(controller, id, sid, parameters, forceFullCheck, conversionFunctor);
491 		case TYPE_COLOR:
492 		case TYPE_RGBA:
493 			return addAnimatedPoint4(controller, id, sid, parameters, forceFullCheck, conversionFunctor);
494 		}
495 		return false;
496 	}
497 
498 
499 	//---------------------------------------------------------------
addAnimatedParameter(IParamBlock2 * parameterBlock,int parameterId,const String & id,const String & sid,const String parameters[],bool forceFullCheck,ConversionFunctor * conversionFunctor)500 	bool AnimationExporter::addAnimatedParameter( IParamBlock2 * parameterBlock, int parameterId, const String & id, const String & sid, const String parameters[], bool forceFullCheck, ConversionFunctor* conversionFunctor  )
501 	{
502 		ParamType2 type = parameterBlock->GetParameterType(parameterId);
503 		int animationNumber = parameterBlock->GetAnimNum(parameterId);
504 		if (animationNumber == -1)
505 			return false;
506 
507 #ifdef MAX_2012_OR_NEWER
508 		Control *controller = parameterBlock->GetControllerByIndex(animationNumber);
509 #else
510 		Control *controller = parameterBlock->GetController(animationNumber);
511 #endif
512 
513 		if ( !isAnimated(controller) )
514 			return false;
515 
516 		switch ( type )
517 		{
518 		case TYPE_FLOAT:
519 			return addAnimatedFloat(controller, id, sid, parameters, forceFullCheck, conversionFunctor);
520 		case TYPE_COLOR:
521 		case TYPE_RGBA:
522 			return addAnimatedPoint4(controller, id, sid, parameters, forceFullCheck, conversionFunctor);
523 		}
524 		return false;
525 	}
526 
527 
528     /* void AnimationExporter::addAnimation4( Control * controller, const String & id, const String & sid, const String parameters[])
529      {
530       Control * controllers[4] = {controller->GetXController(), controller->GetYController(), controller->GetZController(), controller->GetWController()};
531       for ( int i = 0; i<4; ++i)
532       {
533        if ( isAnimated(controllers[i]) )
534        {
535         Animation animation(controllers[i], id, sid, parameters[i]);
536         addAnimation(animation);
537        }
538       }
539      }
540 
541     */
542 
543     //---------------------------------------------------------------
isAnimated(Control * controller)544     bool AnimationExporter::isAnimated ( Control * controller )
545     {
546 		return controller && controller->IsAnimated();
547     }
548 
549 
550 	//---------------------------------------------------------------
isAnimated(const Animation & animation,bool forceFullCheck)551 	bool AnimationExporter::isAnimated ( const Animation& animation, bool forceFullCheck )
552 	{
553 		Control* controller = animation.getController();
554 		INode* iNode = animation.getNode();
555 
556 		bool animated = isAnimated(controller) || iNode;
557 
558 		if ( !animated )
559 			return false;
560 
561 		if ( forceFullCheck )
562 			return checkIfIsAnimated(animation);
563 
564 		return animated;
565 	}
566 
567 
isAnimated(IParamBlock * paramBlock,int parameterId)568 	bool AnimationExporter::isAnimated( IParamBlock * paramBlock, int parameterId )
569 	{
570 		Control* controller = paramBlock->GetController(parameterId);
571 		return isAnimated(controller);
572 	}
573 
isAnimated(IParamBlock2 * paramBlock,int parameterId)574 	bool AnimationExporter::isAnimated( IParamBlock2 * paramBlock, int parameterId )
575 	{
576 
577 		int animationNumber = paramBlock->GetAnimNum(parameterId);
578 		if (animationNumber == -1)
579 			return false;
580 		//int pi = paramBlock->AnimNumToParamNum(animationNumber, tabIndex);
581 #ifdef MAX_2012_OR_NEWER
582 		Control *controller = paramBlock->GetControllerByIndex(animationNumber);
583 #else
584 		Control *controller = paramBlock->GetController(animationNumber);
585 #endif
586 
587 		return isAnimated(controller);
588 	}
589 
590 
591 
592 	/** @TODO implement a test that check if an animations animated, i.e. if the values change */
593 	//---------------------------------------------------------------
checkIfIsAnimated(const Animation & animation)594 	bool AnimationExporter::checkIfIsAnimated ( const Animation& animation )
595 	{
596 		Control * controller = animation.getController();
597 		INode * iNode = animation.getNode();
598 
599 		bool isSampling = !controller || mDocumentExporter->getOptions().getSampleAnimation();
600 
601 		IKeyControl * keyInterface = 0;
602 
603 		if (!isSampling)
604 		{
605 			keyInterface = GetKeyControlInterface ( controller );
606 			if ( !keyInterface )
607 				isSampling = true;
608 			else if (keyInterface->GetNumKeys() <= 1)
609 				return false;
610 		}
611 
612 		if ( !isSampling )
613 		{
614 			Class_ID classId = controller->ClassID();
615 
616 			if ( classId.PartB() != 0 )
617 			{
618 				// This is not a Max controller, sample it.
619 				// The only max controllers that have non-zero
620 				// values are not keyable (attach, link, etc).
621 				isSampling = true;
622 			}
623 			else if ( keyInterface && keyInterface->GetNumKeys() > 0 )
624 			{
625 				int keyCount = keyInterface->GetNumKeys();
626 
627 				switch ( classId.PartA() )
628 				{
629 
630 				case LININTERP_FLOAT_CLASS_ID:
631 					return checkIfIsAnimated ( animation,  keyInterface, &AnimationExporter::getFloatValue<ILinFloatKey> );
632 					break;
633 
634 				case LININTERP_POSITION_CLASS_ID:
635 					if ( animation.getDimension() == 1 )
636 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getPointXSingleValue<ILinPoint3Key> );
637 					else
638 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getPointXValue<3, ILinPoint3Key> );
639 
640 					break;
641 
642 				case LININTERP_ROTATION_CLASS_ID:
643 					if ( animation.getDimension() == 1 )
644 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getRotationSingleValue<ILinRotKey> );
645 					else
646 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getRotationValue<ILinRotKey> );
647 					break;
648 
649 				case HYBRIDINTERP_FLOAT_CLASS_ID:
650 //					return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getFloatValue<IBezFloatKey> );
651 				case HYBRIDINTERP_POINT3_CLASS_ID:
652 				case HYBRIDINTERP_POSITION_CLASS_ID:
653 				case HYBRIDINTERP_SCALE_CLASS_ID:
654 				case HYBRIDINTERP_COLOR_CLASS_ID:
655 /*					if ( animation.getDimension() == 1 )
656 					{
657 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getPointXSingleValue<IBezPoint3Key> );
658 					}
659 					else
660 					{
661 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getPointXValue<3, IBezPoint3Key> );
662 					}*/
663 				case HYBRIDINTERP_ROTATION_CLASS_ID:
664 /*					if ( animation.getDimension() == 1 )
665 					{
666 						mKeyValueList.reserve( keyInterface->GetNumKeys() );
667 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getRotationSingleValuePatchEuler );
668 					}
669 					else
670 					{
671 						mKeyValueList.reserve( 3 * keyInterface->GetNumKeys() );
672 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getRotationValuePatchEuler );
673 					}
674 					mKeyValueList.clear();*/
675 					return true; //For hybrid controllers its more complicated te test if they are constant (in-outtangens)
676 					break;
677 				case LININTERP_SCALE_CLASS_ID:
678 					if ( animation.getType() == Animation::SCALE)
679 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getPointXValue<3, IBezScaleKey> );
680 					else if ( animation.getType() == Animation::SCALE_ROT_AXIS )
681 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getScaleRotationAxisValue<IBezScaleKey, false> );
682 					else if ( animation.getType() == Animation::SCALE_ROT_AXIS_R )
683 						return checkIfIsAnimated ( animation, keyInterface, &AnimationExporter::getScaleRotationAxisValue<IBezScaleKey, true> );
684 					break;
685 				default:
686 					isSampling = true;
687 					break;
688 				}
689 			}
690 		}
691 
692 		if ( isSampling )
693 		{
694 			int ticksPerFrame = GetTicksPerFrame();
695 			TimeValue startTime = mDocumentExporter->getOptions().getAnimationStart();
696 			TimeValue endTime = mDocumentExporter->getOptions().getAnimationEnd() + 1;
697 
698 			if ( endTime > startTime )
699 			{
700 				if ( controller )
701 				{
702 					SClass_ID type = controller->SuperClassID();
703 					switch (type)
704 					{
705 					case CTRL_FLOAT_CLASS_ID:
706 						return checkIfSampledFloatIsAnimated(animation, keyInterface, startTime, endTime, ticksPerFrame);
707 						break;
708 					case CTRL_POINT3_CLASS_ID:
709 					case CTRL_POSITION_CLASS_ID:
710 						return checkIfSampledPoint3IsAnimated(animation, keyInterface, startTime, endTime, ticksPerFrame);
711 						break;
712 					case CTRL_ROTATION_CLASS_ID:
713 						return checkIfSampledRotationIsAnimated(animation, keyInterface, startTime, endTime, ticksPerFrame);
714 						break;
715 					}
716 				}
717 				else if ( animation.getNode() )
718 				{
719 					return checkIfSampledTransformationIsAnimated(animation, keyInterface, startTime, endTime, ticksPerFrame);
720 				}
721 			}
722 
723 		}
724 
725 		return false;
726 	}
727 
728 
729 	//---------------------------------------------------------------
checkIfIsAnimated(const Animation & animation,IKeyControl * keyInterface,OutputValueFunctionPtr outputValueFunction)730 	bool AnimationExporter::checkIfIsAnimated ( const Animation & animation, IKeyControl* keyInterface, OutputValueFunctionPtr outputValueFunction )
731 	{
732 		int keyCount = keyInterface->GetNumKeys();
733 		int keyLength = animation.getDimension();
734 
735 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
736 
737 		bool animated = false;
738 
739 		float * firstKeyBuffer = new float[ keyLength ];
740 
741 		float * keyBuffer = new float[ keyLength ];
742 
743 		for ( int i = 0; i < keyCount && !animated; ++i )
744 		{
745 			(this->*outputValueFunction) ( keyBuffer, keyInterface, i, animation );
746 
747 			for ( int j = 0; j < keyLength && !animated; ++j )
748 			{
749 				if ( conversionFunctor )
750 					keyBuffer[j] = (*conversionFunctor) ( keyBuffer[ j ] );
751 
752 				if ( i == 0 )
753 					std::swap(firstKeyBuffer, keyBuffer);
754 				else if ( !COLLADASW::MathUtils::equals(keyBuffer[j], firstKeyBuffer[j]) )
755 					animated = true;
756 			}
757 		}
758 
759 		delete[] keyBuffer;
760 		delete[] firstKeyBuffer;
761 
762 		return animated;
763 	}
764 
765 
766 	//---------------------------------------------------------------
checkIfSampledFloatIsAnimated(const Animation & animation,IKeyControl * keyInterface,TimeValue startTime,TimeValue endTime,int ticksPerFrame)767 	bool AnimationExporter::checkIfSampledFloatIsAnimated ( const Animation & animation, IKeyControl* keyInterface, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
768 	{
769 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
770 
771 		if ( keyCount < 2 )
772 			return false;
773 
774 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
775 
776 		float firstKeyValue;
777 
778 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
779 		{
780 			float keyValue;
781 			animation.getController()->GetValue(time, &keyValue, FOREVER, CTRL_ABSOLUTE);
782 
783 			if ( conversionFunctor )
784 				keyValue = (*conversionFunctor)(keyValue);
785 
786 			if ( time == startTime )
787 				firstKeyValue = keyValue;
788 			else if ( !COLLADASW::MathUtils::equals(keyValue, firstKeyValue) )
789 				return true;
790 		}
791 		return false;
792 	}
793 
794 	//---------------------------------------------------------------
checkIfSampledPoint3IsAnimated(const Animation & animation,IKeyControl * keyInterface,TimeValue startTime,TimeValue endTime,int ticksPerFrame)795 	bool AnimationExporter::checkIfSampledPoint3IsAnimated ( const Animation & animation, IKeyControl* keyInterface, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
796 	{
797 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
798 
799 		if ( keyCount < 2 )
800 			return false;
801 
802 		int keyLength = animation.getDimension();
803 
804 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
805 
806 		float firstFloatKeyValue;
807 		Point3 firstKeyValue;
808 
809 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
810 		{
811 
812 			Point3 keyValue;
813 			animation.getController()->GetValue(time, &keyValue, FOREVER, CTRL_ABSOLUTE);
814 
815 
816 			if ( keyLength == 1)
817 			{
818 				float floatKeyValue = keyValue[ animation.getType() - Animation::POSITION_X ];
819 
820 				if ( conversionFunctor )
821 					floatKeyValue = (*conversionFunctor)( floatKeyValue );
822 
823 				if ( time == startTime )
824 					firstFloatKeyValue = floatKeyValue;
825 				else if ( !COLLADASW::MathUtils::equals(floatKeyValue, firstFloatKeyValue) )
826 					return true;
827 
828 			}
829 			else
830 			{
831 				for ( int j = 0; j < keyLength; ++j )
832 				{
833 					assert( (keyLength == 3) || (keyLength == 4));
834 
835 					if ( conversionFunctor )
836 						keyValue[j] = (*conversionFunctor) ( keyValue[ j ] );
837 
838 					if ( time > startTime && !COLLADASW::MathUtils::equals(keyValue[j], firstKeyValue[j]) )
839 						return true;
840 				}
841 
842 				if ( time == startTime )
843 					firstKeyValue = keyValue;
844 			}
845 		}
846 		return false;
847 	}
848 
849 
850 	//---------------------------------------------------------------
checkIfSampledRotationIsAnimated(const Animation & animation,IKeyControl * keyInterface,TimeValue startTime,TimeValue endTime,int ticksPerFrame)851 	bool AnimationExporter::checkIfSampledRotationIsAnimated ( const Animation & animation, IKeyControl* keyInterface, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
852 	{
853 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
854 		int keyLength = animation.getDimension();
855 
856 		if ( keyCount < 2 )
857 			return false;
858 
859 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
860 
861 		float firstFloatKeyValue;
862 		float firstEulerAngles[ 3 ];
863 
864 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
865 		{
866 
867 			float eulerAngles[ 3 ];
868 
869 			if ( keyLength == 1 )
870 			{
871 				Quat quaternion;
872 				animation.getController()->GetValue(time, &quaternion, FOREVER, CTRL_ABSOLUTE);
873 				QuatToEuler(quaternion, eulerAngles, EULERTYPE_XYZ);
874 			}
875 			else
876 			{
877 				AngAxis angleAxis;
878 				animation.getController()->GetValue(time, &angleAxis, FOREVER, CTRL_ABSOLUTE);
879 				Quat quaternion(angleAxis);
880 				quaternion.GetEuler ( &eulerAngles[ 0 ], &eulerAngles[ 1 ], &eulerAngles[ 2 ] );
881 			}
882 
883 			if ( time > startTime)
884 				patchEuler(mPreviousEulerAngles, eulerAngles);
885 
886 			mPreviousEulerAngles[0] = eulerAngles[0];
887 			mPreviousEulerAngles[1] = eulerAngles[1];
888 			mPreviousEulerAngles[2] = eulerAngles[2];
889 
890 			if ( keyLength == 1)
891 			{
892 				float floatKeyValue = eulerAngles[ animation.getType() - Animation::ROTATION_X ];
893 
894 				if ( conversionFunctor )
895 					floatKeyValue = (*conversionFunctor)(floatKeyValue);
896 
897 				if ( time == startTime )
898 					firstFloatKeyValue = floatKeyValue;
899 				else if ( !COLLADASW::MathUtils::equals(floatKeyValue, firstFloatKeyValue) )
900 					return true;
901 			}
902 			else
903 			{
904 				for ( int j = 0; j < 3; ++j )
905 				{
906 					if ( conversionFunctor )
907 						eulerAngles[j] = (*conversionFunctor) ( eulerAngles[ j ] );
908 
909 					if ( time == startTime )
910 						firstEulerAngles[j] = eulerAngles[j];
911 					else if ( !COLLADASW::MathUtils::equals(eulerAngles[j], firstEulerAngles[j]) )
912 						return true;
913 				}
914 			}
915 		}
916 		return false;
917 	}
918 
919 
920 
checkIfSampledTransformationIsAnimated(const Animation & animation,IKeyControl * keyInterface,TimeValue startTime,TimeValue endTime,int ticksPerFrame)921 	bool AnimationExporter::checkIfSampledTransformationIsAnimated( const Animation & animation, IKeyControl* keyInterface, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
922 	{
923 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
924 		int keyLength = animation.getDimension();
925 
926 		if ( keyCount < 2 )
927 			return false;
928 
929 		INode * iNode = animation.getNode();
930 
931 		Matrix3 transformationMatrix;
932 		Matrix3 firstTransformationMatrix;
933 		Matrix3 parentTransformationMatrix;
934 
935 		INode* parentINode = iNode->GetParentNode();
936 
937 		if (parentINode && parentINode->IsRootNode())
938 			parentINode = 0;
939 
940 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
941 		{
942 			// Export the base NODE TM
943 			transformationMatrix = iNode->GetNodeTM(time);
944 
945 			if (parentINode)
946 			{
947 				// export a relative TM
948 				// We have to use whatever value we exported for this parent
949 				// in order to remain consistent in collada
950 				parentTransformationMatrix = parentINode->GetNodeTM(time);
951 				parentTransformationMatrix.Invert();
952 				transformationMatrix = transformationMatrix * parentTransformationMatrix;
953 			}
954 
955 			const Matrix3& constTransformationMatrix = transformationMatrix;
956 
957 			if ( time == startTime )
958 			{
959 				firstTransformationMatrix = transformationMatrix;
960 			}
961 			else
962 			{
963 				const Matrix3& constTransformationMatrix = transformationMatrix;
964 				const Matrix3& constFirstTransformationMatrix = firstTransformationMatrix;
965 
966 				for ( int row = 0; row < 3; ++row)
967 					for ( int col = 0; col < 4; ++col)
968 						if ( !COLLADASW::MathUtils::equals(constTransformationMatrix[col][row], constFirstTransformationMatrix[col][row] ) )
969 							return true;
970 			}
971 
972 		}
973 
974 		return false;
975 	}
976 
977 
978 
979     //---------------------------------------------------------------
getBaseId(const Animation & animation)980     String AnimationExporter::getBaseId ( const Animation & animation )
981     {
982 
983 		String baseId = animation.getId();
984 
985 		const String& sid = animation.getSid();
986 
987 		if ( !sid.empty() )
988 			baseId += "_" + animation.getSid();
989 
990 
991 		if ( animation.getMatrixIndex() < 0)
992 		{
993 			if ( (animation.getDimension() == 1) && (animation.getParameter()) )
994 				baseId += "." + * ( animation.getParameter() );
995 		}
996 		else
997 		{
998 			baseId += "_" + COLLADASW::Utils::toString(animation.getMatrixIndex()) + "_";
999 		}
1000 		return baseId;
1001     }
1002 
1003     //---------------------------------------------------------------
getTarget(const Animation & animation)1004     String AnimationExporter::getTarget ( const Animation & animation )
1005     {
1006 		String target = animation.getId();
1007 
1008 		const String& sid = animation.getSid();
1009 
1010 		if ( !sid.empty() )
1011 			target += "/" + animation.getSid();
1012 
1013 
1014 		if ( animation.getMatrixIndex() < 0)
1015 		{
1016 			if ( (animation.getDimension() == 1) && (animation.getParameter()) )
1017 				target += "." + * ( animation.getParameter() );
1018 		}
1019 		else
1020 		{
1021 			target += "(" + COLLADASW::Utils::toString(animation.getMatrixIndex()) + ")";
1022 		}
1023 		return target;
1024     }
1025 
1026     //---------------------------------------------------------------
exportSources(Animation & animation)1027     void AnimationExporter::exportSources ( Animation & animation )
1028     {
1029 		Control * controller = animation.getController();
1030 		INode * iNode = animation.getNode();
1031 
1032 		bool isSampling = !controller || mDocumentExporter->getOptions().getSampleAnimation();
1033 
1034         String baseId = getBaseId ( animation );
1035 
1036 		IKeyControl * keyInterface = 0;
1037 
1038 		if (!isSampling)
1039 		{
1040 			keyInterface = GetKeyControlInterface ( controller );
1041 			if ( !keyInterface )
1042 				isSampling = true;
1043 			else if (keyInterface->GetNumKeys() <= 1)
1044 				return;
1045 		}
1046 
1047 
1048 		if ( !isSampling )
1049         {
1050             Class_ID classId = controller->ClassID();
1051 
1052             if ( classId.PartB() != 0 )
1053             {
1054                 // This is not a Max controller, sample it.
1055                 // The only max controllers that have non-zero
1056                 // values are not keyable (attach, link, etc).
1057                 isSampling = true;
1058             }
1059             else if ( keyInterface != NULL && keyInterface->GetNumKeys() > 0 )
1060             {
1061                 int keyCount = keyInterface->GetNumKeys();
1062 
1063                 switch ( classId.PartA() )
1064                 {
1065 
1066                 case LININTERP_FLOAT_CLASS_ID:
1067                     exportInputSource<ILinFloatKey> ( baseId, controller, keyInterface );
1068                     exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getFloatValue<ILinFloatKey> );
1069                     exportInterpolationSource ( baseId, keyInterface, getUniformInterpolation<LibraryAnimations::LINEAR_NAME>, keyInterface->GetNumKeys() );
1070 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1071                     break;
1072 
1073                 case LININTERP_POSITION_CLASS_ID:
1074                     exportInputSource<ILinPoint3Key> ( baseId, controller, keyInterface );
1075 
1076                     if ( animation.getDimension() == 1 )
1077                         exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXSingleValue<ILinPoint3Key> );
1078                     else
1079                         exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXValue<3, ILinPoint3Key> );
1080 
1081                     exportInterpolationSource ( baseId, keyInterface, getUniformInterpolation<LibraryAnimations::LINEAR_NAME>, keyInterface->GetNumKeys() );
1082 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1083                     break;
1084 
1085                 case LININTERP_ROTATION_CLASS_ID:
1086                     exportInputSource<ILinRotKey> ( baseId, controller, keyInterface );
1087 
1088                     if ( animation.getDimension() == 1 )
1089                         exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getRotationSingleValue<ILinRotKey> );
1090                     else
1091                         exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getRotationValue<ILinRotKey> );
1092 
1093                     exportInterpolationSource ( baseId, keyInterface, getUniformInterpolation<LibraryAnimations::LINEAR_NAME>, keyInterface->GetNumKeys() );
1094 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1095                     break;
1096 				case LININTERP_SCALE_CLASS_ID:
1097 					exportInputSource<ILinScaleKey> ( baseId, controller, keyInterface );
1098 					if ( animation.getType() == Animation::SCALE)
1099 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXValue<3, ILinScaleKey> );
1100 					else if ( animation.getType() == Animation::SCALE_ROT_AXIS )
1101 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getScaleRotationAxisValue<ILinScaleKey, false> );
1102 					else if ( animation.getType() == Animation::SCALE_ROT_AXIS_R )
1103 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getScaleRotationAxisValue<ILinScaleKey, true> );
1104 					exportInterpolationSource ( baseId, keyInterface, getUniformInterpolation<LibraryAnimations::LINEAR_NAME>, keyInterface->GetNumKeys() );
1105 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1106 					break;
1107                 case HYBRIDINTERP_FLOAT_CLASS_ID:
1108                     exportInputSource<IBezFloatKey> ( baseId, controller, keyInterface );
1109 
1110                     exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getFloatValue<IBezFloatKey> );
1111 
1112                     exportInTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getFloatInTangentValue );
1113 
1114                     exportOutTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getFloatOutTangentValue );
1115 
1116                     exportInterpolationSource ( baseId, keyInterface, getBezierInterpolation<IBezFloatKey>, keyInterface->GetNumKeys() );
1117 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::IN_TANGENT | Animation::OUT_TANGENT | Animation::INTERPOLATION);
1118 
1119                     break;
1120 				case HYBRIDINTERP_POINT3_CLASS_ID:
1121 				case HYBRIDINTERP_POSITION_CLASS_ID:
1122 				case HYBRIDINTERP_COLOR_CLASS_ID:
1123 					exportInputSource<IBezPoint3Key> ( baseId, controller, keyInterface );
1124 					if ( animation.getDimension() == 1 )
1125 					{
1126 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXSingleValue<IBezPoint3Key> );
1127 						exportInTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXSingleInTangentValue<IBezPoint3Key>);
1128 						exportOutTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXSingleOutTangentValue<IBezPoint3Key> );
1129 					}
1130 					else
1131 					{
1132 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXValue<3, IBezPoint3Key> );
1133 						exportInTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXInTangentValue<3, IBezPoint3Key> );
1134 						exportOutTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXOutTangentValue<3, IBezPoint3Key> );
1135 					}
1136 					exportInterpolationSource ( baseId, keyInterface, getBezierInterpolation<IBezPoint3Key>, keyInterface->GetNumKeys() );
1137 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::IN_TANGENT | Animation::OUT_TANGENT | Animation::INTERPOLATION);
1138 					break;
1139 				case HYBRIDINTERP_POINT4_CLASS_ID:
1140 				case HYBRIDINTERP_FRGBA_CLASS_ID:
1141 					exportInputSource<IBezPoint4Key> ( baseId, controller, keyInterface );
1142 					if ( animation.getDimension() == 1 )
1143 					{
1144 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXSingleValue<IBezPoint4Key> );
1145 						exportInTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXSingleInTangentValue<IBezPoint4Key> );
1146 						exportOutTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXSingleOutTangentValue<IBezPoint4Key> );
1147 					}
1148 					else
1149 					{
1150 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXValue<4, IBezPoint4Key> );
1151 						exportInTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXInTangentValue<4, IBezPoint4Key> );
1152 						exportOutTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXOutTangentValue<4, IBezPoint4Key> );
1153 					}
1154 					exportInterpolationSource ( baseId, keyInterface, getBezierInterpolation<IBezPoint4Key>, keyInterface->GetNumKeys() );
1155 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::IN_TANGENT | Animation::OUT_TANGENT | Animation::INTERPOLATION);
1156 					break;
1157 
1158 				case HYBRIDINTERP_ROTATION_CLASS_ID:
1159 					exportInputSource<IBezQuatKey> ( baseId, controller, keyInterface );
1160 					if ( animation.getDimension() == 1 )
1161 					{
1162 						mKeyValueList.reserve( keyInterface->GetNumKeys() );
1163 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getRotationSingleValuePatchEuler );
1164 						exportInTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getRotationSingleInTangentPatchEuler );
1165 						exportOutTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getRotationSingleOutTangentPatchEuler );
1166 					}
1167 					else
1168 					{
1169 						mKeyValueList.reserve( 3 * keyInterface->GetNumKeys() );
1170 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getRotationValuePatchEuler );
1171 						exportInTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getRotationSingleInTangentPatchEuler );
1172 						exportOutTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getRotationSingleOutTangentPatchEuler );
1173 					}
1174 					mKeyValueList.clear();
1175 					exportInterpolationSource ( baseId, keyInterface, getBezierInterpolation<IBezQuatKey>, keyInterface->GetNumKeys() );
1176 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::IN_TANGENT | Animation::OUT_TANGENT | Animation::INTERPOLATION);
1177 					break;
1178 				case HYBRIDINTERP_SCALE_CLASS_ID:
1179 					exportInputSource<IBezScaleKey> ( baseId, controller, keyInterface );
1180 					if ( animation.getType() == Animation::SCALE)
1181 					{
1182 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXValue<3, IBezScaleKey> );
1183 						exportInTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXInTangentValue<3, IBezScaleKey> );
1184 						exportOutTangentSource ( animation, baseId, keyInterface, &AnimationExporter::getPointXOutTangentValue<3, IBezScaleKey> );
1185 						exportInterpolationSource ( baseId, keyInterface, getBezierInterpolation<IBezScaleKey>, keyInterface->GetNumKeys() );
1186 						animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::IN_TANGENT | Animation::OUT_TANGENT | Animation::INTERPOLATION);
1187 					}
1188 					else if ( animation.getType() == Animation::SCALE_ROT_AXIS )
1189 					{
1190 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getScaleRotationAxisValue<IBezScaleKey, false> );
1191 						exportInterpolationSource ( baseId, keyInterface, getUniformInterpolation<LibraryAnimations::LINEAR_NAME>, keyInterface->GetNumKeys() );
1192 						animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1193 					}
1194 					else if ( animation.getType() == Animation::SCALE_ROT_AXIS_R )
1195 					{
1196 						exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getScaleRotationAxisValue<IBezScaleKey, true> );
1197 						exportInterpolationSource ( baseId, keyInterface, getUniformInterpolation<LibraryAnimations::LINEAR_NAME>, keyInterface->GetNumKeys() );
1198 						animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1199 					}
1200 					break;
1201 
1202 //				case TCBINTERP_FLOAT_CLASS_ID:
1203 //					exportInputSource<ITCBFloatKey> ( baseId, controller, keyInterface );
1204 //					exportOutputSource ( animation, baseId, keyInterface, &AnimationExporter::getFloatValue<ITCBFloatKey> );
1205 
1206 					break;
1207 				default:
1208 					isSampling = true;
1209 					break;
1210                 }
1211             }
1212         }
1213 
1214 		if ( isSampling )
1215 		{
1216 			int ticksPerFrame = GetTicksPerFrame();
1217 			TimeValue startTime = mDocumentExporter->getOptions().getAnimationStart();
1218 			TimeValue endTime = mDocumentExporter->getOptions().getAnimationEnd() + 1;
1219 
1220 			if ( endTime > startTime )
1221 			{
1222 				exportSamplingInputSource(baseId, startTime, endTime, ticksPerFrame);
1223 				if ( controller )
1224 				{
1225 					SClass_ID type = controller->SuperClassID();
1226 					switch (type)
1227 					{
1228 					case CTRL_FLOAT_CLASS_ID:
1229 						exportSamplingFloatOutputSource(animation, baseId, keyInterface, startTime, endTime, ticksPerFrame);
1230 						animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1231 						break;
1232 					case CTRL_POINT3_CLASS_ID:
1233 					case CTRL_POSITION_CLASS_ID:
1234 						exportSamplingPoint3OutputSource(animation, baseId, keyInterface, startTime, endTime, ticksPerFrame);
1235 						animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1236 						break;
1237 					case CTRL_ROTATION_CLASS_ID:
1238 						exportSamplingRotationOutputSource(animation, baseId, keyInterface, startTime, endTime, ticksPerFrame);
1239 						animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1240 						break;
1241 					}
1242 				}
1243 				else if ( iNode )
1244 				{
1245 					exportSamplingTransformationOutputSource(animation, baseId, keyInterface, startTime, endTime, ticksPerFrame);
1246 					animation.setInputTypeFlags(Animation::INPUT | Animation::OUTPUT | Animation::INTERPOLATION);
1247 				}
1248 				exportSamplingInterpolationSource(baseId, startTime, endTime, ticksPerFrame);
1249 			}
1250 
1251 		}
1252     }
1253 
1254 
1255     //---------------------------------------------------------------
1256     template <class KeyClassName>
exportInputSource(const String & baseId,Control * controller,IKeyControl * keyInterface)1257     void AnimationExporter::exportInputSource ( const String & baseId, Control * controller, IKeyControl* keyInterface )
1258     {
1259         int keyCount = keyInterface->GetNumKeys();
1260 
1261         COLLADASW::FloatSource source ( mSW );
1262         source.setId ( baseId + INPUT_SOURCE_ID_SUFFIX );
1263         source.setArrayId ( baseId + INPUT_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
1264         source.setAccessorStride ( 1 );
1265         source.getParameterNameList().push_back ( "TIME" );
1266         source.setAccessorCount ( keyCount );
1267         source.prepareToAppendValues();
1268 
1269         for ( int i = 0; i < keyCount; ++i )
1270         {
1271             KeyClassName key;
1272             keyInterface->GetKey ( i, &key );
1273             source.appendValues ( key.time * mTimeFactor );
1274         }
1275 
1276         source.finish();
1277     }
1278 
1279 
1280     //---------------------------------------------------------------
exportOutputSource(const Animation & animation,const String & baseId,IKeyControl * keyInterface,OutputValueFunctionPtr outputValueFunction)1281     void AnimationExporter::exportOutputSource ( const Animation & animation, const String & baseId, IKeyControl* keyInterface, OutputValueFunctionPtr outputValueFunction )
1282     {
1283         int keyCount = keyInterface->GetNumKeys();
1284         int keyLength = animation.getDimension();
1285 
1286         COLLADASW::FloatSource source ( mSW );
1287         source.setId ( baseId + OUTPUT_SOURCE_ID_SUFFIX );
1288         source.setArrayId ( baseId + OUTPUT_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
1289         source.setAccessorStride ( keyLength );
1290 
1291         for ( int i = 0; i < keyLength; ++i )
1292 		{
1293 			if ( animation.getParameter() )
1294 				source.getParameterNameList().push_back ( * ( animation.getParameter() + i) );
1295 			else
1296 				source.getParameterNameList().push_back ( EMPTY_STRING );
1297 		}
1298 
1299         source.setAccessorCount ( keyCount );
1300 
1301         source.prepareToAppendValues();
1302 
1303         float * keyBuffer = new float[ keyLength ];
1304 
1305         for ( int i = 0; i < keyCount; ++i )
1306         {
1307             (this->*outputValueFunction) ( keyBuffer, keyInterface, i, animation );
1308 
1309             for ( int j = 0; j < keyLength; ++j )
1310             {
1311                 ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
1312 
1313                 if ( conversionFunctor )
1314                     source.appendValues ( (*conversionFunctor) ( keyBuffer[ j ] ) );
1315                 else
1316                     source.appendValues ( keyBuffer[ j ] );
1317             }
1318         }
1319 
1320         delete[] keyBuffer;
1321 
1322         source.finish();
1323     }
1324 
1325     //---------------------------------------------------------------
1326 	template <class KeyType>
getFloatValue(float * keyValues,IKeyControl * keyInterface,const int & keyIndex,const Animation & animation)1327     void AnimationExporter::getFloatValue ( float * keyValues, IKeyControl * keyInterface, const int & keyIndex, const Animation & animation )
1328     {
1329         KeyType key;
1330         keyInterface->GetKey ( keyIndex, &key );
1331         *keyValues = key.val;
1332     }
1333 
1334     //---------------------------------------------------------------
1335 	template<class KeyType>
getPointXSingleValue(float * keyValues,IKeyControl * keyInterface,const int & keyIndex,const Animation & animation)1336     void AnimationExporter::getPointXSingleValue ( float * keyValues, IKeyControl * keyInterface, const int & keyIndex, const Animation & animation )
1337     {
1338         KeyType key;
1339         keyInterface->GetKey ( keyIndex, &key );
1340         *keyValues = key.val[ animation.getType() - Animation::POSITION_X ];
1341     }
1342 
1343     //---------------------------------------------------------------
1344 	template<int dimension, class KeyType>
getPointXValue(float * keyValues,IKeyControl * keyInterface,const int & keyIndex,const Animation & animation)1345     void AnimationExporter::getPointXValue ( float * keyValues, IKeyControl * keyInterface, const int & keyIndex, const Animation & animation )
1346     {
1347         KeyType key;
1348         keyInterface->GetKey ( keyIndex, &key );
1349 		for ( int i = 0; i<dimension; ++i)
1350 			keyValues[ i ] = key.val[ i ];
1351     }
1352 
1353     //---------------------------------------------------------------
1354 	template<class KeyType>
getRotationSingleValue(float * keyValues,IKeyControl * keyInterface,const int & keyIndex,const Animation & animation)1355     void AnimationExporter::getRotationSingleValue ( float * keyValues, IKeyControl * keyInterface, const int & keyIndex, const Animation & animation )
1356     {
1357         KeyType key;
1358         keyInterface->GetKey ( keyIndex, &key );
1359         float eulerAngles[ 3 ];
1360         key.val.GetEuler ( &eulerAngles[ 0 ], &eulerAngles[ 1 ], &eulerAngles[ 2 ] );
1361         *keyValues = eulerAngles[ animation.getType() - Animation::ROTATION_X ];
1362     }
1363 
1364     //---------------------------------------------------------------
1365 	template<class KeyType>
getRotationValue(float * keyValues,IKeyControl * keyInterface,const int & keyIndex,const Animation & animation)1366     void AnimationExporter::getRotationValue ( float * keyValues, IKeyControl * keyInterface, const int & keyIndex, const Animation & animation )
1367     {
1368         KeyType key;
1369         keyInterface->GetKey ( keyIndex, &key );
1370         key.val.GetEuler ( &keyValues[ 0 ], &keyValues[ 1 ], &keyValues[ 2 ] );
1371     }
1372 
1373 
1374 	//---------------------------------------------------------------
getRotationSingleValuePatchEuler(float * keyValues,IKeyControl * keyInterface,const int & keyIndex,const Animation & animation)1375 	void AnimationExporter::getRotationSingleValuePatchEuler ( float * keyValues, IKeyControl * keyInterface, const int & keyIndex, const Animation & animation )
1376 	{
1377 		IBezQuatKey key;
1378 		keyInterface->GetKey ( keyIndex, &key );
1379 		float eulerAngles[ 3 ];
1380 		key.val.GetEuler ( &eulerAngles[ 0 ], &eulerAngles[ 1 ], &eulerAngles[ 2 ] );
1381 		if ( keyIndex > 0)
1382 			patchEuler(mPreviousEulerAngles, eulerAngles);
1383 
1384 		mPreviousEulerAngles[0] = eulerAngles[0];
1385 		mPreviousEulerAngles[1] = eulerAngles[1];
1386 		mPreviousEulerAngles[2] = eulerAngles[2];
1387 
1388 		*keyValues = eulerAngles[ animation.getType() - Animation::ROTATION_X ];
1389 
1390 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
1391 		if ( conversionFunctor )
1392 			mKeyValueList.push_back((float)((*conversionFunctor)(*keyValues)));
1393 		else
1394 			mKeyValueList.push_back((float)(*keyValues));
1395 
1396 	}
1397 
1398 	//---------------------------------------------------------------
getRotationValuePatchEuler(float * keyValues,IKeyControl * keyInterface,const int & keyIndex,const Animation & animation)1399 	void AnimationExporter::getRotationValuePatchEuler ( float * keyValues, IKeyControl * keyInterface, const int & keyIndex, const Animation & animation )
1400 	{
1401 		IBezQuatKey key;
1402 		keyInterface->GetKey ( keyIndex, &key );
1403 		float eulerAngles[ 3 ];
1404 		key.val.GetEuler ( &eulerAngles[ 0 ], &eulerAngles[ 1 ], &eulerAngles[ 2 ] );
1405 		if ( keyIndex > 0)
1406 			patchEuler(mPreviousEulerAngles, eulerAngles);
1407 
1408 		mPreviousEulerAngles[0] = eulerAngles[0];
1409 		mPreviousEulerAngles[1] = eulerAngles[1];
1410 		mPreviousEulerAngles[2] = eulerAngles[2];
1411 
1412 		keyValues[ 0 ] = eulerAngles[0];
1413 		keyValues[ 1 ] = eulerAngles[1];
1414 		keyValues[ 2 ] = eulerAngles[2];
1415 
1416 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
1417 		if ( conversionFunctor )
1418 		{
1419 			mKeyValueList.push_back((float)((*conversionFunctor)(eulerAngles[0])));
1420 			mKeyValueList.push_back((float)((*conversionFunctor)(eulerAngles[1])));
1421 			mKeyValueList.push_back((float)((*conversionFunctor)(eulerAngles[2])));
1422 		}
1423 		else
1424 		{
1425 			mKeyValueList.push_back(eulerAngles[0]);
1426 			mKeyValueList.push_back(eulerAngles[1]);
1427 			mKeyValueList.push_back(eulerAngles[2]);
1428 		}
1429 	}
1430 
1431 
1432 	//---------------------------------------------------------------
1433 	template<class KeyType, bool reversed>
getScaleRotationAxisValue(float * keyValues,IKeyControl * keyInterface,const int & keyIndex,const Animation & animation)1434 	void AnimationExporter::getScaleRotationAxisValue( float * keyValues, IKeyControl * keyInterface, const int & keyIndex, const Animation & animation )
1435 	{
1436 		KeyType key;
1437 		keyInterface->GetKey ( keyIndex, &key );
1438 		AngAxis angleAxis(key.val.q);
1439 
1440 		keyValues[0] = angleAxis.axis.x;
1441 		keyValues[1] = angleAxis.axis.y;
1442 		keyValues[2] = angleAxis.axis.z;
1443 		if ( reversed )
1444 			keyValues[3] = -COLLADASW::MathUtils::radToDegF(angleAxis.angle);
1445 		else
1446 			keyValues[3] = COLLADASW::MathUtils::radToDegF(angleAxis.angle);
1447 	}
1448 
1449 
1450 	//---------------------------------------------------------------
exportTangentSource(const String & sourceIdSuffix,const Animation & animation,const String & baseId,IKeyControl * keyInterface,TangentValueFunctionPtr tangentValueFunction)1451     void AnimationExporter::exportTangentSource ( const String & sourceIdSuffix, const Animation & animation, const String & baseId, IKeyControl* keyInterface, TangentValueFunctionPtr tangentValueFunction )
1452     {
1453         int keyCount = keyInterface->GetNumKeys();
1454 		int keyLength = animation.getDimension();
1455 
1456         COLLADASW::FloatSource source ( mSW );
1457         source.setId ( baseId + sourceIdSuffix );
1458         source.setArrayId ( baseId + sourceIdSuffix + ARRAY_ID_SUFFIX );
1459         source.setAccessorStride ( 2 * keyLength );
1460 		for ( int i = 0; i < keyLength; ++i )
1461 		{
1462 			source.getParameterNameList().push_back ( "X" );
1463 			source.getParameterNameList().push_back ( "Y" );
1464 		}
1465         source.setAccessorCount ( keyCount );
1466         source.prepareToAppendValues();
1467 
1468 		float * keyBufferX = new float[ keyLength ];
1469 		float * keyBufferY = new float[ keyLength ];
1470 
1471 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
1472 
1473         for ( int i = 0; i < keyCount; ++i )
1474         {
1475             ( this->*tangentValueFunction ) ( keyBufferX, keyBufferY, keyInterface, i, keyCount, animation );
1476 			for ( int j = 0; j < keyLength; ++j )
1477 			{
1478 				if ( conversionFunctor )
1479 					source.appendValues ( keyBufferX[j], (*conversionFunctor)(keyBufferY[j]) );
1480 				else
1481 					source.appendValues ( keyBufferX[j], keyBufferY[j] );
1482 			}
1483         }
1484 
1485 		delete[] keyBufferX;
1486 		delete[] keyBufferY;
1487 
1488         source.finish();
1489     }
1490 
1491     //---------------------------------------------------------------
exportInTangentSource(const Animation & animation,const String & baseId,IKeyControl * keyInterface,TangentValueFunctionPtr tangentValueFunction)1492     void AnimationExporter::exportInTangentSource ( const Animation & animation, const String & baseId, IKeyControl* keyInterface, TangentValueFunctionPtr tangentValueFunction )
1493     {
1494         exportTangentSource ( INTANGENT_SOURCE_ID_SUFFIX, animation, baseId, keyInterface, tangentValueFunction );
1495     }
1496 
1497     //---------------------------------------------------------------
getFloatInTangentValue(float * inTangentValuesX,float * inTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1498     void AnimationExporter::getFloatInTangentValue ( float * inTangentValuesX, float * inTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1499     {
1500         IBezFloatKey key;
1501         keyInterface->GetKey ( keyIndex, &key );
1502 
1503         float previousTime = ( float ) getPreviousTime ( keyIndex, animation.getController() );
1504 
1505         //testing
1506         /*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1507                 bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1508                 bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1509                 bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1510                 bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1511         */
1512         InterpolationType interpolationInType = getInterpolationInType ( key.flags );
1513 
1514         // export control point. If it is not a BEZIER interpolation, export the point itself
1515 
1516         if ( interpolationInType == BEZIER )
1517         {
1518             if ( GetInTanType ( key.flags ) != BEZKEY_USER )
1519                 key.inLength = DEFAULT_INLENGHT_FLOAT;
1520             else if ( key.flags & BEZKEY_UNCONSTRAINHANDLE )
1521                 key.inLength *= GetTicksPerFrame() / ( float ) ( key.time - previousTime );
1522 
1523             float inInterval = ( key.time - previousTime ) * key.inLength;
1524 
1525             *inTangentValuesX = ( key.time - inInterval ) * mTimeFactor;
1526 
1527             *inTangentValuesY = key.val + key.intan * inInterval;
1528         }
1529 		else if ( interpolationInType == LINEAR )
1530 		{
1531 			*inTangentValuesX = (key.time - ( key.time - previousTime )/3) * mTimeFactor;
1532 			// get the previous key value
1533 			int numKeys = keyInterface->GetNumKeys();
1534 			int previousKeyIndex = ( keyIndex > 0 ) ? keyIndex - 1 : numKeys - 1;
1535 			IBezFloatKey previousKey;
1536 			keyInterface->GetKey ( previousKeyIndex, &previousKey );
1537 			*inTangentValuesY = (key.val - ( key.val - previousKey.val )/3);
1538 		}
1539         else
1540         {
1541             /// @TODO: clarify if this makes sense or if we should export the same as above
1542             *inTangentValuesX = key.time * mTimeFactor;
1543             *inTangentValuesY = key.val;
1544         }
1545     }
1546 
1547 
1548 	//---------------------------------------------------------------
1549 	template <class KeyType>
getPointXSingleInTangentValue(float * inTangentValuesX,float * inTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1550 	void AnimationExporter::getPointXSingleInTangentValue ( float * inTangentValuesX, float * inTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1551 	{
1552 		KeyType key;
1553 		keyInterface->GetKey ( keyIndex, &key );
1554 
1555 		float previousTime = ( float ) getPreviousTime ( keyIndex, animation.getController() );
1556 
1557 		//testing
1558 		/*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1559 		bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1560 		bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1561 		bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1562 		bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1563 		*/
1564 		InterpolationType interpolationInType = getInterpolationInType ( key.flags );
1565 
1566 		int keyParamterIndex = animation.getType() - Animation::POSITION_X;
1567 
1568 		// export control point. If it is not a BEZIER interpolation, export the point itself
1569 
1570 		if ( interpolationInType == BEZIER )
1571 		{
1572 			if ( GetInTanType ( key.flags ) != BEZKEY_USER )
1573 				key.inLength = DEFAULT_INLENGHT_POINTX_FLOAT_ARRAY;
1574 			else if ( key.flags & BEZKEY_UNCONSTRAINHANDLE )
1575 				key.inLength *= GetTicksPerFrame() / ( float ) ( key.time - previousTime );
1576 
1577 			float inInterval = ( key.time - previousTime ) * key.inLength[keyParamterIndex];
1578 			*inTangentValuesX = ( key.time - inInterval ) * mTimeFactor;
1579 			*inTangentValuesY = key.val[keyParamterIndex] + key.intan[keyParamterIndex] * inInterval;
1580 		}
1581 		else if ( interpolationInType == LINEAR )
1582 		{
1583 			*inTangentValuesX = (key.time - ( key.time - previousTime )/3) * mTimeFactor;
1584 			// get the previous key value
1585 			int numKeys = keyInterface->GetNumKeys();
1586 			int previousKeyIndex = ( keyIndex > 0 ) ? keyIndex - 1 : numKeys - 1;
1587 			KeyType previousKey;
1588 			keyInterface->GetKey( previousKeyIndex, &previousKey );
1589 			*inTangentValuesY = (key.val[keyParamterIndex] - ( key.val[keyParamterIndex] - previousKey.val[keyParamterIndex] )/3);
1590 		}
1591 		else
1592 		{
1593 			/// @TODO: clarify if this makes sense or if we should export the same as above
1594 			*inTangentValuesX = key.time * mTimeFactor;
1595 			*inTangentValuesY = key.val[keyParamterIndex];
1596 		}
1597 	}
1598 
1599 
1600 	//---------------------------------------------------------------
1601 	template <int dimension, class KeyType>
getPointXInTangentValue(float * inTangentValuesX,float * inTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1602 	void AnimationExporter::getPointXInTangentValue ( float * inTangentValuesX, float * inTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1603 	{
1604 		KeyType key;
1605 		keyInterface->GetKey ( keyIndex, &key );
1606 
1607 		float previousTime = ( float ) getPreviousTime ( keyIndex, animation.getController() );
1608 
1609 		//testing
1610 		/*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1611 		bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1612 		bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1613 		bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1614 		bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1615 		*/
1616 		InterpolationType interpolationInType = getInterpolationInType ( key.flags );
1617 
1618 		// export control point. If it is not a BEZIER interpolation, export the point itself
1619 
1620 		if ( interpolationInType == BEZIER )
1621 		{
1622 			if ( GetInTanType ( key.flags ) != BEZKEY_USER )
1623 				key.inLength = DEFAULT_INLENGHT_POINTX_FLOAT_ARRAY;
1624 			else if ( key.flags & BEZKEY_UNCONSTRAINHANDLE )
1625 				key.inLength *= GetTicksPerFrame() / ( float ) ( key.time - previousTime );
1626 
1627 			float inInterval;
1628 
1629 			for ( int i = 0; i<dimension; ++i)
1630 			{
1631 				inInterval = ( key.time - previousTime ) * key.inLength[i];
1632 				inTangentValuesX[i] = ( key.time - inInterval ) * mTimeFactor;
1633 				inTangentValuesY[i] = key.val[i] + key.intan[i] * inInterval;
1634 			}
1635 
1636 		}
1637 		else if ( interpolationInType == LINEAR )
1638 		{
1639 			float inTangentX = (key.time - ( key.time - previousTime )/3) * mTimeFactor;
1640 
1641 			// get the previous key value
1642 			int numKeys = keyInterface->GetNumKeys();
1643 			int previousKeyIndex = ( keyIndex > 0 ) ? keyIndex - 1 : numKeys - 1;
1644 			KeyType previousKey;
1645 			keyInterface->GetKey ( previousKeyIndex, &previousKey );
1646 
1647 			for ( int i = 0; i<dimension; ++i)
1648 			{
1649 				inTangentValuesX[i] = inTangentX;
1650 				inTangentValuesY[i] = (key.val[i] - ( key.val[i] - previousKey.val[i] )/3);
1651 			}
1652 		}
1653 		else
1654 		{
1655 			/// @TODO: clarify if this makes sense or if we should export the same as above
1656 			for ( int i = 0; i<dimension; ++i)
1657 			{
1658 				inTangentValuesX[i] = key.time * mTimeFactor;
1659 				inTangentValuesY[i] = key.val[i];
1660 			}
1661 		}
1662 	}
1663 
1664 
1665 	//---------------------------------------------------------------
getRotationSingleInTangentPatchEuler(float * inTangentValuesX,float * inTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1666 	void AnimationExporter::getRotationSingleInTangentPatchEuler ( float * inTangentValuesX, float * inTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1667 	{
1668 		IBezQuatKey key;
1669 		keyInterface->GetKey ( keyIndex, &key );
1670 
1671 		float previousTime = ( float ) getPreviousTime ( keyIndex, animation.getController() );
1672 
1673 		//testing
1674 		/*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1675 		bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1676 		bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1677 		bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1678 		bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1679 		*/
1680 		InterpolationType interpolationInType = getInterpolationInType ( key.flags );
1681 
1682 		int keyParamterIndex = animation.getType() - Animation::ROTATION_X;
1683 
1684 
1685 		// export control point. If it is not a BEZIER interpolation, export the point itself
1686 
1687 		size_t keyValueIndex = keyIndex;
1688 
1689 		if ( interpolationInType == BEZIER )
1690 		{
1691 			float inInterval = ( key.time - previousTime ) * DEFAULT_INLENGHT;
1692 			*inTangentValuesX = ( key.time - inInterval ) * mTimeFactor;
1693 			*inTangentValuesY = mKeyValueList[keyValueIndex];
1694 		}
1695 		else if ( interpolationInType == LINEAR )
1696 		{
1697 			*inTangentValuesX = (key.time - ( key.time - previousTime )/3) * mTimeFactor;
1698 			// get the previous key value
1699 			size_t numKeys = mKeyValueList.size();
1700 			size_t previousKeyIndex = ( keyValueIndex > 0 ) ? keyValueIndex - 1 : numKeys - 1;
1701 			*inTangentValuesY = (mKeyValueList[keyValueIndex] - ( mKeyValueList[keyValueIndex] - mKeyValueList[previousKeyIndex] )/3);
1702 		}
1703 		else
1704 		{
1705 			/// @TODO: clarify if this makes sense or if we should export the same as above
1706 			*inTangentValuesX = key.time * mTimeFactor;
1707 
1708 			*inTangentValuesY = mKeyValueList[keyValueIndex];
1709 		}
1710 	}
1711 
1712 
1713 	//---------------------------------------------------------------
getRotationInTangentPatchEuler(float * inTangentValuesX,float * inTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1714 		void AnimationExporter::getRotationInTangentPatchEuler ( float * inTangentValuesX, float * inTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1715 	{
1716 		IBezQuatKey key;
1717 		keyInterface->GetKey ( keyIndex, &key );
1718 
1719 		float previousTime = ( float ) getPreviousTime ( keyIndex, animation.getController() );
1720 
1721 		//testing
1722 		/*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1723 		bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1724 		bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1725 		bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1726 		bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1727 		*/
1728 		InterpolationType interpolationInType = getInterpolationInType ( key.flags );
1729 
1730 		// export control point. If it is not a BEZIER interpolation, export the point itself
1731 
1732 		size_t keyValueIndex = 3 * keyIndex;
1733 
1734 		if ( interpolationInType == BEZIER )
1735 		{
1736 			float inInterval = ( key.time - previousTime ) * DEFAULT_INLENGHT;
1737 
1738 			inTangentValuesX[0] = ( key.time - inInterval ) * mTimeFactor;
1739 			inTangentValuesY[0] = mKeyValueList[keyValueIndex++];
1740 
1741 			inTangentValuesX[1] = inTangentValuesX[0];
1742 			inTangentValuesY[1] = mKeyValueList[keyValueIndex++];
1743 
1744 			inTangentValuesX[2] = inTangentValuesX[0];
1745 			inTangentValuesY[2] = mKeyValueList[keyValueIndex++];
1746 		}
1747 		else if ( interpolationInType == LINEAR )
1748 		{
1749 			size_t numKeys = mKeyValueList.size();
1750 
1751 			float inTangentX = (key.time - ( key.time - previousTime )/3) * mTimeFactor;
1752 
1753 			for ( size_t i = 0; i < 3; ++i, keyValueIndex++)
1754 			{
1755 				inTangentValuesX[i] = inTangentX;
1756 				// get the previous key value
1757 				size_t previousKeyIndex = ( keyValueIndex > 0 ) ? keyValueIndex - 1 : numKeys - 1;
1758 				inTangentValuesY[i] = (mKeyValueList[keyValueIndex] - ( mKeyValueList[keyValueIndex] - mKeyValueList[previousKeyIndex] )/3);
1759 			}
1760 		}
1761 		else
1762 		{
1763 			/// @TODO: clarify if this makes sense or if we should export the same as above
1764 			inTangentValuesX[0] = key.time * mTimeFactor;
1765 			inTangentValuesX[1] = inTangentValuesX[0];
1766 			inTangentValuesX[2] = inTangentValuesX[0];
1767 
1768 			inTangentValuesY[0] = mKeyValueList[keyValueIndex++];
1769 			inTangentValuesY[1] = mKeyValueList[keyValueIndex++];
1770 			inTangentValuesY[2] = mKeyValueList[keyValueIndex++];
1771 		}
1772 	}
1773 
1774 
1775 	//---------------------------------------------------------------
exportOutTangentSource(const Animation & animation,const String & baseId,IKeyControl * keyInterface,TangentValueFunctionPtr tangentValueFunction)1776     void AnimationExporter::exportOutTangentSource ( const Animation & animation, const String & baseId, IKeyControl* keyInterface, TangentValueFunctionPtr tangentValueFunction )
1777     {
1778         exportTangentSource ( OUTTANGENT_SOURCE_ID_SUFFIX, animation, baseId, keyInterface, tangentValueFunction );
1779     }
1780 
1781     //---------------------------------------------------------------
getFloatOutTangentValue(float * outTangentValuesX,float * outTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1782     void AnimationExporter::getFloatOutTangentValue ( float * outTangentValuesX, float * outTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1783     {
1784         IBezFloatKey key;
1785         keyInterface->GetKey ( keyIndex, &key );
1786 
1787         float nextTime = ( float ) getNextTime ( keyIndex, keyCount, animation.getController() );
1788 
1789         //testing
1790         /*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1791                 bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1792                 bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1793                 bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1794                 bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1795         */
1796         InterpolationType interpolationOutType = getInterpolationOutType ( key.flags );
1797 
1798         // export control point. If it is not a BEZIER interpolation, export the point itself
1799 
1800         if ( interpolationOutType == BEZIER )
1801         {
1802             if ( GetOutTanType ( key.flags ) != BEZKEY_USER )
1803                 key.outLength = DEFAULT_OUTLENGHT_FLOAT;
1804             else if ( key.flags & BEZKEY_UNCONSTRAINHANDLE )
1805                 key.outLength *= GetTicksPerFrame() / ( float ) ( nextTime - key.time );
1806 
1807             float outInterval = ( nextTime - key.time ) * key.outLength;
1808 
1809             *outTangentValuesX = ( key.time + outInterval ) * mTimeFactor;
1810 
1811             *outTangentValuesY = key.val + key.outtan * outInterval;
1812         }
1813         else if ( interpolationOutType == LINEAR )
1814 		{
1815 			*outTangentValuesX = (key.time + ( nextTime - key.time )/3) * mTimeFactor;
1816 			// get the next key value
1817 			int numKeys = keyInterface->GetNumKeys();
1818 			int nextKeyIndex = (keyIndex + 1 < numKeys ) ? keyIndex + 1 : 0;
1819 			IBezFloatKey nextKey;
1820 			keyInterface->GetKey ( nextKeyIndex, &nextKey );
1821 			*outTangentValuesY = (key.val + ( nextKey.val - key.val )/3);
1822 		}
1823 		else
1824         {
1825             *outTangentValuesX = key.time * mTimeFactor;
1826             *outTangentValuesY = key.val;
1827         }
1828     }
1829 
1830 
1831 	//---------------------------------------------------------------
1832 	template <class KeyType>
getPointXSingleOutTangentValue(float * outTangentValuesX,float * outTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1833 	void AnimationExporter::getPointXSingleOutTangentValue ( float * outTangentValuesX, float * outTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1834 	{
1835 		KeyType key;
1836 		keyInterface->GetKey ( keyIndex, &key );
1837 
1838 		float nextTime = ( float ) getNextTime ( keyIndex, keyCount, animation.getController() );
1839 
1840 		//testing
1841 		/*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1842 		bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1843 		bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1844 		bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1845 		bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1846 		*/
1847 		InterpolationType interpolationOutType = getInterpolationOutType ( key.flags );
1848 
1849 		int keyParamterIndex = animation.getType() - Animation::POSITION_X;
1850 
1851 		// export control point. If it is not a BEZIER interpolation, export the point itself
1852 
1853 		if ( interpolationOutType == BEZIER )
1854 		{
1855 
1856 			if ( GetOutTanType ( key.flags ) != BEZKEY_USER )
1857 				key.outLength = DEFAULT_OUTLENGHT_POINTX_FLOAT_ARRAY;
1858 			else if ( key.flags & BEZKEY_UNCONSTRAINHANDLE )
1859 				key.outLength *= GetTicksPerFrame() / ( float ) ( nextTime - key.time );
1860 
1861 			float outInterval = ( nextTime - key.time ) * key.outLength[keyParamterIndex];
1862 
1863 			*outTangentValuesX = ( key.time + outInterval ) * mTimeFactor;
1864 			*outTangentValuesY = key.val[keyParamterIndex] + key.outtan[keyParamterIndex] * outInterval;
1865 		}
1866 		else if ( interpolationOutType == LINEAR )
1867 		{
1868 			*outTangentValuesX = (key.time + ( nextTime - key.time )/3) * mTimeFactor;
1869 			// get the next key value
1870 			int numKeys = keyInterface->GetNumKeys();
1871 			int nextKeyIndex = (keyIndex + 1 < numKeys ) ? keyIndex + 1 : 0;
1872 			KeyType nextKey;
1873 			keyInterface->GetKey ( nextKeyIndex, &nextKey );
1874 			*outTangentValuesY = (key.val[keyParamterIndex] + ( nextKey.val[keyParamterIndex] - key.val[keyParamterIndex] )/3);
1875 		}
1876 		else
1877 		{
1878 			/// @TODO: clarify if this makes sense or if we should export the same as above
1879 			*outTangentValuesX = key.time * mTimeFactor;
1880 			*outTangentValuesY = key.val[keyParamterIndex];
1881 		}
1882 	}
1883 
1884 
1885 	//---------------------------------------------------------------
1886 	template <int dimension, class KeyType>
getPointXOutTangentValue(float * outTangentValuesX,float * outTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1887 	void AnimationExporter::getPointXOutTangentValue ( float * outTangentValuesX, float * outTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1888 	{
1889 		KeyType key;
1890 		keyInterface->GetKey ( keyIndex, &key );
1891 
1892 		float nextTime = ( float ) getNextTime ( keyIndex, keyCount, animation.getController() );
1893 
1894 		//testing
1895 		/*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1896 		bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1897 		bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1898 		bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1899 		bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1900 		*/
1901 		InterpolationType interpolationOutType = getInterpolationOutType ( key.flags );
1902 
1903 		// export control point. If it is not a BEZIER interpolation, export the point itself
1904 
1905 		if ( interpolationOutType == BEZIER )
1906 		{
1907 			if ( GetOutTanType ( key.flags ) != BEZKEY_USER )
1908 				key.outLength = DEFAULT_OUTLENGHT_POINTX_FLOAT_ARRAY;
1909 			else if ( key.flags & BEZKEY_UNCONSTRAINHANDLE )
1910 				key.outLength *= GetTicksPerFrame() / ( float ) ( nextTime - key.time );
1911 
1912 			float outInterval;
1913 
1914 			for ( int i = 0; i<dimension; ++i)
1915 			{
1916 				outInterval = ( nextTime - key.time ) * key.outLength[i];
1917 				outTangentValuesX[i] = ( key.time + outInterval ) * mTimeFactor;
1918 				outTangentValuesY[i] = key.val[i] + key.outtan[i] * outInterval;
1919 			}
1920 		}
1921 		else if ( interpolationOutType == LINEAR )
1922 		{
1923 			float outTangentX = (key.time + ( nextTime - key.time )/3) * mTimeFactor;
1924 			// get the next key value
1925 			int numKeys = keyInterface->GetNumKeys();
1926 			int nextKeyIndex = (keyIndex + 1 < numKeys ) ? keyIndex + 1 : 0;
1927 			KeyType nextKey;
1928 			keyInterface->GetKey ( nextKeyIndex, &nextKey );
1929 
1930 			for ( int i = 0; i<dimension; ++i)
1931 			{
1932 				outTangentValuesX[i] = outTangentX;
1933 				outTangentValuesY[i] = (key.val[i] + ( nextKey.val[i] - key.val[i] )/3);
1934 			}
1935 		}
1936 		else
1937 		{
1938 			/// @TODO: clarify if this makes sense or if we should export the same as above
1939 			for ( int i = 0; i<dimension; ++i)
1940 			{
1941 				outTangentValuesX[i] = key.time * mTimeFactor;
1942 				outTangentValuesY[i] = key.val[i];
1943 			}
1944 		}
1945 	}
1946 
1947 
1948 	//---------------------------------------------------------------
getRotationSingleOutTangentPatchEuler(float * outTangentValuesX,float * outTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1949 	void AnimationExporter::getRotationSingleOutTangentPatchEuler ( float * outTangentValuesX, float * outTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1950 	{
1951 		IBezQuatKey key;
1952 		keyInterface->GetKey ( keyIndex, &key );
1953 
1954 		float nextTime = ( float ) getNextTime ( keyIndex, keyCount, animation.getController() );
1955 
1956 		//testing
1957 		/*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
1958 		bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
1959 		bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
1960 		bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
1961 		bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
1962 		*/
1963 		InterpolationType interpolationOutType = getInterpolationOutType ( key.flags );
1964 
1965 		int keyParamterIndex = animation.getType() - Animation::ROTATION_X;
1966 
1967 
1968 		// export control point. If it is not a BEZIER interpolation, export the point itself
1969 
1970 		size_t keyValueIndex = keyIndex;
1971 
1972 		if ( interpolationOutType == BEZIER )
1973 		{
1974 			float outInterval = ( nextTime - key.time ) * DEFAULT_INLENGHT;
1975 			*outTangentValuesX = ( key.time + outInterval ) * mTimeFactor;
1976 			*outTangentValuesY = mKeyValueList[keyValueIndex];
1977 		}
1978 		else if ( interpolationOutType == LINEAR )
1979 		{
1980 			*outTangentValuesX = (key.time + ( nextTime - key.time )/3) * mTimeFactor;
1981 			// get the next key value
1982 			int numKeys = (int)mKeyValueList.size();
1983 			int nextKeyIndex = (keyIndex + 1 < numKeys ) ? keyIndex + 1 : 0;
1984 			*outTangentValuesY = (mKeyValueList[keyValueIndex] + ( mKeyValueList[nextKeyIndex] - mKeyValueList[keyValueIndex] )/3);
1985 		}
1986 		else
1987 		{
1988 			/// @TODO: clarify if this makes sense or if we should export the same as above
1989 			*outTangentValuesX = key.time * mTimeFactor;
1990 
1991 			*outTangentValuesY = mKeyValueList[keyValueIndex];
1992 		}
1993 	}
1994 
1995 
1996 	//---------------------------------------------------------------
getRotationOutTangentPatchEuler(float * outTangentValuesX,float * outTangentValuesY,IKeyControl * keyInterface,const int & keyIndex,const int & keyCount,const Animation & animation)1997 	void AnimationExporter::getRotationOutTangentPatchEuler ( float * outTangentValuesX, float * outTangentValuesY, IKeyControl * keyInterface, const int & keyIndex, const int & keyCount, const Animation & animation )
1998 	{
1999 		IBezQuatKey key;
2000 		keyInterface->GetKey ( keyIndex, &key );
2001 
2002 		float nextTime = ( float ) getNextTime ( keyIndex, keyCount, animation.getController() );
2003 
2004 		//testing
2005 		/*        bool b1 = key.flags & BEZKEY_UNCONSTRAINHANDLE;
2006 		bool b2 = GetInTanType( key.flags ) != BEZKEY_USER;
2007 		bool b3 = GetOutTanType( key.flags ) != BEZKEY_USER;
2008 		bool b4 = GetOutTanType( key.flags ) == BEZKEY_LINEAR;
2009 		bool b5 = GetOutTanType( key.flags ) == BEZKEY_STEP;
2010 		*/
2011 		InterpolationType interpolationOutType = getInterpolationOutType ( key.flags );
2012 
2013 		// export control point. If it is not a BEZIER interpolation, export the point itself
2014 
2015 		size_t keyValueIndex = 3 * keyIndex;
2016 
2017 		if ( interpolationOutType == BEZIER )
2018 		{
2019 			float outInterval = ( nextTime - key.time ) * DEFAULT_INLENGHT;
2020 
2021 			outTangentValuesX[0] = ( key.time + outInterval ) * mTimeFactor;
2022 			outTangentValuesY[0] = mKeyValueList[keyValueIndex++];
2023 
2024 			outTangentValuesX[1] = outTangentValuesX[0];
2025 			outTangentValuesY[1] = mKeyValueList[keyValueIndex++];
2026 
2027 			outTangentValuesX[2] = outTangentValuesX[0];
2028 			outTangentValuesY[2] = mKeyValueList[keyValueIndex++];
2029 		}
2030 		else if ( interpolationOutType == LINEAR )
2031 		{
2032 			int numKeys = (int)mKeyValueList.size();
2033 
2034 			float outTangentX = (key.time + ( nextTime - key.time )/3) * mTimeFactor;
2035 
2036 			for ( size_t i = 0; i < 3; ++i, keyValueIndex++)
2037 			{
2038 				outTangentValuesX[i] = outTangentX;
2039 				// get the next key value
2040 				int nextKeyIndex = (keyIndex + 1 < numKeys ) ? keyIndex + 1 : 0;
2041 				outTangentValuesY[i] = (mKeyValueList[keyValueIndex] + ( mKeyValueList[nextKeyIndex] - mKeyValueList[keyValueIndex] )/3);
2042 			}
2043 		}
2044 		else
2045 		{
2046 			/// @TODO: clarify if this makes sense or if we should export the same as above
2047 			outTangentValuesX[0] = key.time * mTimeFactor;
2048 			outTangentValuesX[1] = outTangentValuesX[0];
2049 			outTangentValuesX[2] = outTangentValuesX[0];
2050 
2051 			outTangentValuesY[0] = mKeyValueList[keyValueIndex++];
2052 			outTangentValuesY[1] = mKeyValueList[keyValueIndex++];
2053 			outTangentValuesY[2] = mKeyValueList[keyValueIndex++];
2054 		}
2055 	}
2056 
2057 
2058 	//---------------------------------------------------------------
getInterpolationInType(const DWORD & flags)2059 	AnimationExporter::InterpolationType AnimationExporter::getInterpolationInType ( const DWORD & flags )
2060 	{
2061 		switch ( GetInTanType ( flags ) )
2062 		{
2063 		case BEZKEY_LINEAR:
2064 			return LINEAR;
2065 		case BEZKEY_STEP:
2066 			return STEP;
2067 		default:
2068 			return BEZIER;
2069 		}
2070 	}
2071 
2072 	//---------------------------------------------------------------
getInterpolationOutType(const DWORD & flags)2073 	AnimationExporter::InterpolationType AnimationExporter::getInterpolationOutType ( const DWORD & flags )
2074 	{
2075 		switch ( GetOutTanType ( flags ) )
2076 		{
2077 		case BEZKEY_LINEAR:
2078 			return LINEAR;
2079 		case BEZKEY_STEP:
2080 			return STEP;
2081 		default:
2082 			return BEZIER;
2083 		}
2084 	}
2085 
2086     //---------------------------------------------------------------
exportInterpolationSource(const String & baseId,IKeyControl * keyInterface,InterpolationTypeFunctionPtr interpolationTypeFunction,int keyCount)2087     void AnimationExporter::exportInterpolationSource ( const String & baseId, IKeyControl * keyInterface, InterpolationTypeFunctionPtr interpolationTypeFunction, int keyCount )
2088     {
2089         COLLADASW::NameSource source ( mSW );
2090         source.setId ( baseId + INTERPOLATION_SOURCE_ID_SUFFIX );
2091         source.setArrayId ( baseId + INTERPOLATION_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
2092         source.setAccessorStride ( 1 );
2093         source.getParameterNameList().push_back ( "INTERPOLATION" );
2094         source.setAccessorCount ( keyCount );
2095         source.prepareToAppendValues();
2096 
2097         for ( int i = 0; i < keyCount; ++i )
2098             source.appendValues ( interpolationTypeFunction ( keyInterface, i ) );
2099 
2100         source.finish();
2101     }
2102 
2103     //---------------------------------------------------------------
2104     template <const String & interpolationTypeName>
getUniformInterpolation(IKeyControl * keyInterface,int keyIndex)2105     const String & AnimationExporter::getUniformInterpolation ( IKeyControl * keyInterface, int keyIndex )
2106     {
2107         return interpolationTypeName;
2108     }
2109 
2110     //---------------------------------------------------------------
2111 	template <class KeyType>
getBezierInterpolation(IKeyControl * keyInterface,int keyIndex)2112     const String & AnimationExporter::getBezierInterpolation ( IKeyControl * keyInterface, int keyIndex )
2113     {
2114         KeyType key;
2115 		keyInterface->GetKey ( keyIndex, &key );
2116 		InterpolationType interpolationOutType =  getInterpolationOutType ( key.flags );
2117 		if ( interpolationOutType == STEP )
2118 		{
2119 			// step always wins
2120 			return getNameOfInterpolation( STEP );
2121 		}
2122 		int numKeys = keyInterface->GetNumKeys();
2123 		if ( keyIndex + 1 < numKeys )
2124 		{
2125 			KeyType nextKey;
2126 			keyInterface->GetKey ( keyIndex + 1, &nextKey );
2127 			InterpolationType nextInterpolationInType =  getInterpolationInType ( nextKey.flags );
2128 			if ( nextInterpolationInType == STEP )
2129 			{
2130 				// step always wins
2131 				return getNameOfInterpolation( STEP );
2132 			}
2133 
2134 			if ( (interpolationOutType == LINEAR) && (nextInterpolationInType == LINEAR) )
2135 			{
2136 				// linear only, if both are linear
2137 				return getNameOfInterpolation( LINEAR );
2138 			}
2139 			else
2140 			{
2141 				// general case
2142 				return getNameOfInterpolation( BEZIER );
2143 			}
2144 		}
2145 		else
2146 		{
2147 			return getNameOfInterpolation ( interpolationOutType );
2148 		}
2149     }
2150 
2151 
2152 	//---------------------------------------------------------------
exportSamplingInputSource(const String & baseId,TimeValue startTime,TimeValue endTime,int ticksPerFrame)2153 	void AnimationExporter::exportSamplingInputSource ( const String & baseId, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
2154 	{
2155 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
2156 
2157 		COLLADASW::FloatSource source ( mSW );
2158 		source.setId ( baseId + INPUT_SOURCE_ID_SUFFIX );
2159 		source.setArrayId ( baseId + INPUT_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
2160 		source.setAccessorStride ( 1 );
2161 		source.getParameterNameList().push_back ( "TIME" );
2162 		source.setAccessorCount ( keyCount );
2163 		source.prepareToAppendValues();
2164 
2165 
2166 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
2167 		{
2168 			source.appendValues ( (float)time / (float)TIME_TICKSPERSEC );
2169 		}
2170 
2171 		source.finish();
2172 	}
2173 
2174 
2175 
2176 	//---------------------------------------------------------------
exportSamplingFloatOutputSource(const Animation & animation,const String & baseId,IKeyControl * keyInterface,TimeValue startTime,TimeValue endTime,int ticksPerFrame)2177 	void AnimationExporter::exportSamplingFloatOutputSource ( const Animation & animation, const String & baseId, IKeyControl* keyInterface, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
2178 	{
2179 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
2180 
2181 		COLLADASW::FloatSource source ( mSW );
2182 		source.setId ( baseId + OUTPUT_SOURCE_ID_SUFFIX );
2183 		source.setArrayId ( baseId + OUTPUT_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
2184 		source.setAccessorStride ( 1 );
2185 
2186 		if ( animation.getParameter() )
2187 			source.getParameterNameList().push_back ( *(animation.getParameter()) );
2188 		else
2189 			source.getParameterNameList().push_back ( EMPTY_STRING );
2190 
2191 		source.setAccessorCount ( keyCount );
2192 		source.prepareToAppendValues();
2193 
2194 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
2195 
2196 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
2197 		{
2198 			float keyValue;
2199 			animation.getController()->GetValue(time, &keyValue, FOREVER, CTRL_ABSOLUTE);
2200 			if ( conversionFunctor )
2201 				source.appendValues ( (*conversionFunctor)(keyValue) );
2202 			else
2203 				source.appendValues ( keyValue );
2204 		}
2205 
2206 		source.finish();
2207 	}
2208 
2209 
2210 	//---------------------------------------------------------------
exportSamplingPoint3OutputSource(const Animation & animation,const String & baseId,IKeyControl * keyInterface,TimeValue startTime,TimeValue endTime,int ticksPerFrame)2211 	void AnimationExporter::exportSamplingPoint3OutputSource ( const Animation & animation, const String & baseId, IKeyControl* keyInterface, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
2212 	{
2213 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
2214 		int keyLength = animation.getDimension();
2215 
2216 		COLLADASW::FloatSource source ( mSW );
2217 		source.setId ( baseId + OUTPUT_SOURCE_ID_SUFFIX );
2218 		source.setArrayId ( baseId + OUTPUT_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
2219 		source.setAccessorStride ( keyLength );
2220 
2221 		for ( int i = 0; i < keyLength; ++i )
2222 		{
2223 			if ( animation.getParameter() )
2224 				source.getParameterNameList().push_back ( * ( animation.getParameter() + i) );
2225 			else
2226 				source.getParameterNameList().push_back ( EMPTY_STRING );
2227 		}
2228 
2229 		source.setAccessorCount ( keyCount );
2230 		source.prepareToAppendValues();
2231 
2232 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
2233 
2234 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
2235 		{
2236 
2237 			Point3 keyValue;
2238 			animation.getController()->GetValue(time, &keyValue, FOREVER, CTRL_ABSOLUTE);
2239 
2240 			if ( keyLength == 1)
2241 			{
2242 				if ( conversionFunctor )
2243 					source.appendValues ( (*conversionFunctor) ( keyValue[ animation.getType() - Animation::POSITION_X ] ) );
2244 				else
2245 					source.appendValues ( keyValue[ animation.getType() - Animation::POSITION_X ] );
2246 			}
2247 			else
2248 			{
2249 				assert( (keyLength == 3) || (keyLength == 4));
2250 
2251 				for ( int j = 0; j < 3; ++j )
2252 				{
2253 					if ( conversionFunctor )
2254 						source.appendValues ( (*conversionFunctor) ( keyValue[ j ] ) );
2255 					else
2256 						source.appendValues ( keyValue[ j ] );
2257 				}
2258 
2259 				// If the point3 controller has dimension 4, it is a rgba controller. In this case alpha is one.
2260 				if ( keyLength == 4 )
2261 				{
2262 					source.appendValues ( 1 );
2263 				}
2264 			}
2265 		}
2266 		source.finish();
2267 	}
2268 
2269 
2270 	//---------------------------------------------------------------
exportSamplingRotationOutputSource(const Animation & animation,const String & baseId,IKeyControl * keyInterface,TimeValue startTime,TimeValue endTime,int ticksPerFrame)2271 	void AnimationExporter::exportSamplingRotationOutputSource ( const Animation & animation, const String & baseId, IKeyControl* keyInterface, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
2272 	{
2273 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
2274 		int keyLength = animation.getDimension();
2275 
2276 		COLLADASW::FloatSource source ( mSW );
2277 		source.setId ( baseId + OUTPUT_SOURCE_ID_SUFFIX );
2278 		source.setArrayId ( baseId + OUTPUT_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
2279 		source.setAccessorStride ( keyLength );
2280 
2281 		for ( int i = 0; i < keyLength; ++i )
2282 		{
2283 			if ( animation.getParameter() )
2284 				source.getParameterNameList().push_back ( * ( animation.getParameter() + i) );
2285 			else
2286 				source.getParameterNameList().push_back ( EMPTY_STRING );
2287 		}
2288 
2289 		source.setAccessorCount ( keyCount );
2290 		source.prepareToAppendValues();
2291 
2292 		ConversionFunctorType conversionFunctor = animation.getConversionFunctor();
2293 
2294 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
2295 		{
2296 
2297 			float eulerAngles[ 3 ];
2298 
2299 			if ( keyLength == 1 )
2300 			{
2301 				Quat quaternion;
2302 				animation.getController()->GetValue(time, &quaternion, FOREVER, CTRL_ABSOLUTE);
2303 				QuatToEuler(quaternion, eulerAngles, EULERTYPE_XYZ);
2304 			}
2305 			else
2306 			{
2307 				AngAxis angleAxis;
2308 				animation.getController()->GetValue(time, &angleAxis, FOREVER, CTRL_ABSOLUTE);
2309 				Quat quaternion(angleAxis);
2310 				quaternion.GetEuler ( &eulerAngles[ 0 ], &eulerAngles[ 1 ], &eulerAngles[ 2 ] );
2311 			}
2312 
2313 			if ( time > startTime)
2314 				patchEuler(mPreviousEulerAngles, eulerAngles);
2315 
2316 			mPreviousEulerAngles[0] = eulerAngles[0];
2317 			mPreviousEulerAngles[1] = eulerAngles[1];
2318 			mPreviousEulerAngles[2] = eulerAngles[2];
2319 
2320 			if ( keyLength == 1)
2321 			{
2322 				if ( conversionFunctor )
2323 					source.appendValues( (*conversionFunctor)(eulerAngles[ animation.getType() - Animation::ROTATION_X ]) );
2324 				else
2325 					source.appendValues( eulerAngles[ animation.getType() - Animation::ROTATION_X ] );
2326 			}
2327 			else
2328 			{
2329 				if ( conversionFunctor )
2330 				{
2331 					eulerAngles[0] = (*conversionFunctor)( eulerAngles[0] );
2332 					eulerAngles[1] = (*conversionFunctor)( eulerAngles[1] );
2333 					eulerAngles[2] = (*conversionFunctor)( eulerAngles[2] );
2334 				}
2335 				source.appendValues( eulerAngles[0], eulerAngles[1], eulerAngles[2] );
2336 			}
2337 
2338 
2339 		}
2340 		source.finish();
2341 	}
2342 
2343 
exportSamplingTransformationOutputSource(const Animation & animation,const String & baseId,IKeyControl * keyInterface,TimeValue startTime,TimeValue endTime,int ticksPerFrame)2344 	void AnimationExporter::exportSamplingTransformationOutputSource( const Animation & animation, const String & baseId, IKeyControl* keyInterface, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
2345 	{
2346 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
2347 		int keyLength = animation.getDimension();
2348 
2349 		COLLADASW::Float4x4SourceF source ( mSW );
2350 		source.setId ( baseId + OUTPUT_SOURCE_ID_SUFFIX );
2351 		source.setArrayId ( baseId + OUTPUT_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
2352 		source.setAccessorStride ( keyLength );
2353 
2354 		if ( animation.getParameter() )
2355 			source.getParameterNameList().push_back ( * ( animation.getParameter()) );
2356 		else
2357 			source.getParameterNameList().push_back ( EMPTY_STRING );
2358 
2359 		source.setAccessorCount ( keyCount );
2360 		source.prepareToAppendValues();
2361 
2362 		INode * iNode = animation.getNode();
2363 
2364 		Matrix3 transformationMatrix, parentTransformationMatrix;
2365 		INode* parentINode = iNode->GetParentNode();
2366 
2367 		if (parentINode && parentINode->IsRootNode())
2368 			parentINode = 0;
2369 
2370 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
2371 		{
2372 			// Export the base NODE TM
2373 			transformationMatrix = iNode->GetNodeTM(time);
2374 
2375 			if (parentINode)
2376 			{
2377 				// export a relative TM
2378 				// We have to use whatever value we exported for this parent
2379 				// in order to remain consistent in collada
2380 				parentTransformationMatrix = parentINode->GetNodeTM(time);
2381 				parentTransformationMatrix.Invert();
2382 				transformationMatrix = transformationMatrix * parentTransformationMatrix;
2383 			}
2384 
2385 			const Matrix3& constTransformationMatrix = transformationMatrix;
2386 			for ( int row = 0; row < 3; ++row)
2387 				source.appendValues(constTransformationMatrix[0][row], constTransformationMatrix[1][row], constTransformationMatrix[2][row], constTransformationMatrix[3][row]);
2388 			source.appendValues(0, 0, 0, 1);
2389 
2390 		}
2391 
2392 		source.finish();
2393 	}
2394 
2395 
2396 	//---------------------------------------------------------------
exportSamplingInterpolationSource(const String & baseId,TimeValue startTime,TimeValue endTime,int ticksPerFrame)2397 	void AnimationExporter::exportSamplingInterpolationSource ( const String & baseId, TimeValue startTime, TimeValue endTime, int ticksPerFrame )
2398 	{
2399 		int keyCount = (endTime - startTime) / ticksPerFrame + 1;
2400 
2401 		COLLADASW::NameSource source ( mSW );
2402 		source.setId ( baseId + INTERPOLATION_SOURCE_ID_SUFFIX );
2403 		source.setArrayId ( baseId + INTERPOLATION_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX );
2404 		source.setAccessorStride ( 1 );
2405 		source.getParameterNameList().push_back ( "INTERPOLATION" );
2406 		source.setAccessorCount ( keyCount );
2407 		source.prepareToAppendValues();
2408 
2409 		for (TimeValue time = startTime; time < endTime; time += ticksPerFrame)
2410 			source.appendValues ( LINEAR_NAME );
2411 
2412 		source.finish();
2413 	}
2414 
2415 
2416     //---------------------------------------------------------------
exportSampler(const Animation & animation)2417     void AnimationExporter::exportSampler ( const Animation & animation )
2418     {
2419 
2420 		if ( animation.hasAnyInputFlagsSet() )
2421 		{
2422 			String baseId = getBaseId ( animation );
2423 
2424 			LibraryAnimations::Sampler sampler(mSW, baseId + SAMPLER_ID_SUFFIX );
2425 
2426 			if ( animation.inputFlagSet(Animation::INPUT) )
2427 				sampler.addInput ( COLLADASW::InputSemantic::INPUT, "#" + baseId + INPUT_SOURCE_ID_SUFFIX );
2428 
2429 			if ( animation.inputFlagSet(Animation::OUTPUT) )
2430 				sampler.addInput ( COLLADASW::InputSemantic::OUTPUT, "#" + baseId + OUTPUT_SOURCE_ID_SUFFIX );
2431 
2432 			if ( animation.inputFlagSet(Animation::IN_TANGENT) )
2433 				sampler.addInput ( COLLADASW::InputSemantic::IN_TANGENT, "#" + baseId + INTANGENT_SOURCE_ID_SUFFIX );
2434 
2435 			if ( animation.inputFlagSet(Animation::OUT_TANGENT) )
2436 				sampler.addInput ( COLLADASW::InputSemantic::OUT_TANGENT, "#" + baseId + OUTTANGENT_SOURCE_ID_SUFFIX );
2437 
2438 			if ( animation.inputFlagSet(Animation::INTERPOLATION) )
2439 				sampler.addInput ( COLLADASW::InputSemantic::INTERPOLATION, "#" + baseId + INTERPOLATION_SOURCE_ID_SUFFIX );
2440 
2441 			addSampler ( sampler );
2442 		}
2443     }
2444 
2445     //---------------------------------------------------------------
exportChannel(const Animation & animation)2446     void AnimationExporter::exportChannel ( const Animation & animation )
2447     {
2448 		if ( animation.hasAnyInputFlagsSet() )
2449 		{
2450 	        addChannel ( "#" + getBaseId ( animation ) + SAMPLER_ID_SUFFIX, getTarget ( animation ) );
2451 		}
2452     }
2453 
2454 
2455 
2456 	//---------------------------------------------------------------
angleApproach(float pval,float & val)2457 	void AnimationExporter::angleApproach(float pval, float& val)
2458 	{
2459 		while (val - pval > MathUtils::PI_f)
2460 			val -= MathUtils::PI_f * 2.0f;
2461 		while (val - pval < -MathUtils::PI_f)
2462 			val += MathUtils::PI_f * 2.0f;
2463 	}
2464 
2465 
2466 	//---------------------------------------------------------------
patchEuler(float * pval,float * val)2467 	void AnimationExporter::patchEuler(float* pval, float* val)
2468 	{
2469 		// Approach these Eulers to the previous value.
2470 		for (int i = 0; i < 3; ++i)
2471 			angleApproach(pval[i], val[i]);
2472 
2473 		float distanceSq = 0.0f;
2474 		for (int i = 0; i < 3; ++i)
2475 			distanceSq += (val[i] - pval[i]) * (val[i] - pval[i]);
2476 
2477 		// All quaternions can be expressed two ways. Check if the second way is better.
2478 		float alternative[3] = { val[0] + MathUtils::PI_f, MathUtils::PI_f - val[1], val[2] + MathUtils::PI_f };
2479 		for (int i = 0; i < 3; ++i)
2480 			angleApproach(pval[i], alternative[i]);
2481 
2482 		float alternateDistanceSq = 0.0f; for (int i = 0; i < 3; ++i) alternateDistanceSq += (alternative[i] - pval[i]) * (alternative[i] - pval[i]);
2483 
2484 		if (alternateDistanceSq < distanceSq)
2485 		{
2486 			// Pick the alternative
2487 			for (int i = 0; i < 3; ++i)
2488 				val[i] = alternative[i];
2489 		}
2490 	}
2491 
2492 }
2493