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 
19 #include "COLLADAMaxStableHeaders.h"
20 
21 #include "COLLADASWNode.h"
22 #include "COLLADASWInstanceGeometry.h"
23 #include "COLLADASWInstanceController.h"
24 #include "COLLADASWInstanceCamera.h"
25 #include "COLLADASWInstanceLight.h"
26 #include "COLLADASWInstanceNode.h"
27 #include "COLLADASWInputList.h"
28 
29 #include "Math/COLLADABUMathUtils.h"
30 
31 #include "COLLADAMaxVisualSceneExporter.h"
32 #include "COLLADAMaxExportSceneGraph.h"
33 #include "COLLADAMaxGeometryExporter.h"
34 #include "COLLADAMaxMaterialExporter.h"
35 #include "COLLADAMaxAnimationExporter.h"
36 #include "COLLADAMaxCameraExporter.h"
37 #include "COLLADAMaxLightExporter.h"
38 #include "COLLADAMaxControllerExporter.h"
39 #include "COLLADAMaxDocumentExporter.h"
40 
41 #include <decomp.h>
42 #include <iInstanceMgr.h>
43 
44 #include <ILayerControl.h>
45 
46 namespace COLLADAMax
47 {
48 
49 
50 	const String VisualSceneExporter::NODE_ID_PRAEFIX = "node-";
51     const String VisualSceneExporter::XREF_NODE_NAME = "XRefs";
52 
53 	const String VisualSceneExporter::MATRIX_SID = "matrix";
54 	const String VisualSceneExporter::TRANSLATE_SID = "translation";
55 	const String VisualSceneExporter::ROTATE_SID = "rotation";
56 	const String VisualSceneExporter::ROTATE_X_SID = "rotationX";
57 	const String VisualSceneExporter::ROTATE_Y_SID = "rotationY";
58 	const String VisualSceneExporter::ROTATE_Z_SID = "rotationZ";
59 	const String VisualSceneExporter::ROTATE_SCALE_AXIS_SID = "ScaleAxisRotation";
60 	const String VisualSceneExporter::ROTATE_SCALE_AXIS_INVERSE_SID = "InverseScaleAxisRotation";
61 	const String VisualSceneExporter::SCALE_SID = "scale";
62 
63 
64 	const String VisualSceneExporter::MATRIX_PARAMETERS[ 1 ] =
65 	{"TRANSFORM"
66 	};
67 
68 
69 	const String VisualSceneExporter::TRANSLATION_PARAMETERS[ 3 ] =
70 	{"X", "Y", "Z"
71 	};
72 
73 	const String VisualSceneExporter::ROTATION_PARAMETER[ 1 ] =
74 	{"ANGLE"
75 	};
76 
77 	const String VisualSceneExporter::ROTATION_PARAMETERS[ 4 ] =
78 	{"X", "Y", "Z", "ANGLE"
79 	};
80 
81 	//---------------------------------------------------------------
VisualSceneExporter(COLLADASW::StreamWriter * streamWriter,ExportSceneGraph * exportSceneGraph,const String & sceneId,DocumentExporter * documentExporter)82 	VisualSceneExporter::VisualSceneExporter( COLLADASW::StreamWriter * streamWriter, ExportSceneGraph * exportSceneGraph, const String & sceneId, DocumentExporter * documentExporter )
83 		: COLLADASW::LibraryVisualScenes( streamWriter ),
84 		mExportSceneGraph ( exportSceneGraph ),
85 		mEffectMap ( documentExporter->getEffectExporter()->getEffectMap() ),
86 		mVisualSceneId ( sceneId ),
87 		mDocumentExporter ( documentExporter )
88 	{}
89 
90 	//---------------------------------------------------------------
doExport()91 	void VisualSceneExporter::doExport()
92 	{
93 		openVisualScene( mVisualSceneId );
94 		exportEnvironmentAmbientLightNode();
95 		doExport( mExportSceneGraph->getRootExportNode());
96 		closeLibrary();
97 	}
98 
99 	//---------------------------------------------------------------
exportEnvironmentAmbientLightNode()100 	void VisualSceneExporter::exportEnvironmentAmbientLightNode()
101 	{
102 		COLLADASW::Node colladaNode( mSW );
103 		colladaNode.setNodeName( LightExporter::ENVIRONMENT_AMBIENT_LIGHT_NAME );
104 		colladaNode.start();
105 
106 		COLLADASW::InstanceLight instanceLight(mSW, "#" + LightExporter::ENVIRONMENT_AMBIENT_LIGHT_ID );
107 		instanceLight.add();
108 
109 		colladaNode.end();
110 	}
111 
112 	//---------------------------------------------------------------
doExport(ExportNode * exportNode)113 	void VisualSceneExporter::doExport ( ExportNode* exportNode )
114 	{
115 		if ( !exportNode->getIsInVisualScene() )
116 			return;
117 
118 		INode *node = exportNode->getINode();
119 
120 		// if true, we do not write a COLLADA node for this max node
121 		bool exportOnlyChilds = node->IsRootNode() != 0;
122 
123 		COLLADASW::Node colladaNode( mSW );
124 
125 		if ( !exportOnlyChilds )
126 		{
127 			colladaNode.setNodeId( getNodeId(*exportNode) );
128 
129 			if ( exportNode->hasSid() )
130 				colladaNode.setNodeSid(exportNode->getSid());
131 
132 			colladaNode.setNodeName( COLLADASW::Utils::checkNCName( NativeString(node->GetName()) ) );
133 
134 			if ( exportNode->getIsJoint() )
135 			{
136 				colladaNode.setType( COLLADASW::Node::JOINT );
137 			}
138 
139 			colladaNode.start();
140 
141 			exportTransformations ( exportNode, colladaNode);
142 
143 			ExportNode::Type nodeType = exportNode->getType();
144 
145 
146 			COLLADASW::Node colladaPivotNode ( mSW );
147 
148 			bool hasPivotNode = !node->IsRootNode();
149 
150 			if ( hasPivotNode )
151 			{
152 				hasPivotNode = !( (nodeType == ExportNode::BONE) || (nodeType == ExportNode::HELPER) );
153 			}
154 
155 			if ( hasPivotNode )
156 			{
157 				hasPivotNode = !node->IsGroupHead();
158 			}
159 
160 			if ( hasPivotNode )
161 			{
162 				Matrix3 objectOffsetTransformationMatrix(true);
163 
164 				// Calculate the pivot transform. It should already be in local space.
165 				calculateObjectOffsetTransformation(node, objectOffsetTransformationMatrix);
166 
167 				hasPivotNode = !objectOffsetTransformationMatrix.IsIdentity();
168 
169 				if ( hasPivotNode )
170 				{
171 					//open the pivot node and pivot transformation matrix
172 					colladaPivotNode.start();
173 					double matrix[ 4 ][ 4 ] ;
174 					matrix3ToDouble4x4 ( matrix, objectOffsetTransformationMatrix );
175 					colladaNode.addMatrix ( matrix );
176 				}
177 			}
178 
179 
180 			if ( exportNode->getType() == ExportNode::MESH )
181 			{
182 				if ( exportNode->hasControllers() )
183 				{
184 					COLLADASW::InstanceController instanceController ( mSW );
185 					ExportNodeSet referencedJoints = exportNode->getControllerList()->getReferencedJoints();
186 
187 					for ( ExportNodeSet::const_iterator it = referencedJoints.begin(); it!=referencedJoints.end(); ++it)
188 						instanceController.addSkeleton('#' + getNodeId(**it));
189 
190 					String controllerId = mDocumentExporter->getExportedObjectExportNode(ObjectIdentifier(exportNode->getLastController()->getDerivedObject(), 0))->getLastControllerId();
191 					assert( !controllerId.empty() );
192 
193 					instanceController.setUrl ( "#" + controllerId );
194 					//				instanceController.setUrl ( "#" + exportNode->getLastControllerId() );
195 					fillInstanceMaterialList(instanceController.getBindMaterial().getInstanceMaterialList(), exportNode);
196 					instanceController.add();
197 				}
198 				else
199 				{
200 					COLLADASW::InstanceGeometry instanceGeometry ( mSW );
201 
202 					String geometryId = GeometriesExporter::getGeometryId(*mDocumentExporter->getExportedObjectExportNode(ObjectIdentifier(exportNode->getInitialPose())));
203 					assert( !geometryId.empty() );
204 
205 					instanceGeometry.setUrl ( "#" + geometryId );
206 					fillInstanceMaterialList(instanceGeometry.getBindMaterial().getInstanceMaterialList(), exportNode);
207 					instanceGeometry.add();
208 				}
209 			}
210 			if ( exportNode->getType() == ExportNode::SPLINE )
211 			{
212 				COLLADASW::InstanceGeometry instanceGeometry ( mSW );
213 
214 				String geometryId = GeometriesExporter::getGeometryId(*mDocumentExporter->getExportedObjectExportNode(ObjectIdentifier(exportNode->getInitialPose())));
215 				assert( !geometryId.empty() );
216 
217 				instanceGeometry.setUrl ( "#" + geometryId );
218 				instanceGeometry.add();
219 			}
220 			else if ( exportNode->getType() == ExportNode::CAMERA )
221 			{
222 				String cameraId = CameraExporter::getCameraId(*mDocumentExporter->getExportedObjectExportNode(ObjectIdentifier(exportNode->getCamera())));
223 				assert( !cameraId.empty() );
224 
225 				COLLADASW::InstanceCamera instanceCamera(mSW, "#" + cameraId);
226 				//			COLLADASW::InstanceCamera instanceCamera(mSW, "#" + CameraExporter::getCameraId(*exportNode));
227 				instanceCamera.add();
228 			}
229 			else if ( exportNode->getType() == ExportNode::LIGHT )
230 			{
231 				String lightId = LightExporter::getLightId(*mDocumentExporter->getExportedObjectExportNode(ObjectIdentifier(exportNode->getLight())));
232 				assert( !lightId.empty() );
233 
234 				COLLADASW::InstanceLight instanceLight(mSW, "#" + lightId);
235 				instanceLight.add();
236 			}
237 
238 			if ( hasPivotNode )
239 			{
240 				//close the pivot node
241 				colladaPivotNode.end();
242 			}
243         }
244 
245         //export instance nodes for XRef scenes attached to current INode
246         const ExportSceneGraph::XRefSceneGraphList* xRefScenes = xRefScenes = mExportSceneGraph->getXRefSceneGraphList(node);
247 
248 		if ( xRefScenes != NULL )
249 		{
250             //add new node if we do not have one yet
251             if (exportOnlyChilds)
252             {
253                 colladaNode.setNodeName(XREF_NODE_NAME);
254                 colladaNode.start();
255             }
256 
257 			for ( ExportSceneGraph::XRefSceneGraphList::const_iterator it = xRefScenes->begin(); it != xRefScenes->end(); ++it)
258 			{
259 				COLLADASW::URI target = mDocumentExporter->getXRefOutputURI(*it);
260 				target.setFragment(getNodeId( *(it->exportSceneGraph->getRootExportNode()) ) );
261 
262 				COLLADASW::InstanceNode instanceNode(mSW, target);
263 				instanceNode.add();
264 			}
265 
266             //close new node if we've opened it before
267             if (exportOnlyChilds)
268             {
269                 colladaNode.end();
270             }
271 		}
272 
273 		//export the child nodes
274 		size_t numberOfChildren = exportNode->getNumberOfChildren();
275 
276 		for ( size_t i = 0; i < numberOfChildren; ++i )
277 			doExport ( exportNode->getChild ( i ) );
278 
279 		if ( !exportOnlyChilds )
280 		{
281 			colladaNode.addExtraTechniqueParameter(Extra::TECHNIQUE_PROFILE_OPENCOLLADA, Extra::CAST_SHADOWS_PROPERTY, exportNode->getCastShadows());
282 			colladaNode.addExtraTechniqueParameter(Extra::TECHNIQUE_PROFILE_OPENCOLLADA, Extra::RECEIVE_SHADOWS_PROPERTY, exportNode->getReceiveShadows());
283 
284 			if (INode* node = exportNode->getINode()) {
285 				colladaNode.addExtraTechniqueParameter(Extra::TECHNIQUE_PROFILE_OPENCOLLADA, Extra::PRIMARY_VISIBILITY_PROPERTY, node->GetPrimaryVisibility());
286 				colladaNode.addExtraTechniqueParameter(Extra::TECHNIQUE_PROFILE_OPENCOLLADA, Extra::SECONDARY_VISIBILITY_PROPERTY, node->GetSecondaryVisibility());
287 			}
288 
289 			if ( mDocumentExporter->getOptions().getExportUserDefinedProperties() )
290 			{
291 				// export user defined data stored for the max node
292 #ifdef MAX_9_OR_NEWER
293 				MSTR userPropertiesBuffer;
294 #else
295 				TSTR userPropertiesBuffer;
296 #endif
297 				node->GetUserPropBuffer(userPropertiesBuffer);
298 				if ( !userPropertiesBuffer.isNull() )
299 				{
300 					String xmlEncodedUserData = Utils::translateToXML(NativeString(userPropertiesBuffer.data()).toUtf8String());
301 					colladaNode.addExtraTechniqueParameter(Extra::TECHNIQUE_PROFILE_OPENCOLLADA, Extra::USERDEFINED_PROPERTIES, xmlEncodedUserData);
302 				}
303 			}
304 			colladaNode.end();
305 		}
306 	}
307 
308 	//---------------------------------------------------------------
exportTransformations(ExportNode * exportNode,const COLLADASW::Node & colladaNode)309 	void VisualSceneExporter::exportTransformations ( ExportNode * exportNode, const COLLADASW::Node & colladaNode )
310 	{
311 		const String & fullNodeId = getNodeId(*exportNode);
312 
313 		INode * iNode = exportNode->getINode();
314 
315 		INode *parent = iNode->GetParentNode();
316 
317 		ExportNode::Type nodeType = exportNode->getType();
318 
319 		bool exportObjectOffsetMatrix = !( (nodeType == ExportNode::BONE) || (nodeType == ExportNode::HELPER) );
320 
321 #if 0
322 		// Add the inverse of the parents object transformation matrix, if its not  identity
323 		if ( !objectOffsetTransformationMatrix.IsIdentity() )
324 		{
325 			Matrix3 inverseObjectOffsetTransformationMatrix(objectOffsetTransformationMatrix);
326 			inverseObjectOffsetTransformationMatrix.Invert();
327 			double matrix[ 4 ][ 4 ] ;
328 			matrix3ToDouble4x4 ( matrix, inverseObjectOffsetTransformationMatrix );
329 			if ( exportObjectOffsetMatrix )
330 				colladaNode.addMatrix ( matrix );
331 		}
332 
333 #endif
334 
335 		//Matrix3 transformationMatrix = iNode->GetObjectTM ( 0 );
336 		Matrix3 transformationMatrix = getWorldTransform( iNode );
337 
338 		if ( parent && !parent->IsRootNode() )
339 		{
340 			//transformationMatrix *= Inverse(parent->GetNodeTM(0));
341 			transformationMatrix *= Inverse ( getWorldTransform(parent) );
342 		}
343 
344 		if ( !applyFirstInstanceTransform(transformationMatrix, iNode) )
345 			return;
346 
347 		Control* transformationController = iNode->GetTMController();
348 
349 		if ( transformationController )
350 		{
351 			SClass_ID sc = transformationController->SuperClassID();
352 			Class_ID c = transformationController->ClassID();
353 			int g = 5;
354 		}
355 
356 		AnimationExporter * animationExporter = mDocumentExporter->getAnimationExporter();
357 		const Options& options = mDocumentExporter->getOptions();
358 
359 		if ( options.getBakeMatrices() || AnimationExporter::forceSampleMatrices(iNode) )
360 		{
361 			double matrix[ 4 ][ 4 ] ;
362 			matrix3ToDouble4x4 ( matrix, transformationMatrix );
363 			bool animatedMatrix = animationExporter->addAnimatedFloat4x4 ( iNode, fullNodeId, MATRIX_SID, MATRIX_PARAMETERS, true );
364 
365 			if ( animatedMatrix )
366 				colladaNode.addMatrix (MATRIX_SID, matrix );
367 			else
368 				colladaNode.addMatrix (matrix );
369 		}
370 
371 		else
372 		{
373 			// Decompose the transform
374 			AffineParts affineParts;
375 			decomp_affine ( transformationMatrix, &affineParts );
376 			affineParts.k *= affineParts.f;
377 			affineParts.f = 1.0f;
378 
379 			// Translation
380 			Control * translationController = ( transformationController ) ? transformationController->GetPositionController() : 0 ;
381 
382 			if (!AnimationExporter::isAnimated(translationController)) {
383 				if (!affineParts.t.Equals(Point3::Origin, TOLERANCE))
384 					colladaNode.addTranslate(affineParts.t.x, affineParts.t.y, affineParts.t.z);
385 			}
386 			else {
387 				// Animated
388 
389 				ILayerControl* ilc = GetILayerControlInterface(translationController);
390 				if (options.getExportLayersAsClips() && ilc != NULL && ilc->GetLayerCount() > 1)
391 				{
392 #if defined(MAX_RELEASE_R17) && (MAX_RELEASE >= MAX_RELEASE_R17)
393 
394 					for (int i = 0; i < ilc->GetLayerCount(); ++i) {
395 						// Export layers as named animations
396 						NativeString layerName(i == 0 ? L"" : ilc->GetLayerName(i).data()); // Leave the base layer without a name
397 
398 						Control* layerControl = ilc->GetSubCtrl(i);
399 						Point3 translation;
400 						layerControl->GetValue(mDocumentExporter->getOptions().getAnimationStart(), &translation, FOREVER, CTRL_ABSOLUTE);
401 
402 						String sidAppend = (i == 0) ? "" : "_" + layerName;
403 
404 						colladaNode.addTranslate(TRANSLATE_SID + sidAppend, translation.x, translation.y, translation.z);
405 
406 						animationExporter->addAnimatedPoint3(layerControl, layerName, fullNodeId, TRANSLATE_SID + "_" + layerName, TRANSLATION_PARAMETERS, true);
407 					}
408 #endif
409 				}
410 				else
411 				{
412 					// No layers
413 					colladaNode.addTranslate(TRANSLATE_SID, affineParts.t.x, affineParts.t.y, affineParts.t.z);
414 					animationExporter->addAnimatedPoint3(translationController, "", fullNodeId, TRANSLATE_SID, TRANSLATION_PARAMETERS, true);
415 				}
416 
417 			}
418 
419 
420 			// Rotation
421 
422 			Control * rotationController = (transformationController) ? transformationController->GetRotationController() : 0;
423 
424 			if ( !AnimationExporter::isAnimated(rotationController) )
425 			{
426 				// Save as axis-angle rotation.
427 				Matrix3 rotationMatrix;
428 				affineParts.q.MakeMatrix ( rotationMatrix );
429 
430 				AngAxis angleAxisRotation = AngAxis ( rotationMatrix );
431 
432 				if ( !angleAxisRotation.axis.Equals ( Point3::Origin ) && !COLLADASW::MathUtils::equalsZero ( angleAxisRotation.angle ) )
433 				{
434 					Point3 & rotationAxis = angleAxisRotation.axis;
435 					colladaNode.addRotate ( rotationAxis.x, rotationAxis.y, rotationAxis.z, -COLLADASW::MathUtils::radToDeg ( angleAxisRotation.angle ) );
436 				}
437 			}
438 
439 			else
440 			{
441 				// Animated
442 
443 				ILayerControl* ilc = GetILayerControlInterface(rotationController);
444 				if (options.getExportLayersAsClips() && ilc != NULL && ilc->GetLayerCount() > 1)
445 				{
446 #if defined(MAX_RELEASE_R17) && (MAX_RELEASE >= MAX_RELEASE_R17)
447 
448 					for (int i = 0; i < ilc->GetLayerCount(); ++i) {
449 						// Export layers as named animations
450 						NativeString layerName(i == 0 ? L"" : ilc->GetLayerName(i).data()); // Leave the base layer without a name
451 
452 						Control* layerControl = ilc->GetSubCtrl(i);
453 
454 						float eulerAngles[3];
455 						Quat quaternion;
456 						layerControl->GetValue(mDocumentExporter->getOptions().getAnimationStart(), &quaternion, FOREVER, CTRL_ABSOLUTE);
457 						QuatToEuler(quaternion, eulerAngles, EULERTYPE_XYZ);
458 
459 						String sidAppend = (i == 0) ? "" : "_" + layerName;
460 
461 						// Add nodes for the additive animations to target
462 						colladaNode.addRotateZ(ROTATE_Z_SID + sidAppend, 0);
463 						colladaNode.addRotateY(ROTATE_Y_SID + sidAppend, 0);
464 						colladaNode.addRotateX(ROTATE_X_SID + sidAppend, 0);
465 
466 						animationExporter->addAnimatedAngle(layerControl, layerName, fullNodeId, ROTATE_Z_SID + sidAppend, ROTATION_PARAMETER, Animation::ROTATION_Z, true);
467 						animationExporter->addAnimatedAngle(layerControl, layerName, fullNodeId, ROTATE_Y_SID + sidAppend, ROTATION_PARAMETER, Animation::ROTATION_Y, true);
468 						animationExporter->addAnimatedAngle(layerControl, layerName, fullNodeId, ROTATE_X_SID + sidAppend, ROTATION_PARAMETER, Animation::ROTATION_X, true);
469 					}
470 #endif
471 				}
472 				else
473 				{
474 
475 					float eulerAngles[3];
476 					Quat quaternion;
477 					rotationController->GetValue(mDocumentExporter->getOptions().getAnimationStart(), &quaternion, FOREVER, CTRL_ABSOLUTE);
478 					QuatToEuler(quaternion, eulerAngles, EULERTYPE_XYZ);
479 
480 					// Add the scene nodes
481 					colladaNode.addRotateZ(ROTATE_Z_SID, COLLADASW::MathUtils::radToDeg(eulerAngles[2]));
482 					colladaNode.addRotateY(ROTATE_Y_SID, COLLADASW::MathUtils::radToDeg(eulerAngles[1]));
483 					colladaNode.addRotateX(ROTATE_X_SID, COLLADASW::MathUtils::radToDeg(eulerAngles[0]));
484 
485 					// Export the entire rotation as a single animation
486 					animationExporter->addAnimatedAngle(rotationController, "", fullNodeId, ROTATE_Z_SID, ROTATION_PARAMETER, Animation::ROTATION_Z, true);
487 					animationExporter->addAnimatedAngle(rotationController, "", fullNodeId, ROTATE_Y_SID, ROTATION_PARAMETER, Animation::ROTATION_Y, true);
488 					animationExporter->addAnimatedAngle(rotationController, "", fullNodeId, ROTATE_X_SID, ROTATION_PARAMETER, Animation::ROTATION_X, true);
489 				}
490 
491 			}
492 
493 			//Scaling
494 
495 			// Check for animated scale. If not animated and equal to the identity, don't export.
496 			// Animated scale includes animated scale axis, so export that carefully.
497 			Control* scaleController = transformationController ? transformationController->GetScaleController() : 0;
498 
499 			SClass_ID scid = scaleController ? scaleController->SuperClassID() : 0;
500 			Class_ID cid = scaleController ? scaleController->ClassID() : Class_ID();
501 
502 			bool hasAnimatedScale = animationExporter->addAnimatedPoint3( scaleController, "", fullNodeId, SCALE_SID, TRANSLATION_PARAMETERS, true );
503 
504 			if ( hasAnimatedScale || !affineParts.k.Equals ( Point3 ( 1.0f, 1.0f, 1.0f ), TOLERANCE ) )
505 			{
506 				AngAxis scaleRotation ( affineParts.u );
507 
508 				// Rotate to match the scale axis
509 				bool hasScaleAxis = hasAnimatedScale ||
510 					!scaleRotation.axis.Equals ( Point3::Origin, TOLERANCE ) ||
511 					! ( COLLADASW::MathUtils::equalsZero ( scaleRotation.angle ) ) ||
512 					! ( COLLADASW::MathUtils::equals3 ( affineParts.k.x, affineParts.k.y, affineParts.k.z ) );
513 
514 				bool hasAnimatedScaleAxis = false;
515 				if ( hasScaleAxis )
516 				{
517 					Point3 & rotationAxis = scaleRotation.axis;
518 
519 					if ( hasAnimatedScale )
520 						hasAnimatedScaleAxis = animationExporter->addAnimatedAxisAngle( scaleController, fullNodeId, ROTATE_SCALE_AXIS_INVERSE_SID, ROTATION_PARAMETERS, Animation::SCALE_ROT_AXIS_R, true );
521 
522 					if ( hasAnimatedScaleAxis )
523 						colladaNode.addRotate ( ROTATE_SCALE_AXIS_INVERSE_SID, rotationAxis.x, rotationAxis.y, rotationAxis.z, -COLLADASW::MathUtils::radToDeg ( scaleRotation.angle ) );
524 					else if ( !COLLADABU::Math::Utils::equalsZero(scaleRotation.angle) )
525 						colladaNode.addRotate ( rotationAxis.x, rotationAxis.y, rotationAxis.z, -COLLADASW::MathUtils::radToDeg ( scaleRotation.angle ) );
526 				}
527 
528 				if ( hasAnimatedScale )
529 					colladaNode.addScale ( SCALE_SID, affineParts.k.x, affineParts.k.y, affineParts.k.z );
530 				else
531 					colladaNode.addScale ( affineParts.k.x, affineParts.k.y, affineParts.k.z );
532 
533 				// Rotate back to the rotation basis
534 				if ( hasScaleAxis )
535 				{
536 					Point3 & rotationAxis = scaleRotation.axis;
537 					if ( hasAnimatedScaleAxis )
538 					{
539 						colladaNode.addRotate ( ROTATE_SCALE_AXIS_SID, rotationAxis.x, rotationAxis.y, rotationAxis.z, COLLADASW::MathUtils::radToDeg ( scaleRotation.angle ) );
540 						animationExporter->addAnimatedAxisAngle( scaleController, fullNodeId, ROTATE_SCALE_AXIS_SID, ROTATION_PARAMETERS, Animation::SCALE_ROT_AXIS, false );
541 					}
542 					else if ( !COLLADABU::Math::Utils::equalsZero(scaleRotation.angle) )
543 					{
544 						colladaNode.addRotate ( rotationAxis.x, rotationAxis.y, rotationAxis.z, COLLADASW::MathUtils::radToDeg ( scaleRotation.angle ) );
545 					}
546 				}
547 			}
548 		}
549 
550 #if 0
551 		Matrix3 thisNodeObjectOffsetTransformationMatrix(true);
552 		if ( !iNode->IsRootNode() )
553 		{
554 			// Calculate the pivot transform. It should already be in local space.
555 			calculateObjectOffsetTransformation(iNode, thisNodeObjectOffsetTransformationMatrix);
556 
557 			// only export the pivot node if the transform is not an identity
558 			// or if the node is a group head node (this is a temporary fix until we add a PIVOT type)
559 			if ( !thisNodeObjectOffsetTransformationMatrix.IsIdentity() || iNode->IsGroupHead() )
560 			{
561 				double matrix[ 4 ][ 4 ] ;
562 				matrix3ToDouble4x4 ( matrix, thisNodeObjectOffsetTransformationMatrix );
563 				if ( exportObjectOffsetMatrix )
564 					colladaNode.addMatrix ( matrix );
565 			}
566 		}
567 
568 		return thisNodeObjectOffsetTransformationMatrix;
569 #endif
570 	}
571 
572 	//---------------------------------------------------------------
matrix3ToDouble4x4(double copy[][4],const Matrix3 & original)573 	void VisualSceneExporter::matrix3ToDouble4x4 ( double copy[][ 4 ], const Matrix3 & original )
574 	{
575 		copy[ 0 ][ 0 ] = original[ 0 ][ 0 ];
576 		copy[ 1 ][ 0 ] = original[ 0 ][ 1 ];
577 		copy[ 2 ][ 0 ] = original[ 0 ][ 2 ];
578 		copy[ 3 ][ 0 ] = 0;
579 		copy[ 0 ][ 1 ] = original[ 1 ][ 0 ];
580 		copy[ 1 ][ 1 ] = original[ 1 ][ 1 ];
581 		copy[ 2 ][ 1 ] = original[ 1 ][ 2 ];
582 		copy[ 3 ][ 1 ] = 0;
583 		copy[ 0 ][ 2 ] = original[ 2 ][ 0 ];
584 		copy[ 1 ][ 2 ] = original[ 2 ][ 1 ];
585 		copy[ 2 ][ 2 ] = original[ 2 ][ 2 ];
586 		copy[ 3 ][ 2 ] = 0;
587 		copy[ 0 ][ 3 ] = original[ 3 ][ 0 ];
588 		copy[ 1 ][ 3 ] = original[ 3 ][ 1 ];
589 		copy[ 2 ][ 3 ] = original[ 3 ][ 2 ];
590 		copy[ 3 ][ 3 ] = 1;
591 
592 	}
593 
594 	//---------------------------------------------------------------
calculateObjectOffsetTransformation(INode * maxNode,Matrix3 & transformationMatrix)595 	void VisualSceneExporter::calculateObjectOffsetTransformation(INode* maxNode, Matrix3& transformationMatrix)
596 	{
597 
598 		// When sampling matrices, we apply the sample the ObjTMAfterWSM
599 		// to get the final
600 		/*		if (OPTS->BakeMatrices())
601 		{
602 		IDerivedObject* derivedObj = maxNode->GetWSMDerivedObject();
603 		if (derivedObj != NULL)
604 		{
605 		// If we have WSM attached, always export a pivot
606 		TimeValue t = OPTS->AnimStart();
607 		transformationMatrix = maxNode->GetObjTMAfterWSM(t) * Inverse(maxNode->GetNodeTM(t));
608 		return;
609 		}
610 		}
611 		*/
612 		Point3 objectOffsetPosition = maxNode->GetObjOffsetPos();
613 		Quat objectOffsetRotation = maxNode->GetObjOffsetRot();
614 		ScaleValue objectOffsetScale = maxNode->GetObjOffsetScale();
615 
616 		// this should already be in local space
617 		// only do this if necessary to preserve identity tags
618 		transformationMatrix.IdentityMatrix();
619 		ApplyScaling(transformationMatrix, objectOffsetScale);
620 		RotateMatrix(transformationMatrix, objectOffsetRotation);
621 		transformationMatrix.Translate(objectOffsetPosition);
622 
623 		transformationMatrix.ValidateFlags();
624 	}
625 
626 	//---------------------------------------------------------------
getNodeId(const ExportNode & exportNode)627 	String VisualSceneExporter::getNodeId( const ExportNode& exportNode )
628 	{
629 		return NODE_ID_PRAEFIX + exportNode.getId();
630 	}
631 
632 	//---------------------------------------------------------------
getWorldTransform(INode * node,TimeValue time)633 	Matrix3 VisualSceneExporter::getWorldTransform( INode* node, TimeValue time )
634 	{
635 		return node->GetNodeTM( time );
636 	}
637 
638 	//---------------------------------------------------------------
getWorldTransform(INode * node)639 	Matrix3 VisualSceneExporter::getWorldTransform( INode* node )
640 	{
641 		return getWorldTransform( node, mDocumentExporter->getOptions().getAnimationStart() );
642 	}
643 
644 	//---------------------------------------------------------------
applyFirstInstanceTransform(Matrix3 & transformationMatrix,INode * node)645 	bool VisualSceneExporter::applyFirstInstanceTransform( Matrix3& transformationMatrix, INode* node )
646 	{
647 		bool hasSkinnedObject = SkinController::isSkinned(node->GetObjectRef());
648 		if (hasSkinnedObject)
649 		{
650 			// Figure out whether this is the instance or the original
651 			INodeTab instanceNodes;
652 			IInstanceMgr::GetInstanceMgr()->GetInstances(*node, instanceNodes);
653 
654 			if ( instanceNodes.Count() == 0)
655 				return false;
656 
657 			INode* firstInstanceNode = instanceNodes[0];
658 			if (node == firstInstanceNode)
659 				return false;
660 
661 			// For all instances, export the difference between the first
662 			// instance's local transformation and the current local transformation.
663 			// Note that you cannot export animations for this.
664 
665 			// Max is strange in this. It always moves back the skin to its original position,
666 			// when animating, whatever the new transform it might contain. So, we need to take out
667 			// both the initial TM and the first Instance's TM to figure out where to place this instance.
668 
669 			// The first instance TM include its pivot TM
670 			Matrix3 firstInstanceTM = firstInstanceNode->GetObjTMBeforeWSM(0);
671 			INode *parentNode = firstInstanceNode->GetParentNode();
672 			if ( parentNode && !parentNode->IsRootNode() )
673 			{
674 				Matrix3 parentTM = getWorldTransform(parentNode);
675 				parentTM.Invert();
676 				firstInstanceTM *= parentTM;
677 			}
678 
679 			firstInstanceTM.Invert();
680 			transformationMatrix *= firstInstanceTM;
681 		}
682 		return true;
683 	}
684 
685 	//---------------------------------------------------------------
fillInstanceMaterialList(COLLADASW::InstanceMaterialList & instanceMaterialList,ExportNode * exportNode)686 	void VisualSceneExporter::fillInstanceMaterialList( COLLADASW::InstanceMaterialList & instanceMaterialList, ExportNode * exportNode )
687 	{
688 		const ExportNode::MeshSymbolMap & symbolMap = exportNode->getMeshSymbolMap();
689 
690 		if ( symbolMap.empty() )
691 		{
692 			String materialId = MaterialExporter::getMaterialIdFromEffectId( EffectExporter::getEffectId( exportNode->getWireFrameColor() ) );
693 			//String materialSymbol = symbolIt->first;
694 			const String & materialSymbol = GeometryExporter::COLOR_MATERIAL_SYMBOL;
695 			instanceMaterialList.push_back( COLLADASW::InstanceMaterial ( materialSymbol, "#" + materialId ) );
696 		}
697 		else
698 		{
699 			ExportNode::MeshSymbolMap::const_iterator symbolIt = symbolMap.begin();
700 
701 			for ( ; symbolIt != symbolMap.end(); ++symbolIt )
702 			{
703 				ExportNode::Symbol symbol = symbolIt->second;
704 
705 				if ( symbol.used )
706 				{
707 					Mtl * material = symbolIt->first;
708 					EffectMap::const_iterator it = mEffectMap.find( material );
709 					assert( it != mEffectMap.end() );
710 					String materialId = MaterialExporter::getMaterialIdFromEffectId( it->second );
711 					instanceMaterialList.push_back( COLLADASW::InstanceMaterial( symbol.name, "#" + materialId ) );
712 
713 					// if it is as stdmat2, we need to export the bind vertex input
714 					Class_ID materialClassId = material->ClassID();
715 
716 					if ( materialClassId.PartA() == DMTL2_CLASS_ID || materialClassId.PartA() == DMTL_CLASS_ID )
717 					{
718 						StdMat2* standartMaterial = (StdMat2*)material;
719 
720 						COLLADASW::InstanceMaterial& instanceMaterial = instanceMaterialList.back();
721 
722 						int numSubTexMaps = standartMaterial->NumSubTexmaps();
723 						for ( int i = 0; i < numSubTexMaps; i++ )
724 						{
725 							bool exportThisMap = standartMaterial->GetMapState ( i ) == 2;
726 							if ( exportThisMap )
727 							{
728 								Texmap* texMap = standartMaterial->GetSubTexmap ( i );
729 								int mapChannel = texMap->GetMapChannel();
730 								String semantic = EffectExporter::createTexcoordSementicFromMapchannel( mapChannel );
731 								const String & inputSemantic = COLLADASW::InputList::getSemanticString(COLLADASW::InputSemantic::TEXCOORD);
732 								// max starts with 1 to index the texture maps. To start with 0 in COLLADA, we always substract one from the channel to get the set
733 								// This is also relevant when the inouts of texture coordinates
734 								instanceMaterial.push_back( COLLADASW::BindVertexInput( semantic, inputSemantic, mapChannel - 1) );
735 							}
736 						}
737 					}
738 
739 				}
740 			}
741 		}
742 
743 	}
744 
745 
746 
747 }
748