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 }