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