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 "COLLADAMayaReferenceManager.h" 18 #include "COLLADAMayaExportOptions.h" 19 #include "COLLADAMayaDagHelper.h" 20 21 #include "maya/MFileIO.h" 22 #include "maya/MFnDagNode.h" 23 #include "maya/MFnTransform.h" 24 #if MAYA_API_VERSION >= 201400 25 #include "maya/MFnReference.h" 26 #endif 27 28 namespace COLLADAMaya 29 { 30 31 // -------------------------------------- ReferenceManager()32 ReferenceManager::ReferenceManager() 33 : mReferences (0) 34 , mFiles (0) 35 { 36 37 } 38 39 // -------------------------------------- ~ReferenceManager()40 ReferenceManager::~ReferenceManager() 41 { 42 deleteReferences(); 43 deleteFiles(); 44 } 45 46 // -------------------------------------- deleteReferences()47 void ReferenceManager::deleteReferences () 48 { 49 if ( mReferences.size() > 0 ) 50 { 51 for ( uint i=0; i<mReferences.size(); ++i ) 52 { 53 Reference* reference = mReferences[i]; 54 delete reference; 55 } 56 mReferences.clear(); 57 } 58 } 59 60 // -------------------------------------- deleteFiles()61 void ReferenceManager::deleteFiles () 62 { 63 if ( mFiles.size() > 0 ) 64 { 65 for ( uint i=0; i<mFiles.size(); ++i ) 66 { 67 ReferenceFile* file = mFiles[i]; 68 delete file; 69 } 70 mFiles.clear(); 71 } 72 } 73 74 // -------------------------------------- initialize()75 void ReferenceManager::initialize() 76 { 77 deleteReferences (); 78 deleteFiles (); 79 80 if ( !ExportOptions::exportXRefs() || ExportOptions::dereferenceXRefs() ) return; 81 82 #if MAYA_API_VERSION >= 600 83 84 MStatus status; 85 MStringArray referenceFilenames; 86 MFileIO::getReferences ( referenceFilenames ); 87 88 uint referenceCount = referenceFilenames.length(); 89 mReferences.reserve( referenceCount ); 90 for (uint i = 0; i < referenceCount; ++i) 91 { 92 MString& filename = referenceFilenames[i]; 93 MObject referenceNode = getReferenceNode ( filename ); 94 if ( referenceNode != MObject::kNullObj ) processReference ( referenceNode ); 95 } 96 #endif 97 } 98 99 // -------------------------------------- getReferenceNode(const MString & filename)100 MObject ReferenceManager::getReferenceNode ( const MString& filename ) 101 { 102 MString referenceNodeName; 103 MString command = MString ( "file -q -rfn \"" ) + filename + "\""; 104 MGlobal::executeCommand ( command, referenceNodeName ); 105 return DagHelper::getNode ( referenceNodeName ); 106 } 107 108 // -------------------------------------- processReference(const MObject & referenceNode)109 void ReferenceManager::processReference ( const MObject& referenceNode ) 110 { 111 MStatus status; 112 MFnDependencyNode referenceNodeFn ( referenceNode, &status ); 113 if (status != MStatus::kSuccess) return; 114 115 #if MAYA_API_VERSION >= 600 116 MString referenceNodeName = MFnDependencyNode( referenceNode ).name(); 117 118 Reference* reference = new Reference(); 119 reference->referenceNode = referenceNode; 120 mReferences.push_back ( reference ); 121 122 // Get the paths of the root transforms included in this reference 123 MObjectArray subReferences; 124 getRootObjects ( referenceNode, reference->paths, subReferences ); 125 uint pathCount = reference->paths.length(); 126 127 // Process the sub-references first 128 uint subReferenceCount = subReferences.length(); 129 for (uint i = 0; i < subReferenceCount; ++i) 130 { 131 MObject& subReference = subReferences[i]; 132 if ( subReference != MObject::kNullObj ) processReference ( subReference ); 133 } 134 135 // Retrieve the reference node's filename 136 MString command = MString("reference -rfn \"") + referenceNodeFn.name() + MString("\" -q -filename;"); 137 MString filename; 138 status = MGlobal::executeCommand ( command, filename ); 139 if (status != MStatus::kSuccess || filename.length() == 0) return; 140 141 // Strip the filename of the multiple file token 142 int stripIndex = filename.index('{'); 143 if (stripIndex != -1) filename = filename.substring(0, stripIndex - 1); 144 145 // Avoid transform look-ups on COLLADA references. 146 int extLocation = filename.rindex('.'); 147 if (extLocation > 0) 148 { 149 MString ext = filename.substring(extLocation + 1, filename.length() - 1).toLowerCase(); 150 if (ext == "dae" || ext == "xml") return; 151 } 152 153 // Check for already existing file information 154 // Otherwise create a new file information sheet with current node names 155 for ( ReferenceFileList::iterator it = mFiles.begin(); it != mFiles.end(); ++it ) 156 { 157 if ((*it)->filename == filename) 158 { 159 reference->file = (*it); 160 break; 161 } 162 } 163 164 if ( reference->file == NULL ) reference->file = processReferenceFile(filename); 165 166 // Get the list of the root transform's first child's unreferenced parents. 167 // This is a list of the imported nodes! 168 for (uint j = 0; j < pathCount; ++j) 169 { 170 MDagPath path = reference->paths[j]; 171 if (path.childCount() > 0) 172 { 173 path.push ( path.child(0) ); 174 MFnDagNode childNode ( path ); 175 if (!childNode.object().hasFn(MFn::kTransform)) continue; 176 177 uint parentCount = childNode.parentCount(); 178 for (uint k = 0; k < parentCount; ++k) 179 { 180 MFnDagNode parentNode(childNode.parent(k)); 181 if (parentNode.object() == MObject::kNullObj || parentNode.isFromReferencedFile()) continue; 182 183 MDagPath parentPath = MDagPath::getAPathTo(parentNode.object()); 184 if (parentPath.length() > 0) 185 { 186 ReferenceRootList::iterator it = 187 reference->reroots.insert( reference->reroots.end(), ReferenceRoot() ); 188 (*it).index = j; 189 (*it).reroot = parentPath; 190 } 191 } 192 } 193 } 194 #endif 195 } 196 197 // -------------------------------------- processReferenceFile(const MString & filename)198 ReferenceFile* ReferenceManager::processReferenceFile(const MString& filename) 199 { 200 ReferenceFile* file = new ReferenceFile(); 201 file->filename = filename; 202 mFiles.push_back(file); 203 204 #if MAYA_API_VERSION >= 800 205 return file; // Versions 8.00 and 8.50 of Maya don't allow us to create references inside a plug-in. 206 207 #elif MAYA_API_VERSION >= 600 208 209 // Get the original transformations for this file. 210 // 1. Create a new reference 211 MString tempFilename; 212 MObject tempReferenceNode; 213 214 { 215 MString command = MString("file -r -type \"COLLADA importer\" -namespace \"_TEMP_EXP_NAMESPACE\" \"") + filename + "\";"; 216 MGlobal::executeCommand(command, tempFilename); 217 218 tempFilename = getLastReferenceFilename ( tempFilename ); 219 220 MObject tempReferenceNode = getReferenceNode ( tempFilename ); 221 MString tempNodeName = MFnDependencyNode(tempReferenceNode).name(); 222 command = MString("file -loadReference \"") + tempNodeName + "\" \"" + tempFilename + "\";"; 223 MGlobal::executeCommand(command); 224 } 225 226 // 2. Get the original transformations for the root transforms of the temporary reference object 227 MDagPathArray tempRoots; 228 MObjectArray subReferences; 229 getRootObjects ( tempReferenceNode, tempRoots, subReferences ); 230 uint tempRootCount = tempRoots.length(); 231 for (uint j = 0; j < tempRootCount; ++j) 232 { 233 MFnTransform tempT(tempRoots[j]); 234 file->originalTransformations.push_back( tempT.transformation() ); 235 } 236 237 // 3. Get the original node names. This will be used as the URL for export 238 file->rootNames.setLength(tempRootCount); 239 for (uint j = 0; j < tempRootCount; ++j) 240 { 241 MString& originalName = file->rootNames[j]; 242 originalName = tempRoots[j].partialPathName(); 243 originalName = originalName.substring ( originalName.index(':') + 1, originalName.length() ); 244 } 245 246 // 4. Cleanup: remove this reference 247 MString command = MString("file -rr \"") + tempFilename + "\";"; 248 MGlobal::executeCommand ( command ); 249 250 #endif // MAYA >= 600 251 252 return file; 253 } 254 255 // -------------------------------------- getLastReferenceFilename(const MString & referenceResult)256 MString ReferenceManager::getLastReferenceFilename( const MString& referenceResult ) 257 { 258 #if MAYA_API_VERSION >= 650 259 return referenceResult; 260 #else 261 MStringArray filenames; 262 MGlobal::executeCommand("file -q -r;", filenames); 263 // TODO 264 // if ( ImportOptions::isOpenMode() ) 265 // { 266 // return (filenames.length() > 0) ? filenames[filenames.length() - 1] : ""; 267 // } 268 // else 269 { 270 return referenceResult; 271 } 272 #endif 273 } 274 275 // -------------------------------------- getRootObjects(const MObject & referenceNode,MDagPathArray & rootPaths,MObjectArray & subReferences)276 void ReferenceManager::getRootObjects( 277 const MObject& referenceNode, 278 MDagPathArray& rootPaths, 279 MObjectArray& subReferences) 280 { 281 rootPaths.clear(); 282 subReferences.clear(); 283 284 MFnDependencyNode referenceNodeFn(referenceNode); 285 286 // Get the paths of all the dag nodes included in this reference 287 MStringArray nodeNames; 288 MString command = MString("reference -rfn \"") + referenceNodeFn.name() + "\" -q -node -dp;"; 289 MGlobal::executeCommand(command, nodeNames); 290 291 uint nodeNameCount = nodeNames.length(); 292 MDagPathArray nodePaths; 293 for (uint j = 0; j < nodeNameCount; ++j) 294 { 295 MObject o = DagHelper::getNode(nodeNames[j]); 296 MDagPath p = DagHelper::getShortestDagPath(o); 297 if (p.length() > 0) 298 { 299 nodePaths.append(p); 300 } 301 else 302 { 303 if (o != MObject::kNullObj && o.apiType() == MFn::kReference 304 && strstr(nodeNames[j].asChar(), "_UNKNOWN_REF_NODE") == NULL) 305 { 306 subReferences.append(o); 307 } 308 } 309 } 310 311 // Keep only the root transform for the reference in our path arrays 312 uint nodePathCount = nodePaths.length(); 313 for (uint j = 0; j < nodePathCount; ++j) 314 { 315 const MDagPath& p = nodePaths[j]; 316 if ( !isRootTransform ( nodePaths, p ) ) continue; 317 rootPaths.append(p); 318 } 319 } 320 321 // -------------------------------------- isRootTransform(const MDagPathArray & allPaths,const MDagPath & testPath)322 bool ReferenceManager::isRootTransform ( const MDagPathArray& allPaths, const MDagPath& testPath ) 323 { 324 MStatus status; 325 MFnTransform transform(testPath, &status); 326 if (status != MStatus::kSuccess) return false; 327 uint pathCount = allPaths.length(); 328 329 uint parentCount = transform.parentCount(); 330 for (uint k = 0; k < parentCount; ++k) 331 { 332 MFnDependencyNode parentNode(transform.parent(k)); 333 if (!parentNode.isFromReferencedFile()) continue; 334 335 for (uint m = 0; m < pathCount; ++m) 336 { 337 if ( allPaths[m].node() == parentNode.object() ) return false; 338 } 339 } 340 341 return true; 342 } 343 344 // -------------------------------------- getReferenceFilename(const MDagPath & path)345 MString ReferenceManager::getReferenceFilename ( const MDagPath& path ) 346 { 347 MString command = MString("reference -q -f ") + path.fullPathName(); 348 MString filename; 349 MGlobal::executeCommand(command, filename); 350 return filename; 351 } 352 353 // -------------------------------------- getReferenceFilename(const MObject & dgNode)354 MString ReferenceManager::getReferenceFilename ( const MObject& dgNode ) 355 { 356 MString command = MString("reference -q -f ") + MFnDependencyNode(dgNode).name(); 357 MString filename; 358 MGlobal::executeCommand(command, filename); 359 return filename; 360 } 361 362 #if MAYA_API_VERSION < 201400 isIn(const MDagPath & dagPath,const MObject & referenceNode)363 bool isIn(const MDagPath & dagPath, const MObject & referenceNode) 364 { 365 MDagPathArray rootDagPaths; 366 MObjectArray subReferences; 367 ReferenceManager::getRootObjects(referenceNode, rootDagPaths, subReferences); 368 for (unsigned int i = 0; i < rootDagPaths.length(); ++i) { 369 if (rootDagPaths[i] == dagPath) { 370 return true; 371 } 372 } 373 for (unsigned int i = 0; i < subReferences.length(); ++i) { 374 if (isIn(dagPath, subReferences[i])) { 375 return true; 376 } 377 } 378 return false; 379 } 380 getTopLevelReferenceNode(const MDagPath & dagPath,MObject & outReferenceNode)381 MStatus ReferenceManager::getTopLevelReferenceNode(const MDagPath & dagPath, MObject & outReferenceNode) 382 { 383 MStringArray references; 384 MStatus status = MFileIO::getReferences(references); 385 unsigned int referenceCount = references.length(); 386 for (unsigned int referenceIndex = 0; referenceIndex < referenceCount; ++referenceIndex) 387 { 388 MString referenceFilename = references[referenceIndex]; 389 MObject referenceNode = ReferenceManager::getReferenceNode(referenceFilename); 390 391 if (isIn(dagPath, referenceNode)) { 392 outReferenceNode = referenceNode; 393 return MS::kSuccess; 394 } 395 } 396 return MS::kFailure; 397 } 398 #else //#if MAYA_API_VERSION < 201400 getTopLevelReferenceNode(const MDagPath & dagPath,MObject & outReferenceNode)399 MStatus ReferenceManager::getTopLevelReferenceNode(const MDagPath & dagPath, MObject & outReferenceNode) 400 { 401 MStatus status; 402 403 MFnDagNode fnDagNode(dagPath, &status); 404 if (!status) return status; 405 406 bool isFromReferencedFile = fnDagNode.isFromReferencedFile(&status); 407 if (!status) return status; 408 409 if (!isFromReferencedFile) { 410 // dagPath is not a reference 411 return MS::kFailure; 412 } 413 414 // Get full dag path 415 MString fullPathName; 416 fullPathName = dagPath.fullPathName(&status); 417 if (!status) return status; 418 419 // Get nearest reference node 420 MString RNPath; 421 MString command = MString("referenceQuery -referenceNode ") + fullPathName; 422 status = MGlobal::executeCommand(command, RNPath); 423 if (!status) return status; 424 425 MObject RN = DagHelper::getNode(RNPath); 426 if (RN.isNull()) return MS::kFailure; 427 428 MFnReference fnRN(RN, &status); 429 if (!status) return status; 430 431 // Get parent most reference node 432 MObject parentRN = fnRN.parentReference(&status); 433 if (!status) return status; 434 while (!parentRN.isNull()) 435 { 436 RN = parentRN; 437 438 status = fnRN.setObject(parentRN); 439 if (!status) return status; 440 441 parentRN = fnRN.parentReference(&status); 442 if (!status) return status; 443 } 444 445 outReferenceNode = RN; 446 return MS::kSuccess; 447 } 448 #endif //#if MAYA_API_VERSION < 201400 449 getReferenceFilename(const MObject & referenceNode,MString & referenceFilename)450 MStatus ReferenceManager::getReferenceFilename(const MObject & referenceNode, MString & referenceFilename) 451 { 452 MString command = MString("referenceQuery -filename ") + MFnDependencyNode(referenceNode).name(); 453 return MGlobal::executeCommand(command, referenceFilename); 454 } 455 }