1 /* 2 Copyright (c) 2008-2009 NetAllied Systems GmbH 3 4 This file is part of COLLADAMaya. 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 Copyright (c) 2004-2005 Alias Systems Corp. 10 11 Licensed under the MIT Open Source License, 12 for details please see LICENSE file or the website 13 http://www.opensource.org/licenses/mit-license.php 14 */ 15 16 #include "COLLADAMayaStableHeaders.h" 17 #include "COLLADAMayaAttributeParser.h" 18 #include "COLLADAMayaGeometryExporter.h" 19 #include "COLLADAMayaPhysXExporter.h" 20 #include "COLLADAMayaGeometryPolygonExporter.h" 21 #include "COLLADAMayaExportOptions.h" 22 #include "COLLADAMayaSyntax.h" 23 #include "COLLADAMayaSceneGraph.h" 24 #include "COLLADAMayaAnimationHelper.h" 25 #include "COLLADAMayaDagHelper.h" 26 #include "COLLADAMayaAnimationExporter.h" 27 #include "COLLADAMayaControllerExporter.h" 28 29 #include "COLLADAMayaPhysicsExporter.h" 30 31 #include <algorithm> 32 33 #include <maya/MItDependencyNodes.h> 34 #include <maya/MFnAttribute.h> 35 #include <maya/MFnMesh.h> 36 #include <maya/MItMeshPolygon.h> 37 #include <maya/MItMeshVertex.h> 38 #include <maya/MItDag.h> 39 #include <maya/MFnNumericData.h> 40 41 #include "COLLADASWSource.h" 42 #include "COLLADASWBaseInputElement.h" 43 #include "COLLADASWInputList.h" 44 #include "COLLADASWExtraTechnique.h" 45 #include "COLLADASWExtra.h" 46 47 namespace COLLADAMaya 48 { 49 static const char* NAME_SUFFIX_INVALID = "_MAKE_NAME_INVALID"; 50 51 // -------------------------------------------------------- GeometryExporter(COLLADASW::StreamWriter * streamWriter,DocumentExporter * documentExporter)52 GeometryExporter::GeometryExporter ( COLLADASW::StreamWriter* streamWriter, 53 DocumentExporter* documentExporter ) 54 : COLLADASW::LibraryGeometries ( streamWriter ) 55 , mDocumentExporter ( documentExporter ) 56 { 57 } 58 59 // -------------------------------------------------------- ~GeometryExporter(void)60 GeometryExporter::~GeometryExporter ( void ) 61 { 62 mPolygonSources.clear(); 63 mVertexSources.clear(); 64 } 65 66 // -------------------------------------------------------- exportGeometries()67 void GeometryExporter::exportGeometries() 68 { 69 if (!ExportOptions::exportPolygonMeshes() && 70 // PhysX may reference geometry 71 !ExportOptions::exportPhysics()) 72 return; 73 74 // Get the list with the transform nodes. 75 SceneGraph* sceneGraph = mDocumentExporter->getSceneGraph(); 76 SceneElementsList* exportNodesTree = sceneGraph->getExportNodesTree(); 77 78 // Export all/selected DAG nodes 79 size_t length = exportNodesTree->size(); 80 for ( size_t i = 0; i < length; ++i ) 81 { 82 SceneElement* sceneElement = ( *exportNodesTree ) [i]; 83 exportGeometries ( sceneElement, sceneElement->getIsVisible () ); 84 } 85 86 endExport(); 87 } 88 89 // -------------------------------------------------------- exportGeometries(SceneElement * sceneElement,bool isVisible)90 void GeometryExporter::exportGeometries ( SceneElement* sceneElement, bool isVisible ) 91 { 92 // If we have a external reference, we don't need to export the data here. 93 if ( !sceneElement->getIsLocal() ) return; 94 95 bool exportSceneElement = false; 96 SceneElement::Type sceneElementType = sceneElement->getType(); 97 98 if (ExportOptions::exportPolygonMeshes() && 99 sceneElementType == SceneElement::MESH ) 100 { 101 if ( sceneElement->getIsExportNode () ) exportSceneElement = true; 102 else 103 { 104 if ( sceneElement->getIsForced () ) exportSceneElement = true; 105 else if ( !isVisible && ExportOptions::exportInvisibleNodes () ) exportSceneElement = true; 106 } 107 } 108 else if (ExportOptions::exportPhysics() && 109 sceneElementType == SceneElement::PHYSX_SHAPE) 110 { 111 const MObject & shape = sceneElement->getNode(); 112 113 MString shapeType; 114 PhysXShape::GetType(shape, shapeType); 115 116 if (shapeType == SHAPE_TYPE_CONVEX_HULL || 117 shapeType == SHAPE_TYPE_TRIANGLE_MESH) 118 { 119 MObject mesh; 120 PhysXShape::GetConnectedInMesh(shape, mesh); 121 if (mesh.isNull() || !ExportOptions::exportPolygonMeshes()) 122 { 123 exportSceneElement = true; 124 } 125 } 126 } 127 128 if ( exportSceneElement ) 129 { 130 // Get the current dag path 131 MDagPath dagPath = sceneElement->getPath(); 132 133 // Check if the current scene element isn't already exported. 134 SceneGraph* sceneGraph = mDocumentExporter->getSceneGraph(); 135 if ( sceneGraph->findExportedElement ( dagPath ) ) return; 136 137 // Check if the current element is an instance. 138 // We don't need to export instances, because we export the original instanced element. 139 bool isInstance = ( dagPath.isInstanced() && dagPath.instanceNumber() > 0 ); 140 141 // If the original instanced element isn't already exported, we have to export it now. 142 if ( isInstance ) 143 { 144 // Get the original instanced element. 145 MDagPath instancedPath; 146 dagPath.getPath ( instancedPath, 0 ); 147 148 // Check if the original instanced element is already exported. 149 SceneElement* exportedElement = sceneGraph->findExportedElement ( instancedPath ); 150 if ( exportedElement == 0 ) 151 { 152 // Export the original instanced element. 153 SceneElement* instancedSceneElement = sceneGraph->findElement ( instancedPath ); 154 exportControllerOrGeometry ( instancedSceneElement ); 155 } 156 } 157 else 158 { 159 // Handle the geometry in depend on it is a controller or not. 160 exportControllerOrGeometry ( sceneElement ); 161 } 162 } 163 164 // Check if the element is visible (inherit visibility to children) 165 if ( isVisible ) isVisible = sceneElement->getIsVisible (); 166 167 // Recursive call for all the child elements 168 for ( uint i=0; i<sceneElement->getChildCount(); ++i ) 169 { 170 SceneElement* childElement = sceneElement->getChild ( i ); 171 exportGeometries ( childElement, isVisible ); 172 } 173 } 174 175 // -------------------------------------------------------- exportControllerOrGeometry(SceneElement * sceneElement)176 void GeometryExporter::exportControllerOrGeometry ( 177 SceneElement* sceneElement ) 178 { 179 // Get the controller library 180 ControllerExporter* controller = mDocumentExporter->getControllerExporter(); 181 182 // Get the current node element. 183 MObject node = sceneElement->getPath ().node(); 184 185 // Add the controller and/or geometry to our libraries 186 bool hasSkinController = ExportOptions::exportJoints() && controller->hasSkinController(node); 187 bool hasMorphController = controller->hasMorphController ( node ); 188 if ( hasSkinController || hasMorphController ) 189 { 190 // Handle the controllers 191 handleControllers ( sceneElement ); 192 } 193 else 194 { 195 // Export the element and push it in the exported scene graph. 196 if ( exportGeometry ( sceneElement ) ) 197 { 198 SceneGraph* sceneGraph = mDocumentExporter->getSceneGraph(); 199 sceneGraph->addExportedElement( sceneElement ); 200 } 201 } 202 } 203 204 // -------------------------------------------------------- exportGeometry(SceneElement * sceneElement)205 bool GeometryExporter::exportGeometry ( SceneElement* sceneElement ) 206 { 207 // Get the current dag path 208 MDagPath dagPath = sceneElement->getPath(); 209 String pathName = dagPath.fullPathName ().asChar (); 210 211 // Generate the unique collada mesh id. 212 const String& colladaMeshId = generateColladaMeshId ( dagPath ); 213 if ( colladaMeshId.empty () ) return false; 214 215 // Set the node id. 216 sceneElement->setNodeId ( colladaMeshId ); 217 218 bool isInstanced = dagPath.isInstanced(); 219 uint instanceNumber = dagPath.instanceNumber(); 220 221 // Get the node of the current mesh 222 MObject meshNode; 223 if (sceneElement->getType() == SceneElement::PHYSX_SHAPE) { 224 PhysXShape::GetConnectedInMesh(sceneElement->getNode(), meshNode); 225 if (meshNode.isNull()) 226 { 227 PhysXShape::GetInMesh(sceneElement->getNode(), meshNode); 228 } 229 } 230 else { 231 meshNode = dagPath.node(); 232 } 233 234 // Write the mesh data 235 String meshName = mDocumentExporter->dagPathToColladaName ( dagPath ); 236 bool result = exportMesh(meshNode, colladaMeshId, meshName); 237 238 if (ExportOptions::exportPhysics()) 239 { 240 // Bullet --------------------------------------------------------- 241 MObject transform = dagPath.transform(); 242 int shapeType; 243 bool shapeResult = DagHelper::getPlugValue(transform, ATTR_COLLISION_SHAPE, shapeType); 244 245 if (shapeResult) 246 { 247 if (shapeType == COLLADAMaya::PhysicsExporter::Convex_mesh) 248 { 249 openConvexMesh(colladaMeshId, meshName); 250 closeConvexMesh(); 251 } 252 } 253 254 // PhysX ---------------------------------------------------------- 255 PhysXExporter& physXExporter = *mDocumentExporter->getPhysXExporter(); 256 MObject shape; 257 if (physXExporter.needsConvexHullOf(*sceneElement, shape)) 258 { 259 openConvexMesh(colladaMeshId, colladaMeshId); 260 if (ExportOptions::exportConvexMeshGeometries()) 261 { 262 std::vector<PhysXXML::Point> vertices; 263 MString mayaMeshId; 264 if (physXExporter.getShapeVertices(shape, vertices, mayaMeshId)) 265 { 266 String meshId = mayaMeshId.asChar(); 267 268 // <source> ----------------------------------------------- 269 COLLADASW::FloatSource vertexSource(mSW); 270 vertexSource.setId(meshId + POSITIONS_SOURCE_ID_SUFFIX); 271 vertexSource.setNodeName(meshId + POSITIONS_SOURCE_ID_SUFFIX); 272 vertexSource.setArrayId(meshId + POSITIONS_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX); 273 vertexSource.setAccessorStride(3); 274 275 size_t vertexCount = vertices.size(); 276 vertexSource.setAccessorCount(static_cast<unsigned long>(vertexCount)); 277 278 vertexSource.getParameterNameList().push_back(XYZW_PARAMETERS[0]); 279 vertexSource.getParameterNameList().push_back(XYZW_PARAMETERS[1]); 280 vertexSource.getParameterNameList().push_back(XYZW_PARAMETERS[2]); 281 vertexSource.prepareToAppendValues(); 282 283 for (size_t i = 0; i < vertexCount; ++i) 284 { 285 vertexSource.appendValues( 286 vertices[i].x, 287 vertices[i].y, 288 vertices[i].z 289 ); 290 } 291 vertexSource.finish(); 292 293 // <vertices> --------------------------------------------- 294 COLLADASW::VerticesElement vertices(mSW); 295 vertices.setId(meshId + VERTICES_ID_SUFFIX); 296 vertices.setNodeName(meshId + VERTICES_ID_SUFFIX); 297 298 // Get the input list 299 COLLADASW::InputList* inputList = &vertices.getInputList(); 300 301 // Always push the vertex positions in the vertices element 302 // (we have to create a vertices element with a reference) 303 inputList->push_back(COLLADASW::Input(COLLADASW::InputSemantic::POSITION, COLLADASW::URI(EMPTY_STRING, meshId + POSITIONS_SOURCE_ID_SUFFIX))); 304 305 vertices.add(); 306 } 307 } 308 closeConvexMesh(); 309 } 310 } 311 312 return result; 313 } 314 315 // -------------------------------------------------------- generateColladaMeshId(const MDagPath dagPath)316 const String GeometryExporter::generateColladaMeshId ( const MDagPath dagPath ) 317 { 318 // Get the maya mesh id. 319 String mayaMeshId = mDocumentExporter->dagPathToColladaId ( dagPath ); 320 321 if (dagPath.isInstanced()) 322 { 323 MDagPathArray paths; 324 dagPath.getAllPathsTo(dagPath.node(), paths); 325 mayaMeshId = mDocumentExporter->dagPathToColladaId(paths[0]); 326 } 327 328 // Generate a COLLADA id for the new object. 329 String colladaMeshId = findColladaGeometryId ( mayaMeshId ); 330 331 // Check, if the unique id for the current geometry is already generated. 332 if ( !colladaMeshId.empty () ) return colladaMeshId; 333 334 // Get the node of the current mesh 335 MObject meshNode = dagPath.node(); 336 337 // Attach a function set to the mesh node. 338 // We access all of the meshes data through the function set 339 MStatus status; 340 MFnDependencyNode fnMesh(meshNode, &status); 341 if ( status != MStatus::kSuccess ) return colladaMeshId; 342 343 // Check if there is an extra attribute "colladaId" and use this as export id. 344 MString attributeValue; 345 DagHelper::getPlugValue ( meshNode, COLLADA_ID_ATTRIBUTE_NAME, attributeValue ); 346 if ( attributeValue != EMPTY_CSTRING ) 347 { 348 // Generate a valid collada name, if necessary. 349 colladaMeshId = mDocumentExporter->mayaNameToColladaName ( attributeValue, false ); 350 } 351 else 352 { 353 // Generate a COLLADA id for the new object 354 colladaMeshId = DocumentExporter::mayaNameToColladaName ( fnMesh.name() ); 355 } 356 357 // Make the id unique and store it in a map. 358 colladaMeshId = mGeometryIdList.addId ( colladaMeshId ); 359 mMayaIdColladaIdMap [ mayaMeshId ] = colladaMeshId; 360 361 return colladaMeshId; 362 } 363 364 // -------------------------------------------------------- exportMesh(MObject & mesh,const String & colladaMeshId,const String & mayaMeshName)365 bool GeometryExporter::exportMesh ( 366 MObject & mesh, 367 const String& colladaMeshId, 368 const String& mayaMeshName ) 369 { 370 // Clear the list with the current polygons and the list with the vertexes 371 mPolygonSources.clear(); 372 mVertexSources.clear(); 373 374 // Retrieve all uv set names for this mesh. 375 MStringArray uvSetNames; 376 getUVSetNames ( mesh, uvSetNames ); 377 378 // Opens the mesh tag in the collada document 379 openMesh ( colladaMeshId, mayaMeshName ); 380 381 // Export the vertex positions 382 exportVertexPositions ( mesh, colladaMeshId ); 383 384 // Export the vertex normals 385 bool hasFaceVertexNormals = exportVertexNormals ( mesh, colladaMeshId ); 386 387 // Export the texture coordinates 388 exportTextureCoords ( mesh, colladaMeshId, uvSetNames ); 389 390 // The list for the color sets. We have to clean! 391 MStringArray colorSetNames; 392 393 // Export the color sets 394 exportColorSets ( mesh, colladaMeshId, colorSetNames ); 395 396 // Export the texture tangents and binormals. 397 // For texturing std::map channels, export the texture tangents and bi-normals, on request 398 if ( ExportOptions::exportTexTangents() ) 399 { 400 exportTextureTangentsAndBinormals ( mesh, colladaMeshId ); 401 } 402 403 // Export the vertexes 404 exportVertices ( colladaMeshId ); 405 406 // Create a polygon exporter and export the polygon sources. 407 COLLADASW::StreamWriter* streamWriter = mDocumentExporter->getStreamWriter(); 408 GeometryPolygonExporter polygonExporter ( streamWriter, mDocumentExporter ); 409 polygonExporter.exportPolygonSources ( mesh, colladaMeshId, uvSetNames, colorSetNames, &mPolygonSources, &mVertexSources, hasFaceVertexNormals ); 410 411 closeMesh(); 412 413 // Export the original maya name and the double sided value in an extra tag. 414 exportExtraTechniqueParameters ( mesh, mayaMeshName ); 415 416 closeGeometry(); 417 418 return true; 419 } 420 421 // -------------------------------------------------------- getUVSetNames(const MObject & mesh,MStringArray & uvSetNames)422 void GeometryExporter::getUVSetNames(const MObject& mesh, MStringArray& uvSetNames) 423 { 424 MFnMesh fnMesh(mesh); 425 426 std::set<std::wstring> duplicateLookup; 427 428 MPlug uvSetPlug = fnMesh.findPlug ( ATTR_UV_SET ); 429 unsigned int countElements = uvSetPlug.numElements(); 430 for ( uint i = 0; i < countElements; i++ ) 431 { 432 // get uvSet[<index>] and uvSet[<index>].uvSetName 433 MPlug uvSetElememtPlug = uvSetPlug.elementByPhysicalIndex ( i ); 434 MPlug uvSetNamePlug = uvSetElememtPlug.child ( 0 ); 435 436 // get value of plug (uvSet's name) 437 MString uvSetName; 438 uvSetNamePlug.getValue ( uvSetName ); 439 440 std::set<std::wstring>::iterator it = duplicateLookup.find( uvSetName.asWChar() ); 441 bool noDuplicate = it == duplicateLookup.end(); 442 if( noDuplicate ) 443 uvSetNames.append ( uvSetName ); 444 else 445 { 446 //@remark: 447 // [fixing issue63] Making name invalid for lookup uvarrays but maintain index in uvset name list. 448 // cause: 449 // 1. maya does not allow retrieving uv-coords by index ONLY by uvset-name 450 // 2. maya retrieves for fnMesh only non-duplicated-name for uv-names 451 // // Get UVSets for this mesh 452 // MStringArray UVSets; 453 // MStatus status = fnMesh.getUVSetNames( UVSets ); 454 // uint countUVSByFnMesh = UVSets.length(); 455 456 MString uvSetNameWithIndex( uvSetName ); 457 uvSetNameWithIndex += NAME_SUFFIX_INVALID; 458 uvSetNames.append ( uvSetNameWithIndex ); 459 } 460 461 duplicateLookup.insert( uvSetName.asWChar() ); 462 } 463 } 464 465 // -------------------------------------------------------- 466 class Element 467 { 468 public: Element(COLLADASW::StreamWriter & streamWriter,const String & mName,const String & mSID="")469 Element(COLLADASW::StreamWriter& streamWriter, const String& mName, const String& mSID = "") 470 : mStreamWriter(streamWriter) 471 { 472 mStreamWriter.openElement(mName); 473 if (!mSID.empty()) { 474 mStreamWriter.appendAttribute(COLLADASW::CSWC::CSW_ATTRIBUTE_SID, mSID); 475 } 476 } 477 ~Element()478 ~Element() 479 { 480 mStreamWriter.closeElement(); 481 } 482 483 private: 484 COLLADASW::StreamWriter& mStreamWriter; 485 }; 486 487 class ExtraAttributeExporter : public AttributeParser 488 { 489 public: ExtraAttributeExporter(COLLADASW::Technique & technique)490 ExtraAttributeExporter(COLLADASW::Technique& technique) 491 : mTechnique(technique) 492 {} 493 494 private: 495 COLLADASW::Technique& mTechnique; 496 497 // AttributeParser overrides 498 onBeforePlug(MPlug & plug)499 virtual bool onBeforePlug(MPlug & plug) override 500 { 501 MStatus status; 502 503 MObject attr = plug.attribute(&status); 504 if (!status) return false; 505 506 MFnAttribute fnAttr(attr, &status); 507 if (!status) return false; 508 509 MString attrName = fnAttr.name(&status); 510 if (!status) return false; 511 512 bool isDynamic = fnAttr.isDynamic(&status); 513 if (!status) return false; 514 515 if (!isDynamic) 516 return false; 517 518 bool isHidden = fnAttr.isHidden(&status); 519 if (!status) return false; 520 521 if (isHidden) 522 return false; 523 524 return true; 525 } 526 onBoolean(MPlug & plug,const MString & name,bool value)527 virtual void onBoolean(MPlug & plug, const MString & name, bool value) override 528 { 529 mTechnique.addParameter(name.asChar(), value, "", PARAM_TYPE_BOOL, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 530 } 531 onByte(MPlug & plug,const MString & name,char value)532 virtual void onByte(MPlug & plug, const MString & name, char value) override 533 { 534 const size_t size = 5; 535 char text[size]; 536 snprintf(text, size, "0x%X", value); 537 mTechnique.addParameter(name.asChar(), COLLADABU::StringUtils::translateToXML(text), "", PARAM_TYPE_BYTE, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 538 } 539 onChar(MPlug & plug,const MString & name,char value)540 virtual void onChar(MPlug & plug, const MString & name, char value) override 541 { 542 char text[2] = { value, '\0' }; 543 mTechnique.addParameter(name.asChar(), COLLADABU::StringUtils::translateToXML(text), "", PARAM_TYPE_CHAR, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 544 } 545 onShort(MPlug & plug,const MString & name,short value)546 virtual void onShort(MPlug & plug, const MString & name, short value) override 547 { 548 mTechnique.addParameter(name.asChar(), value, "", PARAM_TYPE_SHORT, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 549 } 550 onShort2(MPlug & plug,const MString & name,short value[2])551 virtual void onShort2(MPlug & plug, const MString & name, short value[2]) override 552 { 553 mTechnique.addParameter(name.asChar(), value[0], value[1], "", PARAM_TYPE_SHORT2, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 554 } 555 onShort3(MPlug & plug,const MString & name,short value[3])556 virtual void onShort3(MPlug & plug, const MString & name, short value[3]) override 557 { 558 mTechnique.addParameter(name.asChar(), value[0], value[1], value[2], "", PARAM_TYPE_SHORT3, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 559 } 560 onInteger(MPlug & plug,const MString & name,int value)561 virtual void onInteger(MPlug & plug, const MString & name, int value) override 562 { 563 mTechnique.addParameter(name.asChar(), value, "", PARAM_TYPE_LONG, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 564 } 565 onInteger2(MPlug & plug,const MString & name,int value[2])566 virtual void onInteger2(MPlug & plug, const MString & name, int value[2]) override 567 { 568 mTechnique.addParameter(name.asChar(), value[0], value[1], "", PARAM_TYPE_LONG2, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 569 } 570 onInteger3(MPlug & plug,const MString & name,int value[3])571 virtual void onInteger3(MPlug & plug, const MString & name, int value[3]) override 572 { 573 mTechnique.addParameter(name.asChar(), value[0], value[1], value[2], "", PARAM_TYPE_LONG3, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 574 } 575 onFloat(MPlug & plug,const MString & name,float value)576 virtual void onFloat(MPlug & plug, const MString & name, float value) override 577 { 578 mTechnique.addParameter(name.asChar(), value, "", "float", COLLADASW::CSWC::CSW_ELEMENT_PARAM); 579 } 580 onFloat2(MPlug & plug,const MString & name,float value[2])581 virtual void onFloat2(MPlug & plug, const MString & name, float value[2]) override 582 { 583 mTechnique.addParameter(name.asChar(), value[0], value[1], "", PARAM_TYPE_FLOAT2, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 584 } 585 onFloat3(MPlug & plug,const MString & name,float value[3])586 virtual void onFloat3(MPlug & plug, const MString & name, float value[3]) override 587 { 588 mTechnique.addParameter(name.asChar(), value[0], value[1], value[2], "", PARAM_TYPE_FLOAT3, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 589 } 590 onDouble(MPlug & plug,const MString & name,double value)591 virtual void onDouble(MPlug & plug, const MString & name, double value) override 592 { 593 mTechnique.addParameter(name.asChar(), value, "", PARAM_TYPE_DOUBLE, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 594 } 595 onDouble2(MPlug & plug,const MString & name,double value[2])596 virtual void onDouble2(MPlug & plug, const MString & name, double value[2]) override 597 { 598 mTechnique.addParameter(name.asChar(), value[0], value[1], "", PARAM_TYPE_DOUBLE2, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 599 } 600 onDouble3(MPlug & plug,const MString & name,double value[3])601 virtual void onDouble3(MPlug & plug, const MString & name, double value[3]) override 602 { 603 mTechnique.addParameter(name.asChar(), value[0], value[1], value[2], "", PARAM_TYPE_DOUBLE3, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 604 } 605 onDouble4(MPlug & plug,const MString & name,double value[4])606 virtual void onDouble4(MPlug & plug, const MString & name, double value[4]) override 607 { 608 mTechnique.addParameter(name.asChar(), value[0], value[1], value[2], value[3], "", PARAM_TYPE_DOUBLE4, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 609 } 610 onString(MPlug & plug,const MString & name,const MString & value)611 virtual void onString(MPlug & plug, const MString & name, const MString & value) override 612 { 613 mTechnique.addParameter(name.asChar(), COLLADABU::StringUtils::translateToXML(value.asChar()), "", PARAM_TYPE_STRING, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 614 } 615 onEnum(MPlug & plug,const MString & name,int enumValue,const MString & enumName)616 virtual void onEnum(MPlug & plug, const MString & name, int enumValue, const MString & enumName) override 617 { 618 mTechnique.addParameter(name.asChar(), COLLADABU::StringUtils::translateToXML(enumName.asChar()), "", PARAM_TYPE_ENUM, COLLADASW::CSWC::CSW_ELEMENT_PARAM); 619 } 620 }; 621 622 // -------------------------------------------------------- exportExtraTechniqueParameters(const MObject & mesh,const String & mayaMeshName)623 void GeometryExporter::exportExtraTechniqueParameters ( 624 const MObject& mesh, 625 const String& mayaMeshName ) 626 { 627 bool doubleSided = isDoubleSided ( mesh ); 628 629 COLLADASW::Extra extraSource ( mSW ); 630 extraSource.openExtra(); 631 632 COLLADASW::Technique techniqueSource ( mSW ); 633 techniqueSource.openTechnique ( PROFILE_MAYA ); 634 techniqueSource.addParameter ( PARAMETER_MAYA_ID, mayaMeshName ); 635 techniqueSource.addParameter ( PARAMETER_DOUBLE_SIDED, doubleSided ); 636 637 // Also export extra attributes 638 MFnDependencyNode fnDependencyNode(mesh); 639 ExtraAttributeExporter extraAttributeExporter(techniqueSource); 640 AttributeParser::parseAttributes(fnDependencyNode, extraAttributeExporter); 641 642 techniqueSource.closeTechnique(); 643 644 extraSource.closeExtra(); 645 } 646 647 // -------------------------------------------------------- isDoubleSided(const MObject & mesh)648 bool GeometryExporter::isDoubleSided(const MObject& mesh) 649 { 650 MFnMesh fnMesh(mesh); 651 652 MPlug doubleSidedPlug = fnMesh.findPlug ( ATTR_DOUBLE_SIDED ); 653 bool doubleSided; 654 doubleSidedPlug.getValue ( doubleSided ); 655 656 if ( doubleSided ) 657 { 658 // Also check the backfaceCulling plug 659 MPlug backfaceCullingPlug = fnMesh.findPlug ( ATTR_BACKFACE_CULLING ); 660 int enumValue = 0; 661 backfaceCullingPlug.getValue ( enumValue ); 662 663 if ( enumValue != 0 ) doubleSided = false; 664 } 665 666 return doubleSided; 667 } 668 669 //--------------------------------------------------------------- exportVertexPositions(const MObject & mesh,const String & meshId)670 void GeometryExporter::exportVertexPositions(const MObject& mesh, const String &meshId) 671 { 672 MFnMesh fnMesh(mesh); 673 674 COLLADASW::FloatSource vertexSource ( mSW ); 675 vertexSource.setId ( meshId + POSITIONS_SOURCE_ID_SUFFIX ); 676 vertexSource.setNodeName ( meshId + POSITIONS_SOURCE_ID_SUFFIX ); 677 vertexSource.setArrayId ( meshId + POSITIONS_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX ); 678 vertexSource.setAccessorStride ( 3 ); 679 680 // Retrieves the vertex positions. 681 MPointArray vertexArray; 682 fnMesh.getPoints ( vertexArray, MSpace::kObject ); // With the meteorite-example, 10MB will be allocated! 683 uint vertexCount = vertexArray.length(); 684 vertexSource.setAccessorCount ( vertexCount ); 685 686 vertexSource.getParameterNameList().push_back ( XYZW_PARAMETERS[0] ); 687 vertexSource.getParameterNameList().push_back ( XYZW_PARAMETERS[1] ); 688 vertexSource.getParameterNameList().push_back ( XYZW_PARAMETERS[2] ); 689 vertexSource.prepareToAppendValues(); 690 691 // Remove the vertex position tweaks that will be exported as animations 692 MPlug positionTweakArrayPlug = fnMesh.findPlug ( ATTR_VERTEX_POSITION_TWEAKS ); 693 uint positionTweakCount = positionTweakArrayPlug.numElements(); 694 for ( uint i = 0; i < positionTweakCount; ++i ) 695 { 696 MPlug positionTweakPlug = positionTweakArrayPlug.elementByPhysicalIndex ( i ); 697 MPlug positionTweakPlugX = positionTweakPlug.child ( 0 ); 698 MPlug positionTweakPlugY = positionTweakPlug.child ( 1 ); 699 MPlug positionTweakPlugZ = positionTweakPlug.child ( 2 ); 700 701 AnimationSampleCache* animCache = mDocumentExporter->getAnimationCache(); 702 bool xAnimated = AnimationHelper::isAnimated ( animCache, positionTweakPlugX ) != kISANIM_None; 703 bool yAnimated = AnimationHelper::isAnimated ( animCache, positionTweakPlugY ) != kISANIM_None; 704 bool zAnimated = AnimationHelper::isAnimated ( animCache, positionTweakPlugZ ) != kISANIM_None; 705 706 if ( !xAnimated && !yAnimated && !zAnimated ) continue; 707 708 uint logicalIndex = positionTweakPlug.logicalIndex(); 709 if ( logicalIndex >= vertexCount ) continue; 710 711 MPoint &pointData = vertexArray[logicalIndex]; 712 713 MObject vectorObject; 714 positionTweakPlug.getValue ( vectorObject ); 715 MFnNumericData data ( vectorObject ); 716 717 MPoint positionTweak; 718 data.getData ( positionTweak.x, positionTweak.y, positionTweak.z ); 719 720 pointData -= positionTweak; 721 vertexArray[logicalIndex] = pointData; 722 723 // Export the Point3 animation - note that this is a relative animation. 724 for ( uint k=0; k<3; ++k ) 725 { 726 MPlug childPlug = positionTweakPlug.child ( k ); 727 728 // FCDAnimated* animated = vertexSource->GetSourceData().GetAnimated(3 * logicalIndex + k); 729 // animated->SetRelativeAnimationFlag(); 730 // ANIM->AddPlugAnimation(positionTweakPlug.child(k), animated, kSingle | kLength); 731 732 // TODO Parameters??? TEST! 733 AnimationExporter* animExporter = mDocumentExporter->getAnimationExporter(); 734 animExporter->addPlugAnimation(childPlug, VERTEX_SID, kSingle | kLength, MEulerRotation::kXYZ, XYZW_PARAMETERS, true, -1, true); 735 } 736 } 737 738 for ( uint i = 0; i < vertexCount; ++i ) 739 { 740 // Convert the maya internal unit type from centimeters 741 // into the working units of the current scene! 742 MPoint &pointData = vertexArray[i]; 743 vertexSource.appendValues ( 744 COLLADABU::Math::Utils::equalsZero ( vertexArray[i].x, getTolerance () ) ? 0 : MDistance::internalToUI ( vertexArray[i].x ), 745 COLLADABU::Math::Utils::equalsZero ( vertexArray[i].y, getTolerance () ) ? 0 : MDistance::internalToUI ( vertexArray[i].y ), 746 COLLADABU::Math::Utils::equalsZero ( vertexArray[i].z, getTolerance () ) ? 0 : MDistance::internalToUI ( vertexArray[i].z ) ); 747 } 748 vertexSource.finish(); 749 750 // Add input to the mesh <vertices> node 751 mPolygonSources.push_back ( SourceInput ( vertexSource, COLLADASW::InputSemantic::VERTEX ) ); 752 } 753 754 //--------------------------------------------------------------- exportVertexNormals(MObject & mesh,const String & meshId)755 bool GeometryExporter::exportVertexNormals ( 756 MObject& mesh, 757 const String& meshId ) 758 { 759 if ( !ExportOptions::exportNormals () ) return false; 760 761 MFnMesh fnMesh(mesh); 762 763 // Export the normals 764 uint normalCount = fnMesh.numNormals (); 765 MFloatVectorArray normals ( normalCount ); 766 bool perVertexNormals = exportNormals ( mesh, meshId, normals ); 767 768 // Export the tangents 769 exportTangentsAndBinormals ( mesh, meshId, perVertexNormals, normals ); 770 771 return !perVertexNormals; 772 } 773 774 // ------------------------------------------------------- hasMissingVertexColor(const MObject & mesh,const MString & colorSetName)775 bool GeometryExporter::hasMissingVertexColor( 776 const MObject & mesh, 777 const MString & colorSetName 778 ) 779 { 780 MFnMesh fnMesh(mesh); 781 MItMeshPolygon iPolygon(mesh); 782 while (!iPolygon.isDone()) 783 { 784 unsigned int vertexCount = iPolygon.polygonVertexCount(); 785 for (unsigned iVertex = 0; iVertex < vertexCount; ++iVertex) 786 { 787 int colorIndex = -1; 788 fnMesh.getColorIndex(iPolygon.index(), iVertex, colorIndex, &colorSetName); 789 if (colorIndex == -1) 790 { 791 return true; 792 } 793 } 794 795 iPolygon.next(); 796 } 797 return false; 798 } 799 800 // ------------------------------------------------------- exportColorSets(const MObject & mesh,const String & meshId,MStringArray & colorSetNames)801 void GeometryExporter::exportColorSets ( 802 const MObject& mesh, 803 const String& meshId, 804 MStringArray& colorSetNames ) 805 { 806 if ( !ExportOptions::exportVertexColors() ) return; 807 808 MFnMesh fnMesh(mesh); 809 810 //MStringArray colorSetNames; 811 fnMesh.getColorSetNames ( colorSetNames ); 812 size_t numColorSets = colorSetNames.length (); 813 814 // Process the color sets 815 for ( unsigned int i=0; i<numColorSets; ++i ) 816 { 817 const MString mColorSetName = colorSetNames [i]; 818 String colorSetName = mColorSetName.asChar (); 819 if ( colorSetName.length() == 0 ) continue; 820 821 // Retrieve the color set data 822 MColorArray colorArray; 823 fnMesh.getColors ( colorArray, &mColorSetName ); 824 825 // Set a default color to vertices with no color. 826 bool missingVertexColor = hasMissingVertexColor(mesh, mColorSetName); 827 if (missingVertexColor) 828 { 829 const MColor defaultVertexColor(0.0f, 0.0f, 0.0f, 0.0f); 830 831 MGlobal::displayWarning( 832 MString("Mesh has vertices with invalid vertex color indices (") + 833 MString(meshId.c_str()) + 834 MString("). Using default color") + 835 " R=" + defaultVertexColor.r + 836 " G=" + defaultVertexColor.g + 837 " B=" + defaultVertexColor.b + 838 " A=" + defaultVertexColor.a 839 ); 840 841 colorArray.append(defaultVertexColor); 842 } 843 844 size_t numColorValues = colorArray.length (); 845 if ( numColorValues == 0 ) continue; 846 847 size_t stride = 4; 848 String colorId = meshId + "-" + colorSetName; 849 850 // Create the source 851 COLLADASW::FloatSourceF colorSource ( mSW ); 852 colorSource.setId ( colorId ); 853 colorSource.setNodeName ( colorId ); 854 colorSource.setArrayId ( colorId + ARRAY_ID_SUFFIX ); 855 colorSource.setAccessorStride ( ( unsigned long ) stride ); 856 colorSource.setAccessorCount ( ( unsigned long ) numColorValues ); 857 for ( size_t p=0; p<stride; ++p ) 858 colorSource.getParameterNameList().push_back ( RGBA_PARAMETERS[p] ); 859 colorSource.prepareToAppendValues(); 860 861 // Push the data from the colorSet into the color source of the collada document. 862 for ( unsigned int j=0; j<numColorValues; ++j ) 863 { 864 MColor color = colorArray[j]; 865 colorSource.appendValues ( color.r, color.g, color.b, color.a ); 866 } 867 868 colorSource.finish(); 869 870 // Don't put the source into the vertex sources, put it into the polygon sources. 871 // That's about the other plug-ins, they don't support this. 872 bool perVertexColor = false; 873 if ( ExportOptions::exportVertexColorsPerVertex() ) 874 { 875 uint verticesCount = ( uint ) fnMesh.numVertices(); 876 perVertexColor = ( numColorValues == verticesCount ); 877 } 878 879 if ( perVertexColor ) 880 { 881 // Insert a per-vertex color set input 882 mVertexSources.push_back ( SourceInput ( colorSource, COLLADASW::InputSemantic::COLOR, i ) ); 883 } 884 else 885 { 886 // Insert a per-face-vertex color set input 887 mPolygonSources.push_back ( SourceInput ( colorSource, COLLADASW::InputSemantic::COLOR, i ) ); 888 } 889 } 890 } 891 892 // -------------------------------------------------------------------- exportVertices(const String & meshId)893 void GeometryExporter::exportVertices ( const String& meshId ) 894 { 895 COLLADASW::VerticesElement vertices ( mSW ); 896 vertices.setId ( meshId + VERTICES_ID_SUFFIX ); 897 vertices.setNodeName ( meshId + VERTICES_ID_SUFFIX ); 898 899 // Get the input list 900 COLLADASW::InputList* inputList = &vertices.getInputList(); 901 902 // Always push the vertex positions in the vertices element 903 // (we have to create a vertices element with a reference) 904 inputList->push_back ( COLLADASW::Input ( COLLADASW::InputSemantic::POSITION, COLLADASW::URI ( EMPTY_STRING, meshId + POSITIONS_SOURCE_ID_SUFFIX ) ) ); 905 906 // Push all other vertex sources into the vertices element 907 Sources::iterator it = mVertexSources.begin(); 908 for ( ; it!=mVertexSources.end(); ++it ) 909 { 910 // Get the current vertices source and read the id 911 const SourceInput& sourceInput = *it; 912 const COLLADASW::SourceBase& source = sourceInput.getSource(); 913 String sourceId = source.getId(); 914 915 // Get the type of the current vertex source 916 const COLLADASW::InputSemantic::Semantics& type = sourceInput.getType(); 917 918 // Push the vertex source to the collada vertices 919 inputList->push_back ( COLLADASW::Input ( type, COLLADASW::URI ( EMPTY_STRING, sourceId ) ) ); 920 } 921 922 vertices.add(); 923 } 924 925 // -------------------------------------------------------------------- exportTextureCoords(const MObject & mesh,const String & meshId,const MStringArray & uvSetNames)926 void GeometryExporter::exportTextureCoords ( 927 const MObject& mesh, 928 const String& meshId, 929 const MStringArray& uvSetNames ) 930 { 931 if ( !ExportOptions::exportTexCoords() ) return; 932 933 MFnMesh fnMesh(mesh); 934 935 uint texCoordsCount = uvSetNames.length(); 936 for ( uint iTexCoords=0; iTexCoords<texCoordsCount; ++iTexCoords ) 937 { 938 MFloatArray uArray, vArray; 939 MString uvSetName = uvSetNames[iTexCoords]; 940 String uvSetNameStr = uvSetName.asChar(); 941 fnMesh.getUVs ( uArray, vArray, &uvSetName ); 942 uint uvCount = uArray.length(); 943 944 //@remark 945 //1. ignoring empty uvsets or uvsets with different uv-lengths 946 //2. [fixing issue63] also ignores invalid uvsets with same names 947 // cause uvSetNames-list contains all uvset names; but duplicated 948 // uvset-names are invalidated by getUVSetNames(..) 949 if ( uvCount == 0 || vArray.length() != uvCount ) 950 continue; 951 952 // Get the stride 953 uint stride = 2; 954 String texCoordId = meshId + "-" + uvSetNameStr; 955 956 COLLADASW::FloatSource texCoordSource ( mSW ); 957 texCoordSource.setId ( texCoordId ); 958 texCoordSource.setNodeName ( texCoordId ); 959 texCoordSource.setArrayId ( texCoordId + ARRAY_ID_SUFFIX ); 960 texCoordSource.setAccessorStride ( stride ); 961 texCoordSource.setAccessorCount ( uvCount ); 962 963 for ( uint i=0; i<stride; ++i ) 964 { 965 texCoordSource.getParameterNameList().push_back ( STPQ_PARAMETERS[i] ); 966 } 967 968 texCoordSource.prepareToAppendValues(); 969 970 for ( uint j = 0; j < uvCount; ++j ) 971 { 972 texCoordSource.appendValues ( 973 COLLADABU::Math::Utils::equalsZero ( uArray[j], getTolerance () ) ? 0 : uArray[j], 974 COLLADABU::Math::Utils::equalsZero ( vArray[j], getTolerance () ) ? 0 : vArray[j] ); 975 } 976 977 texCoordSource.finish(); 978 979 //@remark 980 // 1. getUVSetNames() retrieves the list by uvSetPlug.elementByPhysicalIndex ( .. ) 981 // -> so the index in the array is the 'realIndex' 982 // 2. [fixing issue63] uv-sets with same names are invalidated by getUVSetNames() adding suffix to maintain index in list 983 mPolygonSources.push_back ( SourceInput ( texCoordSource, COLLADASW::InputSemantic::TEXCOORD, iTexCoords ) ); 984 } 985 } 986 987 // -------------------------------------------------- endExport()988 void GeometryExporter::endExport() 989 { 990 closeLibrary(); 991 } 992 993 // -------------------------------------------------- exportNormals(const MObject & mesh,const String & meshId,MFloatVectorArray & normals)994 bool GeometryExporter::exportNormals( 995 const MObject & mesh, 996 const String &meshId, 997 MFloatVectorArray &normals ) 998 { 999 MFnMesh fnMesh(mesh); 1000 1001 uint normalCount = normals.length(); 1002 1003 // Implement NormalSource 1004 COLLADASW::FloatSource normalSource ( mSW ); 1005 normalSource.setId ( meshId + NORMALS_SOURCE_ID_SUFFIX ); 1006 normalSource.setNodeName ( meshId + NORMALS_SOURCE_ID_SUFFIX ); 1007 normalSource.setArrayId ( meshId + NORMALS_SOURCE_ID_SUFFIX + ARRAY_ID_SUFFIX ); 1008 normalSource.setAccessorStride ( 3 ); 1009 normalSource.setAccessorCount ( normalCount ); 1010 normalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[0] ); 1011 normalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[1] ); 1012 normalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[2] ); 1013 normalSource.prepareToAppendValues(); 1014 1015 uint verticesCount = ( uint ) fnMesh.numVertices(); 1016 1017 // Don't put the source into the vertex sources, put it into the polygon sources. 1018 // That's about the other plugins, they don't support this. 1019 bool perVertexNormals = false; 1020 if ( ExportOptions::exportNormalsPerVertex() ) 1021 { 1022 perVertexNormals = ( normalCount == verticesCount ); 1023 } 1024 1025 if ( perVertexNormals ) 1026 { 1027 // Get the unindexed normals in a separate buffer 1028 MFloatVectorArray unindexedNormals; 1029 fnMesh.getNormals ( unindexedNormals, MSpace::kObject ); 1030 1031 // Index the normals to match the vertex indices 1032 for ( MItMeshPolygon meshPolygonIter ( mesh ); 1033 !meshPolygonIter.isDone(); meshPolygonIter.next() ) 1034 { 1035 uint vertexCount = meshPolygonIter.polygonVertexCount(); 1036 for ( uint i = 0; i < vertexCount; ++i ) 1037 { 1038 int normalIndex = meshPolygonIter.normalIndex ( i ); 1039 int vertexIndex = meshPolygonIter.vertexIndex ( i ); 1040 normals[vertexIndex] = unindexedNormals[normalIndex]; 1041 } 1042 } 1043 1044 if ( !SourceInput::containsSourceBase ( &mVertexSources, &normalSource ) ) 1045 mVertexSources.push_back ( SourceInput ( normalSource, COLLADASW::InputSemantic::NORMAL ) ); 1046 } 1047 else 1048 { 1049 // Retrieve the per-face, per-vertex normals 1050 fnMesh.getNormals ( normals, MSpace::kObject ); 1051 1052 // Erase the normal source from the list of vertex sources, if it is inside 1053 mPolygonSources.push_back ( SourceInput ( normalSource, COLLADASW::InputSemantic::NORMAL ) ); 1054 SourceInput::eraseSourceBase ( &mVertexSources, &normalSource ); 1055 } 1056 1057 for ( uint i = 0; i < normalCount; ++i ) 1058 { 1059 MFloatVector &normal = normals[i]; 1060 normalSource.appendValues ( 1061 COLLADABU::Math::Utils::equalsZero ( normal.x, getTolerance () ) ? 0 : normal.x, 1062 COLLADABU::Math::Utils::equalsZero ( normal.y, getTolerance () ) ? 0 : normal.y, 1063 COLLADABU::Math::Utils::equalsZero ( normal.z, getTolerance () ) ? 0 : normal.z ); 1064 } 1065 1066 normalSource.finish(); 1067 1068 return perVertexNormals; 1069 } 1070 1071 // -------------------------------------------------------- exportTextureTangentsAndBinormals(const MObject & mesh,const String & meshId)1072 void GeometryExporter::exportTextureTangentsAndBinormals ( 1073 const MObject& mesh, 1074 const String& meshId ) 1075 { 1076 MFnMesh fnMesh(mesh); 1077 1078 // Get the names of the used uv sets. 1079 MStringArray uvSetNames; 1080 fnMesh.getUVSetNames ( uvSetNames ); 1081 unsigned int numUvSets = uvSetNames.length (); 1082 1083 // Write the texture tangents 1084 for ( unsigned int i=0; i<numUvSets; ++i ) 1085 { 1086 MString uvSetName = uvSetNames [i]; 1087 1088 // Texture Tangents 1089 MFloatVectorArray texTangents; 1090 fnMesh.getTangents ( texTangents, MSpace::kObject, &uvSetName ); 1091 1092 // Just export, if the tangents exist. 1093 unsigned int texTangentsCount = texTangents.length (); 1094 if ( texTangentsCount != 0 ) 1095 { 1096 COLLADASW::FloatSource texTangentSource ( mSW ); 1097 String texTangentSourceId = meshId + TEXTANGENT_SOURCE_ID_SUFFIX; 1098 if ( numUvSets > 1 ) texTangentSourceId += COLLADABU::Utils::toString (i+1); 1099 texTangentSource.setId ( texTangentSourceId ); 1100 texTangentSource.setArrayId ( texTangentSourceId + COLLADASW::LibraryGeometries::ARRAY_ID_SUFFIX ); 1101 texTangentSource.setAccessorStride ( 3 ); 1102 texTangentSource.setAccessorCount( (unsigned long)texTangentsCount ); 1103 1104 texTangentSource.getParameterNameList().push_back ( XYZW_PARAMETERS[0] ); 1105 texTangentSource.getParameterNameList().push_back ( XYZW_PARAMETERS[1] ); 1106 texTangentSource.getParameterNameList().push_back ( XYZW_PARAMETERS[2] ); 1107 1108 texTangentSource.prepareToAppendValues(); 1109 for ( unsigned int j=0; j<texTangentsCount; ++j ) 1110 { 1111 MFloatVector& texTangent = texTangents [j]; 1112 texTangentSource.appendValues ( 1113 COLLADABU::Math::Utils::equalsZero ( texTangent.x, getTolerance () ) ? 0.0 : texTangent.x, 1114 COLLADABU::Math::Utils::equalsZero ( texTangent.y, getTolerance () ) ? 0.0 : texTangent.y, 1115 COLLADABU::Math::Utils::equalsZero ( texTangent.z, getTolerance () ) ? 0.0 : texTangent.z ); 1116 } 1117 texTangentSource.finish(); 1118 1119 // Add input to the mesh polygon's node. 1120 mPolygonSources.push_back ( SourceInput ( texTangentSource, COLLADASW::InputSemantic::TEXTANGENT, (int) i ) ); 1121 } 1122 1123 // Texture Binormals 1124 MFloatVectorArray texBinormals; 1125 fnMesh.getBinormals ( texBinormals, MSpace::kObject, &uvSetName ); 1126 1127 // Just export, if the tangents exist. 1128 unsigned int texBinormalsCount = texBinormals.length (); 1129 if ( texBinormalsCount != 0 ) 1130 { 1131 COLLADASW::FloatSource texBinormalSource ( mSW ); 1132 String texBinormalSourceId = meshId + TEXBINORMAL_SOURCE_ID_SUFFIX; 1133 if ( numUvSets > 1 ) texBinormalSourceId += COLLADABU::Utils::toString (i+1); 1134 texBinormalSource.setId ( texBinormalSourceId ); 1135 texBinormalSource.setArrayId ( texBinormalSourceId + COLLADASW::LibraryGeometries::ARRAY_ID_SUFFIX ); 1136 texBinormalSource.setAccessorStride ( 3 ); 1137 texBinormalSource.setAccessorCount( (unsigned long)texBinormalsCount ); 1138 1139 texBinormalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[0] ); 1140 texBinormalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[1] ); 1141 texBinormalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[2] ); 1142 1143 texBinormalSource.prepareToAppendValues(); 1144 for ( unsigned int j=0; j<texBinormalsCount; ++j ) 1145 { 1146 MFloatVector& texBinormal = texBinormals [j]; 1147 texBinormalSource.appendValues ( 1148 COLLADABU::Math::Utils::equalsZero ( texBinormal.x, getTolerance () ) ? 0 : texBinormal.x, 1149 COLLADABU::Math::Utils::equalsZero ( texBinormal.y, getTolerance () ) ? 0 : texBinormal.y, 1150 COLLADABU::Math::Utils::equalsZero ( texBinormal.z, getTolerance () ) ? 0 : texBinormal.z ); 1151 } 1152 texBinormalSource.finish(); 1153 1154 // Add input to the mesh polygon's node. 1155 mPolygonSources.push_back ( SourceInput ( texBinormalSource, COLLADASW::InputSemantic::TEXBINORMAL, (int) i ) ); 1156 } 1157 } 1158 } 1159 1160 // -------------------------------------------------- exportTangentsAndBinormals(MObject & mesh,const String & meshId,const bool perVertexNormals,const MFloatVectorArray & normals)1161 void GeometryExporter::exportTangentsAndBinormals ( 1162 MObject& mesh, 1163 const String &meshId, 1164 const bool perVertexNormals, 1165 const MFloatVectorArray &normals ) 1166 { 1167 // Implement TangentSource and BinormalSource 1168 if ( ExportOptions::exportTangents() ) 1169 { 1170 MFnMesh fnMesh(mesh); 1171 1172 // Geo-tangent and -binormal 1173 COLLADASW::FloatSource tangentSource ( mSW ); 1174 COLLADASW::FloatSource binormalSource ( mSW ); 1175 1176 // Geo-tangent 1177 tangentSource.setId ( meshId + TANGENT_ID_SUFFIX ); 1178 tangentSource.setNodeName ( meshId + TANGENT_ID_SUFFIX ); 1179 tangentSource.setArrayId ( meshId + TANGENT_ID_SUFFIX + ARRAY_ID_SUFFIX ); 1180 tangentSource.setAccessorStride ( 3 ); 1181 tangentSource.getParameterNameList().push_back ( XYZW_PARAMETERS[0] ); 1182 tangentSource.getParameterNameList().push_back ( XYZW_PARAMETERS[1] ); 1183 tangentSource.getParameterNameList().push_back ( XYZW_PARAMETERS[2] ); 1184 1185 // Geo-binormal 1186 binormalSource.setId ( meshId + BINORMAL_ID_SUFFIX ); 1187 binormalSource.setNodeName ( meshId + BINORMAL_ID_SUFFIX ); 1188 binormalSource.setArrayId ( meshId + BINORMAL_ID_SUFFIX + ARRAY_ID_SUFFIX ); 1189 binormalSource.setAccessorStride ( 3 ); 1190 binormalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[0] ); 1191 binormalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[1] ); 1192 binormalSource.getParameterNameList().push_back ( XYZW_PARAMETERS[2] ); 1193 1194 uint normalCount = fnMesh.numNormals(); 1195 MVectorArray tangents ( normalCount ), binormals ( normalCount ); 1196 1197 if ( perVertexNormals ) 1198 { 1199 // Calculate the geometric tangents and binormals(T/Bs) 1200 getPerVertexNormalsTangents ( mesh, normals, tangents, binormals ); 1201 1202 if ( !SourceInput::containsSourceBase ( &mVertexSources, &tangentSource ) ) 1203 mVertexSources.push_back ( SourceInput ( tangentSource, COLLADASW::InputSemantic::TANGENT ) ); 1204 1205 if ( !SourceInput::containsSourceBase ( &mVertexSources, &binormalSource ) ) 1206 mVertexSources.push_back ( SourceInput ( binormalSource, COLLADASW::InputSemantic::BINORMAL ) ); 1207 } 1208 else 1209 { 1210 // Calculate the geometric tangents and binormals(T/Bs) 1211 getTangents ( mesh, normals, normalCount, binormals, tangents ); 1212 1213 // Erase the normal source from the list of vertex sources, if it is inside 1214 SourceInput::eraseSourceBase ( &mVertexSources, &tangentSource ); 1215 SourceInput::eraseSourceBase ( &mVertexSources, &binormalSource ); 1216 1217 // Push them in the polygon sources list. 1218 mPolygonSources.push_back ( SourceInput ( tangentSource, COLLADASW::InputSemantic::TANGENT ) ); 1219 mPolygonSources.push_back ( SourceInput ( binormalSource, COLLADASW::InputSemantic::BINORMAL ) ); 1220 } 1221 1222 // Geo-tangent 1223 uint tangentCount = tangents.length(); 1224 tangentSource.setAccessorCount ( tangentCount ); 1225 tangentSource.prepareToAppendValues(); 1226 for ( uint i = 0; i < tangentCount; ++i ) 1227 { 1228 MVector &tangent = tangents[i]; 1229 tangentSource.appendValues ( 1230 COLLADABU::Math::Utils::equalsZero ( tangent.x, getTolerance () ) ? 0 : tangent.x, 1231 COLLADABU::Math::Utils::equalsZero ( tangent.y, getTolerance () ) ? 0 : tangent.y, 1232 COLLADABU::Math::Utils::equalsZero ( tangent.z, getTolerance () ) ? 0 : tangent.z ); 1233 } 1234 tangentSource.finish(); 1235 1236 // Geo-binormal 1237 uint binormalCount = binormals.length(); 1238 binormalSource.setAccessorCount ( binormalCount ); 1239 binormalSource.prepareToAppendValues(); 1240 for ( uint i = 0; i < binormalCount; ++i ) 1241 { 1242 MVector &binormal = binormals[i]; 1243 binormalSource.appendValues ( 1244 COLLADABU::Math::Utils::equalsZero ( binormal.x, getTolerance () ) ? 0 : binormal.x, 1245 COLLADABU::Math::Utils::equalsZero ( binormal.y, getTolerance () ) ? 0 : binormal.y, 1246 COLLADABU::Math::Utils::equalsZero ( binormal.z, getTolerance () ) ? 0 : binormal.z ); 1247 } 1248 binormalSource.finish(); 1249 } 1250 } 1251 1252 // -------------------------------------------------- getPerVertexNormalsTangents(MObject & mesh,const MFloatVectorArray & normals,MVectorArray & tangents,MVectorArray & binormals)1253 void GeometryExporter::getPerVertexNormalsTangents( 1254 MObject & mesh, 1255 const MFloatVectorArray &normals, 1256 MVectorArray &tangents, 1257 MVectorArray &binormals ) 1258 { 1259 MFnMesh fnMesh(mesh); 1260 1261 // Calculate and export the geometric tangents and binormals(T/Bs) 1262 // Retrieve all the vertex positions for our calculations 1263 MPointArray vertexPositions; 1264 fnMesh.getPoints ( vertexPositions ); 1265 uint vertexCount = vertexPositions.length(); 1266 1267 for ( MItMeshVertex vertexIt ( mesh ); !vertexIt.isDone(); vertexIt.next() ) 1268 { 1269 MIntArray vertexNeighbors; 1270 int vertexIndex = vertexIt.index(); 1271 vertexIt.getConnectedVertices ( vertexNeighbors ); 1272 1273 if ( vertexNeighbors.length() == 0 || 1274 vertexNeighbors[0] >= ( int ) vertexCount || 1275 vertexIndex >= ( int ) vertexCount ) 1276 { 1277 tangents[vertexIndex] = MFloatVector::yAxis; 1278 binormals[vertexIndex] = MFloatVector::zAxis; 1279 continue; 1280 } 1281 1282 // Calculate the T/Bs (code repeated below) 1283 MPoint& neighborPosition = vertexPositions[vertexNeighbors[0]]; 1284 MVector directionV = neighborPosition - vertexPositions[vertexIndex]; 1285 tangents[vertexIndex] = ( directionV ^ normals[vertexIndex] ).normal(); 1286 binormals[vertexIndex] = ( normals[vertexIndex] ^ tangents[vertexIndex] ).normal(); 1287 } 1288 } 1289 1290 // -------------------------------------------------- getTangents(const MObject & mesh,const MFloatVectorArray & normals,uint normalCount,MVectorArray & binormals,MVectorArray & tangents)1291 void GeometryExporter::getTangents ( 1292 const MObject &mesh, 1293 const MFloatVectorArray &normals, 1294 uint normalCount, 1295 MVectorArray &binormals, 1296 MVectorArray &tangents ) 1297 { 1298 MFnMesh fnMesh(mesh); 1299 1300 // Retrieve all the vertex positions for our calculations 1301 MPointArray vertexPositions; 1302 fnMesh.getPoints ( vertexPositions ); 1303 uint vertexCount = vertexPositions.length(); 1304 for ( uint i = 0; i < normalCount; ++i ) 1305 { 1306 binormals[i] = tangents[i] = MFloatVector::zero; 1307 } 1308 1309 for ( MItMeshPolygon faceIt ( mesh ); !faceIt.isDone(); faceIt.next() ) 1310 { 1311 int faceVertexCount = faceIt.polygonVertexCount(); 1312 for ( int i = 0; i < faceVertexCount; ++i ) 1313 { 1314 int normalIndex = faceIt.normalIndex ( i ); 1315 if ( normalIndex >= ( int ) normalCount ) continue; 1316 1317 // Don't recalculate T/Bs 1318 if ( !tangents[normalIndex].isEquivalent ( MFloatVector::zero ) ) continue; 1319 1320 // Retrieve the vertex position and the position of its closest neighbor within the face 1321 int vertexIndex = faceIt.vertexIndex ( i ); 1322 int neighborVertexIndex = faceIt.vertexIndex ( ( i == 0 ) ? faceVertexCount - 1 : i - 1 ); 1323 if ( neighborVertexIndex >= ( int ) vertexCount || vertexIndex >= ( int ) vertexCount ) continue; 1324 1325 MPoint& vertexPosition = vertexPositions[vertexIndex]; 1326 MPoint& neighborPosition = vertexPositions[neighborVertexIndex]; 1327 1328 // Calculate the T/Bs (code repeated above) 1329 MFloatVector directionV = MFloatVector ( neighborPosition - vertexPosition ); 1330 1331 tangents[normalIndex] = ( directionV ^ normals[normalIndex] ).normal(); 1332 binormals[normalIndex] = ( normals[normalIndex] ^ tangents[normalIndex] ).normal(); 1333 } 1334 } 1335 } 1336 1337 // ------------------------------------ findColladaGeometryId(const String & mayaMeshId)1338 const String& GeometryExporter::findColladaGeometryId ( const String& mayaMeshId ) 1339 { 1340 const StringToStringMap::const_iterator it = mMayaIdColladaIdMap.find ( mayaMeshId ); 1341 if ( it != mMayaIdColladaIdMap.end () ) 1342 { 1343 return it->second; 1344 } 1345 return EMPTY_STRING; 1346 } 1347 1348 // ------------------------------------ handleControllers(SceneElement * sceneElement)1349 void GeometryExporter::handleControllers ( 1350 SceneElement* sceneElement ) 1351 { 1352 // Get the scene graph 1353 SceneGraph* sceneGraph = mDocumentExporter->getSceneGraph(); 1354 1355 // Get the current dag path 1356 MDagPath dagPath = sceneElement->getPath(); 1357 1358 // The stacks of the controllers for the affected nodes. 1359 ControllerStack stack; 1360 ControllerMeshStack meshStack; 1361 1362 // Iterate upstream finding all the nodes which affect the mesh. 1363 if ( !ControllerExporter::findAffectedNodes( dagPath.node(), stack, meshStack ) ) return; 1364 1365 // // Disable the blend shape influences. 1366 // ControllerExporter::disableBlendShape( stack ); 1367 // Disable any effects on the nodes. 1368 ControllerExporter::setControllerNodeStatesToNoEffect ( stack ); 1369 // Set all meshes as visible and not intermediate. 1370 ControllerExporter::setValidMeshParameters ( meshStack ); 1371 1372 // Export the element and push it in the exported scene graph. 1373 if ( exportGeometry ( sceneElement ) ) 1374 { 1375 sceneGraph->addExportedElement( sceneElement ); 1376 } 1377 1378 // Export the controllerStack items 1379 for ( size_t i=0; i<meshStack.size(); ++i ) 1380 { 1381 ControllerMeshItem item = meshStack[i]; 1382 MDagPath currentDagPath = MDagPath::getAPathTo ( item.mesh ); 1383 MFnDagNode dagFn ( currentDagPath ); 1384 bool isIntermediate = dagFn.isIntermediateObject(); 1385 String currentPath = currentDagPath.fullPathName().asChar(); 1386 1387 if ( sceneGraph->findExportedElement( currentDagPath ) == NULL ) 1388 { 1389 SceneElement* meshSceneElement = sceneGraph->findElement( currentDagPath ); 1390 if ( meshSceneElement != NULL && meshSceneElement->getHasJoint() ) 1391 { 1392 // Export the geometry 1393 if ( exportGeometry ( meshSceneElement ) ) 1394 { 1395 // Push it in the list of exported elements. 1396 sceneGraph->addExportedElement( meshSceneElement ); 1397 } 1398 } 1399 } 1400 } 1401 1402 // Reset all the controller node states. 1403 ControllerExporter::resetControllerNodeStates ( stack ); 1404 // // Enable the blend shape influences. 1405 // ControllerExporter::enableBlendShape ( stack ); 1406 // Reset the meshes as visible and not intermediate. 1407 ControllerExporter::resetMeshParameters ( meshStack ); 1408 // Delete the controller stack items and clear the stack. 1409 ControllerExporter::deleteControllerStackItems ( stack ); 1410 } 1411 1412 1413 // ------------------------------------ getColladaGeometryId(MDagPath dagPath)1414 const String& GeometryExporter::getColladaGeometryId ( MDagPath dagPath ) 1415 { 1416 // Get the node of the current mesh 1417 MObject meshNode = dagPath.node(); 1418 if ( !meshNode.hasFn ( MFn::kMesh ) ) 1419 { 1420 MGlobal::displayError ( "No mesh object!" ); 1421 return EMPTY_STRING; 1422 } 1423 1424 // Attach a function set to the mesh node. 1425 // We access all of the meshes data through the function set 1426 MStatus status; 1427 MFnMesh fnMesh ( meshNode, &status ); 1428 if ( status != MStatus::kSuccess ) 1429 { 1430 MGlobal::displayError ( "No mesh object!" ); 1431 return EMPTY_STRING; 1432 } 1433 1434 // Get the maya mesh id. 1435 String mayaMeshId = mDocumentExporter->dagPathToColladaId ( dagPath ); 1436 1437 // Check for instances. 1438 bool isInstanced = fnMesh.isInstanced ( true ); 1439 if ( isInstanced ) 1440 { 1441 // Take the first instance. 1442 MDagPathArray dagPathes; 1443 fnMesh.getAllPaths ( dagPathes ); 1444 mayaMeshId = mDocumentExporter->dagPathToColladaId ( dagPathes[0] ); 1445 } 1446 1447 // Get the geometry collada id. 1448 const String& colladaMeshId = findColladaGeometryId ( mayaMeshId ); 1449 1450 return colladaMeshId; 1451 } 1452 1453 }