Scoba

Untitled

Aug 13th, 2016
855
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 100.62 KB | None | 0 0
  1. # Copyright 2014, Aidan Shafran
  2.  
  3. # CoDMayaTools is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 3 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15.  
  16. # -----------------------------------------------------------------------------------------
  17. # VERSION INFO
  18. # VERSION 1
  19. # + Original - XModel Exporter, XAnim Exporter, and ViewModel tools
  20. # VERSION 1.1
  21. # + Added feature to switch the gun in a weapon rig file
  22. # * Fixed trying to write to the export file before it was created
  23. # * Changed the "No joints selected; exporting with a default TAG_ORIGIN." warning to not create a warning dialog after export
  24. # * Other small random fixes
  25. # VERSION 1.2
  26. # + Added button to export multiple models/animations at once
  27. # * Moved some of the user-changeable variables in the script to the top of the file in the CUSTOMIZATION section
  28. # VERSION 1.3
  29. # * Fixed excessive TRI_VERT_RATIO error (this can still happen if something is wrong with your model, but this update should help)
  30. # VERSION 1.4
  31. # * Changed new version message to open website forum topic instead of message box
  32. # * Fixed models without skins exporting in Object space instead of World space
  33. # VERSION 1.5
  34. # * Organized code a bit
  35. # * Renamed project to "CoDMayaTools" (from "CoDExportTools"), as it now does both importing and exporting
  36. # * Fixed material/object names exporting with : in them (namespace markers)
  37. # + Added CoD5 XModel importing
  38. # + Auto updating
  39. # + IWI to DDS converter
  40. # VERSION 1.5.1
  41. # + Added CoD4 XModel importing
  42. # + Automatically exports black vertices as white (can disable this in the customization section at the top of the script file)
  43. # * Bug fixes
  44. # Version 1.5.2 - Scobalula
  45. # *Added ability to grab information from "Notetrack"
  46. # *Removed Error for more than 128 Bones and instead changed to give a warning for people using BO1.
  47. #
  48.  
  49.  
  50. # TODO: Speed up joint weight loading
  51.  
  52. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  53. # ---------------------------------------------------------- Customization (You can change these values!) ----------------------------------------------------------
  54. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  55. MAX_WARNINGS_SHOWN = 100 # Maximum number of warnings to show per export
  56. EXPORT_WINDOW_NUMSLOTS = 10 # Number of slots in the export windows
  57. CONVERT_BLACK_VERTS_TO_WHITE = True # To automatically export any black vertices as white, set to 'True'. Otherwise, set to 'False'.
  58.  
  59.  
  60.  
  61. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  62. # ---------------------------------------------------------------------------- Global ------------------------------------------------------------------------------
  63. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  64. import os
  65. import maya.cmds as cmds
  66. import maya.mel as mel
  67. import math
  68. import sys
  69. import datetime
  70. import os.path
  71. import traceback
  72. import maya.OpenMaya as OpenMaya
  73. import maya.OpenMayaAnim as OpenMayaAnim
  74. import urllib2
  75. import socket
  76. import webbrowser
  77. import Queue
  78. import _winreg as reg
  79. import time
  80. import struct
  81. import shutil
  82. import zipfile
  83. import re
  84.  
  85. WarningsDuringExport = 0 # Number of warnings shown during current export
  86. CM_TO_INCH = 0.3937007874015748031496062992126 # 1cm = 50/127in
  87. FILE_VERSION = 1.51
  88. VERSION_CHECK_URL = "http://www.aidanshafran.com/codmayatools/version.txt"
  89. GLOBAL_STORAGE_REG_KEY = (reg.HKEY_CURRENT_USER, "Software\\CoDMayaTools") # Registry path for global data storage
  90. # name : control code name, control friendly name, data storage node name, refresh function, export function
  91. OBJECT_NAMES = {'menu' : ["CoDMayaToolsMenu", "Call of Duty Tools", None, None, None],
  92. 'progress' : ["CoDMayaToolsProgressbar", "Progress", None, None, None],
  93. 'xmodel': ["CoDMayaXModelExportWindow", "Export XModel", "XModelExporterInfo", "RefreshXModelWindow", "ExportXModel"],
  94. 'xanim' : ["CoDMayaXAnimExportWindow", "Export XAnim", "XAnimExporterInfo", "RefreshXAnimWindow", "ExportXAnim"]}
  95.  
  96.  
  97.  
  98. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  99. # ------------------------------------------------------------------------- Import Common --------------------------------------------------------------------------
  100. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  101. def ImportFileSelectDialog(codRootPath, type):
  102. importFrom = None
  103. if cmds.about(version=True)[:4] == "2012": # There is a bug in later versions of Maya with the file browser dialog and files with no extension
  104. importFrom = cmds.fileDialog2(fileMode=1, fileFilter="%s Files (*)" % type, caption="Import %s" % type, startingDirectory=os.path.join(codRootPath, "raw/%s/" % type.lower()))
  105. else:
  106. importFrom = cmds.fileDialog2(fileMode=1, dialogStyle=1, fileFilter="%s Files (*)" % type, caption="Import %s" % type, startingDirectory=os.path.join(codRootPath, "raw/%s/" % type.lower()))
  107.  
  108. if importFrom == None or len(importFrom) == 0 or importFrom[0].strip() == "":
  109. return None
  110. path = importFrom[0].strip()
  111.  
  112. pathSplit = os.path.splitext(path) # Fix bug with Maya 2013
  113. if pathSplit[1] == ".*":
  114. path = pathSplit
  115.  
  116. return path
  117.  
  118. def UnitQuaternionToDegrees(x, y, z):
  119. w = math.sqrt(1 - x*x - y*y - z*z) # The 4th component of a quaternion can be found from the other 3 components in unit quaternions
  120. euler = OpenMaya.MQuaternion(x, y, z, w).asEulerRotation()
  121. return (math.degrees(euler.x), math.degrees(euler.y), math.degrees(euler.z))
  122.  
  123. def ReadJointRotation(f):
  124. rot = struct.unpack('<hhh', f.read(6))
  125. # Rotation is stored as a unit quaternion, but only the X, Y, and Z components are given, as integers scaled to -32768 to 32767
  126. rot = UnitQuaternionToDegrees(rot[0] / 32768.0, rot[1] / 32768.0, rot[2] / 32768.0)
  127. return rot
  128.  
  129. def ReadNullTerminatedString(f):
  130. byte = f.read(1)
  131. string = ""
  132. while struct.unpack('B', byte)[0] != 0:
  133. string += byte
  134. byte = f.read(1)
  135.  
  136. return string
  137.  
  138. def AutoCapsJointName(name):
  139. if name.startswith("tag"):
  140. return name.upper()
  141.  
  142. name = name.capitalize()
  143.  
  144. name = name.replace("_le_", "_LE_")
  145. name = name.replace("_ri_", "_RI_")
  146.  
  147. if name[-3:] == "_le":
  148. name = name[:-3] + "_LE"
  149. if name[-3:] == "_ri":
  150. name = name[:-3] + "_RI"
  151.  
  152. # Capitalize the letter after each underscore
  153. indices = set([m.start() for m in re.finditer("_", name)])
  154. return "".join(c.upper() if (i-1) in indices else c for i, c in enumerate(name))
  155.  
  156.  
  157.  
  158. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  159. # -------------------------------------------------------------------------- Import XAnim --------------------------------------------------------------------------
  160. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  161. def ImportXAnim(game):
  162. codRootPath = GetRootFolder(True, game) # Only call this once, because it might create a dialog box
  163. xanimPath = ImportFileSelectDialog(codRootPath, "XAnim")
  164. if not xanimPath:
  165. return
  166.  
  167. print("Importing XAnim '%s'" % os.path.basename(xanimPath))
  168. with open(xanimPath, "rb") as f:
  169. # Check file version
  170. version = f.read(2)
  171. if len(version) == 0 or struct.unpack('H', version)[0] != 17:
  172. MessageBox("ERROR: Not a valid XAnim file")
  173. return
  174.  
  175. # Header
  176. numFrames = struct.unpack('<H', f.read(2))[0]
  177. numJoints = struct.unpack('<H', f.read(2))[0]
  178. fileInfoBitfield = struct.unpack('<H', f.read(2))[0]
  179. framerate = struct.unpack('<H', f.read(2))[0]
  180.  
  181. # Get anim type as string
  182. animType = "absolute"
  183. if fileInfoBitfield & 2:
  184. animType = "delta"
  185. elif fileInfoBitfield & 256:
  186. animType = "relative"
  187. elif fileInfoBitfield & 1024:
  188. animType = "additive"
  189.  
  190. # ???
  191. if animType == "absolute":
  192. f.read(2) # ???
  193. else:
  194. print("Cannot read anim type '%s'" % animType)
  195. return
  196.  
  197. # Read joint names
  198. joints = []
  199. for i in range(numJoints):
  200. joints.append(ReadNullTerminatedString(f))
  201. print joints
  202.  
  203. # Read joint frame data
  204. for i in range(numJoints):
  205. numRotations = struct.unpack('<H', f.read(2))[0]
  206. for j in range(numRotations):
  207. rot = ReadJointRotation(f)
  208.  
  209. numPositions = struct.unpack('<H', f.read(2))[0]
  210. for j in range(numPositions):
  211. pos = struct.unpack('<fff', f.read(12))
  212. print pos
  213.  
  214.  
  215.  
  216. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  217. # -------------------------------------------------------------------------- Import XModel -------------------------------------------------------------------------
  218. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  219. def ImportXModel(game):
  220. codRootPath = GetRootFolder(True, game) # Only call this once, because it might create a dialog box
  221. xmodelPath = ImportFileSelectDialog(codRootPath, "XModel")
  222. if not xmodelPath:
  223. return
  224.  
  225. # Show progress bar
  226. if cmds.control("w"+OBJECT_NAMES['progress'][0], exists=True):
  227. cmds.deleteUI("w"+OBJECT_NAMES['progress'][0])
  228. progressWindow = cmds.window("w"+OBJECT_NAMES['progress'][0], title=OBJECT_NAMES['progress'][1], width=302, height=22)
  229. cmds.columnLayout()
  230. progressControl = cmds.progressBar(OBJECT_NAMES['progress'][0], width=300, progress=0)
  231. cmds.showWindow(progressWindow)
  232. cmds.refresh() # Force the progress bar to be drawn
  233.  
  234. try:
  235. print("Importing XModel '%s'" % os.path.basename(xmodelPath))
  236. with open(xmodelPath, "rb") as f:
  237. version = f.read(2)
  238. if len(version) == 0 or struct.unpack('H', version)[0] != 25:
  239. MessageBox("ERROR: Not a valid XModel file")
  240. return
  241.  
  242. if game == "CoD4":
  243. f.read(25) # ???
  244. elif game == "CoD5":
  245. f.read(26) # ???
  246.  
  247. ReadNullTerminatedString(f) # ???
  248.  
  249. lods = []
  250. for i in range(4): # 4 is possible number of lods
  251. someInt = struct.unpack('<I', f.read(4))
  252. lodFileName = ReadNullTerminatedString(f)
  253.  
  254. if lodFileName != "":
  255. lods.append({"name":lodFileName})
  256.  
  257. if len(lods) == 0:
  258. MessageBox("ERROR: This XModel has no data (no LOD files)!")
  259. return
  260.  
  261. f.read(4) # Spacer if next int isn't 0, otherwise ???
  262. count = struct.unpack('<I', f.read(4))[0]
  263. print (lods, count)
  264. for i in range(count):
  265. subcount = struct.unpack('<I', f.read(4))[0]
  266. f.read((subcount * 48) + 36) # ???
  267.  
  268. for lod in lods:
  269. materials = []
  270. numMaterials = struct.unpack('<H', f.read(2))[0]
  271. for i in range(numMaterials):
  272. materials.append(ReadNullTerminatedString(f))
  273. lod["materials"] = materials
  274.  
  275. # Load joint data (24 bytes per joint) ???
  276.  
  277. lodToLoad = lods[0]
  278.  
  279. if len(lods) > 1:
  280. buttons = []
  281. lodDict = {}
  282. for lod in lods:
  283. buttons.append(lod["name"])
  284. lodDict[lod["name"]] = lod
  285. buttons.sort()
  286.  
  287. result = cmds.confirmDialog(title="Select LOD level", message="This model has more than one LOD level. Select which one to import:", button=buttons, defaultButton=buttons[0], dismissString="EXIT")
  288. if result in lodDict:
  289. lodToLoad = lodDict[result]
  290.  
  291. lodToLoad["transformGroup"] = cmds.group(empty=True, name=lodToLoad["name"])
  292.  
  293. lodToLoad["materialMaps"] = LoadMaterials(lodToLoad, codRootPath)
  294. lodToLoad["joints"] = LoadJoints(lodToLoad, codRootPath)
  295. LoadSurfaces(lodToLoad, codRootPath)
  296. AutoIKHandles(lodToLoad)
  297. cmds.select(lodToLoad["transformGroup"], replace=True)
  298. finally:
  299. # Delete progress bar
  300. cmds.deleteUI(progressWindow, window=True)
  301.  
  302. def LoadSurfaces(lod, codRootPath):
  303. print("Loading XModel surface '%s'" % lod["name"])
  304.  
  305. with open(os.path.join(codRootPath, "raw/xmodelsurfs/%s" % lod["name"]), "rb") as f:
  306. version = f.read(2)
  307. if len(version) == 0 or struct.unpack('H', version)[0] != 25:
  308. MessageBox("ERROR: Not a valid XModel surface file")
  309. return
  310.  
  311. numMeshes = struct.unpack('<H', f.read(2))[0]
  312.  
  313. if numMeshes != len(lod["materials"]):
  314. MessageBox("ERROR: Different number of meshes and materials on LOD '%s'" % lod["name"])
  315. return
  316.  
  317. meshesCreated = []
  318.  
  319. cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue=numMeshes*5+1, progress=0)
  320.  
  321. for i in range(numMeshes):
  322. cmds.window("w"+OBJECT_NAMES['progress'][0], edit=True, title="Loading mesh %i..." % i)
  323.  
  324. # Read mesh header
  325. a = struct.unpack('<B', f.read(1))[0] # ???
  326. b = struct.unpack('<H', f.read(2))[0] # ???
  327.  
  328. numVerts = struct.unpack('<H', f.read(2))[0]
  329. numTris = struct.unpack('<H', f.read(2))[0]
  330. numVerts2 = struct.unpack('<H', f.read(2))[0]
  331.  
  332. physiqued = numVerts != numVerts2
  333. if physiqued:
  334. f.read(2) # ???
  335. print("\tMesh %i is physiqued... this may not load correctly" % i)
  336. if numVerts2 != 0:
  337. while struct.unpack('H', f.read(2))[0] != 0: # Search for next 0 short ???
  338. pass
  339. f.read(2) # ???
  340. else:
  341. f.read(4) # ???
  342.  
  343. vertexArray = OpenMaya.MFloatPointArray()
  344. uArray = OpenMaya.MFloatArray()
  345. vArray = OpenMaya.MFloatArray()
  346. polygonCounts = OpenMaya.MIntArray(numTris, 3)
  347. polygonConnects = OpenMaya.MIntArray()
  348. vertsWeights = []
  349.  
  350. ProgressBarStep()
  351.  
  352. # Read vertices
  353. for j in range(numVerts):
  354. f.read(12) # ???
  355.  
  356. color = struct.unpack('<BBBB', f.read(4))
  357. uv = struct.unpack('<ff', f.read(8))
  358.  
  359. f.read(24) # ???
  360.  
  361. numWeights = 0
  362. finalBoneNumber = 0
  363.  
  364. if physiqued:
  365. numWeights = struct.unpack('<B', f.read(1))[0]
  366. finalBoneNumber = struct.unpack('<H', f.read(2))[0]
  367.  
  368. pos = struct.unpack('<fff', f.read(12))
  369.  
  370. totalWeight = 0
  371. weights = []
  372.  
  373. for k in range(numWeights):
  374. weight = struct.unpack('<HH', f.read(4)) # [0] = bone number, [1] = weight mapped to integer (range 0-(2^16))
  375. totalWeight += weight[1] / 65536.0
  376. weights.append((lod["joints"][weight[0]]["name"], weight[1] / 65536.0))
  377.  
  378. weights.append((lod["joints"][finalBoneNumber]["name"], 1 - totalWeight)) # Final bone gets remaining weight
  379. vertsWeights.append(weights)
  380.  
  381. vertexArray.append(pos[0]/CM_TO_INCH, pos[1]/CM_TO_INCH, pos[2]/CM_TO_INCH)
  382. uArray.append(uv[0])
  383. vArray.append(1-uv[1])
  384.  
  385. # Read face indices
  386. for j in range(numTris):
  387. face = struct.unpack('<HHH', f.read(6))
  388. polygonConnects.append(face[0])
  389. polygonConnects.append(face[1])
  390. polygonConnects.append(face[2])
  391.  
  392. ProgressBarStep()
  393.  
  394. # Create mesh
  395. mesh = OpenMaya.MFnMesh()
  396. transform = mesh.create(numVerts, numTris, vertexArray, polygonCounts, polygonConnects)
  397.  
  398. # UV map
  399. mesh.setUVs(uArray, vArray)
  400. mesh.assignUVs(polygonCounts, polygonConnects)
  401.  
  402. # Rename mesh
  403. transformDagPath = OpenMaya.MDagPath()
  404. OpenMaya.MDagPath.getAPathTo(transform, transformDagPath)
  405.  
  406. newPath = cmds.parent(transformDagPath.fullPathName(), lod["transformGroup"])
  407. newPath = cmds.rename(newPath, "mesh%i" % i)
  408.  
  409. meshesCreated.append(newPath)
  410.  
  411. ProgressBarStep()
  412.  
  413. # Joint weights
  414. # TODO: Make this faster!!! Soooo sloowwwwwww
  415. skin = cmds.skinCluster(lod["joints"][0]["name"], newPath)[0] # Bind the mesh to the root joint for now
  416. for j, vertWeights in enumerate(vertsWeights):
  417. cmds.skinPercent(skin, "%s.vtx[%i]" % (newPath, j), zeroRemainingInfluences=True, transformValue=vertWeights)
  418.  
  419. ProgressBarStep()
  420.  
  421. # Apply textures
  422. shader = cmds.shadingNode("lambert", name=lod["materials"][i], asShader=True)
  423. cmds.select(newPath)
  424. cmds.hyperShade(assign=shader)
  425.  
  426. colorMap = cmds.shadingNode("file", name=lod["materials"][i] + "_colorMap", asTexture=True)
  427. cmds.connectAttr("%s.outColor" % colorMap, "%s.color" % shader)
  428.  
  429. if "colorMap" in lod["materialMaps"][lod["materials"][i]]:
  430. cmds.setAttr("%s.fileTextureName" % colorMap, os.path.join(codRootPath, "raw/images/%s/%s.dds" % (lod["name"], lod["materialMaps"][lod["materials"][i]]["colorMap"])), type="string")
  431.  
  432. # Merge duplicates
  433. mel.eval("polyMergeVertex -d 0.01 -am 1 -ch 0 %s;" % newPath) # Merge duplicate verts
  434. mel.eval("polyMergeUV -d 0.01 -ch 0 %s;" % newPath) # Merge duplicate UVs
  435. ProgressBarStep()
  436.  
  437. if len(f.read(1)) != 0: # Check if it's at the end of the file
  438. MessageBox("The export completed, however it's quite likely that at least one of the meshes did not import correctly. See the Script Editor output for more information.")
  439. ProgressBarStep()
  440.  
  441. def LoadJoints(lod, codRootPath):
  442. print("Loading XModel joints '%s'" % lod["name"])
  443. cmds.window("w"+OBJECT_NAMES['progress'][0], edit=True, title="Loading joints...")
  444.  
  445. joints = []
  446. with open(os.path.join(codRootPath, "raw/xmodelparts/%s" % lod["name"]), "rb") as f:
  447. version = f.read(2)
  448. if len(version) == 0 or struct.unpack('H', version)[0] != 25:
  449. MessageBox("ERROR: Not a valid XModel parts file")
  450. return
  451.  
  452. # Number of bones
  453. numJoints = struct.unpack('<H', f.read(2))[0]
  454.  
  455. cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue=numJoints*2+1, progress=0)
  456.  
  457. if numJoints == 0: # Special case
  458. joints.append({"parent": -1, "pos": (0.0,0.0,0.0), "rot": (0.0,0.0,0.0), "name": "TAG_ORIGIN"})
  459. cmds.select(clear=True)
  460. cmds.joint(name=joints[0]["name"], orientation=(0.0,0.0,0.0), position=(0.0,0.0,0.0), relative=True)
  461. ProgressBarStep()
  462. return joints
  463.  
  464.  
  465. f.read(2) # ???
  466.  
  467. # Joint data
  468. joints.append({"parent": -1, "pos": (0.0,0.0,0.0), "rot": (0.0,0.0,0.0)}) # parent joint
  469. for i in range(numJoints):
  470. parentJoint = struct.unpack('<B', f.read(1))[0]
  471. pos = struct.unpack('<fff', f.read(12))
  472. rot = ReadJointRotation(f)
  473.  
  474. joints.append({"parent": parentJoint, "pos": pos, "rot": rot})
  475. ProgressBarStep()
  476.  
  477. for i in range(numJoints+1):
  478. joints[i]["name"] = AutoCapsJointName(ReadNullTerminatedString(f))
  479.  
  480. for joint in joints:
  481. if joint["parent"] >= 0: # Select parent
  482. cmds.select(joints[joint["parent"]]["name"], replace=True)
  483. else:
  484. cmds.select(clear=True)
  485.  
  486. cmds.joint(name=joint["name"], orientation=joint["rot"], position=(joint["pos"][0]/CM_TO_INCH, joint["pos"][1]/CM_TO_INCH, joint["pos"][2]/CM_TO_INCH), relative=True)
  487. ProgressBarStep()
  488.  
  489. ProgressBarStep()
  490. return joints
  491.  
  492. def LoadMaterials(lod, codRootPath):
  493. noDupMaterials = list(set(lod["materials"]))
  494.  
  495. cmds.window("w"+OBJECT_NAMES['progress'][0], edit=True, title="Loading materials...")
  496. cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue=len(noDupMaterials)*2+1, progress=0)
  497.  
  498. iwdImages = LoadMainIWDImages(codRootPath)
  499. ProgressBarStep()
  500.  
  501. # Create output folder
  502. if not os.path.exists(os.path.join(codRootPath, "raw/images/%s/" % lod["name"])):
  503. os.mkdir(os.path.join(codRootPath, "raw/images/%s/" % lod["name"]))
  504.  
  505. # Create material info file
  506. infofile = open(os.path.join(codRootPath, "raw/images/%s/%s" % (lod["name"], "%s Material Info.txt" % lod["name"])), "w")
  507.  
  508. # Load materials
  509. outMaterialList = {}
  510. for material in noDupMaterials:
  511. materialMaps = {}
  512. # http://www.diegologic.net/diegologic/Programming/CoD4%20Material.html
  513. print("Loading material '%s'" % material)
  514. with open(os.path.join(codRootPath, "raw/materials/%s" % material), "rb") as f:
  515. f.read(48) # Skip start of header
  516. numMaps = struct.unpack('<H', f.read(2))[0]
  517. f.read(14) # Skip the rest of header
  518.  
  519. for i in range(numMaps):
  520. mapTypeOffset = struct.unpack('<I', f.read(4))[0]
  521. f.read(4) # Skip
  522. mapNameOffset = struct.unpack('<I', f.read(4))[0]
  523. current = f.tell()
  524.  
  525. f.seek(mapTypeOffset)
  526. mapType = ReadNullTerminatedString(f)
  527. f.seek(mapNameOffset)
  528. mapName = ReadNullTerminatedString(f)
  529. f.seek(current)
  530. materialMaps[mapType] = mapName
  531.  
  532. infofile.write("Material: %s\n" % material)
  533. for type, mapName in materialMaps.items():
  534. infofile.write("\t%s: %s\n" % (type, mapName))
  535. infofile.write("\n")
  536.  
  537. outMaterialList[material] = materialMaps
  538. ProgressBarStep()
  539.  
  540. # Gather .iwis
  541. rawImages = os.listdir(os.path.join(codRootPath, "raw/images/"))
  542. for type, mapName in materialMaps.items():
  543. outPath = os.path.join(codRootPath, "raw/images/%s/%s%s" % (lod["name"], mapName, ".iwi"))
  544. if os.path.exists(outPath):
  545. continue
  546.  
  547. if (mapName + ".iwi") in rawImages:
  548. shutil.copy(os.path.join(codRootPath, "raw/images/%s%s" % (mapName, ".iwi")), os.path.join(codRootPath, "raw/images/%s/" % lod["name"]))
  549. elif (mapName + ".iwi") in iwdImages:
  550. iwdName = iwdImages[mapName + ".iwi"]
  551. zip = zipfile.ZipFile(os.path.join(codRootPath, "main/%s" % iwdName), "r")
  552.  
  553. # Extract from zip
  554. source = zip.open("images/%s%s" % (mapName, ".iwi"))
  555. target = file(outPath, "wb")
  556. shutil.copyfileobj(source, target)
  557. source.close()
  558. target.close()
  559.  
  560. if type == "colorMap":
  561. IWIToDDS(outPath)
  562.  
  563. ProgressBarStep()
  564.  
  565. infofile.close()
  566. return outMaterialList
  567.  
  568. def AutoIKHandles(lod):
  569. if len(lod["joints"]) < 2:
  570. return
  571.  
  572. if result == "Yes":
  573. # Arms
  574. SafeIKHandle("IK_Arm_LE", "J_Shoulder_LE", "J_Wrist_LE")
  575. SafeIKHandle("IK_Arm_RI", "J_Shoulder_RI", "J_Wrist_RI")
  576.  
  577. # Left hand
  578. SafeIKHandle("IK_Index_LE", "J_Index_LE_1", "J_Index_LE_3")
  579. SafeIKHandle("IK_Mid_LE", "J_Mid_LE_1", "J_Mid_LE_3")
  580. SafeIKHandle("IK_Ring_LE", "J_Ring_LE_1", "J_Ring_LE_3")
  581. SafeIKHandle("IK_Pinky_LE", "J_Pinky_LE_1", "J_Pinky_LE_3")
  582. SafeIKHandle("IK_Thumb_LE", "J_Thumb_LE_1", "J_Thumb_LE_3")
  583.  
  584. # Right hand
  585. SafeIKHandle("IK_Index_RI", "J_Index_RI_1", "J_Index_RI_3")
  586. SafeIKHandle("IK_Mid_RI", "J_Mid_RI_1", "J_Mid_RI_3")
  587. SafeIKHandle("IK_Ring_RI", "J_Ring_RI_1", "J_Ring_RI_3")
  588. SafeIKHandle("IK_Pinky_RI", "J_Pinky_RI_1", "J_Pinky_RI_3")
  589. SafeIKHandle("IK_Thumb_RI", "J_Thumb_RI_1", "J_Thumb_RI_3")
  590.  
  591. # Legs
  592. SafeIKHandle("IK_Leg_LE", "J_Hip_LE", "J_Ankle_LE")
  593. SafeIKHandle("IK_Leg_RI", "J_Hip_RI", "J_Ankle_RI")
  594.  
  595. def SafeIKHandle(name, joint1, joint2):
  596. # Only apply the IK Handle if both joints exist
  597. if cmds.objExists(joint1) and cmds.nodeType(joint1) == 'joint' and cmds.objExists(joint2) and cmds.nodeType(joint2) == 'joint':
  598. cmds.ikHandle(name=name, startJoint=joint1, endEffector=joint2)
  599.  
  600. def LoadMainIWDImages(codRootPath):
  601. iwdImages = {}
  602.  
  603. if not os.path.exists(os.path.join(codRootPath, "main/")):
  604. return iwdImages
  605.  
  606. iwds = os.listdir(os.path.join(codRootPath, "main/"))
  607. for iwd in iwds:
  608. if not iwd.endswith(".iwd"):
  609. continue
  610.  
  611. zip = zipfile.ZipFile(os.path.join(codRootPath, "main/") + iwd, "r")
  612. images = zip.namelist()
  613. images = [x for x in images if x.startswith("images/")]
  614. for i in range(len(images)):
  615. imageName = images[i][7:]
  616. if len(imageName) > 0:
  617. iwdImages[imageName] = iwd
  618.  
  619. return iwdImages
  620.  
  621.  
  622.  
  623. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  624. # --------------------------------------------------------------------------- IWI to DDS ---------------------------------------------------------------------------
  625. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  626. def IWIToDDS(inIWIPath):
  627. splitPath = os.path.splitext(inIWIPath)
  628. outDDSPath = splitPath[0] + ".dds"
  629.  
  630. print("Converting %s to DDS" % os.path.basename(inIWIPath))
  631.  
  632. if not os.path.exists(inIWIPath):
  633. return False
  634.  
  635. with open(inIWIPath, 'rb') as inf:
  636. # http://www.matejtomcik.com/Public/Projects/IWIExtractor/
  637. if inf.read(3) != "IWi": # Read file identifier
  638. print("\tERROR: Not a valid IWI file")
  639. return False
  640.  
  641. header = struct.unpack('<BBBHHBBIIII', inf.read(25))
  642.  
  643. if header[0] != 6: # Make sure the IWI version is 6
  644. print("\tERROR: Unsupported IWI version")
  645. return False
  646.  
  647. imageType = None
  648.  
  649. if header[1] == 0xB: # DXT1
  650. imageType = "DXT1"
  651. elif header[1] == 0xC: # DXT3
  652. imageType = "DXT3"
  653. elif header[1] == 0xD: # DXT5
  654. imageType = "DXT5"
  655. else:
  656. print("\tERROR: Unknown image format")
  657. return False
  658.  
  659. with open(outDDSPath, 'wb') as outf:
  660. # http://msdn.microsoft.com/en-us/library/windows/desktop/bb943991(v=vs.85).aspx
  661. outf.write("DDS ") # File indentifier
  662.  
  663. # DDS_HEADER size, flags, height, width, pitch, depth, mipmap count
  664. outf.write(struct.pack('<7I', 124, 659463, header[4], header[3], 0, 0, 1))
  665. outf.write(struct.pack('<11I', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) # Reserved
  666. # DDS_PIXELFORMAT size, flags, type, masks
  667. outf.write(struct.pack('II4s5I', 32, 4, imageType, 0, 0, 0, 0, 0))
  668. # DDS_HEADER caps1
  669. outf.write(struct.pack('5I', 4198408, 0, 0, 0, 0))
  670.  
  671. # Copy main image data to outfile
  672. inf.seek(header[8])
  673. outf.write(inf.read(header[7] - header[8]))
  674.  
  675. # For now, don't bother copying the other mipmaps
  676.  
  677. # Copy mipmap 1
  678. #inf.seek(header[9])
  679. #outf.write(inf.read(header[8] - header[9]))
  680.  
  681. # Copy mipmap 2
  682. #inf.seek(header[10])
  683. #outf.write(inf.read(header[9] - header[10]))
  684.  
  685. # Copy the rest of the data ???
  686. #inf.seek(28)
  687.  
  688. return True
  689.  
  690. def IWIToDDSUser():
  691. codRootPath = GetRootFolder() # Only call this once, because it might create a dialog box
  692. files = cmds.fileDialog2(fileMode=4, fileFilter="IWI Images (*.iwi)", caption="Select IWI file", startingDirectory=os.path.join(codRootPath, "raw/images/"))
  693. if files == None or len(files) == 0 or files[0].strip() == "":
  694. return
  695. success = True
  696. for file in files:
  697. if not IWIToDDS(file):
  698. success = False
  699.  
  700. if not success:
  701. MessageBox("One or more of the IWIs failed to convert. See the Script Editor output for more information.")
  702.  
  703.  
  704.  
  705. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  706. # ---------------------------------------------------------------- Export Joints (XModel and XAnim) ----------------------------------------------------------------
  707. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  708. def GetJointList():
  709. joints = []
  710.  
  711. # Get selected objects
  712. selectedObjects = OpenMaya.MSelectionList()
  713. OpenMaya.MGlobal.getActiveSelectionList(selectedObjects)
  714.  
  715. for i in range(selectedObjects.length()):
  716. # Get object path and node
  717. dagPath = OpenMaya.MDagPath()
  718. selectedObjects.getDagPath(i, dagPath)
  719. dagNode = OpenMaya.MFnDagNode(dagPath)
  720.  
  721. # Ignore nodes that aren't joints or arn't top-level
  722. if not dagPath.hasFn(OpenMaya.MFn.kJoint) or not RecursiveCheckIsTopNode(selectedObjects, dagNode):
  723. ProgressBarStep()
  724. continue
  725.  
  726. # Breadth first search of joint tree
  727. searchQueue = Queue.Queue(0)
  728. searchQueue.put((-1, dagNode, True)) # (index = child node's parent index, child node)
  729. while not searchQueue.empty():
  730. node = searchQueue.get()
  731. index = len(joints)
  732.  
  733. if node[2]:
  734. joints.append((node[0], node[1]))
  735. else:
  736. index = node[0]
  737.  
  738. for i in range(node[1].childCount()):
  739. dagPath = OpenMaya.MDagPath()
  740. childNode = OpenMaya.MFnDagNode(node[1].child(i))
  741. childNode.getPath(dagPath)
  742. searchQueue.put((index, childNode, selectedObjects.hasItem(dagPath) and dagPath.hasFn(OpenMaya.MFn.kJoint)))
  743.  
  744. ProgressBarStep()
  745.  
  746. return joints
  747.  
  748. def RecursiveCheckIsTopNode(cSelectionList, currentNode): # Checks if the given node has ANY selected parent, grandparent, etc joints
  749. if currentNode.parentCount() == 0:
  750. return True
  751.  
  752. for i in range(currentNode.parentCount()):
  753. parentDagPath = OpenMaya.MDagPath()
  754. parentNode = OpenMaya.MFnDagNode(currentNode.parent(i))
  755. parentNode.getPath(parentDagPath)
  756.  
  757. if not parentDagPath.hasFn(OpenMaya.MFn.kJoint): # Not a joint, but still check parents
  758. if not RecursiveCheckIsTopNode(cSelectionList, parentNode):
  759. return False # A parent joint is selected, we're done
  760. else:
  761. continue # No parent joints are selected, ignore this node
  762.  
  763. if cSelectionList.hasItem(parentDagPath):
  764. return False
  765. else:
  766. if not RecursiveCheckIsTopNode(cSelectionList, parentNode):
  767. return False
  768.  
  769. return True
  770.  
  771. def WriteJointData(f, jointNode):
  772. # Get the joint's transform
  773. path = OpenMaya.MDagPath()
  774. jointNode.getPath(path)
  775. transform = OpenMaya.MFnTransform(path)
  776.  
  777. # Get joint position
  778. pos = transform.getTranslation(OpenMaya.MSpace.kWorld)
  779.  
  780. # Get scale (almost always 1)
  781. scaleUtil = OpenMaya.MScriptUtil()
  782. scaleUtil.createFromList([1,1,1], 3)
  783. scalePtr = scaleUtil.asDoublePtr()
  784. transform.getScale(scalePtr)
  785. scale = [OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 0), OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 1), OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 2)]
  786.  
  787. # Get rotation matrix (mat is a 4x4, but the last row and column arn't needed)
  788. rotQuaternion = OpenMaya.MQuaternion()
  789. transform.getRotation(rotQuaternion, OpenMaya.MSpace.kWorld)
  790. mat = rotQuaternion.asMatrix()
  791.  
  792. # Debug info: as euler rotation
  793. #eulerRotation = rotQuaternion.asEulerRotation()
  794. #eulerRotation.reorderIt(OpenMaya.MEulerRotation.kXYZ)
  795.  
  796. # Write
  797. f.write("OFFSET %f, %f, %f\n" % (pos.x*CM_TO_INCH, pos.y*CM_TO_INCH, pos.z*CM_TO_INCH))
  798. f.write("SCALE %f, %f, %f\n" % (scale[0], scale[1], scale[2]))
  799. f.write("X %f, %f, %f\n" % (mat(0,0), mat(0,1), mat(0,2)))
  800. #f.write("\t\t// World-space euler angels XYZ: %f, %f, %f\n" % (math.degrees(eulerRotation.x), math.degrees(eulerRotation.y), math.degrees(eulerRotation.z)))
  801. f.write("Y %f, %f, %f\n" % (mat(1,0), mat(1,1), mat(1,2)))
  802. f.write("Z %f, %f, %f\n" % (mat(2,0), mat(2,1), mat(2,2)))
  803.  
  804.  
  805.  
  806. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  807. # -------------------------------------------------------------------------- Export XModel -------------------------------------------------------------------------
  808. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  809. def ExportXModel(filePath):
  810. # Progress bar
  811. numSelectedObjects = len(cmds.ls(selection=True))
  812. if numSelectedObjects == 0:
  813. return "Error: No objects selected for export"
  814.  
  815. cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue=numSelectedObjects*2+1)
  816.  
  817. # Get data
  818. joints = GetJointList()
  819. if len(joints) > 128:
  820. cmds.confirmDialog( title='WARNING', message='There is ' + str(len(joints)) + ' bones selected which exceeds WaWs 128 bone limit. (BO1 = 160, WaW = 128)' , button=['Continue'], defaultButton='Continue')
  821. shapes = GetShapes(joints)
  822. if type(shapes) == str:
  823. return shapes
  824.  
  825. # Open file
  826. f = None
  827. try:
  828. # Create export directory if it doesn't exist
  829. directory = os.path.dirname(filePath)
  830. if not os.path.exists(directory):
  831. os.makedirs(directory)
  832.  
  833. # Create file
  834. f = open(filePath, 'w')
  835. except (IOError, OSError) as e:
  836. typex, value, traceback = sys.exc_info()
  837. return "Unable to create file:\n\n%s" % value.strerror
  838.  
  839. # Write header
  840. f.write("// Export filename: '%s'\n" % os.path.normpath(filePath))
  841. if cmds.file(query=True, exists=True):
  842. f.write("// Source filename: '%s'\n" % os.path.normpath(os.path.abspath(cmds.file(query=True, sceneName=True))))
  843. else:
  844. f.write("// Source filename: Unsaved\n")
  845. f.write("// Export time: %s\n\n" % datetime.datetime.now().strftime("%a %b %d %Y, %H:%M:%S"))
  846. f.write("MODEL\n")
  847. f.write("VERSION 6\n\n")
  848.  
  849. # Write joints
  850. if len(joints) == 0:
  851. print "No joints selected; exporting with a default TAG_ORIGIN."
  852. f.write("NUMBONES 1\n")
  853. f.write("BONE 0 -1 \"TAG_ORIGIN\"\n\n")
  854.  
  855. f.write("BONE 0\n")
  856. f.write("OFFSET 0.000000 0.000000 0.000000\n")
  857. f.write("SCALE 1.000000 1.000000 1.000000\n")
  858. f.write("X 1.000000 0.000000 0.000000\n")
  859. f.write("Y 0.000000 1.000000 0.000000\n")
  860. f.write("Z 0.000000 0.000000 1.000000\n")
  861. else:
  862. f.write("NUMBONES %i\n" % len(joints))
  863. for i, joint in enumerate(joints):
  864. name = joint[1].partialPathName().split("|")
  865. name = name[len(name)-1].split(":") # Remove namespace prefixes
  866. name = name[len(name)-1]
  867. f.write("BONE %i %i \"%s\"\n" % (i, joint[0], name))
  868.  
  869. for i, joint in enumerate(joints):
  870. f.write("\nBONE %i\n" % i)
  871. WriteJointData(f, joint[1])
  872.  
  873. # Write verts
  874. f.write("\nNUMVERTS %i\n" % len(shapes["verts"]))
  875. for i, vert in enumerate(shapes["verts"]):
  876. f.write("VERT %i\n" % i)
  877. f.write("OFFSET %f, %f, %f\n" % (vert[0].x*CM_TO_INCH, vert[0].y*CM_TO_INCH, vert[0].z*CM_TO_INCH)) # Offsets are stored in CM, but cod uses inches
  878. f.write("BONES %i\n" % max(len(vert[1]), 1))
  879. if len(vert[1]) > 0:
  880. for bone in vert[1]:
  881. f.write("BONE %i %f\n" % (bone[0], bone[1]))
  882. else:
  883. f.write("BONE 0 1.000000\n")
  884. f.write("\n")
  885.  
  886. # Write faces
  887. f.write("NUMFACES %i\n" % len(shapes["faces"]))
  888. for j, face in enumerate(shapes["faces"]):
  889. f.write("TRI %i %i 0 0\n" % (face[0], face[1]))
  890. for i in range(0, 3):
  891. f.write("VERT %i\n" % face[2][i])
  892. f.write("NORMAL %f %f %f\n" % (face[5][i].x, face[5][i].y, face[5][i].z))
  893. f.write("COLOR %f %f %f %f\n" % (face[4][i].r, face[4][i].g, face[4][i].b, face[4][i].a))
  894. f.write("UV 1 %f %f\n" % (face[3][i][0], face[3][i][1]))
  895. f.write("\n")
  896.  
  897. # Write objects
  898. f.write("NUMOBJECTS %i\n" % len(shapes["meshes"]))
  899. for i, object in enumerate(shapes["meshes"]):
  900. f.write("OBJECT %i \"%s\"\n" % (i, object.split(":")[-1]))
  901.  
  902. # Write materials
  903. f.write("\nNUMMATERIALS %i\n" % len(shapes["materials"]))
  904. for i, material in enumerate(shapes["materials"]):
  905. f.write("MATERIAL %i \"%s\" \"%s\" \"%s\"\n" % (i, material[0].split(":")[-1], "Lambert", material[1]))
  906.  
  907. # According to the Modrepository page on the XModel format, the following values don't matter
  908. f.write("COLOR 0.000000 0.000000 0.000000 1.000000\n"
  909. "TRANSPARENCY 0.000000 0.000000 0.000000 1.000000\n"
  910. "AMBIENTCOLOR 0.000000 0.000000 0.000000 1.000000\n"
  911. "INCANDESCENCE 0.000000 0.000000 0.000000 1.000000\n"
  912. "COEFFS 0.800000 0.000000\n"
  913. "GLOW 0.000000 0\n"
  914. "REFRACTIVE 6 1.000000\n"
  915. "SPECULARCOLOR -1.000000 -1.000000 -1.000000 1.000000\n"
  916. "REFLECTIVECOLOR -1.000000 -1.000000 -1.000000 1.000000\n"
  917. "REFLECTIVE -1 -1.000000\n"
  918. "BLINN -1.000000 -1.000000\n"
  919. "PHONG -1.000000\n\n")
  920.  
  921. f.close()
  922. ProgressBarStep()
  923. cmds.refresh()
  924.  
  925. def GetMaterialsFromMesh(mesh, dagPath):
  926. textures = {}
  927.  
  928. # http://rabidsquirrelgames.googlecode.com/svn/trunk/Maya%20plugin/fileExportCmd.py
  929. # The code below gets a dictionary of [material name: material file name], ex: [a_material: a_material.dds]
  930. shaders = OpenMaya.MObjectArray()
  931. shaderIndices = OpenMaya.MIntArray()
  932. mesh.getConnectedShaders(dagPath.instanceNumber(), shaders, shaderIndices)
  933.  
  934. for i in range(shaders.length()):
  935. shaderNode = OpenMaya.MFnDependencyNode(shaders[i])
  936. shaderPlug = shaderNode.findPlug("surfaceShader")
  937. material = OpenMaya.MPlugArray()
  938. shaderPlug.connectedTo(material, 1, 0);
  939.  
  940. for j in range(material.length()):
  941. materialNode = OpenMaya.MFnDependencyNode(material[j].node())
  942. colorPlug = materialNode.findPlug("color")
  943.  
  944. dgIt = OpenMaya.MItDependencyGraph(
  945. colorPlug,
  946. OpenMaya.MFn.kFileTexture,
  947. OpenMaya.MItDependencyGraph.kUpstream,
  948. OpenMaya.MItDependencyGraph.kBreadthFirst,
  949. OpenMaya.MItDependencyGraph.kNodeLevel)
  950.  
  951. texturePath = ""
  952.  
  953. try: # If there is no texture, this part can throw an exception
  954. dgIt.disablePruningOnFilter()
  955. textureNode = OpenMaya.MFnDependencyNode(dgIt.currentItem())
  956. texturePlug = textureNode.findPlug("fileTextureName")
  957. texturePath = os.path.basename(texturePlug.asString())
  958. except Exception:
  959. pass
  960.  
  961. textures[i] = (materialNode.name(), texturePath)
  962.  
  963. texturesToFaces = []
  964. for i in range(shaderIndices.length()):
  965. if shaderIndices[i] in textures:
  966. texturesToFaces.append(textures[shaderIndices[i]])
  967. else:
  968. texturesToFaces.append(None)
  969.  
  970. return texturesToFaces
  971.  
  972. # Converts a set of vertices (toConvertVertexIndices) from object-relative IDs to face-relative IDs
  973. # vertexIndices is a list of object-relative vertex indices in face order (from polyIter.getVertices)
  974. # toConvertVertexIndices is any set of vertices from the same faces as vertexIndices, not necessarily the same length
  975. # Returns false if a vertex index is unable to be converted (= bad vertex values)
  976. def VerticesObjRelToLocalRel(vertexIndices, toConvertVertexIndices):
  977. # http://svn.gna.org/svn/cal3d/trunk/cal3d/plugins/cal3d_maya_exporter/MayaMesh.cpp
  978. localVertexIndices = OpenMaya.MIntArray()
  979.  
  980. for i in range(toConvertVertexIndices.length()):
  981. found = False
  982. for j in range(vertexIndices.length()):
  983. if toConvertVertexIndices[i] == vertexIndices[j]:
  984. localVertexIndices.append(j)
  985. found = True
  986. break
  987. if not found:
  988. return False
  989.  
  990. return localVertexIndices
  991.  
  992. def GetShapes(joints):
  993. # Vars
  994. meshes = []
  995. verts = []
  996. tris = []
  997. materialDict = {}
  998. materials = []
  999.  
  1000. # Convert the joints to a dictionary, for simple searching for joint indices
  1001. jointDict = {}
  1002. for i, joint in enumerate(joints):
  1003. jointDict[joint[1].partialPathName()] = i
  1004.  
  1005. # Get all selected objects
  1006. selectedObjects = OpenMaya.MSelectionList()
  1007. OpenMaya.MGlobal.getActiveSelectionList(selectedObjects)
  1008.  
  1009. # The global vert index at the start of each object
  1010. currentStartingVertIndex = 0
  1011.  
  1012. # Loop through all objects
  1013. for i in range(0, selectedObjects.length()):
  1014. # Get data on object
  1015. object = OpenMaya.MObject()
  1016. dagPath = OpenMaya.MDagPath()
  1017. selectedObjects.getDependNode(i, object)
  1018. selectedObjects.getDagPath(i, dagPath)
  1019.  
  1020. # Ignore dag nodes that aren't shapes or shape transforms
  1021. if not dagPath.hasFn(OpenMaya.MFn.kMesh):
  1022. ProgressBarStep()
  1023. continue
  1024.  
  1025. # Lower path to shape node
  1026. # Selecting a shape transform or shape will get the same dagPath to the shape using this
  1027. dagPath.extendToShape()
  1028.  
  1029. # Check for duplicates
  1030. if dagPath.partialPathName() in meshes:
  1031. ProgressBarStep()
  1032. continue
  1033.  
  1034. # Add shape to list
  1035. meshes.append(dagPath.partialPathName())
  1036.  
  1037. # Get mesh
  1038. mesh = OpenMaya.MFnMesh(dagPath)
  1039.  
  1040. # Get skin cluster
  1041. clusterName = mel.eval("findRelatedSkinCluster " + dagPath.partialPathName()) # I couldn't figure out how to get the skin cluster via the API
  1042. hasSkin = False
  1043. if clusterName != None and clusterName != "" and not clusterName.isspace():
  1044. hasSkin = True
  1045. selList = OpenMaya.MSelectionList()
  1046. selList.add(clusterName)
  1047. clusterNode = OpenMaya.MObject()
  1048. selList.getDependNode(0, clusterNode)
  1049. skin = OpenMayaAnim.MFnSkinCluster(clusterNode)
  1050.  
  1051. # Loop through all vertices
  1052. vertIter = OpenMaya.MItMeshVertex(dagPath)
  1053. while not vertIter.isDone():
  1054. if not hasSkin:
  1055. verts.append((vertIter.position(OpenMaya.MSpace.kWorld), []))
  1056. vertIter.next()
  1057. continue
  1058.  
  1059. # Get weight values
  1060. weightValues = OpenMaya.MDoubleArray()
  1061. numWeights = OpenMaya.MScriptUtil() # Need this because getWeights crashes without being passed a count
  1062. skin.getWeights(dagPath, vertIter.currentItem(), weightValues, numWeights.asUintPtr())
  1063.  
  1064. # Get weight names
  1065. weightJoints = OpenMaya.MDagPathArray()
  1066. skin.influenceObjects(weightJoints)
  1067.  
  1068. # Make sure the list of weight values and names match
  1069. if weightValues.length() != weightJoints.length():
  1070. PrintWarning("Failed to retrieve vertex weight list on '%s.vtx[%d]'; using default joints." % (dagPath.partialPathName(), vertIter.index()))
  1071.  
  1072. # Remove weights of value 0 or weights from unexported joints
  1073. finalWeights = []
  1074. weightsSize = 0
  1075. for i in range(0, weightJoints.length()):
  1076. if weightValues[i] < 0.000001: # 0.000001 is the smallest decimal in xmodel exports
  1077. continue
  1078. jointName = weightJoints[i].partialPathName()
  1079. if not jointName in jointDict:
  1080. PrintWarning("Unexported joint %s is influencing vertex '%s.vtx[%d]' by %f%%" % (("'%s'" % jointName).ljust(15), dagPath.partialPathName(), vertIter.index(), weightValues[i]*100))
  1081. else:
  1082. finalWeights.append([jointDict[jointName], weightValues[i]])
  1083. weightsSize += weightValues[i]
  1084.  
  1085. # Make sure the total weight adds up to 1
  1086. if weightsSize > 0:
  1087. weightMultiplier = 1 / weightsSize
  1088. for weight in finalWeights:
  1089. weight[1] *= weightMultiplier
  1090.  
  1091. verts.append((
  1092. vertIter.position(OpenMaya.MSpace.kWorld), # XYZ position
  1093. finalWeights # List of weights
  1094. ))
  1095.  
  1096. # Next vert
  1097. vertIter.next()
  1098.  
  1099. # Get materials used by this mesh
  1100. meshMaterials = GetMaterialsFromMesh(mesh, dagPath)
  1101.  
  1102. # Loop through all faces
  1103. polyIter = OpenMaya.MItMeshPolygon(dagPath)
  1104. currentObjectVertexOffset = 0
  1105. while not polyIter.isDone():
  1106. # Get this poly's material
  1107. polyMaterial = meshMaterials[polyIter.index()]
  1108.  
  1109. # Every face must have a material
  1110. if polyMaterial == None:
  1111. PrintWarning("Found no material on face '%s.f[%d]'; ignoring face" % (dagPath.partialPathName(), polyIter.index()))
  1112. polyIter.next()
  1113. continue
  1114.  
  1115. # Add this poly's material to the global list of used materials
  1116. if not polyMaterial[0] in materialDict:
  1117. materialDict[polyMaterial[0]] = len(materials)
  1118. materials.append(polyMaterial)
  1119.  
  1120. # Get vertex indices of this poly, and the vertex indices of this poly's triangles
  1121. trianglePoints = OpenMaya.MPointArray()
  1122. triangleIndices = OpenMaya.MIntArray()
  1123. vertexIndices = OpenMaya.MIntArray()
  1124. polyIter.getTriangles(trianglePoints, triangleIndices)
  1125. polyIter.getVertices(vertexIndices)
  1126.  
  1127. # localTriangleIndices is the same as triangleIndices, except each vertex is listed as the face-relative index intead of the object-realtive index
  1128. localTriangleIndices = VerticesObjRelToLocalRel(vertexIndices, triangleIndices)
  1129. if localTriangleIndices == False:
  1130. return "Failed to convert object-relative vertices to face-relative on poly '%s.f[%d]'" % (dagPath.partialPathName(), polyIter.index())
  1131.  
  1132. # Note: UVs, normals, and colors, are "per-vertex per face", because even though two faces may share
  1133. # a vertex, they might have different UVs, colors, or normals. So, each face has to contain this info
  1134. # for each of it's vertices instead of each vertex alone
  1135. Us = OpenMaya.MFloatArray()
  1136. Vs = OpenMaya.MFloatArray()
  1137. normals = OpenMaya.MVectorArray()
  1138. polyIter.getUVs(Us, Vs)
  1139. polyIter.getNormals(normals, OpenMaya.MSpace.kWorld)
  1140.  
  1141. # Add each triangle in this poly to the global face list
  1142. for i in range(triangleIndices.length()/3): # vertexIndices.length() has 3 values per triangle
  1143. # Put local indices into an array for easy access
  1144. locals = [localTriangleIndices[i*3], localTriangleIndices[i*3+1], localTriangleIndices[i*3+2]]
  1145.  
  1146. # Using polyIter.getColors() doesn't always work - sometimes values in the return array would
  1147. # be valid Python objects, but when used they would cause Maya to completely crash. No idea
  1148. # why that happens, but getting the colors individually fixed the problem.
  1149. vert0Color = OpenMaya.MColor()
  1150. vert1Color = OpenMaya.MColor()
  1151. vert2Color = OpenMaya.MColor()
  1152. polyIter.getColor(vert0Color, locals[0])
  1153. polyIter.getColor(vert1Color, locals[1])
  1154. polyIter.getColor(vert2Color, locals[2])
  1155.  
  1156. # Make sure it has color
  1157. if CONVERT_BLACK_VERTS_TO_WHITE:
  1158. if vert0Color == OpenMaya.MColor(0,0,0):
  1159. vert0Color = OpenMaya.MColor(1,1,1)
  1160. if vert1Color == OpenMaya.MColor(0,0,0):
  1161. vert1Color = OpenMaya.MColor(1,1,1)
  1162. if vert2Color == OpenMaya.MColor(0,0,0):
  1163. vert2Color = OpenMaya.MColor(1,1,1)
  1164. elif vert0Color == OpenMaya.MColor(0,0,0) or vert1Color == OpenMaya.MColor(0,0,0) or vert2Color == OpenMaya.MColor(0,0,0):
  1165. PrintWarning("A color on face '%s.f[%d]' is 0" % (dagPath.partialPathName(), polyIter.index()))
  1166.  
  1167. # Note: Vertices are in 0,2,1 order to make CoD happy
  1168. tris.append((
  1169. len(meshes)-1, # Shape index
  1170. materialDict[polyMaterial[0]], # Matertial index
  1171. (currentStartingVertIndex + triangleIndices[i*3], currentStartingVertIndex + triangleIndices[i*3+2], currentStartingVertIndex + triangleIndices[i*3+1]), # Vert indices
  1172. ((Us[locals[0]], 1-Vs[locals[0]]), (Us[locals[2]], 1-Vs[locals[2]]), (Us[locals[1]], 1-Vs[locals[1]])), # UVs
  1173. (vert0Color, vert2Color, vert1Color), # Colors
  1174. (OpenMaya.MVector(normals[locals[0]]), OpenMaya.MVector(normals[locals[2]]), OpenMaya.MVector(normals[locals[1]])) # Normals; Must copy the normals into a new container, because the original is destructed at the end of this poltIter iteration.
  1175. ))
  1176.  
  1177. # Next poly
  1178. polyIter.next()
  1179.  
  1180. # Update starting vertex index
  1181. currentStartingVertIndex = len(verts)
  1182.  
  1183. ProgressBarStep()
  1184.  
  1185. # Error messages
  1186. if len(meshes) == 0:
  1187. return "No meshes selected to export."
  1188. if len(verts) == 0:
  1189. return "No vertices found in selected meshes."
  1190. if len(tris) == 0:
  1191. return "No faces found in selected meshes."
  1192. if len(materials) == 0:
  1193. return "No materials found on the selected meshes."
  1194.  
  1195. # Done!
  1196. return {"meshes": meshes, "verts": verts, "faces": tris, "materials": materials}
  1197.  
  1198.  
  1199.  
  1200. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1201. # -------------------------------------------------------------------------- Export XAnim --------------------------------------------------------------------------
  1202. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1203. def ExportXAnim(filePath):
  1204. # Progress bar
  1205. numSelectedObjects = len(cmds.ls(selection=True))
  1206. if numSelectedObjects == 0:
  1207. return "Error: No objects selected for export"
  1208.  
  1209. cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue=numSelectedObjects+1)
  1210.  
  1211. # Get data
  1212. joints = GetJointList()
  1213. if len(joints) == 0:
  1214. return "Error: No joints selected for export"
  1215. if len(joints) > 128:
  1216. cmds.confirmDialog( title='WARNING', message='There is ' + str(len(joints)) + ' bones selected which exceeds WaWs 128 bone limit. (BO1 = 160, WaW = 128)' , button=['Continue'], defaultButton='Continue')
  1217.  
  1218. # Get settings
  1219. frameStart = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", query=True, value=True)
  1220. frameEnd = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", query=True, value=True)
  1221. fps = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FPSField", query=True, value=True)
  1222. if frameStart < 0 or frameStart > frameEnd:
  1223. return "Error: Invalid frame range (start < 0 or start > end)"
  1224. if fps <= 0:
  1225. return "Error: Invalid FPS (fps < 0)"
  1226.  
  1227. # Open file
  1228. f = None
  1229. try:
  1230. # Create export directory if it doesn't exist
  1231. directory = os.path.dirname(filePath)
  1232. if not os.path.exists(directory):
  1233. os.makedirs(directory)
  1234.  
  1235. # Create files
  1236. f = open(filePath, 'w')
  1237. except (IOError, OSError) as e:
  1238. typex, value, traceback = sys.exc_info()
  1239. return "Unable to create files:\n\n%s" % value.strerror
  1240.  
  1241. # Write header
  1242. f.write("// Export filename: '%s'\n" % os.path.normpath(filePath))
  1243. if cmds.file(query=True, exists=True):
  1244. f.write("// Source filename: '%s'\n" % os.path.normpath(os.path.abspath(cmds.file(query=True, sceneName=True))))
  1245. else:
  1246. f.write("// Source filename: Unsaved\n")
  1247. f.write("// Export time: %s\n\n" % datetime.datetime.now().strftime("%a %b %d %Y, %H:%M:%S"))
  1248. f.write("ANIMATION\n")
  1249. f.write("VERSION 3\n\n")
  1250.  
  1251. # Write parts
  1252. f.write("NUMPARTS %i\n" % len(joints))
  1253. for i, joint in enumerate(joints):
  1254. name = joint[1].partialPathName().split("|")
  1255. name = name[len(name)-1].split(":") # Remove namespace prefixes
  1256. name = name[len(name)-1]
  1257. f.write("PART %i \"%s\"\n" % (i, name))
  1258.  
  1259. # Write animation data
  1260. f.write("\nFRAMERATE %i\n" % fps)
  1261. f.write("NUMFRAMES %i\n" % (frameEnd-frameStart+1))
  1262.  
  1263. currentFrame = cmds.currentTime(query=True)
  1264. for i in range(frameStart, frameEnd+1):
  1265. f.write("\nFRAME %i" % i)
  1266. cmds.currentTime(i)
  1267.  
  1268. for j, joint in enumerate(joints):
  1269. f.write("\nPART %i\n" % j)
  1270. WriteJointData(f, joint[1])
  1271.  
  1272. cmds.currentTime(currentFrame)
  1273.  
  1274. # Write notetrack
  1275. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1276. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1277. notes = noteList.split(",")
  1278. cleanNotes = []
  1279.  
  1280. for note in notes:
  1281. parts = note.split(":")
  1282. if note.strip() == "" or len(parts) < 2:
  1283. continue
  1284.  
  1285. name = "".join([c for c in parts[0] if c.isalnum() or c=="_"])
  1286. if name == "":
  1287. continue
  1288.  
  1289. frame=0
  1290. try:
  1291. frame = int(parts[1])
  1292. except ValueError:
  1293. continue
  1294.  
  1295. cleanNotes.append((name, frame))
  1296.  
  1297. f.write("\nNOTETRACKS\n")
  1298. for i, joint in enumerate(joints):
  1299. if i == 0 and len(cleanNotes) > 0:
  1300. f.write("\nPART 0\nNUMTRACKS 1\nNOTETRACK 0\n")
  1301. f.write("NUMKEYS %i\n" % len(cleanNotes))
  1302. for note in cleanNotes:
  1303. f.write("FRAME %i \"%s\"\n" % (note[1], note[0]))
  1304. else:
  1305. f.write("\nPART %i\nNUMTRACKS 0\n" % i)
  1306.  
  1307. f.close()
  1308. ProgressBarStep()
  1309. cmds.refresh()
  1310.  
  1311.  
  1312.  
  1313. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1314. # ------------------------------------------------------------------------ Viewmodel Tools -------------------------------------------------------------------------
  1315. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1316. def DoesObjectExist(name, type):
  1317. if not cmds.objExists(name):
  1318. MessageBox("Error: Missing %s '%s'" % (type, name))
  1319. return False
  1320.  
  1321. return True
  1322.  
  1323. def CreateNewGunsleeveMayaFile(required_parameter):
  1324. global WarningsDuringExport
  1325.  
  1326. # Save reminder
  1327. if not SaveReminder(False):
  1328. return
  1329.  
  1330. # Get paths
  1331. filePath = cmds.file(query=True, sceneName=True)
  1332. split1 = os.path.split(filePath)
  1333. split2 = os.path.splitext(split1[1])
  1334. exportPath = os.path.join(split1[0], "gunsleeves_" + split2[0] + ".xmodel_export")
  1335.  
  1336. # Create a new file and import models
  1337. cmds.file(force=True, newFile=True)
  1338. cmds.file(os.path.join(GetRootFolder(), "bin/maya/rigs/viewmodel/ViewModel_DefMesh.mb"), i=True, type="mayaBinary")
  1339. cmds.file(filePath, i=True, type="mayaBinary")
  1340.  
  1341. # Check to make sure objects exist
  1342. if not DoesObjectExist("J_Gun", "joint"): return
  1343. if not DoesObjectExist("tag_weapon", "tag"): return
  1344. if not DoesObjectExist("GunExport", "object set"): return
  1345. if not DoesObjectExist("DefViewSkeleton", "object set"): return
  1346. if not DoesObjectExist("tag_view", "tag"): return
  1347. if not cmds.objExists("viewmodelSleeves_OpForce") and not cmds.objExists("viewmodelSleeves_Marines"):
  1348. MessageBox("Error: Missing viewsleeves 'viewmodelSleeves_OpForce' or 'viewmodelSleeves_Marines'")
  1349. return
  1350.  
  1351. # Attach gun to rig
  1352. cmds.select("J_Gun", replace=True)
  1353. cmds.select("tag_weapon", add=True)
  1354. cmds.parent()
  1355.  
  1356. # Select things to export
  1357. cmds.select("GunExport", replace=True)
  1358. cmds.select("DefViewSkeleton", toggle=True)
  1359. cmds.select("tag_view", toggle=True)
  1360. if cmds.objExists("viewmodelSleeves_OpForce"):
  1361. cmds.select("viewmodelSleeves_OpForce", toggle=True, hierarchy=True)
  1362. else:
  1363. cmds.select("viewmodelSleeves_Marines", toggle=True, hierarchy=True)
  1364.  
  1365. # Export
  1366. if cmds.control("w"+OBJECT_NAMES['progress'][0], exists=True):
  1367. cmds.deleteUI("w"+OBJECT_NAMES['progress'][0])
  1368. progressWindow = cmds.window("w"+OBJECT_NAMES['progress'][0], title=OBJECT_NAMES['progress'][1], width=302, height=22)
  1369. cmds.columnLayout()
  1370. progressControl = cmds.progressBar(OBJECT_NAMES['progress'][0], width=300)
  1371. cmds.showWindow(progressWindow)
  1372. cmds.refresh() # Force the progress bar to be drawn
  1373.  
  1374. # Export
  1375. WarningsDuringExport = 0
  1376. response = None
  1377. try:
  1378. response = ExportXModel(exportPath)
  1379. except Exception as e:
  1380. response = "An unhandled error occurred during export:\n\n" + traceback.format_exc()
  1381.  
  1382. # Delete progress bar
  1383. cmds.deleteUI(progressWindow, window=True)
  1384.  
  1385. # Handle response
  1386. if type(response) == str or type(response) == unicode:
  1387. MessageBox(response)
  1388. elif WarningsDuringExport > 0:
  1389. MessageBox("Warnings occurred during export. Check the script editor output for more details.")
  1390.  
  1391. if type(response) != str and type(response) != unicode:
  1392. MessageBox("Export saved to\n\n" + os.path.normpath(exportPath))
  1393.  
  1394. def CreateNewViewmodelRigFile(required_parameter):
  1395. # Save reminder
  1396. if not SaveReminder(False):
  1397. return
  1398.  
  1399. # Get path
  1400. filePath = cmds.file(query=True, sceneName=True)
  1401.  
  1402. # Create a new file and import models
  1403. cmds.file(force=True, newFile=True)
  1404. cmds.file(os.path.join(GetRootFolder(), "bin/maya/rigs/viewmodel/ViewModel_Rig.mb"), reference=True, type="mayaBinary", namespace="rig", options="v=0")
  1405. cmds.file(filePath, reference=True, type="mayaBinary", namespace="VM_Gun")
  1406.  
  1407. # Check to make sure objects exist
  1408. if not DoesObjectExist("VM_Gun:J_Gun", "joint"): return
  1409. if not cmds.objExists("rig:DefMesh:tag_weapon") and not cmds.objExists("ConRig:DefMesh:tag_weapon"):
  1410. MessageBox("Error: Missing viewsleeves 'rig:DefMesh:tag_weapon' or 'ConRig:DefMesh:tag_weapon'")
  1411. return
  1412.  
  1413. # Connect gun to rig
  1414. if cmds.objExists("rig:DefMesh:tag_weapon"):
  1415. cmds.select("rig:DefMesh:tag_weapon", replace=True)
  1416. else:
  1417. cmds.select("ConRig:DefMesh:tag_weapon", replace=True)
  1418.  
  1419. cmds.select("VM_Gun:J_Gun", toggle=True)
  1420. cmds.parentConstraint(weight=1, name="VMParentConstraint")
  1421. cmds.select(clear=True)
  1422.  
  1423. def SwitchGunInCurrentRigFile(required_parameter):
  1424. # Save reminder
  1425. if not SaveReminder():
  1426. return
  1427.  
  1428. # Make sure the rig is correct
  1429. if not cmds.objExists("rig:DefMesh:tag_weapon") and not cmds.objExists("ConRig:DefMesh:tag_weapon"):
  1430. MessageBox("Error: Missing rig:DefMesh:tag_weapon' or 'ConRig:DefMesh:tag_weapon'")
  1431. return
  1432.  
  1433. if not DoesObjectExist("VM_Gun:J_Gun", "joint"): return
  1434.  
  1435. # Prompt user to select a new gun file
  1436. gunPath = cmds.fileDialog2(fileMode=1, fileFilter="Maya Files (*.ma *.mb)", caption="Select a New Gun File", startingDirectory=GetRootFolder())
  1437. if gunPath == None or len(gunPath) == 0 or gunPath[0].strip() == "":
  1438. return
  1439. gunPath = gunPath[0].strip()
  1440.  
  1441. # Delete the constraint
  1442. cmds.delete("VMParentConstraint")
  1443.  
  1444. # Delete any hand attachments
  1445. if cmds.objExists("rig:Hand_Extra_RI_GRP.Parent"):
  1446. parentRI = cmds.getAttr("rig:Hand_Extra_RI_GRP.Parent")
  1447. if parentRI != "":
  1448. cmds.delete(parentRI)
  1449. if cmds.objExists("rig:Hand_Extra_LE_GRP.Parent"):
  1450. parentLE = cmds.getAttr("rig:Hand_Extra_LE_GRP.Parent")
  1451. if parentLE != "":
  1452. cmds.delete(parentLE)
  1453.  
  1454. # Switch guns
  1455. cmds.file(gunPath, loadReference="VM_GunRN");
  1456.  
  1457. # Connect gun to rig
  1458. if cmds.objExists("rig:DefMesh:tag_weapon"):
  1459. cmds.select("rig:DefMesh:tag_weapon", replace=True)
  1460. else:
  1461. cmds.select("ConRig:DefMesh:tag_weapon", replace=True)
  1462.  
  1463. cmds.select("VM_Gun:J_Gun", toggle=True)
  1464. cmds.parentConstraint(weight=1, name="VMParentConstraint")
  1465. cmds.select(clear=True)
  1466.  
  1467.  
  1468.  
  1469. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1470. # ---------------------------------------------------------------------- XModel Export Window ----------------------------------------------------------------------
  1471. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1472. def CreateXModelWindow():
  1473. # Create window
  1474. if cmds.control(OBJECT_NAMES['xmodel'][0], exists=True):
  1475. cmds.deleteUI(OBJECT_NAMES['xmodel'][0])
  1476.  
  1477. cmds.window(OBJECT_NAMES['xmodel'][0], title=OBJECT_NAMES['xmodel'][1], width=340, height=1, retain=True, maximizeButton=False)
  1478. form = cmds.formLayout(OBJECT_NAMES['xmodel'][0]+"_Form")
  1479.  
  1480. # Controls
  1481. slotDropDown = cmds.optionMenu(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown", changeCommand="CoDMayaTools.RefreshXModelWindow()", annotation="Each slot contains different a export path, settings, and saved selection")
  1482. for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
  1483. cmds.menuItem(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown"+("_s%i" % i), label="Slot %i" % i)
  1484.  
  1485. separator1 = cmds.separator(style='in', height=16)
  1486. separator2 = cmds.separator(style='in')
  1487.  
  1488. saveToLabel = cmds.text(label="Save to:", annotation="This is where the .xmodel_export is saved to")
  1489. saveToField = cmds.textField(OBJECT_NAMES['xmodel'][0]+"_SaveToField", height=21, changeCommand="CoDMayaTools.GeneralWindow_SaveToField('xmodel')", annotation="This is where the .xmodel_export is saved to")
  1490. fileBrowserButton = cmds.button(label="...", height=21, command="CoDMayaTools.GeneralWindow_FileBrowser('xmodel', \"XModel Intermediate File (*.xmodel_export)\")", annotation="Open a file browser dialog")
  1491.  
  1492. exportSelectedButton = cmds.button(label="Export Selected", command="CoDMayaTools.GeneralWindow_ExportSelected('xmodel', False)", annotation="Export all currently selected objects from the scene (current frame)\nWarning: Will automatically overwrite if the export path if it already exists")
  1493. saveSelectionButton = cmds.button(label="Save Selection", command="CoDMayaTools.GeneralWindow_SaveSelection('xmodel')", annotation="Save the current object selection")
  1494. getSavedSelectionButton = cmds.button(label="Get Saved Selection", command="CoDMayaTools.GeneralWindow_GetSavedSelection('xmodel')", annotation="Reselect the saved selection")
  1495.  
  1496. exportMultipleSlotsButton = cmds.button(label="Export Multiple Slots", command="CoDMayaTools.GeneralWindow_ExportMultiple('xmodel')", annotation="Automatically export multiple slots at once, using each slot's saved selection")
  1497. exportInMultiExportCheckbox = cmds.checkBox(OBJECT_NAMES['xmodel'][0]+"_UseInMultiExportCheckBox", label="Use current slot for Export Multiple", changeCommand="CoDMayaTools.GeneralWindow_ExportInMultiExport('xmodel')", annotation="Check this make the 'Export Multiple Slots' button export this slot")
  1498.  
  1499. # Setup form
  1500. cmds.formLayout(form, edit=True,
  1501. attachForm=[(slotDropDown, 'top', 6), (slotDropDown, 'left', 10), (slotDropDown, 'right', 10),
  1502. (separator1, 'left', 0), (separator1, 'right', 0),
  1503. (separator2, 'left', 0), (separator2, 'right', 0),
  1504. (saveToLabel, 'left', 12),
  1505. (fileBrowserButton, 'right', 10),
  1506. (exportMultipleSlotsButton, 'bottom', 6), (exportMultipleSlotsButton, 'left', 10),
  1507. (exportInMultiExportCheckbox, 'bottom', 9), (exportInMultiExportCheckbox, 'right', 6),
  1508. (exportSelectedButton, 'left', 10),
  1509. (saveSelectionButton, 'right', 10)],
  1510. #(exportSelectedButton, 'bottom', 6), (exportSelectedButton, 'left', 10),
  1511. #(saveSelectionButton, 'bottom', 6), (saveSelectionButton, 'right', 10),
  1512. #(getSavedSelectionButton, 'bottom', 6)],
  1513.  
  1514. attachControl=[ (separator1, 'top', 0, slotDropDown),
  1515. (saveToLabel, 'bottom', 9, exportSelectedButton),
  1516. (saveToField, 'bottom', 5, exportSelectedButton), (saveToField, 'left', 5, saveToLabel), (saveToField, 'right', 5, fileBrowserButton),
  1517. (fileBrowserButton, 'bottom', 5, exportSelectedButton),
  1518. (exportSelectedButton, 'bottom', 5, separator2),
  1519. (saveSelectionButton, 'bottom', 5, separator2),
  1520. (getSavedSelectionButton, 'bottom', 5, separator2), (getSavedSelectionButton, 'right', 10, saveSelectionButton),
  1521. (separator2, 'bottom', 5, exportMultipleSlotsButton)])
  1522.  
  1523. def RefreshXModelWindow():
  1524. # Refresh/create node
  1525. if len(cmds.ls(OBJECT_NAMES['xmodel'][2])) == 0:
  1526. cmds.createNode("renderLayer", name=OBJECT_NAMES['xmodel'][2], skipSelect=True)
  1527.  
  1528. cmds.lockNode(OBJECT_NAMES['xmodel'][2], lock=False)
  1529.  
  1530. if not cmds.attributeQuery("slot", node=OBJECT_NAMES['xmodel'][2], exists=True):
  1531. cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="slot", attributeType='short', defaultValue=1)
  1532. if not cmds.attributeQuery("paths", node=OBJECT_NAMES['xmodel'][2], exists=True):
  1533. cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="paths", multi=True, dataType='string')
  1534. cmds.setAttr(OBJECT_NAMES['xmodel'][2]+".paths", size=EXPORT_WINDOW_NUMSLOTS)
  1535. if not cmds.attributeQuery("selections", node=OBJECT_NAMES['xmodel'][2], exists=True):
  1536. cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="selections", multi=True, dataType='stringArray')
  1537. cmds.setAttr(OBJECT_NAMES['xmodel'][2]+".selections", size=EXPORT_WINDOW_NUMSLOTS)
  1538. if not cmds.attributeQuery("useinmultiexport", node=OBJECT_NAMES['xmodel'][2], exists=True):
  1539. cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="useinmultiexport", multi=True, attributeType='bool', defaultValue=False)
  1540. cmds.setAttr(OBJECT_NAMES['xmodel'][2]+".useinmultiexport", size=EXPORT_WINDOW_NUMSLOTS)
  1541.  
  1542. cmds.lockNode(OBJECT_NAMES['xmodel'][2], lock=True)
  1543.  
  1544. # Set values
  1545. slotIndex = cmds.optionMenu(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown", query=True, select=True)
  1546. path = cmds.getAttr(OBJECT_NAMES['xmodel'][2]+(".paths[%i]" % slotIndex))
  1547. cmds.setAttr(OBJECT_NAMES['xmodel'][2]+".slot", slotIndex)
  1548. cmds.textField(OBJECT_NAMES['xmodel'][0]+"_SaveToField", edit=True, fileName=path)
  1549.  
  1550. useInMultiExport = cmds.getAttr(OBJECT_NAMES['xmodel'][2]+(".useinmultiexport[%i]" % slotIndex))
  1551. cmds.checkBox(OBJECT_NAMES['xmodel'][0]+"_UseInMultiExportCheckBox", edit=True, value=useInMultiExport)
  1552.  
  1553.  
  1554.  
  1555. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1556. # ----------------------------------------------------------------------- XAnim Export Window ----------------------------------------------------------------------
  1557. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1558. def CreateXAnimWindow():
  1559. # Create window
  1560. if cmds.control(OBJECT_NAMES['xanim'][0], exists=True):
  1561. cmds.deleteUI(OBJECT_NAMES['xanim'][0])
  1562.  
  1563. cmds.window(OBJECT_NAMES['xanim'][0], title=OBJECT_NAMES['xanim'][1], width=1, height=1, retain=True, maximizeButton=False)
  1564. form = cmds.formLayout(OBJECT_NAMES['xanim'][0]+"_Form")
  1565.  
  1566. # Controls
  1567. slotDropDown = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", changeCommand="CoDMayaTools.RefreshXAnimWindow()", annotation="Each slot contains different a export path, frame range, notetrack, and saved selection")
  1568. for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
  1569. cmds.menuItem(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown"+("_s%i" % i), label="Slot %i" % i)
  1570.  
  1571. separator1 = cmds.separator(style='in')
  1572. separator2 = cmds.separator(style='in')
  1573. separator3 = cmds.separator(style='in')
  1574.  
  1575. framesLabel = cmds.text(label="Frames:", annotation="Range of frames to export")
  1576. framesStartField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", height=21, width=35, minValue=0, changeCommand=XAnimWindow_UpdateFrameRange, annotation="Starting frame to export (inclusive)")
  1577. framesToLabel = cmds.text(label="to")
  1578. framesEndField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", height=21, width=35, minValue=0, changeCommand=XAnimWindow_UpdateFrameRange, annotation="Ending frame to export (inclusive)")
  1579. fpsLabel = cmds.text(label="FPS:")
  1580. fpsField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FPSField", height=21, width=35, value=1, minValue=1, changeCommand=XAnimWindow_UpdateFramerate, annotation="Animation FPS")
  1581.  
  1582. notetracksLabel = cmds.text(label="Notetrack:", annotation="Notetrack info for the animation")
  1583. noteList = cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", allowMultiSelection=False, selectCommand=XAnimWindow_SelectNote, annotation="List of notes in the notetrack")
  1584. addNoteButton = cmds.button(label="Add Note", width=75, command=XAnimWindow_AddNote, annotation="Add a note to the notetrack")
  1585. ReadNotesButton = cmds.button(label="Grab Notes", width=75, command=ReadXanimNotes, annotation="Grab Notes from Notetrack in Outliner")
  1586. RenameNoteTrack = cmds.button(label="Rename Note", command=RenameXanimNotes, annotation="Rename the currently selected note.")
  1587. removeNoteButton = cmds.button(label="Remove Note", command=XAnimWindow_RemoveNote, annotation="Remove the currently selected note from the notetrack")
  1588. noteFrameLabel = cmds.text(label="Frame:", annotation="The frame the currently selected note is applied to")
  1589. noteFrameField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_NoteFrameField", changeCommand=XAnimWindow_UpdateNoteFrame, height=21, width=30, minValue=0, annotation="The frame the currently selected note is applied to")
  1590.  
  1591. saveToLabel = cmds.text(label="Save to:", annotation="This is where .xanim_export is saved to")
  1592. saveToField = cmds.textField(OBJECT_NAMES['xanim'][0]+"_SaveToField", height=21, changeCommand="CoDMayaTools.GeneralWindow_SaveToField('xanim')", annotation="This is where .xanim_export is saved to")
  1593. fileBrowserButton = cmds.button(label="...", height=21, command="CoDMayaTools.GeneralWindow_FileBrowser('xanim', \"XAnim Intermediate File (*.xanim_export)\")", annotation="Open a file browser dialog")
  1594.  
  1595. exportSelectedButton = cmds.button(label="Export Selected", command="CoDMayaTools.GeneralWindow_ExportSelected('xanim', False)", annotation="Export all currently selected joints from the scene (specified frames)\nWarning: Will automatically overwrite if the export path if it already exists")
  1596. saveSelectionButton = cmds.button(label="Save Selection", command="CoDMayaTools.GeneralWindow_SaveSelection('xanim')", annotation="Save the current object selection")
  1597. getSavedSelectionButton = cmds.button(label="Get Saved Selection", command="CoDMayaTools.GeneralWindow_GetSavedSelection('xanim')", annotation="Reselect the saved selection")
  1598.  
  1599. exportMultipleSlotsButton = cmds.button(label="Export Multiple Slots", command="CoDMayaTools.GeneralWindow_ExportMultiple('xanim')", annotation="Automatically export multiple slots at once, using each slot's saved selection")
  1600. exportInMultiExportCheckbox = cmds.checkBox(OBJECT_NAMES['xanim'][0]+"_UseInMultiExportCheckBox", label="Use current slot for Export Multiple", changeCommand="CoDMayaTools.GeneralWindow_ExportInMultiExport('xanim')", annotation="Check this make the 'Export Multiple Slots' button export this slot")
  1601. IgnoreUslessNotes = cmds.checkBox("Scoba_IgnoreUslessNotes", label="Ignore Useless Notes like reload_large, etc.", annotation="Check this if you want to ignre notes like reload_large, etc.", value=True)
  1602. # Setup form
  1603. cmds.formLayout(form, edit=True,
  1604. attachForm=[(slotDropDown, 'top', 6), (slotDropDown, 'left', 10), (slotDropDown, 'right', 10),
  1605. (separator1, 'left', 0), (separator1, 'right', 0),
  1606. (framesLabel, 'left', 10),
  1607. (fpsLabel, 'left', 10),
  1608. (notetracksLabel, 'left', 10),
  1609. (noteList, 'left', 10),
  1610. (IgnoreUslessNotes, 'left', 10),
  1611. (addNoteButton, 'right', 10),
  1612. (ReadNotesButton, 'right', 10),
  1613. (RenameNoteTrack, 'right', 10),
  1614. (removeNoteButton, 'right', 10),
  1615. (noteFrameField, 'right', 10),
  1616. (separator2, 'left', 0), (separator2, 'right', 0),
  1617. (saveToLabel, 'left', 12),
  1618. (fileBrowserButton, 'right', 10),
  1619. (exportMultipleSlotsButton, 'bottom', 6), (exportMultipleSlotsButton, 'left', 10),
  1620. (exportInMultiExportCheckbox, 'bottom', 9), (exportInMultiExportCheckbox, 'right', 6),
  1621. (exportSelectedButton, 'left', 10),
  1622. (saveSelectionButton, 'right', 10),
  1623. (separator3, 'left', 0), (separator3, 'right', 0)],
  1624.  
  1625. attachControl=[ (separator1, 'top', 6, slotDropDown),
  1626. (framesLabel, 'top', 8, separator1),
  1627. (framesStartField, 'top', 5, separator1), (framesStartField, 'left', 4, framesLabel),
  1628. (framesToLabel, 'top', 8, separator1), (framesToLabel, 'left', 4+35+4, framesLabel),
  1629. (framesEndField, 'top', 5, separator1), (framesEndField, 'left', 4, framesToLabel),
  1630. (fpsLabel, 'top', 8, framesStartField),
  1631. (fpsField, 'top', 5, framesStartField), (fpsField, 'left', 21, fpsLabel),
  1632. (notetracksLabel, 'top', 5, fpsField),
  1633. (noteList, 'top', 5, notetracksLabel), (noteList, 'right', 10, removeNoteButton), (noteList, 'bottom', 30, separator2),
  1634. (IgnoreUslessNotes, 'top', 10, noteList), (IgnoreUslessNotes, 'right', 10, removeNoteButton),
  1635. (addNoteButton, 'top', 5, notetracksLabel),
  1636. (ReadNotesButton, 'top', 5, addNoteButton),
  1637. (RenameNoteTrack, 'top', 5, ReadNotesButton),
  1638. (removeNoteButton, 'top', 5, RenameNoteTrack),
  1639. (noteFrameField, 'top', 5, removeNoteButton),
  1640. (noteFrameLabel, 'top', 8, removeNoteButton), (noteFrameLabel, 'right', 4, noteFrameField),
  1641. (separator2, 'bottom', 5, fileBrowserButton),
  1642. (saveToLabel, 'bottom', 10, exportSelectedButton),
  1643. (saveToField, 'bottom', 5, exportSelectedButton), (saveToField, 'left', 5, saveToLabel), (saveToField, 'right', 5, fileBrowserButton),
  1644. (fileBrowserButton, 'bottom', 5, exportSelectedButton),
  1645. (exportSelectedButton, 'bottom', 5, separator3),
  1646. (saveSelectionButton, 'bottom', 5, separator3),
  1647. (getSavedSelectionButton, 'bottom', 5, separator3), (getSavedSelectionButton, 'right', 10, saveSelectionButton),
  1648. (separator3, 'bottom', 5, exportMultipleSlotsButton)
  1649. ])
  1650.  
  1651. def XAnimWindow_UpdateFrameRange(required_parameter):
  1652. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1653. start = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", query=True, value=True)
  1654. end = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", query=True, value=True)
  1655. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".frameRanges[%i]" % slotIndex), start, end, type='long2')
  1656.  
  1657. def XAnimWindow_UpdateFramerate(required_parameter):
  1658. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1659. fps = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FPSField", query=True, value=True)
  1660. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".framerate[%i]" % slotIndex), fps)
  1661.  
  1662. def XAnimWindow_AddNote(required_parameter):
  1663. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1664. if cmds.promptDialog(title="Add Note to Slot %i's Notetrack" % slotIndex, message="Enter the note's name:\t\t ") != "Confirm":
  1665. return
  1666.  
  1667. userInput = cmds.promptDialog(query=True, text=True)
  1668. noteName = "".join([c for c in userInput if c.isalnum() or c=="_"]) # Remove all non-alphanumeric characters
  1669. if noteName == "":
  1670. MessageBox("Invalid note name")
  1671. return
  1672.  
  1673. existingItems = cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", query=True, allItems=True)
  1674.  
  1675. if existingItems != None and noteName in existingItems:
  1676. MessageBox("A note with this name already exists")
  1677.  
  1678. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1679. noteList += "%s:%i," % (noteName, cmds.currentTime(query=True))
  1680. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
  1681.  
  1682. cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, append=noteName, selectIndexedItem=len((existingItems or []))+1)
  1683. XAnimWindow_SelectNote()
  1684.  
  1685. def ReadXanimNotes(required_parameter):
  1686. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1687. existingItems = cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", query=True, allItems=True)
  1688. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1689.  
  1690. isWraithAnim = False
  1691.  
  1692. isNotWraithAnimButHasNoteTrack = False
  1693.  
  1694. if cmds.objExists('WraithNotes'):
  1695. isWraithAnim = True
  1696.  
  1697. if cmds.objExists('NoteTrack'):
  1698. isNotWraithAnimButHasNoteTrack = True
  1699.  
  1700. if cmds.objExists('WraithNotes') and cmds.objExists('NoteTrack'):
  1701. cmds.confirmDialog( title='ERROR', message='WraithNotes and NoteTrack both exist in this scene, please delete one and try again.' , button=['Ok'], defaultButton='Ok')
  1702. return
  1703.  
  1704.  
  1705.  
  1706. if isWraithAnim:
  1707. cmds.select( clear=True )
  1708. cmds.select( 'WraithNotes', hi=True )
  1709. cmds.select( 'WraithNotes', d=True ) # Select WraithNotes and it's children and then deselect it to avoid issues.
  1710.  
  1711. notes = cmds.ls( selection=True ) # Grab what is selected.
  1712.  
  1713. for NoteTrack in notes: # Go through each one.
  1714. if not "Shape" in NoteTrack: # Avoid ones with Shape at end.
  1715. for note in cmds.keyframe(NoteTrack, attribute="translateX", sl=False, q=True, tc=True): # See where are the keyframes.
  1716. IsUneededNote = ( # If you find a Note that is not needed in WaW and you want to remove it from further anims add it here:
  1717. NoteTrack == "reload_large"
  1718. or NoteTrack == "reload_small"
  1719. or NoteTrack == "reload_medium"
  1720. )
  1721. if cmds.checkBox("Scoba_IgnoreUslessNotes", query=True, value=True) and IsUneededNote:
  1722. continue
  1723. if NoteTrack == "end": # End is not allowed, completely ignore.
  1724. continue
  1725. noteList += "%s:%i," % (NoteTrack, note) # Add Notes to Aidan's list.
  1726. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
  1727. cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, append=NoteTrack, selectIndexedItem=len((existingItems or []))+1)
  1728. elif isNotWraithAnimButHasNoteTrack:
  1729. for note in cmds.keyframe("NoteTrack", attribute="MainNote", sl=False, q=True, tc=True): # cmds.keyframe("NoteTrack", attribute="MainNote", sl=False, q=True, tc=True) lists all the keyframes for this object's attribute, so we loop through it.
  1730. noteName = cmds.getAttr('NoteTrack.MainNote',x=True, asString=True, t=note) # Here is where we grab the Note from the attribute "MainNote", asString allows us to return it as string instead of intiger.
  1731. IsUneededNote = ( # If you find a Note that is not needed in WaW and you want to remove it from further anims add it here:
  1732. noteName == "reload_large"
  1733. or noteName == "reload_small"
  1734. or noteName == "reload_medium"
  1735. )
  1736. if cmds.checkBox("Scoba_IgnoreUslessNotes", query=True, value=True) and IsUneededNote:
  1737. continue
  1738. if noteName == "end": # End is not allowed, completely ignore.
  1739. continue
  1740. if "sndnt#" in noteName:
  1741. noteName = noteName[6:] # This essentially, in laymans terms, strips the notetrack's name of the first 6 characters if it contains "sndnt#" in the name.
  1742. if "rmbnt#" in noteName:
  1743. noteName = noteName[6:]
  1744. noteList += "%s:%i," % (noteName, note) # Add Notes to Aidan's list.
  1745. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
  1746. cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, append=noteName, selectIndexedItem=len((existingItems or []))+1)
  1747. else:
  1748. cmds.confirmDialog( title='ERROR', message='Can\'t find Notetracks for Wriath Anim or Normal anim.' , button=['Ok'], defaultButton='Ok')
  1749.  
  1750. XAnimWindow_SelectNote()
  1751.  
  1752. def RenameXanimNotes(required_parameter):
  1753. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1754. currentIndex = cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", query=True, selectIndexedItem=True)
  1755. if currentIndex != None and len(currentIndex) > 0 and currentIndex[0] >= 1:
  1756. if cmds.promptDialog(title="Rename NoteTrack in slot", message="Enter new notetrack name:\t\t ") != "Confirm":
  1757. return
  1758.  
  1759. userInput = cmds.promptDialog(query=True, text=True)
  1760. noteName = "".join([c for c in userInput if c.isalnum() or c=="_"]) # Remove all non-alphanumeric characters
  1761. if noteName == "":
  1762. MessageBox("Invalid note name")
  1763. return
  1764. currentIndex = currentIndex[0]
  1765. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1766. notes = noteList.split(",")
  1767. noteInfo = notes[currentIndex-1].split(":")
  1768. note = int(noteInfo[1])
  1769. NoteTrack = userInput
  1770.  
  1771. # REMOVE NOTE
  1772.  
  1773. cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, removeIndexedItem=currentIndex)
  1774. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1775. notes = noteList.split(",")
  1776. del notes[currentIndex-1]
  1777. noteList = ",".join(notes)
  1778. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
  1779.  
  1780. # REMOVE NOTE
  1781. print int(noteInfo[1])
  1782. print userInput
  1783. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1784. noteList += "%s:%i," % (NoteTrack, note) # Add Notes to Aidan's list.
  1785. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
  1786. cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, append=NoteTrack, selectIndexedItem=currentIndex)
  1787. XAnimWindow_SelectNote()
  1788.  
  1789.  
  1790.  
  1791. def XAnimWindow_RemoveNote(required_parameter):
  1792. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1793. currentIndex = cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", query=True, selectIndexedItem=True)
  1794. if currentIndex != None and len(currentIndex) > 0 and currentIndex[0] >= 1:
  1795. currentIndex = currentIndex[0]
  1796. cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, removeIndexedItem=currentIndex)
  1797. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1798. notes = noteList.split(",")
  1799. del notes[currentIndex-1]
  1800. noteList = ",".join(notes)
  1801. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
  1802. XAnimWindow_SelectNote()
  1803.  
  1804. def XAnimWindow_UpdateNoteFrame(newFrame):
  1805. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1806. currentIndex = cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", query=True, selectIndexedItem=True)
  1807. if currentIndex != None and len(currentIndex) > 0 and currentIndex[0] >= 1:
  1808. currentIndex = currentIndex[0]
  1809. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1810. notes = noteList.split(",")
  1811. parts = notes[currentIndex-1].split(":")
  1812. if len(parts) < 2:
  1813. error("Error parsing notetrack string (A) at %i: %s" % (currentIndex, noteList))
  1814. notes[currentIndex-1] = "%s:%i" % (parts[0], newFrame)
  1815. noteList = ",".join(notes)
  1816. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
  1817.  
  1818. def XAnimWindow_SelectNote():
  1819. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1820. currentIndex = cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", query=True, selectIndexedItem=True)
  1821. if currentIndex != None and len(currentIndex) > 0 and currentIndex[0] >= 1:
  1822. currentIndex = currentIndex[0]
  1823. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1824. notes = noteList.split(",")
  1825. parts = notes[currentIndex-1].split(":")
  1826. if len(parts) < 2:
  1827. error("Error parsing notetrack string (B) at %i: %s" % (currentIndex, noteList))
  1828.  
  1829. frame=0
  1830. try:
  1831. frame = int(parts[1])
  1832. except ValueError:
  1833. pass
  1834.  
  1835. noteFrameField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_NoteFrameField", edit=True, value=frame)
  1836.  
  1837. def RefreshXAnimWindow():
  1838. # Refresh/create node
  1839. if len(cmds.ls(OBJECT_NAMES['xanim'][2])) == 0:
  1840. cmds.createNode("renderLayer", name=OBJECT_NAMES['xanim'][2], skipSelect=True)
  1841.  
  1842. cmds.lockNode(OBJECT_NAMES['xanim'][2], lock=False)
  1843.  
  1844. if not cmds.attributeQuery("slot", node=OBJECT_NAMES['xanim'][2], exists=True):
  1845. cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="slot", attributeType='short', defaultValue=1)
  1846. if not cmds.attributeQuery("paths", node=OBJECT_NAMES['xanim'][2], exists=True):
  1847. cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="paths", multi=True, dataType='string')
  1848. cmds.setAttr(OBJECT_NAMES['xanim'][2]+".paths", size=EXPORT_WINDOW_NUMSLOTS)
  1849. if not cmds.attributeQuery("selections", node=OBJECT_NAMES['xanim'][2], exists=True):
  1850. cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="selections", multi=True, dataType='stringArray')
  1851. cmds.setAttr(OBJECT_NAMES['xanim'][2]+".selections", size=EXPORT_WINDOW_NUMSLOTS)
  1852. if not cmds.attributeQuery("frameRanges", node=OBJECT_NAMES['xanim'][2], exists=True):
  1853. cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="frameRanges", multi=True, dataType='long2')
  1854. cmds.setAttr(OBJECT_NAMES['xanim'][2]+".frameRanges", size=EXPORT_WINDOW_NUMSLOTS)
  1855. if not cmds.attributeQuery("framerate", node=OBJECT_NAMES['xanim'][2], exists=True):
  1856. cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="framerate", multi=True, attributeType='long', defaultValue=30)
  1857. cmds.setAttr(OBJECT_NAMES['xanim'][2]+".framerate", size=EXPORT_WINDOW_NUMSLOTS)
  1858. if not cmds.attributeQuery("notetracks", node=OBJECT_NAMES['xanim'][2], exists=True):
  1859. cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="notetracks", multi=True, dataType='string') # Formatted as "<name>:<frame>,<name>:<frame>,..."
  1860. cmds.setAttr(OBJECT_NAMES['xanim'][2]+".notetracks", size=EXPORT_WINDOW_NUMSLOTS)
  1861. if not cmds.attributeQuery("useinmultiexport", node=OBJECT_NAMES['xanim'][2], exists=True):
  1862. cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="useinmultiexport", multi=True, attributeType='bool', defaultValue=False)
  1863. cmds.setAttr(OBJECT_NAMES['xanim'][2]+".useinmultiexport", size=EXPORT_WINDOW_NUMSLOTS)
  1864.  
  1865. cmds.lockNode(OBJECT_NAMES['xanim'][2], lock=True)
  1866.  
  1867. # Set values
  1868. slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
  1869. cmds.setAttr(OBJECT_NAMES['xanim'][2]+".slot", slotIndex)
  1870.  
  1871. path = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".paths[%i]" % slotIndex))
  1872. cmds.textField(OBJECT_NAMES['xanim'][0]+"_SaveToField", edit=True, fileName=path)
  1873.  
  1874. frameRange = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".frameRanges[%i]" % slotIndex))
  1875. if frameRange == None:
  1876. cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".frameRanges[%i]" % slotIndex), 0, 0, type='long2')
  1877. cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", edit=True, value=0)
  1878. cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", edit=True, value=0)
  1879. else:
  1880. cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", edit=True, value=frameRange[0][0])
  1881. cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", edit=True, value=frameRange[0][1])
  1882.  
  1883. framerate = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".framerate[%i]" % slotIndex))
  1884. cmds.intField(OBJECT_NAMES['xanim'][0]+"_FPSField", edit=True, value=framerate)
  1885.  
  1886. noteFrameField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_NoteFrameField", edit=True, value=0)
  1887. cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, removeAll=True)
  1888. noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
  1889. notes = noteList.split(",")
  1890. for note in notes:
  1891. parts = note.split(":")
  1892. if note.strip() == "" or len(parts) == 0:
  1893. continue
  1894.  
  1895. name = "".join([c for c in parts[0] if c.isalnum() or c=="_"])
  1896. if name == "":
  1897. continue
  1898.  
  1899. cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, append=name)
  1900.  
  1901. useInMultiExport = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".useinmultiexport[%i]" % slotIndex))
  1902. cmds.checkBox(OBJECT_NAMES['xanim'][0]+"_UseInMultiExportCheckBox", edit=True, value=useInMultiExport)
  1903.  
  1904.  
  1905.  
  1906. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1907. # ---------------------------------------------------------------------- General Export Window ---------------------------------------------------------------------
  1908. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1909. # GeneralWindow_... are callback functions that are used by both export windows
  1910. def GeneralWindow_SaveToField(windowID):
  1911. slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
  1912. filePath = cmds.textField(OBJECT_NAMES[windowID][0]+"_SaveToField", query=True, fileName=True)
  1913. cmds.setAttr(OBJECT_NAMES[windowID][2]+(".paths[%i]" % slotIndex), filePath, type='string')
  1914.  
  1915. def GeneralWindow_FileBrowser(windowID, formatExtension):
  1916. defaultFolder = GetRootFolder()
  1917. if windowID == 'xanim':
  1918. defaultFolder = os.path.join(defaultFolder, 'xanim_export/')
  1919. elif windowID == 'xmodel':
  1920. defaultFolder = os.path.join(defaultFolder, 'model_export/')
  1921. saveTo = cmds.fileDialog2(fileMode=0, fileFilter=formatExtension, caption="Export To", startingDirectory=defaultFolder)
  1922. if saveTo == None or len(saveTo) == 0 or saveTo[0].strip() == "":
  1923. return
  1924. saveTo = saveTo[0].strip()
  1925.  
  1926. cmds.textField(OBJECT_NAMES[windowID][0]+"_SaveToField", edit=True, fileName=saveTo)
  1927. GeneralWindow_SaveToField(windowID)
  1928.  
  1929. def GeneralWindow_SaveSelection(windowID):
  1930. slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
  1931. selection = cmds.ls(selection=True)
  1932. if selection == None or len(selection) == 0:
  1933. return
  1934. cmds.setAttr(OBJECT_NAMES[windowID][2]+(".selections[%i]" % slotIndex), len(selection), *selection, type='stringArray')
  1935.  
  1936. def GeneralWindow_GetSavedSelection(windowID):
  1937. slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
  1938. selection = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".selections[%i]" % slotIndex))
  1939.  
  1940. validSelection = []
  1941. for obj in selection:
  1942. if cmds.objExists(obj):
  1943. validSelection.append(obj)
  1944.  
  1945. # Remove non-existing objects from the saved list
  1946. cmds.setAttr(OBJECT_NAMES[windowID][2]+(".selections[%i]" % slotIndex), len(validSelection), *validSelection, type='stringArray')
  1947.  
  1948. if validSelection == None or len(validSelection) == 0:
  1949. MessageBox("No selection saved to slot %i" % slotIndex)
  1950. return False
  1951.  
  1952. cmds.select(validSelection)
  1953. return True
  1954.  
  1955. def GeneralWindow_ExportSelected(windowID, exportingMultiple):
  1956. global WarningsDuringExport
  1957.  
  1958. slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
  1959.  
  1960. # Get path
  1961. filePath = cmds.textField(OBJECT_NAMES[windowID][0]+"_SaveToField", query=True, fileName=True)
  1962. if filePath.strip() == "":
  1963. if exportingMultiple:
  1964. MessageBox("Invalid path on slot %i:\n\nPath is empty." % slotIndex)
  1965. else:
  1966. MessageBox("Invalid path:\n\nPath is empty.")
  1967. return
  1968.  
  1969. if os.path.isdir(filePath):
  1970. if exportingMultiple:
  1971. MessageBox("Invalid path on slot %i:\n\nPath points to an existing directory." % slotIndex)
  1972. else:
  1973. MessageBox("Invalid path:\n\nPath points to an existing directory.")
  1974. return
  1975.  
  1976. # Save reminder
  1977. if not exportingMultiple and not SaveReminder():
  1978. return
  1979.  
  1980. # Progress bar
  1981. if cmds.control("w"+OBJECT_NAMES['progress'][0], exists=True):
  1982. cmds.deleteUI("w"+OBJECT_NAMES['progress'][0])
  1983. progressWindow = cmds.window("w"+OBJECT_NAMES['progress'][0], title=OBJECT_NAMES['progress'][1], width=302, height=22)
  1984. cmds.columnLayout()
  1985. progressControl = cmds.progressBar(OBJECT_NAMES['progress'][0], width=300)
  1986. cmds.showWindow(progressWindow)
  1987. cmds.refresh() # Force the progress bar to be drawn
  1988.  
  1989. # Export
  1990. if not exportingMultiple:
  1991. WarningsDuringExport = 0
  1992. response = None
  1993. try:
  1994. exec("response = %s(\"%s\")" % (OBJECT_NAMES[windowID][4], filePath))
  1995. except Exception as e:
  1996. response = "An unhandled error occurred during export:\n\n" + traceback.format_exc()
  1997.  
  1998. # Delete progress bar
  1999. cmds.deleteUI(progressWindow, window=True)
  2000.  
  2001. # Handle response
  2002.  
  2003. if type(response) == str or type(response) == unicode:
  2004. if exportingMultiple:
  2005. MessageBox("Slot %i\n\n%s" % (slotIndex, response))
  2006. else:
  2007. MessageBox(response)
  2008. elif WarningsDuringExport > 0 and not exportingMultiple:
  2009. MessageBox("Warnings occurred during export. Check the script editor output for more details.")
  2010.  
  2011. def GeneralWindow_ExportMultiple(windowID):
  2012. originalSlotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
  2013. any = False
  2014. for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
  2015. useInMultiExport = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".useinmultiexport[%i]" % i))
  2016. if useInMultiExport:
  2017. any = True
  2018. break
  2019.  
  2020. if not any:
  2021. MessageBox("No slots set to export.")
  2022. return
  2023.  
  2024. if not SaveReminder():
  2025. return
  2026.  
  2027. WarningsDuringExport = 0
  2028. originalSelection = cmds.ls(selection=True)
  2029.  
  2030. for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
  2031. useInMultiExport = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".useinmultiexport[%i]" % i))
  2032. if useInMultiExport:
  2033. print "Exporting slot %i in multiexport" % i
  2034. cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", edit=True, select=i)
  2035. exec(OBJECT_NAMES[windowID][3] + "()") # Refresh window
  2036. if GeneralWindow_GetSavedSelection(windowID):
  2037. GeneralWindow_ExportSelected(windowID, True)
  2038.  
  2039. if originalSelection == None or len(originalSelection) == 0:
  2040. cmds.select(clear=True)
  2041. else:
  2042. cmds.select(originalSelection)
  2043.  
  2044. if WarningsDuringExport > 0:
  2045. MessageBox("Warnings occurred during export. Check the script editor output for more details.")
  2046.  
  2047. # Reset slot
  2048. cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", edit=True, select=originalSlotIndex)
  2049. exec(OBJECT_NAMES[windowID][3] + "()") # Refresh window
  2050.  
  2051. def GeneralWindow_ExportInMultiExport(windowID):
  2052. slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
  2053. useInMultiExport = cmds.checkBox(OBJECT_NAMES[windowID][0]+"_UseInMultiExportCheckBox", query=True, value=True)
  2054. cmds.setAttr(OBJECT_NAMES[windowID][2]+(".useinmultiexport[%i]" % slotIndex), useInMultiExport)
  2055.  
  2056.  
  2057.  
  2058. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2059. # --------------------------------------------------------------------------- General GUI --------------------------------------------------------------------------
  2060. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2061. def SaveReminder(allowUnsaved=True):
  2062. if cmds.file(query=True, modified=True):
  2063. if cmds.file(query=True, exists=True):
  2064. result = cmds.confirmDialog(message="Save changes to %s?" % cmds.file(query=True, sceneName=True), button=["Yes", "No", "Cancel"], defaultButton="Yes", title="Save Changes")
  2065. if result == "Yes":
  2066. cmds.file(save=True)
  2067. elif result != "No":
  2068. return False
  2069. else: # The file has never been saved (has no name)
  2070. if allowUnsaved:
  2071. result = cmds.confirmDialog(message="The current scene is not saved. Continue?", button=["Yes", "No"], defaultButton="Yes", title="Save Changes")
  2072. if result != "Yes":
  2073. return False
  2074. else:
  2075. MessageBox("The scene needs to be saved first")
  2076. return False
  2077.  
  2078. return True
  2079.  
  2080. def PrintWarning(message):
  2081. global WarningsDuringExport
  2082. if WarningsDuringExport < MAX_WARNINGS_SHOWN:
  2083. print "WARNING: %s" % message
  2084. WarningsDuringExport += 1
  2085. elif WarningsDuringExport == MAX_WARNINGS_SHOWN:
  2086. print "More warnings not shown because printing text is slow...\n"
  2087. WarningsDuringExport = MAX_WARNINGS_SHOWN+1
  2088.  
  2089. def MessageBox(message):
  2090. cmds.confirmDialog(message=message, button='OK', defaultButton='OK', title=OBJECT_NAMES['menu'][1])
  2091.  
  2092. def ShowWindow(windowID):
  2093. exec(OBJECT_NAMES[windowID][3] + "()") # Refresh window
  2094. cmds.showWindow(OBJECT_NAMES[windowID][0])
  2095.  
  2096. def ProgressBarStep():
  2097. cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, step=1)
  2098.  
  2099. def AboutWindow():
  2100. result = cmds.confirmDialog(message="Call of Duty Tools for Maya, created by Aidan Shafran (with assistance from The Internet).\n\nThis script is under the GNU General Public License. You may modify or redistribute this script, however it comes with no warranty. Go to http://www.gnu.org/licenses/ for more details.", button=['OK', 'Visit Forum Topic', 'CoD File Formats'], defaultButton='OK', title="About " + OBJECT_NAMES['menu'][1])
  2101. if result == "Visit Forum Topic":
  2102. GoToForumTopic()
  2103. elif result == "CoD File Formats":
  2104. webbrowser.open("http://aidanshafran.com/codmayatools/codformats.html")
  2105.  
  2106. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2107. # --------------------------------------------------------------------------- Versioning ---------------------------------------------------------------------------
  2108. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2109. def HasInternetAccess():
  2110. # http://stackoverflow.com/questions/3764291/checking-network-connection
  2111. try:
  2112. urllib2.urlopen('http://74.125.228.100', timeout=1) # Use IP address (google.com) instead of domain name, to avoid DNS lookup time
  2113. return True
  2114. except urllib2.URLError as err:
  2115. return False
  2116.  
  2117. def CheckForUpdates():
  2118. try:
  2119. if not HasInternetAccess(): # Apparently, the timeout does not affect DNS lookup time, so internet connectivity needs to be checked before getting update info to avoid long script load times if there is no internet
  2120. return None
  2121.  
  2122. response = urllib2.urlopen(VERSION_CHECK_URL, timeout=2)
  2123. info = response.readlines()
  2124. response.close()
  2125.  
  2126. if not info or len(info) == 0:
  2127. return None
  2128.  
  2129. mostRecentVersion = float(info[0])
  2130. downloadUpdateURL = info[1] # Location of the most recent file
  2131.  
  2132. if mostRecentVersion > FILE_VERSION:
  2133. return (mostRecentVersion, downloadUpdateURL)
  2134. except Exception:
  2135. pass
  2136.  
  2137. return None
  2138.  
  2139. def DownloadUpdate(downloadUpdateURL):
  2140. try:
  2141. if not HasInternetAccess():
  2142. return None
  2143.  
  2144. response = urllib2.urlopen(downloadUpdateURL, timeout=5)
  2145. newCode = response.read()
  2146. response.close()
  2147.  
  2148. root, ext = os.path.splitext(__file__)
  2149. updateFile = root + ".py"
  2150.  
  2151. file = open(updateFile, 'w')
  2152. file.write(newCode)
  2153. file.close()
  2154.  
  2155. MessageBox("The script has been updated. Click 'Reload Script' or restart Maya to apply the changes.")
  2156. except Exception:
  2157. result = cmds.confirmDialog(message="Something went wrong while updating. You can download the update manually from the forum topic.", button=['OK', 'Visit Forum Topic'], defaultButton='OK', title=OBJECT_NAMES['menu'][1])
  2158. if result == "Visit Forum Topic":
  2159. GoToForumTopic()
  2160.  
  2161. def GoToForumTopic():
  2162. webbrowser.open("http://www.ugx-mods.com/forum/index.php?topic=1295.0")
  2163.  
  2164.  
  2165.  
  2166. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2167. # ----------------------------------------------------------------------- Get/Set Root Folder ----------------------------------------------------------------------
  2168. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2169. def SetRootFolder(msg=None, game="CoD5"):
  2170. # Get current root folder (this also makes sure the reg key exists)
  2171. codRootPath = GetRootFolder(False, game)
  2172.  
  2173. # Open input box
  2174. if cmds.promptDialog(title="Set %s Root Path" % game, message=msg or "Change your %s root path:\t\t\t" % game, text=codRootPath) != "Confirm":
  2175. return None
  2176.  
  2177. codRootPath = cmds.promptDialog(query=True, text=True)
  2178.  
  2179. # Check to make sure the path exists
  2180. if not os.path.isdir(codRootPath):
  2181. MessageBox("Given root path does not exist")
  2182. return None
  2183.  
  2184. # Set path
  2185. storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_SET_VALUE)
  2186. reg.SetValueEx(storageKey, "%sRootPath" % game, 0, reg.REG_SZ, codRootPath)
  2187. reg.CloseKey(storageKey)
  2188.  
  2189. return codRootPath
  2190.  
  2191. def GetRootFolder(firstTimePrompt=True, game="CoD5"):
  2192. codRootPath = ""
  2193.  
  2194. try:
  2195. storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
  2196. codRootPath = reg.QueryValueEx(storageKey, "%sRootPath" % game)[0]
  2197. reg.CloseKey(storageKey)
  2198. except WindowsError:
  2199. # First time, create key
  2200. storageKey = reg.CreateKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
  2201.  
  2202. # Try to get root path from cod registry value
  2203. try:
  2204. if game == "CoD5":
  2205. codKey = reg.OpenKey(reg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\Activision\\Call of Duty WAW")
  2206. codRootPath = reg.QueryValueEx(codKey, "InstallPath")[0]
  2207. reg.CloseKey(codKey)
  2208. elif game == "CoD4":
  2209. codKey = reg.OpenKey(reg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\Activision\\Call of Duty 4")
  2210. codRootPath = reg.QueryValueEx(codKey, "InstallPath")[0]
  2211. reg.CloseKey(codKey)
  2212.  
  2213. except WindowsError:
  2214. pass
  2215.  
  2216. if not os.path.isdir(codRootPath):
  2217. codRootPath = ""
  2218.  
  2219. # Set root path value
  2220. reg.SetValueEx(storageKey, "%sRootPath" % game, 0, reg.REG_SZ, codRootPath)
  2221.  
  2222. # First-time prompt
  2223. if firstTimePrompt:
  2224. result = SetRootFolder("Your %s root folder path hasn't been confirmed yet. If the following is not\ncorrect, please fix it:" % game, game)
  2225. if result:
  2226. codRootPath = result
  2227.  
  2228. return codRootPath
  2229.  
  2230.  
  2231.  
  2232. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2233. # ------------------------------------------------------------------------------ Init ------------------------------------------------------------------------------
  2234. # ------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2235. def CreateMenu():
  2236. cmds.setParent(mel.eval("$temp1=$gMainWindow"))
  2237.  
  2238. if cmds.control(OBJECT_NAMES['menu'][0], exists=True):
  2239. cmds.deleteUI(OBJECT_NAMES['menu'][0], menu=True)
  2240.  
  2241. menu = cmds.menu(OBJECT_NAMES['menu'][0], label=OBJECT_NAMES["menu"][1], tearOff=True)
  2242.  
  2243. # Export tools
  2244. cmds.menuItem(label=OBJECT_NAMES['xmodel'][1]+"...", command="CoDMayaTools.ShowWindow('xmodel')")
  2245. cmds.menuItem(label=OBJECT_NAMES['xanim'][1]+"...", command="CoDMayaTools.ShowWindow('xanim')")
  2246.  
  2247. # Viewmodel controls submenu
  2248. cmds.menuItem(label="ViewModel Tools", subMenu=True)
  2249. cmds.menuItem(label="Create New Gunsleeve Maya File", command=CreateNewGunsleeveMayaFile)
  2250. cmds.menuItem(label="Create New ViewModel Rig File", command=CreateNewViewmodelRigFile)
  2251. cmds.menuItem(label="Switch Gun in Current Rig File", command=SwitchGunInCurrentRigFile)
  2252. cmds.setParent(menu, menu=True)
  2253.  
  2254. # Import tools
  2255. cmds.menuItem(divider=True)
  2256. cmds.menuItem(label="Import XModel...", subMenu=True)
  2257. cmds.menuItem(label="...from CoD5", command="CoDMayaTools.ImportXModel('CoD5')")
  2258. cmds.menuItem(label="...from CoD4", command="CoDMayaTools.ImportXModel('CoD4')")
  2259. cmds.menuItem(label="...from FastFile", enable=False)
  2260. cmds.setParent(menu, menu=True)
  2261.  
  2262. cmds.menuItem(label="Import XAnim...", subMenu=True, enable=False)
  2263. cmds.menuItem(label="...from CoD5", command="CoDMayaTools.ImportXAnim('CoD5')")
  2264. cmds.menuItem(label="...from CoD4", enable=False)
  2265. cmds.menuItem(label="...from FastFile", enable=False)
  2266. cmds.setParent(menu, menu=True)
  2267.  
  2268. # IWIxDDS
  2269. cmds.menuItem(divider=True)
  2270. cmds.menuItem(label="Convert IWI to DDS", command="CoDMayaTools.IWIToDDSUser()")
  2271.  
  2272. # Root folder
  2273. cmds.menuItem(divider=True)
  2274. cmds.menuItem(label="Set Root Folder", subMenu=True)
  2275. cmds.menuItem(label="Set CoD5 Root Folder", command="CoDMayaTools.SetRootFolder(None, 'CoD5')")
  2276. cmds.menuItem(label="Set CoD4 Root Folder", command="CoDMayaTools.SetRootFolder(None, 'CoD4')")
  2277. cmds.setParent(menu, menu=True)
  2278.  
  2279. # For easy script updating
  2280. cmds.menuItem(label="Reload Script", command="reload(CoDMayaTools)")
  2281.  
  2282. # Tools Info
  2283. cmds.menuItem(label="About", command="CoDMayaTools.AboutWindow()")
  2284.  
  2285. # Updates
  2286. update = CheckForUpdates()
  2287. if update:
  2288. cmds.setParent(menu, menu=True)
  2289. cmds.menuItem(divider=True)
  2290. cmds.menuItem(label="A newer version (v%s) of CoD Maya Tools is available! (Click to automatically update)" % ('%f' % (update[0])).rstrip('0').rstrip('.'), command="CoDMayaTools.DownloadUpdate('%s')" % update[1])
  2291.  
  2292. # ---- Create windows ----
  2293. CreateMenu()
  2294. CreateXAnimWindow()
  2295. CreateXModelWindow()
Add Comment
Please, Sign In to add comment