unityCommands.mel 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  1. global string $UnityFbxFilePathAttr = "unityFbxModelFilePath";
  2. global string $UnityFbxFileNameAttr = "unityFbxModelFileName";
  3. global string $UnityFbxAnimFilePathAttr = "unityFbxAnimFilePath";
  4. global string $UnityFbxAnimFileNameAttr = "unityFbxAnimFileName";
  5. global string $UnityFbxStripNamespaceAttr = "unityFbxStripNamespace";
  6. global string $UnityFbxNamespaceAttr = "unityFbxStripSpecificNamespace";
  7. global string $UnityFbxNamespaceAttrNiceName = "Strip Specific Namespace";
  8. global string $UnityExportSetNameFormat = "^1s_UnityExportSet";
  9. global string $UnityModuleName = "UnityFbxForMaya";
  10. global string $UnityImportSettingsOptionVar = "UnityFbxImportSettings";
  11. global string $UnityExportSettingsOptionVar = "UnityFbxExportSettings";
  12. global string $UnityDefaultImportSettingsFileName = "unityFbxImportSettings.mel";
  13. global string $UnityDefaultExportSettingsFileName = "unityFbxExportSettings.mel";
  14. global string $UnityDefaultImportSettings = "\
  15. FBXResetImport;\n\
  16. FBXImportAxisConversionEnable -v true;\n\
  17. FBXImportCameras -v true;\n\
  18. FBXImportLights -v true;\n\
  19. FBXImportSetTake -takeIndex -1;\n\
  20. // Add and update animation\n\
  21. FBXImportMode -v merge;";
  22. global string $UnityDefaultExportSettings = "\
  23. FBXResetExport;\n\
  24. // FBX file format\n\
  25. FBXExportInAscii -v false;\n\
  26. FBXExportFileVersion -v FBX201600;\n\
  27. \n\
  28. // Geometry\n\
  29. FBXExportSmoothMesh -v false;\n\
  30. FBXExportInstances -v true;\n\
  31. FBXExportReferencedAssetsContent -v false;\n\
  32. \n\
  33. // Animation\n\
  34. FBXExportAnimationOnly -v false;\n\
  35. \n\
  36. FBXExportCameras -v true;\n\
  37. FBXExportLights -v true;\n\
  38. \n\
  39. FBXExportEmbeddedTextures -v false;\n\
  40. \n\
  41. // Units\n\
  42. FBXExportScaleFactor 1;\n\
  43. FBXExportConvertUnitString cm;\n\
  44. \n\
  45. // Axis Conversion\n\
  46. FBXExportUpAxis y;";
  47. global int $UnityFbxFilePathIndex = 0;
  48. global int $UnityFbxFileNameIndex = 1;
  49. global int $UnityFbxAnimFilePathIndex = 2;
  50. global int $UnityFbxAnimFileNameIndex = 3;
  51. global int $UnityFileNameWithoutExtIndex = 4;
  52. /* Enum */
  53. global int $UnityExportAnim = 0;
  54. global int $UnityExportModel = 1;
  55. global int $UnityExportModelAnim = 2;
  56. global proc unityRemoveNativeMenuOnLoad(){
  57. $removeSendToUnityMenu = `optionVar -q "UnityFbxForMaya_removeSendToUnityMenu"`;
  58. if($removeSendToUnityMenu && `menu -exists "sendToUnityMenu"`){
  59. //Remove the GamePipeline 'SendToUnity' button
  60. menu -e -visible false "sendToUnityMenu";
  61. }
  62. }
  63. // Load a specified settings file
  64. proc int loadUnityFbxSettings(string $settingType, string $optionVarName, string $defaultSettingsFileName, string $defaultSettings){
  65. global string $UnityModuleName;
  66. $fileName = `optionVar -q $optionVarName`;
  67. // if no filename set (optionVar cleared), reset it to default filename
  68. if ($fileName == 0){
  69. $modulePath = `moduleInfo -moduleName $UnityModuleName -path`;
  70. // {$modulePath}/scripts/{$defaultSettingsFileName}
  71. $modulePath = $modulePath + "/scripts/" + $defaultSettingsFileName;
  72. $fileName = $modulePath;
  73. optionVar -stringValue $optionVarName $fileName;
  74. }
  75. // check if the file exists
  76. if (`file -q -ex $fileName` == false){
  77. // create file with default settings
  78. $fileId = `fopen $fileName "w"`;
  79. fprint $fileId $defaultSettings;
  80. fclose $fileId;
  81. }
  82. // if the file still doesn't exist (failed to create)
  83. // load the default settings and print a warning
  84. if (`file -q -ex $fileName` == false){
  85. warning ("Failed to find Unity Fbx "+$settingType+" Settings at: " + $fileName + ", loading default settings.");
  86. eval ($defaultSettings);
  87. return true;
  88. }
  89. eval ("source \"" + $fileName + "\"");
  90. return true;
  91. }
  92. // Load the Export Settings from file
  93. proc int loadUnityFbxExportSettings(){
  94. global string $UnityExportSettingsOptionVar;
  95. global string $UnityDefaultExportSettings;
  96. global string $UnityDefaultExportSettingsFileName;
  97. return loadUnityFbxSettings(
  98. "Export",
  99. $UnityExportSettingsOptionVar,
  100. $UnityDefaultExportSettingsFileName,
  101. $UnityDefaultExportSettings
  102. );
  103. }
  104. // Load the Import Settings from a file
  105. proc int loadUnityFbxImportSettings(){
  106. global string $UnityImportSettingsOptionVar;
  107. global string $UnityDefaultImportSettings;
  108. global string $UnityDefaultImportSettingsFileName;
  109. return loadUnityFbxSettings(
  110. "Import",
  111. $UnityImportSettingsOptionVar,
  112. $UnityDefaultImportSettingsFileName,
  113. $UnityDefaultImportSettings
  114. );
  115. }
  116. proc string getAttribute(string $node, string $attr){
  117. if (`attributeExists $attr $node`){
  118. return `getAttr ($node + "." + $attr)`;
  119. }
  120. return "";
  121. }
  122. proc int getBoolAttribute(string $node, string $attr){
  123. if (`attributeExists $attr $node`){
  124. return `getAttr ($node + "." + $attr)`;
  125. }
  126. return 0;
  127. }
  128. proc storeBoolAttribute(string $node, string $attr, int $attrValue){
  129. if (!attributeExists($attr, $node)){
  130. addAttr -shortName $attr -storable true -attributeType bool $node;
  131. }
  132. setAttr ($node+"."+$attr) $attrValue;
  133. }
  134. proc storeStringAttribute(string $node, string $attr, string $attrValue, string $niceName){
  135. $attrType = "string";
  136. if (!attributeExists($attr, $node)){
  137. if ($niceName != ""){
  138. addAttr -shortName $attr -niceName $niceName -storable true -dataType $attrType $node;
  139. }
  140. else {
  141. addAttr -shortName $attr -storable true -dataType $attrType $node;
  142. }
  143. }
  144. setAttr ($node+"."+$attr) -type $attrType $attrValue;
  145. }
  146. proc int setExists(string $setName){
  147. return stringArrayContains($setName, `ls -sets`);
  148. }
  149. proc int loadUnityPlugin(string $plugin){
  150. if (`pluginInfo -q -loaded $plugin` == false){
  151. loadPlugin $plugin;
  152. if (`pluginInfo -q -loaded $plugin` == false){
  153. return false;
  154. }
  155. }
  156. return true;
  157. };
  158. // show a yes/no style dialog, return true if user clicked confirm, false if user canceled
  159. proc int showConfirmDialog(string $title, string $message, string $confirmButtonName, string $cancelButtonName){
  160. // create a confirm dialog with a yes and no button. Specif
  161. $response = `confirmDialog -title $title
  162. -message $message
  163. -button $confirmButtonName
  164. -button $cancelButtonName
  165. -defaultButton $confirmButtonName
  166. -cancelButton $cancelButtonName
  167. -dismissString $cancelButtonName`;
  168. return ( $response == $confirmButtonName );
  169. }
  170. // get the namespace of the given object
  171. proc string getObjectNamespace(string $objectName){
  172. string $lsResult[] = `ls -showNamespace -an $objectName`;
  173. return $lsResult[1];
  174. }
  175. // =======================
  176. // Determine the export attributes to be used by the export set for the given export path.
  177. // If animation only, check for {model}@{animation}.fbx naming convention and set the name to be used for the export set and namespace (filename without ext)
  178. // to be {model} so that if {model}.fbx has already been imported, the animation is applied.
  179. //
  180. // returns an array of export attributes:
  181. // export file path, file name, animation file path, animation file name, and filename without ext (for the export set and namespace names)
  182. proc string[] getExportSetAttributes(string $exportPath, int $exportAnimOnly){
  183. global int $UnityFbxFilePathIndex;
  184. global int $UnityFbxFileNameIndex;
  185. global int $UnityFbxAnimFilePathIndex;
  186. global int $UnityFbxAnimFileNameIndex;
  187. global int $UnityFileNameWithoutExtIndex;
  188. string $exportAttributes[5];
  189. $exportDir = dirname($exportPath);
  190. $exportFileName = basename($exportPath, "");
  191. $exportAnimDir = $exportDir;
  192. $fileNameWithoutExt = basename($exportPath, ".fbx");
  193. $exportAnimFileName = ($fileNameWithoutExt + "@Take1.fbx");
  194. if($exportAnimOnly){
  195. // import as animation
  196. $exportAnimFileName = $exportFileName;
  197. if(match("@", basename($exportPath, ".fbx")) != ""){
  198. $fileNameWithoutExt = match("[^@]+", $fileNameWithoutExt);
  199. }
  200. }
  201. $fileNameWithoutExt = formValidObjectName($fileNameWithoutExt);
  202. $exportAttributes[$UnityFbxFilePathIndex] = $exportDir;
  203. $exportAttributes[$UnityFbxFileNameIndex] = $exportFileName;
  204. $exportAttributes[$UnityFbxAnimFilePathIndex] = $exportAnimDir;
  205. $exportAttributes[$UnityFbxAnimFileNameIndex] = $exportAnimFileName;
  206. $exportAttributes[$UnityFileNameWithoutExtIndex] = $fileNameWithoutExt;
  207. return $exportAttributes;
  208. }
  209. // Get export set name with format "{$fileNameWithoutExt}_UnityExportSet"
  210. proc string getNewExportSetName(string $fileNameWithoutExt){
  211. global string $UnityExportSetNameFormat;
  212. return `format -stringArg $fileNameWithoutExt $UnityExportSetNameFormat`;
  213. }
  214. // Get the name of the namespace to add contents of fbx into.
  215. // Namespace name is {currentNamespace}:{$fileNameWithoutExt} or :{$fileNameWithoutExt}
  216. // if current namespace is root namespace.
  217. proc string getTargetNamespaceName(string $fileNameWithoutExt){
  218. string $origNamespace = `namespaceInfo -cur -an`;
  219. string $targetNamespace = ":" + $fileNameWithoutExt;
  220. // make sure there are no duplicate colons in namespace name
  221. if($origNamespace != ":"){
  222. $targetNamespace = `format -s $origNamespace -s $fileNameWithoutExt "^1s:^2s"`;
  223. }
  224. return $targetNamespace;
  225. }
  226. // Get or create the export set in the root namespace.
  227. // Return true if a set has been created, and false if it already exists.
  228. proc int getOrCreateExportSet(string $unityExportSet, string $origNamespace){
  229. if (setExists($unityExportSet)){
  230. return false;
  231. }
  232. if(!`namespaceInfo -isRootNamespace $origNamespace`){
  233. namespace -set ":";
  234. }
  235. // if a set is selected when creating a new set, then
  236. // the selected set will be added into the new set.
  237. // avoid this by temporarily deselecting everything.
  238. $origSelection = `ls -sl`;
  239. select -clear;
  240. // couldn't find export set so create it
  241. sets -name $unityExportSet;
  242. if(size($origSelection) > 0){
  243. select -r $origSelection;
  244. }
  245. return true;
  246. }
  247. global proc unityOnToggleStripNamespace(string $exportSetName)
  248. {
  249. global string $UnityFbxStripNamespaceAttr;
  250. global string $UnityFbxNamespaceAttr;
  251. int $stripNamespaces = `getAttr ($exportSetName + "." + $UnityFbxStripNamespaceAttr)`;
  252. $stripNamespaceAttrName = ($exportSetName + "." + $UnityFbxNamespaceAttr);
  253. // temporarily unlock to be able to modify namespace attr
  254. lockNode -lock false $exportSetName;
  255. setAttr -lock (!$stripNamespaces) $stripNamespaceAttrName;
  256. // lock set so it doesn't get deleted when empty
  257. lockNode -lock true $exportSetName;
  258. }
  259. // Add or update the following five attributes of the given export set, to be used for exporting:
  260. // - export directory
  261. // - export file name
  262. // - export animation directory
  263. // - export animation file name
  264. // - target namespace name (namespace that the contents of set belong to)
  265. proc setExportSetAttributes(
  266. string $unityExportSet, int $isAnimFile, int $setCreated,
  267. string $exportAttrs[], int $stripNamespaces
  268. ){
  269. global string $UnityFbxFilePathAttr;
  270. global string $UnityFbxFileNameAttr;
  271. global string $UnityFbxAnimFilePathAttr;
  272. global string $UnityFbxAnimFileNameAttr;
  273. global string $UnityFbxStripNamespaceAttr;
  274. global string $UnityFbxNamespaceAttr;
  275. global string $UnityFbxNamespaceAttrNiceName;
  276. global int $UnityFbxFilePathIndex;
  277. global int $UnityFbxFileNameIndex;
  278. global int $UnityFbxAnimFilePathIndex;
  279. global int $UnityFbxAnimFileNameIndex;
  280. global int $UnityFileNameWithoutExtIndex;
  281. $exportDir = $exportAttrs[$UnityFbxFilePathIndex];
  282. $exportFileName = $exportAttrs[$UnityFbxFileNameIndex];
  283. $exportAnimDir = $exportAttrs[$UnityFbxAnimFilePathIndex];
  284. $exportAnimFileName = $exportAttrs[$UnityFbxAnimFileNameIndex];
  285. $fileNameWithoutExt = $exportAttrs[$UnityFileNameWithoutExtIndex];
  286. // unlock set so we can add attributes to it
  287. lockNode -lock false $unityExportSet;
  288. if ((!$isAnimFile || ($isAnimFile && $setCreated)) && $exportDir != ""){
  289. storeStringAttribute($unityExportSet, $UnityFbxFilePathAttr, $exportDir, "");
  290. }
  291. if ((!$isAnimFile || ($isAnimFile && $setCreated)) && $exportFileName != ""){
  292. storeStringAttribute($unityExportSet,$UnityFbxFileNameAttr,$exportFileName, "");
  293. }
  294. if($exportAnimDir != ""){
  295. storeStringAttribute($unityExportSet,$UnityFbxAnimFilePathAttr,$exportAnimDir, "");
  296. }
  297. if($exportAnimFileName != ""){
  298. storeStringAttribute($unityExportSet,$UnityFbxAnimFileNameAttr,$exportAnimFileName, "");
  299. }
  300. storeBoolAttribute($unityExportSet, $UnityFbxStripNamespaceAttr, $stripNamespaces);
  301. storeStringAttribute($unityExportSet, $UnityFbxNamespaceAttr, "", $UnityFbxNamespaceAttrNiceName);
  302. $stripNamespaceAttrName = ($unityExportSet + "." + $UnityFbxStripNamespaceAttr);
  303. setAttr -lock (!$stripNamespaces) $stripNamespaceAttrName;
  304. scriptJob -attributeChange $stripNamespaceAttrName ("unityOnToggleStripNamespace " + $unityExportSet) -protected;
  305. // lock set so it doesn't get deleted when empty
  306. lockNode -lock true $unityExportSet;
  307. }
  308. proc switchUnityProject(string $newProjectPath){
  309. $currentDir = dirname($newProjectPath);
  310. // Change Unity project if fbx is from a different Unity project.
  311. // Get the project based on the folder structure (i.e. folder above Assets)
  312. $head = dirname($currentDir);
  313. $tail = basename($currentDir, "");
  314. // Check that we are not at the root directory.
  315. // dirname($head) returns the last directory name in the path,
  316. // or head if head is the root directory.
  317. while ($head != "" && dirname($head) != $head){
  318. if (`strcmp $tail "Assets"` == 0){
  319. // this is a valid Unity project, so set it
  320. optionVar -sv "UnityProject" $head;
  321. break;
  322. }
  323. $tail = basename($head, "");
  324. $head = dirname($head);
  325. }
  326. }
  327. // =======================
  328. proc importFile(string $filePathStr){
  329. // get the global variables
  330. global string $UnityFbxFilePathAttr;
  331. global string $UnityFbxFileNameAttr;
  332. global string $UnityFbxAnimFilePathAttr;
  333. global string $UnityFbxAnimFileNameAttr;
  334. global int $UnityFbxFilePathIndex;
  335. global int $UnityFbxFileNameIndex;
  336. global int $UnityFbxAnimFilePathIndex;
  337. global int $UnityFbxAnimFileNameIndex;
  338. global int $UnityFileNameWithoutExtIndex;
  339. $isAnimFile = false;
  340. if(match("@", basename($filePathStr, ".fbx")) != ""){
  341. // import as animation
  342. $isAnimFile = true;
  343. }
  344. $exportAttrs = getExportSetAttributes($filePathStr, $isAnimFile);
  345. $currentDir = $exportAttrs[$UnityFbxFilePathIndex];
  346. $fileName = $exportAttrs[$UnityFbxFileNameIndex];
  347. $currentAnimDir = $exportAttrs[$UnityFbxAnimFilePathIndex];
  348. $animFileName = $exportAttrs[$UnityFbxAnimFileNameIndex];
  349. $fileNameWithoutExt = $exportAttrs[$UnityFileNameWithoutExtIndex];
  350. $unityExportSet = getNewExportSetName($fileNameWithoutExt);
  351. string $origNamespace = `namespaceInfo -cur -an`;
  352. string $targetNamespace = getTargetNamespaceName($fileNameWithoutExt);
  353. // warn if namespace already exists
  354. if(`namespace -exists $targetNamespace`){
  355. if(!showConfirmDialog("Warning: " + $fileName,
  356. $targetNamespace + " namespace already exists, the imported objects will be added to the existing namespace and export set.",
  357. "Continue", "Cancel"
  358. )){
  359. // cancelled, don't import this fbx
  360. return;
  361. }
  362. }
  363. else{
  364. namespace -add $targetNamespace;
  365. }
  366. // Gather everything that is in the scene
  367. $origItemsInScene = `ls -tr -o -r true`;
  368. // Get or create the Unity Fbx Export Set
  369. $setCreated = getOrCreateExportSet($unityExportSet, $origNamespace);
  370. // unlock set so we can add attributes to it
  371. lockNode -lock false $unityExportSet;
  372. if(!$isAnimFile){
  373. // reset attribute values, in case import fails
  374. storeStringAttribute($unityExportSet, $UnityFbxFilePathAttr, "", "");
  375. storeStringAttribute($unityExportSet, $UnityFbxFileNameAttr, "", "");
  376. }
  377. if(`namespaceInfo -cur -an` != $targetNamespace){
  378. namespace -set $targetNamespace;
  379. }
  380. file -import -type "FBX" -ignoreVersion -ra true -mergeNamespacesOnClash true -pr -importFrameRate true -importTimeRange "override" $filePathStr;
  381. if(`namespaceInfo -cur -an` != $origNamespace){
  382. namespace -set $origNamespace;
  383. }
  384. setExportSetAttributes($unityExportSet, $isAnimFile, $setCreated, $exportAttrs, /*strip namespaces*/ true);
  385. if (setExists($unityExportSet) == true){
  386. // figure out what has been added after import
  387. $itemsInScene = `ls -tr -o -r true`;
  388. $newItems = stringArrayRemove($origItemsInScene, $itemsInScene);
  389. // add newly imported items to set
  390. if (size($newItems) > 0){
  391. sets -include $unityExportSet $newItems;
  392. }
  393. }
  394. }
  395. global proc int loadUnityDependencies(){
  396. // GamePipeline plugin 'SendToUnitySelection' command used in export
  397. $pluginsToLoad = {"GamePipeline", "fbxmaya"};
  398. $ext = "mll";
  399. if (`about -macOS` == true){
  400. $ext = "bundle";
  401. }
  402. // iterate over all the plugins, loading them with extenstion ext, and combining the results
  403. // to return if any of the loads failed
  404. $result = true;
  405. for($plugin in $pluginsToLoad){
  406. $result = $result && `loadUnityPlugin ($plugin + "." + $ext)`;
  407. }
  408. unityRemoveNativeMenuOnLoad();
  409. return $result;
  410. }
  411. global proc unityImport(){
  412. if(!loadUnityDependencies()){
  413. error("Failed to load Unity dependencies");
  414. return;
  415. }
  416. if(!loadUnityFbxImportSettings()){
  417. return;
  418. }
  419. $unityProject = `optionVar -q "UnityProject"`;
  420. $filePaths = `fileDialog2 -dialogStyle 2 -caption "FBX Import" -dir ($unityProject + "/Assets") -fileFilter "*.fbx" -selectFileFilter "FBX" -fileMode 4`;
  421. // store path and filename
  422. if(size($filePaths) <= 0){
  423. return;
  424. }
  425. for($i=0; $i<size($filePaths); ++$i){
  426. $filePathStr = $filePaths[$i];
  427. importFile($filePathStr);
  428. }
  429. // switch project if file imported from a different Unity project
  430. switchUnityProject($filePaths[0]);
  431. }
  432. // returns the intersection of two string arrays
  433. proc string[] getIntersection(string $set1[], string $set2[]){
  434. string $myIntersector = `stringArrayIntersector`;
  435. stringArrayIntersector -edit -reset $myIntersector;
  436. stringArrayIntersector -edit -intersect $set1 $myIntersector;
  437. stringArrayIntersector -edit -intersect $set2 $myIntersector;
  438. string $intersection[] = `stringArrayIntersector -query $myIntersector`;
  439. // Delete the intersector
  440. deleteUI $myIntersector;
  441. return $intersection;
  442. }
  443. // Find the most common namespace among the selected objects. If there are multiple (because of nested namespaces),
  444. // prefer the most specific. For example if you have:
  445. //
  446. // test1:test2:sphere
  447. // test1:test3:cube
  448. // test1:test2:cylinder
  449. //
  450. // test1 will be returned because all objects have that namespace in common. However if you have:
  451. //
  452. // test1:test2:sphere
  453. // test1:test2:cube
  454. // test1:test2:cylinder
  455. //
  456. // Then it will return test1:test2.
  457. proc string getCommonNamespace(string $origSelection[]){
  458. // gather up all the unique namespaces
  459. string $selectedNamespaces[];
  460. for($i = 0; $i < size($origSelection); $i++){
  461. string $currNamespace = getObjectNamespace($origSelection[$i]);
  462. while ($currNamespace != ":" && !stringArrayContains($currNamespace, $selectedNamespaces)){
  463. $selectedNamespaces[size($selectedNamespaces)] = $currNamespace;
  464. $currNamespace = `namespaceInfo -p $currNamespace`;
  465. // make sure the namespace always starts with a colon
  466. if(!startsWith($currNamespace, ":")){
  467. $currNamespace = ":" + $currNamespace;
  468. }
  469. }
  470. }
  471. // go through selection to find common namespace to set as default
  472. string $commonNamespace = ":";
  473. int $maxNamespaceCount = 0;
  474. for($i = 0; $i < size($selectedNamespaces); $i++){
  475. $currNamespace = $selectedNamespaces[$i];
  476. // get contents of namespace
  477. string $namespaceContents[] = `namespaceInfo -ls $currNamespace -r`;
  478. string $intersection[] = getIntersection($origSelection, $namespaceContents);
  479. int $intersectionSize = size($intersection);
  480. if($intersectionSize > $maxNamespaceCount ||
  481. // prefer more specific namespaces
  482. ($maxNamespaceCount > 0 &&
  483. $intersectionSize == $maxNamespaceCount &&
  484. size($currNamespace) > size($commonNamespace)))
  485. {
  486. $commonNamespace = $currNamespace;
  487. $maxNamespaceCount = $intersectionSize;
  488. }
  489. }
  490. return $commonNamespace;
  491. }
  492. proc exportSet(string $unitySet, int $exportAnim){
  493. global string $UnityFbxFilePathAttr;
  494. global string $UnityFbxFileNameAttr;
  495. global string $UnityFbxAnimFilePathAttr;
  496. global string $UnityFbxAnimFileNameAttr;
  497. global string $UnityFbxStripNamespaceAttr;
  498. global string $UnityFbxNamespaceAttr;
  499. string $unitySetContents[] = `listConnections $unitySet`;
  500. int $stripNamespaces = getBoolAttribute($unitySet, $UnityFbxStripNamespaceAttr);
  501. string $namespaceToStrip = getAttribute($unitySet, $UnityFbxNamespaceAttr);
  502. // if there is no namespace manually set, find the common namespace
  503. if ($stripNamespaces){
  504. if ($namespaceToStrip == ""){
  505. $namespaceToStrip = getCommonNamespace($unitySetContents);
  506. }
  507. else {
  508. // make sure the namespace to strip is an absolute namespace
  509. if(!startsWith($namespaceToStrip, ":")){
  510. $namespaceToStrip = ":" + $namespaceToStrip;
  511. }
  512. }
  513. }
  514. string $animatedObjectSet = "";
  515. if($exportAnim){
  516. string $animCurveSelect[] = `ls -typ animCurve`;
  517. string $animatedTransforms[] = `listConnections -t transform $animCurveSelect`;
  518. string $setAnimatedTransforms[] = getIntersection($animatedTransforms, $unitySetContents);
  519. select -r $setAnimatedTransforms;
  520. $animatedObjectSet = `sets`;
  521. select -r -ne $animatedObjectSet;
  522. }
  523. else{
  524. select -r -ne $unitySet;
  525. }
  526. $pathAttr = $UnityFbxFilePathAttr;
  527. $nameAttr = $UnityFbxFileNameAttr;
  528. if($exportAnim){
  529. $pathAttr = $UnityFbxAnimFilePathAttr;
  530. $nameAttr = $UnityFbxAnimFileNameAttr;
  531. }
  532. string $unityFbxFilePath = getAttribute($unitySet, $pathAttr);
  533. string $unityFbxFileName = getAttribute($unitySet, $nameAttr);
  534. // make sure the file path exists
  535. if(!(`filetest -d $unityFbxFilePath`)){
  536. sysFile -makeDir $unityFbxFilePath;
  537. }
  538. $strCmd = "";
  539. if ($unityFbxFilePath != "" && $unityFbxFileName != ""){
  540. // export selected, relative to given namespace
  541. string $exportFormat = "file -force -options \"\" -typ \"FBX export\" -relativeNamespace \"^1s\" -es \"^2s/^3s\"";
  542. string $relativeNamespace = ":";
  543. if ($stripNamespaces){
  544. $relativeNamespace = $namespaceToStrip;
  545. }
  546. $strCmd = `format -s $relativeNamespace -s $unityFbxFilePath -s $unityFbxFileName $exportFormat`;
  547. eval $strCmd;
  548. }
  549. if(`objExists $animatedObjectSet`){
  550. delete $animatedObjectSet;
  551. }
  552. }
  553. proc int isUnityExportSet(string $mayaSet){
  554. global string $UnityFbxFilePathAttr;
  555. global string $UnityFbxFileNameAttr;
  556. if(!endsWith($mayaSet, "_UnityExportSet")){
  557. return false;
  558. }
  559. if(!`attributeExists $UnityFbxFilePathAttr $mayaSet`){
  560. return false;
  561. }
  562. if(!`attributeExists $UnityFbxFileNameAttr $mayaSet`){
  563. return false;
  564. }
  565. return true;
  566. }
  567. proc string[] getUnityExportSets(){
  568. //if the selection set ends w "_UnityExportSet" and it has at least the custom attributes UnityFbxModelFilePath & UnityFbxModelFileName then it's one of ours.
  569. string $unityExportSets[];
  570. string $mayaSets[] = `ls -sets`;
  571. int $i = 0;
  572. for($k=0; $k<size($mayaSets); ++$k){
  573. if(isUnityExportSet($mayaSets[$k])){
  574. $unityExportSets[$i] = $mayaSets[$k];
  575. $i++;
  576. }
  577. }
  578. return $unityExportSets;
  579. }
  580. proc setupNewExportSet(
  581. string $modelPath,
  582. string $modelFilename,
  583. string $animPath,
  584. string $animFilename,
  585. int $stripNamespaces,
  586. string $selectedObjects[]){
  587. // make sure all necessary variables are set
  588. if ($modelFilename == "" && $animFilename == ""){
  589. error ("Unity FBX Export Set Creation: Missing filename for export.");
  590. return;
  591. }
  592. if ($modelPath == "" && $animPath == ""){
  593. error ("Unity FBX Export Set Creation: Missing filepath for export.");
  594. return;
  595. }
  596. if ($modelFilename == ""){
  597. if(match("@", basename($animFilename, ".fbx")) != ""){
  598. $modelFilename = match("[^@]+", $animFilename) + ".fbx";
  599. }
  600. else{
  601. $modelFilename = basename($animFilename, ".fbx") + "_modelOnly.fbx";
  602. }
  603. }
  604. if ($animFilename == ""){
  605. $animFilename = basename($modelFilename, ".fbx") + "@Take1.fbx";
  606. }
  607. if($modelPath == ""){
  608. $modelPath = $animPath;
  609. }
  610. else if($animPath == ""){
  611. $animPath = $modelPath;
  612. }
  613. string $exportFileNameWithoutExt = formValidObjectName(basename($modelFilename, ".fbx"));
  614. $unityExportSet = getNewExportSetName($exportFileNameWithoutExt);
  615. string $origNamespace = `namespaceInfo -cur -an`;
  616. // Get or create the Unity Fbx Export Set
  617. $setCreated = getOrCreateExportSet($unityExportSet, $origNamespace);
  618. if(!$setCreated){
  619. // warn user
  620. if(!showConfirmDialog("Warning",
  621. "Creating set will overwrite contents of " + $unityExportSet + " export set. To export selection + contents of existing set, first add selection to set.",
  622. "Continue", "Cancel"
  623. )){
  624. // cancelled, don't export this fbx
  625. print ("Cancelled set creation");
  626. return;
  627. }
  628. }
  629. global int $UnityFbxFilePathIndex;
  630. global int $UnityFbxFileNameIndex;
  631. global int $UnityFbxAnimFilePathIndex;
  632. global int $UnityFbxAnimFileNameIndex;
  633. global int $UnityFileNameWithoutExtIndex;
  634. // Get the export set attributes
  635. string $exportAttrs[5];
  636. $exportAttrs[$UnityFbxFilePathIndex] = $modelPath;
  637. $exportAttrs[$UnityFbxFileNameIndex] = $modelFilename;
  638. $exportAttrs[$UnityFbxAnimFilePathIndex] = $animPath;
  639. $exportAttrs[$UnityFbxAnimFileNameIndex] = $animFilename;
  640. $exportAttrs[$UnityFileNameWithoutExtIndex] = $exportFileNameWithoutExt;
  641. setExportSetAttributes($unityExportSet, /*isAnimOnly*/ false, $setCreated, $exportAttrs, $stripNamespaces);
  642. if (setExists($unityExportSet) == true){
  643. // clear contents of set
  644. sets -clear $unityExportSet;
  645. // add newly imported items to set
  646. if (size($selectedObjects) > 0){
  647. sets -include $unityExportSet $selectedObjects;
  648. }
  649. }
  650. // switch project if file exported to a different Unity project
  651. switchUnityProject($modelPath);
  652. }
  653. // ==== Functions for creating export set dialog ==========
  654. global proc unityOnCreateExportSet(
  655. string $window,
  656. string $modelPathField,
  657. string $modelFileField,
  658. string $animPathField,
  659. string $animFileField,
  660. string $stripNamespaceCheckbox){
  661. $origSelection = `ls -sl`;
  662. if(size($origSelection) <= 0){
  663. // nothing selected
  664. error ("Unity FBX Export Set Creation: Nothing selected");
  665. return;
  666. }
  667. string $modelPath = "";
  668. string $modelFilename = "";
  669. if($modelPathField != 0){
  670. $modelPath = `textField -q -text $modelPathField`;
  671. $modelFilename = `textField -q -text $modelFileField`;
  672. if ($modelFilename != "" && !endsWith($modelFilename, ".fbx")){
  673. $modelFilename = $modelFilename + ".fbx";
  674. }
  675. }
  676. string $animPath = "";
  677. string $animFilename = "";
  678. if($animPathField != 0){
  679. $animPath = `textField -q -text $animPathField`;
  680. $animFilename = `textField -q -text $animFileField`;
  681. if ($animFilename != "" && !endsWith($animFilename, ".fbx")){
  682. $animFilename = $animFilename + ".fbx";
  683. }
  684. }
  685. // make sure all necessary variables are set
  686. if ($modelFilename == "" && $animFilename == ""){
  687. error ("Unity FBX Export Set Creation: Missing filename for export.");
  688. return;
  689. }
  690. if ($modelPath == "" && $animPath == ""){
  691. error ("Unity FBX Export Set Creation: Missing filepath for export.");
  692. return;
  693. }
  694. int $stripNamespaces = `checkBox -q -value $stripNamespaceCheckbox`;
  695. setupNewExportSet(
  696. $modelPath,
  697. $modelFilename,
  698. $animPath,
  699. $animFilename,
  700. $stripNamespaces,
  701. $origSelection);
  702. deleteUI -window $window;
  703. }
  704. global proc unityOpenFileDialog(string $textField)
  705. {
  706. string $currentPath = `textField -q -text $textField`;
  707. string $exportPaths[] = `fileDialog2 -ds 2 -cap "FBX Export Selection" -dir $currentPath -fm 3`;
  708. if(size($exportPaths)<=0){
  709. return;
  710. }
  711. string $exportFilePath = $exportPaths[0];
  712. textField -e -text $exportFilePath $textField;
  713. }
  714. proc string createFilePathField(string $label, string $parent, int $labelSize, int $textFieldSize){
  715. rowLayout
  716. -numberOfColumns 3
  717. -columnWidth3 $labelSize $textFieldSize 50
  718. -columnAlign3 "right" "left" "left"
  719. -p $parent;
  720. string $unityProject = `optionVar -q "UnityProject"`;
  721. $unityProject = $unityProject + "/Assets";
  722. text -label $label;
  723. string $textField = `textField -width $textFieldSize -text $unityProject`;
  724. button -label "..." -recomputeSize false -height 15 -command ("unityOpenFileDialog " + $textField);
  725. return $textField;
  726. }
  727. proc string createTextFieldWithLabel(string $label, string $parent, int $labelSize, int $textFieldSize)
  728. {
  729. rowLayout
  730. -numberOfColumns 2
  731. -columnWidth2 $labelSize $textFieldSize
  732. -columnAlign2 "right" "left"
  733. -p $parent;
  734. text -label $label;
  735. string $textField = `textField -width $textFieldSize`;
  736. return $textField;
  737. }
  738. proc string createCheckboxWithLabelLeft(string $label, string $parent, int $labelSize, int $fieldSize){
  739. rowLayout
  740. -numberOfColumns 2
  741. -columnWidth2 $labelSize $fieldSize
  742. -columnAlign2 "left" "left"
  743. -p $parent;
  744. text -label $label;
  745. return `checkBox -value true -label " "`;
  746. }
  747. proc createExportSetDialog(int $exportType){
  748. $origSelection = `ls -sl`;
  749. if(size($origSelection) <= 0){
  750. // nothing selected
  751. print ("Nothing selected");
  752. return;
  753. }
  754. $exportAnim = false;
  755. $exportAnimOnly = false;
  756. switch($exportType){
  757. case 0 /* export animation only */:
  758. $exportAnim = true;
  759. $exportAnimOnly = true;
  760. break;
  761. case 1 /* export model only */:
  762. break;
  763. default: /* export model + animation */
  764. $exportAnim = true;
  765. break;
  766. }
  767. // open up a dialog for choosing the export set options
  768. string $window = `window -title "Unity FBX Export Options" -iconName "Short Name" -widthHeight 500 250`;
  769. string $container = `formLayout -numberOfDivisions 100`;
  770. string $mainOptions = `columnLayout -adjustableColumn true -p $container`;
  771. // go through selection to find common namespace to set as default
  772. string $commonNamespace = getCommonNamespace($origSelection);
  773. string $modelFilename = "Untitled";
  774. // if one item selected, take the name of it as the filename
  775. if (size($origSelection) == 1){
  776. // take the name of the selection without the namespace
  777. string $nTokens[];
  778. int $nNumTokens = `tokenize ("" + $origSelection[0]) ":" $nTokens`;
  779. $modelFilename = $nTokens[$nNumTokens-1];
  780. }
  781. else{
  782. // if multi items selected, but one of them is the root, then take the name of the root as the filename
  783. for($i = 0; $i < size($origSelection); $i++){
  784. string $descendents[] = `listRelatives -type "transform" -allDescendents $origSelection[$i]`;
  785. string $intersection[] = getIntersection($origSelection, $descendents);
  786. if (size($intersection) == size($origSelection)-1){
  787. // take the name of the selection without the namespace
  788. string $nTokens[];
  789. int $nNumTokens = `tokenize ("" + $origSelection[$i]) ":" $nTokens`;
  790. $modelFilename = $nTokens[$nNumTokens-1];
  791. break;
  792. }
  793. }
  794. }
  795. int $labelSize = 150;
  796. int $textFieldSize = 300;
  797. string $modelFilePath = " 0 0";
  798. if(!$exportAnimOnly){
  799. string $modelFilePathField = createFilePathField("Model File Path", $mainOptions, $labelSize, $textFieldSize);
  800. string $modelFileNameField = createTextFieldWithLabel("Model File Name", $mainOptions, $labelSize, $textFieldSize);
  801. textField -e -text ($modelFilename + ".fbx") $modelFileNameField;
  802. $modelFilePath = " " + $modelFilePathField + " " + $modelFileNameField;
  803. }
  804. string $animFilePath = " 0 0";
  805. if($exportAnim){
  806. string $animFilePathField = createFilePathField("Anim File Path", $mainOptions, $labelSize, $textFieldSize);
  807. string $animFileNameField = createTextFieldWithLabel("Anim File Name", $mainOptions, $labelSize, $textFieldSize);
  808. textField -e -text ($modelFilename + "@Take1.fbx") $animFileNameField;
  809. $animFilePath = " " + $animFilePathField + " " + $animFileNameField;
  810. }
  811. string $stripNamespaceCheckbox = createCheckboxWithLabelLeft("Strip Namespaces on Export", $mainOptions, $labelSize, $textFieldSize);
  812. int $buttonWidth = 166;
  813. string $buttons = `rowLayout
  814. -numberOfColumns 3
  815. -adjustableColumn3 1
  816. -columnWidth3 $buttonWidth $buttonWidth $buttonWidth
  817. -columnAlign3 "center" "center" "center" -p $container`;
  818. string $createExportSetCommand = "unityOnCreateExportSet " + $window + " " + $modelFilePath + $animFilePath + " " + $stripNamespaceCheckbox;
  819. button -label "Create Set and Export"
  820. -width $buttonWidth
  821. -command ($createExportSetCommand + ";" + "unityExportModelAnim()")
  822. -p $buttons;
  823. button -label "Create Set"
  824. -width $buttonWidth
  825. -command ($createExportSetCommand)
  826. -p $buttons;
  827. button -label "Cancel"
  828. -width $buttonWidth
  829. -command ("deleteUI -window " + $window)
  830. -p $buttons;
  831. formLayout -edit
  832. -attachForm $buttons "bottom" 1
  833. -attachForm $buttons "left" 1
  834. -attachPosition $buttons "right" 1 99
  835. -attachForm $mainOptions "left" 10
  836. -attachForm $mainOptions "top" 10
  837. $container;
  838. setParent ..;
  839. showWindow $window;
  840. }
  841. // =========================================
  842. proc unityExport(int $exportType){
  843. if(!loadUnityDependencies()){
  844. return;
  845. }
  846. if(!loadUnityFbxExportSettings()){
  847. return;
  848. }
  849. $exportAnim = false;
  850. $exportAnimOnly = false;
  851. switch($exportType){
  852. case 0 /* export animation only */:
  853. $exportAnim = true;
  854. $exportAnimOnly = true;
  855. break;
  856. case 1 /* export model only */:
  857. break;
  858. default: /* export model + animation */
  859. $exportAnim = true;
  860. break;
  861. }
  862. FBXProperty "Export|IncludeGrp|Animation" -v $exportAnim;
  863. FBXExportAnimationOnly -v $exportAnimOnly;
  864. $origSelection = `ls -sl`;
  865. if(size($origSelection) <= 0){
  866. // nothing selected
  867. return;
  868. }
  869. $i = 0;
  870. string $setsToExport[];
  871. string $unityExportSets[] = getUnityExportSets();
  872. for($exportSet in $unityExportSets){
  873. if(!setExists($exportSet)){
  874. continue;
  875. }
  876. // TODO (UNI-39197) move functionality to separate mel file
  877. // check if the selection intersects with this export set
  878. string $exportSetContents[] = `listConnections $exportSet`;
  879. string $intersection[] = getIntersection($origSelection, $exportSetContents);
  880. if(size($intersection) > 0 ||
  881. stringArrayContains($exportSet, $origSelection)){
  882. $setsToExport[$i] = $exportSet;
  883. $i++;
  884. }
  885. }
  886. // if selection doesn't belong to a set, export to a new file
  887. if(size($setsToExport) <= 0){
  888. createExportSetDialog($exportType);
  889. return;
  890. }
  891. for($unitySet in $setsToExport){
  892. print ("exporting set: " + $unitySet);
  893. exportSet($unitySet, $exportAnimOnly);
  894. }
  895. select -cl;
  896. if (size($origSelection) > 0){
  897. select -add -ne $origSelection;
  898. }
  899. }
  900. global proc unityExportAnim(){
  901. global int $UnityExportAnim;
  902. unityExport($UnityExportAnim);
  903. }
  904. global proc unityExportModel(){
  905. global int $UnityExportModel;
  906. unityExport($UnityExportModel);
  907. }
  908. global proc unityExportModelAnim(){
  909. global int $UnityExportModelAnim;
  910. unityExport($UnityExportModelAnim);
  911. }
  912. global proc unityCreateExportSet(){
  913. global int $UnityExportModelAnim;
  914. createExportSetDialog($UnityExportModelAnim);
  915. }