# Copyright 2016, Ray1235
# CoDMayaTools is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# -----------------------------------------------------------------------------------------
#
# Change log now available on Github!
#
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------- Customization (You can change these values!) ----------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# Maximum number of warnings to show per export
MAX_WARNINGS_SHOWN = 1
# Number of slots in the export windows
EXPORT_WINDOW_NUMSLOTS = 100
# To export any black vertices as white, set to 'True'. Otherwise, set to 'False'.
CONVERT_BLACK_VERTS_TO_WHITE = False
# Enable Support for ExportX/Export2Bin
USE_EXPORT_X = False
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------- Global ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
import os
import maya.cmds as cmds
import maya.mel as mel
import math
import sys
import datetime
import os.path
import traceback
import maya.OpenMaya as OpenMaya
import maya.OpenMayaAnim as OpenMayaAnim
import socket
import subprocess
import webbrowser
import queue
import winreg as reg
import time
import struct
import shutil
import zipfile
import re
import json
from PyCoD import xmodel as xModel
from PyCoD import xanim as xAnim
from array import array
from subprocess import Popen, PIPE, STDOUT
WarningsDuringExport = 0 # Number of warnings shown during current export
CM_TO_INCH = 0.3937007874015748031496062992126 # 1cm = 50/127in
M_PI = 3.14159265359
FILE_VERSION = 2.9
VERSION_CHECK_URL = "https://raw.githubusercontent.com/Ray1235/CoDMayaTools/master/version"
# Registry path for global data storage
GLOBAL_STORAGE_REG_KEY = (reg.HKEY_CURRENT_USER, "Software\\CoDMayaTools")
# name : control code name, control friendly name, data storage node name, refresh function, export function
OBJECT_NAMES = {'menu' : ["CoDMayaToolsMenu", "Call of Duty Tools", None, None, None],
'progress' : ["CoDMayaToolsProgressbar", "Progress", None, None, None],
'xmodel': ["CoDMayaXModelExportWindow", "Export XModel", "XModelExporterInfo", "RefreshXModelWindow", "ExportXModel"],
'xanim' : ["CoDMayaXAnimExportWindow", "Export XAnim", "XAnimExporterInfo", "RefreshXAnimWindow", "ExportXAnim"],
'xcam' : ["CoDMayaXCamExportWindow", "Export XCam", "XCamExporterInfo", "RefreshXCamWindow", "ExportXCam"]}
# Working Directory
WORKING_DIR = os.path.dirname(os.path.realpath(__file__))
# Current Game
currentGame = "none"
# Format (JOINT, PARENTNAME) : NEWNAME
# Leave parent to None to rename regardless.
RENAME_DICTONARY = {("tag_weapon", "tag_torso") : "tag_weapon_right",
("tag_weapon1", "tag_torso") : "tag_weapon_left",
("j_gun", None) : "tag_weapon",
("j_gun1", None) : "tag_weapon_le",
("tag_flash1", "j_gun1") : "tag_flash_le",
("tag_brass1", None) : "tag_brass_le",
}
# Tags to attach
GUN_BASE_TAGS = ["j_gun", "j_gun1", "j_gun", "j_gun1", "tag_weapon", "tag_weapon1"]
VIEW_HAND_TAGS = ["t7:tag_weapon_right", "t7:tag_weapon_left", "tag_weapon", "tag_weapon1", "tag_weapon_right", "tag_weapon_left"]
# Supported xModel Versions for importing.
SUPPORTED_XMODELS = [25, 62]
# xModel Versions based off games
XMODEL_VERSION = {
"CoD1" : 5,
"CoD2" : 6,
"CoD4" : 6,
"CoD5" : 6,
"CoD7" : 6,
"CoD12": 7
}
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------ Init ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def CreateMenu():
cmds.setParent(mel.eval("$temp1=$gMainWindow"))
if cmds.control(OBJECT_NAMES['menu'][0], exists=True):
cmds.deleteUI(OBJECT_NAMES['menu'][0], menu=True)
menu = cmds.menu(OBJECT_NAMES['menu'][0],
label=OBJECT_NAMES["menu"][1],tearOff=True)
# Export tools
cmds.menuItem(label=OBJECT_NAMES['xmodel'][1]+"...",
command="CoDMayaTools.ShowWindow('xmodel')")
cmds.menuItem(label=OBJECT_NAMES['xanim'][1]+"...",
command="CoDMayaTools.ShowWindow('xanim')")
cmds.menuItem(label=OBJECT_NAMES['xcam'][1]+"...",
command="CoDMayaTools.ShowWindow('xcam')")
# Import tools
cmds.menuItem(divider=True)
cmds.menuItem(label="Import XModel...",
subMenu=True)
cmds.menuItem(label="...from CoD7",
command="CoDMayaTools.ImportXModel('CoD7')")
cmds.menuItem(label="...from CoD5",
command="CoDMayaTools.ImportXModel('CoD5')")
cmds.menuItem(label="...from CoD4",
command="CoDMayaTools.ImportXModel('CoD4')")
cmds.setParent(menu,
menu=True)
cmds.menuItem(divider=True)
# Utilities Menu
util_menu = cmds.menuItem(label="Utilities",
subMenu=True)
cmds.menuItem(divider=True)
# Rays Animation Toolkit
cmds.menuItem(label="Ray's Camera Animation Toolkit",
subMenu=True)
cmds.menuItem(label="Mark as camera",
command="CoDMayaTools.setObjectAlias('camera')")
cmds.menuItem(label="Mark as weapon",
command="CoDMayaTools.setObjectAlias('weapon')")
cmds.menuItem(divider=True)
cmds.menuItem(label="Generate camera animation",
command="CoDMayaTools.GenerateCamAnim()")
cmds.menuItem(divider=True)
cmds.menuItem(label="Remove camera animation in current range",
command=RemoveCameraKeys)
cmds.menuItem(label="Reset camera",
command=RemoveCameraAnimData)
cmds.setParent(util_menu,
menu=True)
# Attach Weapon To Rig
cmds.menuItem(divider=True)
cmds.menuItem(label="Attach Weapon to Rig", command=lambda x:WeaponBinder())
# IWIxDDS
cmds.menuItem(divider=True)
cmds.menuItem(label="Convert IWI to DDS",
command=lambda x:IWIToDDSUser())
# Viewmodel Tools
cmds.menuItem(label="ViewModel Tools", subMenu=True)
cmds.menuItem(label="Create New Gunsleeve Maya File",
command=lambda x:CreateNewGunsleeveMayaFile())
cmds.menuItem(label="Create New ViewModel Rig File",
command=lambda x:CreateNewViewmodelRigFile())
cmds.menuItem(label="Switch Gun in Current Rig File",
command=lambda x:SwitchGunInCurrentRigFile())
cmds.setParent(menu, menu=True)
# Settings
cmds.menuItem(divider=True)
settings_menu = cmds.menuItem(label="Settings", subMenu=True)
# Game/Game Folder Settings
cmds.menuItem(label="Game Settings", subMenu=True)
cmds.menuItem(label="Set CoD 1 Root Folder", command=lambda x:SetRootFolder(None, 'CoD1'))
cmds.menuItem(label="Set CoD 2 Root Folder", command=lambda x:SetRootFolder(None, 'CoD2'))
cmds.menuItem(label="Set MW Root Folder", command=lambda x:SetRootFolder(None, 'CoD4'))
cmds.menuItem(label="Set WaW Root Folder", command=lambda x:SetRootFolder(None, 'CoD5'))
cmds.menuItem(label="Set Bo1 Root Folder", command=lambda x:SetRootFolder(None, 'CoD7'))
cmds.menuItem(label="Set Bo3 Root Folder", command=lambda x:SetRootFolder(None, 'CoD12'))
cmds.menuItem(divider=True)
cmds.radioMenuItemCollection()
games = GetCurrentGame(True)
cmds.menuItem( label="Current Game:")
cmds.menuItem( label='CoD 1', radioButton=games["CoD1"], command=lambda x:SetCurrentGame("CoD1"))
cmds.menuItem( label='CoD 2', radioButton=games["CoD2"], command=lambda x:SetCurrentGame("CoD2"))
cmds.menuItem( label='CoD MW', radioButton=games["CoD4"], command=lambda x:SetCurrentGame("CoD4"))
cmds.menuItem( label='CoD WaW', radioButton=games["CoD5"], command=lambda x:SetCurrentGame("CoD5"))
cmds.menuItem( label='CoD Bo1', radioButton=games["CoD7"], command=lambda x:SetCurrentGame("CoD7"))
cmds.menuItem( label='CoD Bo3', radioButton=games["CoD12"] , command=lambda x:SetCurrentGame("CoD12"))
cmds.setParent(settings_menu, menu=True)
# ExportX/Export2Bin Options (Deprecated)
if(USE_EXPORT_X):
cmds.menuItem("E2B", label='Use ExportX', checkBox=QueryToggableOption('E2B'), command=lambda x:SetToggableOption('E2B') )
cmds.menuItem(label="Set Path to ExportX", command=lambda x:SetExport2Bin())
# Misc. Options.
cmds.menuItem(divider=True)
cmds.menuItem("AutomaticRename", label='Automatically rename joints (J_GUN, etc.)', checkBox=QueryToggableOption('AutomaticRename'), command=lambda x:SetToggableOption('AutomaticRename') )
cmds.menuItem("PrefixNoteType", label='Automatically prefix notetracks with type (sndnt# or rmbnt#)', checkBox=QueryToggableOption('PrefixNoteType'), command=lambda x:SetToggableOption('PrefixNoteType') )
cmds.menuItem("MeshMerge", label='Merge Meshes on export', checkBox=QueryToggableOption('MeshMerge'), command=lambda x:SetToggableOption('MeshMerge') )
cmds.menuItem("AutoUpdate", label='Auto Updates', checkBox=QueryToggableOption('AutoUpdate'), command=lambda x:SetToggableOption('AutoUpdate') )
# cmds.menuItem("PrintExport", label='Print xmodel_export information.', checkBox=QueryToggableOption('PrintExport'), command=lambda x:SetToggableOption('PrintExport')" )
cmds.setParent(menu, menu=True)
cmds.menuItem(divider=True)
# For easy script updating
cmds.menuItem(label="Reload Script", command="reload(CoDMayaTools)")
# Tools Info
cmds.menuItem(label="About", command=lambda x:AboutWindow())
def SetCurrentGame(game=None):
if game is None:
return
try:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_ALL_ACCESS)
except WindowsError:
storageKey = reg.CreateKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_ALL_ACCESS)
reg.SetValueEx(storageKey, "CurrentGame", 0, reg.REG_SZ, game )
def GetCurrentGame(return_dict=False):
games = {
"CoD1" : False,
"CoD2" : False,
"CoD4" : False,
"CoD5" : False,
"CoD7" : False,
"CoD12" : False
}
# Try get the current game set.
try:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_ALL_ACCESS)
game = reg.QueryValueEx(storageKey, "CurrentGame")[0]
except WindowsError:
# Failed, create it and fall back to Bo3
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_ALL_ACCESS)
try:
reg.SetValueEx(storageKey, "CurrentGame", 0, reg.REG_SZ , 0 ,"CoD12")
game = reg.QueryValueEx(storageKey, "CurrentGame")[0]
except:
# Fall back to Black Ops III if a game isn't set, and we can't create one.
game = "CoD12"
games[game] = True
# Return dictonary for radio buttons
if return_dict:
return games
# Return current game for everything else
else:
return game
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------- Import Common --------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def ImportFileSelectDialog(codRootPath, type):
print(codRootPath)
importFrom = None
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
importFrom = cmds.fileDialog2(fileMode=1, fileFilter="%s Files (*)" % type, caption="Import %s" % type, startingDirectory=os.path.join(codRootPath, "raw/%s/" % type.lower()))
else:
importFrom = cmds.fileDialog2(fileMode=1, dialogStyle=1, fileFilter="%s Files (*)" % type, caption="Import %s" % type, startingDirectory=os.path.join(codRootPath, "raw/%s/" % type.lower()))
if importFrom == None or len(importFrom) == 0 or importFrom[0].strip() == "":
return None
path = importFrom[0].strip()
pathSplit = os.path.splitext(path) # Fix bug with Maya 2013
if pathSplit[1] == ".*":
path = pathSplit
return path
def UnitQuaternionToDegrees(x, y, z):
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
euler = OpenMaya.MQuaternion(x, y, z, w).asEulerRotation()
return (math.degrees(euler.x), math.degrees(euler.y), math.degrees(euler.z))
def ReadJointRotation(f):
rot = struct.unpack(' 1:
buttons = []
lodDict = {}
for lod in lods:
buttons.append(lod["name"])
lodDict[lod["name"]] = lod
buttons.sort()
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")
if result in lodDict:
lodToLoad = lodDict[result]
lodToLoad["transformGroup"] = cmds.group(empty=True, name=lodToLoad["name"])
lodToLoad["materialMaps"] = LoadMaterials(lodToLoad, codRootPath)
lodToLoad["joints"] = LoadJoints(lodToLoad, codRootPath)
LoadSurfaces(lodToLoad, codRootPath, game)
AutoIKHandles(lodToLoad)
cmds.select(lodToLoad["transformGroup"], replace=True)
finally:
# Delete progress bar
cmds.deleteUI(progressWindow, window=True)
def LoadSurfaces(lod, codRootPath, game):
print("Loading XModel surface '%s'" % lod["name"])
with open(os.path.join(codRootPath, "raw/xmodelsurfs/%s" % lod["name"]), "rb") as f:
version = f.read(2)
if len(version) == 0 or struct.unpack('H', version)[0] not in SUPPORTED_XMODELS:
MessageBox("ERROR: Not a valid XModel surface file")
return
numMeshes = struct.unpack('= 0: # Select parent
cmds.select(joints[joint["parent"]]["name"], replace=True)
else:
cmds.select(clear=True)
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)
ProgressBarStep()
ProgressBarStep()
return joints
def LoadMaterials(lod, codRootPath):
noDupMaterials = list(set(lod["materials"]))
cmds.window("w"+OBJECT_NAMES['progress'][0], edit=True, title="Loading materials...")
cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue=len(noDupMaterials)*2+1, progress=0)
iwdImages = LoadMainIWDImages(codRootPath)
ProgressBarStep()
# Create output folder
if not os.path.exists(os.path.join(codRootPath, "raw/images/%s/" % lod["name"])):
os.makedirs(os.path.join(codRootPath, "raw/images/%s/" % lod["name"]))
# Create material info file
infofile = open(os.path.join(codRootPath, "raw/images/%s/%s" % (lod["name"], "%s Material Info.txt" % lod["name"])), "w")
# Load materials
outMaterialList = {}
for material in noDupMaterials:
materialMaps = {}
# http://www.diegologic.net/diegologic/Programming/CoD4%20Material.html
path = os.path.join(codRootPath, "raw/materials/%s" % material)
path = os.path.normpath(path)
print("Loading material '%s'" % material)
if not os.path.exists(path):
print("Failed loading material, path does not exist.")
continue
with open(path, "rb") as f:
f.read(48) # Skip start of header
numMaps = struct.unpack(' 0:
iwdImages[imageName] = iwd
return iwdImages
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------- IWI to DDS ---------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def IWIToDDS(inIWIPath):
splitPath = os.path.splitext(inIWIPath)
outDDSPath = splitPath[0] + ".dds"
supported_headers = [6, 13]
print("Converting %s to DDS" % os.path.basename(inIWIPath))
iwi_data = {}
# Offsets are different for V13 IWIs
iwi_data[6] = [8, 7]
iwi_data[13] = [9, 8]
if not os.path.exists(inIWIPath):
return False
with open(inIWIPath, 'rb') as inf:
# http://www.matejtomcik.com/Public/Projects/IWIExtractor/
if inf.read(3) != "IWi": # Read file identifier
print("\tERROR: Not a valid IWI file")
return False
header = struct.unpack(' -1:
# Name of the bones parent, none for Root bone. Split it to remove dagpath seperator and namespace.
bone_parentname = joints[node[0]][1].split("|")[-1].split(":")[-1]
else:
# Skip.
bone_parentname = None
# Name of the bone. Split it to remove dagpath seperator and namespace.
bone_name = node[1].partialPathName().split("|")[-1].split(":")[-1]
# Check for automatic rename.
if QueryToggableOption("AutomaticRename"):
# Run over dictonary for possible joints to rename.
for potjoints, new_name in RENAME_DICTONARY.iteritems():
# Found one
if bone_name == potjoints[0]:
# Check if it's a child bone of what we want, None to rename regardless.
if potjoints[1] is None or potjoints[1] == bone_parentname:
bone_name = new_name
# Check if we have cosmetic bone.
if cosmeticBone is not None and bone_parentname == cosmeticBone:
# Append it.
cosmetic_list.append((node[0], bone_name, node[1]))
else:
# Not a cosmetic, add it to normal joints.
joints.append((node[0], bone_name, node[1]))
# Our cosmetic parent.
if bone_name == cosmeticBone:
cosmetic_id = index
else:
index = node[0]
for i in range(node[1].childCount()):
dagPath = OpenMaya.MDagPath()
childNode = OpenMaya.MFnDagNode(node[1].child(i))
childNode.getPath(dagPath)
searchQueue.put((index, childNode, selectedObjects.hasItem(dagPath) and dagPath.hasFn(OpenMaya.MFn.kJoint)))
# Cosmetic bones must be at the end, so append them AFTER we've added other bones.
joints = joints + cosmetic_list
return joints, cosmetic_list, cosmetic_id
def GetCameraList():
cameras = []
# Get selected objects
selectedObjects = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(selectedObjects)
for i in range(selectedObjects.length()):
# Get object path and node
dagPath = OpenMaya.MDagPath()
selectedObjects.getDagPath(i, dagPath)
dagNode = OpenMaya.MFnDagNode(dagPath)
# Ignore nodes that aren't cameras or arn't top-level
if not dagPath.hasFn(OpenMaya.MFn.kCamera):
ProgressBarStep()
continue
# Breadth first search of camera tree
searchQueue = queue.Queue(0)
searchQueue.put((-1, dagNode, True)) # (index = child node's parent index, child node)
while not searchQueue.empty():
node = searchQueue.get()
index = len(cameras)
if node[2]:
cameras.append((node[0], node[1]))
else:
index = node[0]
for i in range(node[1].childCount()):
dagPath = OpenMaya.MDagPath()
childNode = OpenMaya.MFnDagNode(node[1].child(i))
childNode.getPath(dagPath)
searchQueue.put((index, childNode, selectedObjects.hasItem(dagPath) and dagPath.hasFn(OpenMaya.MFn.kCamera)))
ProgressBarStep()
return cameras
def RecursiveCheckIsTopNode(cSelectionList, currentNode): # Checks if the given node has ANY selected parent, grandparent, etc joints
if currentNode.parentCount() == 0:
return True
for i in range(currentNode.parentCount()):
parentDagPath = OpenMaya.MDagPath()
parentNode = OpenMaya.MFnDagNode(currentNode.parent(i))
parentNode.getPath(parentDagPath)
if not parentDagPath.hasFn(OpenMaya.MFn.kJoint): # Not a joint, but still check parents
if not RecursiveCheckIsTopNode(cSelectionList, parentNode):
return False # A parent joint is selected, we're done
else:
continue # No parent joints are selected, ignore this node
if cSelectionList.hasItem(parentDagPath):
return False
else:
if not RecursiveCheckIsTopNode(cSelectionList, parentNode):
return False
return True
def GetJointData(jointNode, frame=0):
# Get the joint's transform
path = OpenMaya.MDagPath()
jointNode.getPath(path)
transform = OpenMaya.MFnTransform(path)
# Get joint position
pos = transform.getTranslation(OpenMaya.MSpace.kWorld)
# Get scale (almost always 1)
scaleUtil = OpenMaya.MScriptUtil()
scaleUtil.createFromList([1,1,1], 3)
scalePtr = scaleUtil.asDoublePtr()
transform.getScale(scalePtr)
scale = [OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 0), OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 1), OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 2)]
# Get rotation matrix (mat is a 4x4, but the last row and column arn't needed)
rotQuaternion = OpenMaya.MQuaternion()
transform.getRotation(rotQuaternion, OpenMaya.MSpace.kWorld)
mat = rotQuaternion.asMatrix()
# Debug info: as euler rotation
#eulerRotation = rotQuaternion.asEulerRotation()
#eulerRotation.reorderIt(OpenMaya.MEulerRotation.kXYZ)
# Instead of writing it return it to Export function.
joint_offset = (pos.x*CM_TO_INCH, pos.y*CM_TO_INCH, pos.z*CM_TO_INCH)
joint_matrix = [(mat(0,0), mat(0,1), mat(0,2)),
(mat(1,0), mat(1,1), mat(1,2)),
(mat(2,0), mat(2,1), mat(2,2))]
joint_scale = (scale[0], scale[1], scale[2])
return joint_offset, joint_matrix, joint_scale
def WriteNodeFloat(f, name, value, no_p=False):
if no_p:
f.write("\"%s\" : %f \n" % (name, value))
else:
f.write("\"%s\" : %f ,\n" % (name, value))
def WriteCameraData(initial, outJSON, cameraNode):
# Get the camera's transform
path = OpenMaya.MDagPath()
cameraNode.getPath(path)
cam = OpenMaya.MFnCamera(path)
transform = OpenMaya.MFnTransform(path)
#print fov
#fov = 40
aspectRatio = cam.aspectRatio()
flen = cam.focalLength() * (CM_TO_INCH*1.732050807568877)
fov = cam.verticalFieldOfView() * (180 / M_PI) * 1.571428571428571
fdist = cam.focusDistance() * CM_TO_INCH
fstop = cam.fStop()
lense = 10
# Get camera position
pos = transform.getTranslation(OpenMaya.MSpace.kWorld)
# Get scale (almost always 1)
scaleUtil = OpenMaya.MScriptUtil()
scaleUtil.createFromList([1,1,1], 3)
scalePtr = scaleUtil.asDoublePtr()
transform.getScale(scalePtr)
scale = [OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 0), OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 1), OpenMaya.MScriptUtil.getDoubleArrayItem(scalePtr, 2)]
# Get rotation matrix (mat is a 4x4, but the last row and column arn't needed)
rotQuaternion = OpenMaya.MQuaternion()
transform.getRotation(rotQuaternion, OpenMaya.MSpace.kWorld)
mat = rotQuaternion.asMatrix()
# Debug info: as euler rotation
eulerRotation = rotQuaternion.asEulerRotation()
eulerRotation.reorderIt(OpenMaya.MEulerRotation.kXYZ)
# euler rotation is in radians, not degrees
eulerRotation.x = eulerRotation.x - (3.141/2)
eulerRotation.z = eulerRotation.z - (3.141/2)
#eulerRotation.y = eulerRotation.y + (3.141/2)
#print ("%f %f %f" % (eulerRotation.x*180/3.141, eulerRotation.y*180/3.141, eulerRotation.z*180/3.141))
mat = eulerRotation.asMatrix()
# Write
if(initial):
outJSON.update({
"aspectratio" : aspectRatio
})
outJSON.update({
"origin" : [pos.y * CM_TO_INCH, pos.x * -CM_TO_INCH, pos.z * CM_TO_INCH],
"dir" : [mat(1,0), mat(1,1), mat(1,2)],
"up" : [mat(2,0), mat(2,1), mat(2,2)],
"right" : [mat(0,0), mat(0,1), mat(0,2)],
"flen" : flen,
"fov" : fov,
"fdist" : fdist,
"fstop" : fstop,
"lense" : lense
})
#outJSON["origin"] = [pos.y * CM_TO_INCH, pos.x * -CM_TO_INCH, pos.z * CM_TO_INCH]
#float(pos.x*-CM_TO_INCH), float(pos.z*CM_TO_INCH)])
#outJSON["dir"] = [mat(1,0), mat(1,1), mat(1,2)]
#outJSON["up"] = [mat(2,0), mat(2,1), mat(2,2)]
#outJSON["right"] = [mat(0,0), mat(0,1), mat(0,2)]
#outJSON["flen"] = flen
#outJSON["fov"] = fov
#outJSON["fdist"] = fdist
#outJSON["fstop"] = fstop
#outJSON["lense"] = lense
# Get count for progress bar. No impact on export speed.
def GetNumInfo(selectedObjects):
# Mesh array to check for duplicates.
meshes = []
maxValue = 0
maxValue += len(cmds.ls(selection = True, type = "joint"))
for i in range(0, selectedObjects.length()):
# Grab mesh.
object = OpenMaya.MObject()
dagPath = OpenMaya.MDagPath()
selectedObjects.getDependNode(i, object)
selectedObjects.getDagPath(i, dagPath)
# Check it's a mesh.
if not dagPath.hasFn(OpenMaya.MFn.kMesh):
continue
dagPath.extendToShape()
# Check for duplicate.
if dagPath.partialPathName() in meshes:
continue
# Append.
meshes.append(dagPath.partialPathName())
# Get vert count for this mesh.
maxValue += OpenMaya.MItMeshVertex(dagPath).count()
# Get Face found for this mesh.
maxValue += OpenMaya.MItMeshPolygon(dagPath).count()
# Return value * 2 because we will loop over 2x for getting info and writing it to export.
return maxValue
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------- Export XModel -------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def ExportXModel(filePath, make_gdt=True):
# Get number of objects selected.
numSelectedObjects = len(cmds.ls(selection=True))
# Check if nothing is selected
if numSelectedObjects == 0:
return "Error: No objects selected for export"
# Check if we want to merge meshes into 1.
merge_mesh = QueryToggableOption('MeshMerge')
# Get Version for this xModel based off current game.
version = XMODEL_VERSION[GetCurrentGame()]
# Create Directory/ies
try:
directory = os.path.dirname(filePath)
if not os.path.exists(directory):
os.makedirs(directory)
except OSError as e:
typex, value, traceback = sys.exc_info()
return "Unable to create file:\n\n%s" % value.strerror
# Create new xModel Object
xmodel = xModel.Model()
# Get list of joints including cosmetics
joints = GetJointList("xmodel")
# Export Mesh Information
ExportMeshData(joints[0], xmodel, merge_mesh)
# Export Joints
if joints[0]:
# Run through joints
for i, joint in enumerate(joints[0]):
# Get Data for this joint
boneData = GetJointData(joint[2])
# Check for cosmetic
if(joint[0] == joints[2]):
bone.cosmetic = True
bone = xModel.Bone(joint[1], joint[0])
# Offset
bone.offset = boneData[0]
# Rotation
bone.matrix = boneData[1]
# Scale
bone.scale = boneData[2]
# Append it.
xmodel.bones.append(bone)
# No bones selected, export just TAG_ORIGIN
else:
dummy_bone = xModel.Bone("TAG_ORIGIN", -1)
dummy_bone.offset = (0, 0, 0)
dummy_bone.matrix = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
xmodel.bones.append(dummy_bone)
# Get Extension to determine export type.
extension = os.path.splitext(filePath)[-1].lower()
# Write xModel
if(extension == ".xmodel_bin"):
xmodel.WriteFile_Bin(filePath, version)
else:
xmodel.WriteFile_Raw(filePath, version)
# Seperate Conversion via Export2Bin/ExportX
# Change variable at config at top to enable.
if USE_EXPORT_X:
if QueryToggableOption('E2B'):
try:
RunExport2Bin(filePath)
except:
MessageBox("The xmodel exported successfully however Export2Bin/ExportX failed to run, the model will need to be converted manually.\n\nPlease check your paths.")
def GetMaterialsFromMesh(mesh, dagPath):
textures = {}
# http://rabidsquirrelgames.googlecode.com/svn/trunk/Maya%20plugin/fileExportCmd.py
# The code below gets a dictionary of [material name: material file name], ex: [a_material: a_material.dds]
shaders = OpenMaya.MObjectArray()
shaderIndices = OpenMaya.MIntArray()
mesh.getConnectedShaders(dagPath.instanceNumber(), shaders, shaderIndices)
for i in range(shaders.length()):
shaderNode = OpenMaya.MFnDependencyNode(shaders[i])
shaderPlug = shaderNode.findPlug("surfaceShader")
material = OpenMaya.MPlugArray()
shaderPlug.connectedTo(material, 1, 0);
for j in range(material.length()):
materialNode = OpenMaya.MFnDependencyNode(material[j].node())
colorPlug = materialNode.findPlug("color")
dgIt = OpenMaya.MItDependencyGraph(
colorPlug,
OpenMaya.MFn.kFileTexture,
OpenMaya.MItDependencyGraph.kUpstream,
OpenMaya.MItDependencyGraph.kBreadthFirst,
OpenMaya.MItDependencyGraph.kNodeLevel)
texturePath = ""
try: # If there is no texture, this part can throw an exception
dgIt.disablePruningOnFilter()
textureNode = OpenMaya.MFnDependencyNode(dgIt.currentItem())
texturePlug = textureNode.findPlug("fileTextureName")
texturePath = os.path.basename(texturePlug.asString())
except Exception:
pass
textures[i] = (materialNode.name(), texturePath)
texturesToFaces = []
for i in range(shaderIndices.length()):
if shaderIndices[i] in textures:
texturesToFaces.append(textures[shaderIndices[i]])
else:
texturesToFaces.append(None)
return texturesToFaces
# Converts a set of vertices (toConvertVertexIndices) from object-relative IDs to face-relative IDs
# vertexIndices is a list of object-relative vertex indices in face order (from polyIter.getVertices)
# toConvertVertexIndices is any set of vertices from the same faces as vertexIndices, not necessarily the same length
# Returns false if a vertex index is unable to be converted (= bad vertex values)
def VerticesObjRelToLocalRel(vertexIndices, toConvertVertexIndices):
# http://svn.gna.org/svn/cal3d/trunk/cal3d/plugins/cal3d_maya_exporter/MayaMesh.cpp
localVertexIndices = OpenMaya.MIntArray()
for i in range(toConvertVertexIndices.length()):
found = False
for j in range(vertexIndices.length()):
if toConvertVertexIndices[i] == vertexIndices[j]:
localVertexIndices.append(j)
found = True
break
if not found:
return False
return localVertexIndices
def ExportMeshData(joints, xmodel, merge_mesh = True):
meshes = []
verts = []
tris = []
materialDict = {}
materials = []
# xModel
# Convert the joints to a dictionary, for simple searching for joint indices
jointDict = {}
for i, joint in enumerate(joints):
jointDict[joint[2].partialPathName()] = i
# Get all selected objects
selectedObjects = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(selectedObjects)
# The global vert index at the start of each object
currentStartingVertIndex = 0
global_mesh = xModel.Mesh("GlobalMesh")
progressInfo = GetNumInfo(selectedObjects)
cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue = progressInfo)
# Loop through all objects
for i in range(0, selectedObjects.length()):
# Get data on object
object = OpenMaya.MObject()
dagPath = OpenMaya.MDagPath()
selectedObjects.getDependNode(i, object)
selectedObjects.getDagPath(i, dagPath)
# Ignore dag nodes that aren't shapes or shape transforms
if not dagPath.hasFn(OpenMaya.MFn.kMesh):
continue
# Lower path to shape node
# Selecting a shape transform or shape will get the same dagPath to the shape using this
dagPath.extendToShape()
# Check for duplicates
if dagPath.partialPathName() in meshes:
continue
# Add shape to list
meshes.append(dagPath.partialPathName())
# Create new xMesh
xmesh = xModel.Mesh(dagPath.partialPathName())
# Get Maya Mesh
mesh = OpenMaya.MFnMesh(dagPath)
# Get skin cluster
clusterName = mel.eval("findRelatedSkinCluster " + dagPath.partialPathName())
# Check for skin
hasSkin = False
if clusterName != None and clusterName != "" and not clusterName.isspace():
hasSkin = True
selList = OpenMaya.MSelectionList()
selList.add(clusterName)
clusterNode = OpenMaya.MObject()
selList.getDependNode(0, clusterNode)
skin = OpenMayaAnim.MFnSkinCluster(clusterNode)
# Get vertices
vertIter = OpenMaya.MItMeshVertex(dagPath)
# Loop until we're done iterating over vertices
while not vertIter.isDone():
# Create Vertex
vertex = xModel.Vertex(
(
vertIter.position(OpenMaya.MSpace.kWorld).x*CM_TO_INCH,
vertIter.position(OpenMaya.MSpace.kWorld).y*CM_TO_INCH,
vertIter.position(OpenMaya.MSpace.kWorld).z*CM_TO_INCH
)
)
# Check for influences
if hasSkin:
# Get weight values
weightValues = OpenMaya.MDoubleArray()
numWeights = OpenMaya.MScriptUtil() # Need this because getWeights crashes without being passed a count
skin.getWeights(dagPath, vertIter.currentItem(), weightValues, numWeights.asUintPtr())
# Get weight names
weightJoints = OpenMaya.MDagPathArray()
skin.influenceObjects(weightJoints)
# Make sure the list of weight values and names match
if weightValues.length() != weightJoints.length():
PrintWarning("Failed to retrieve vertex weight list on '%s.vtx[%d]'; using default joints." %
(dagPath.partialPathName(), vertIter.index()))
# Remove weights of value 0 or weights from unexported joints
finalWeights = []
weightsSize = 0
for i in range(0, weightJoints.length()):
# 0.000001 is the smallest decimal in xmodel exports
if weightValues[i] < 0.000001:
continue
jointName = weightJoints[i].partialPathName()
# Check for unexported joints.
if not jointName in jointDict:
PrintWarning("Unexported joint %s is influencing vertex '%s.vtx[%d]' by %f%%\n" %
(("'%s'" % jointName).ljust(15), dagPath.partialPathName(), vertIter.index(), weightValues[i]*100))
else:
finalWeights.append([jointDict[jointName], weightValues[i]])
weightsSize += weightValues[i]
# Make sure the total weight adds up to 1
if weightsSize > 0:
weightMultiplier = 1 / weightsSize
for weight in finalWeights:
weight[1] *= weightMultiplier
vertex.weights.append((weight[0], weight[1]))
# Check if no weights were written (can happend due to deformers)
if not len(vertex.weights):
vertex.weights.append((0, 1.0))
# Add to mesh
xmesh.verts.append(vertex)
# Next vert
ProgressBarStep()
vertIter.next()
# Get materials used by this mesh
meshMaterials = GetMaterialsFromMesh(mesh, dagPath)
# Loop through all faces
polyIter = OpenMaya.MItMeshPolygon(dagPath)
# Loop until we're done iterating over polygons
while not polyIter.isDone():
# Get this poly's material
polyMaterial = meshMaterials[polyIter.index()]
# Every face must have a material
if polyMaterial == None:
PrintWarning("Found no material on face '%s.f[%d]'; ignoring face" %
(dagPath.partialPathName(), polyIter.index()))
polyIter.next()
continue
# Add this poly's material to the global list of used materials
if not polyMaterial[0] in materialDict:
materialDict[polyMaterial[0]] = len(materials)
materials.append(polyMaterial)
# Get vertex indices of this poly, and the vertex indices of this poly's triangles
trianglePoints = OpenMaya.MPointArray()
triangleIndices = OpenMaya.MIntArray()
vertexIndices = OpenMaya.MIntArray()
polyIter.getTriangles(trianglePoints, triangleIndices)
polyIter.getVertices(vertexIndices)
# localTriangleIndices is the same as triangleIndices,
# except each vertex is listed as the face-relative index
# instead of the object-realtive index
localTriangleIndices = VerticesObjRelToLocalRel(vertexIndices, triangleIndices)
if localTriangleIndices == False:
return ("Failed to convert object-relative vertices to face-relative on poly '%s.f[%d]'" %
(dagPath.partialPathName(), polyIter.index()))
# Note: UVs, normals, and colors, are "per-vertex per face", because even though two faces may share
# a vertex, they might have different UVs, colors, or normals. So, each face has to contain this info
# for each of it's vertices instead of each vertex alone
# UVs
Us = OpenMaya.MFloatArray()
Vs = OpenMaya.MFloatArray()
# Normals
normals = OpenMaya.MVectorArray()
# Attempt to get UVs
try:
polyIter.getUVs(Us, Vs)
except:
PrintWarning("Failed to aquire UVs on face '%s.f[%d]'; ignoring face" %
(dagPath.partialPathName(), polyIter.index()))
polyIter.next()
continue
# Attempt to get Normals
try:
polyIter.getNormals(normals, OpenMaya.MSpace.kWorld)
except:
PrintWarning("Failed to aquire Normals on face '%s.f[%d]'; ignoring face" %
(dagPath.partialPathName(), polyIter.index()))
polyIter.next()
continue
# Loop indices
# vertexIndices.length() has 3 values per triangle
for i in range(len(triangleIndices)//3):
# New xModel Face
xface = xModel.Face(0 if merge_mesh else len(meshes)-1 , materialDict[polyMaterial[0]])
# Put local indices into an array for easy access
faceIndices = [
localTriangleIndices[i*3],
localTriangleIndices[i*3+1],
localTriangleIndices[i*3+2]
]
# Vertex Colors
vertColors = [
OpenMaya.MColor(),
OpenMaya.MColor(),
OpenMaya.MColor()
]
# Grab colors
polyIter.getColor(vertColors[0], faceIndices[0])
polyIter.getColor(vertColors[1], faceIndices[1])
polyIter.getColor(vertColors[2], faceIndices[2])
# Face Order
face_order = [0, 2, 1]
# Export Face-Vertex Data
for e in range(3):
xface.indices[face_order[e]] = xModel.FaceVertex(
# Vertex
currentStartingVertIndex + triangleIndices[i*3 + e],
# Normal (XYZ)
(
normals[faceIndices[e]].x,
normals[faceIndices[e]].y,
normals[faceIndices[e]].z
),
# Color (RGBA)
(
vertColors[e].r,
vertColors[e].g,
vertColors[e].b,
vertColors[e].a,
),
# UV (UV)
(Us[faceIndices[e]], 1-Vs[faceIndices[e]]))
# Append face
xmesh.faces.append(xface)
# Next poly
ProgressBarStep()
polyIter.next()
# Update starting vertex index
currentStartingVertIndex = len(verts)
xmodel.meshes.append(xmesh)
# Add Materials
for material in materials:
xmodel.materials.append(
xModel.Material(material[0].split(":")[-1],
"Lambert",
{"color_map" : material[1].split(":")[-1]})
)
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------- Export XAnim --------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def ExportXAnim(filePath):
# Progress bar
numSelectedObjects = len(cmds.ls(selection=True))
if numSelectedObjects == 0:
return "Error: No objects selected for export"
# Get data
joints = GetJointList()
if len(joints[0]) == 0:
return "Error: No joints selected for export"
# Get settings
frameStart = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", query=True, value=True)
frameEnd = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", query=True, value=True)
fps = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FPSField", query=True, value=True)
QMultiplier = math.pow(2,cmds.intField(OBJECT_NAMES['xanim'][0]+"_qualityField", query=True, value=True))
multiplier = 1/QMultiplier
fps = fps/multiplier
# Reverse Bool
reverse = cmds.checkBox("CoDMAYA_ReverseAnim", query=True, value=True)
# Export Tag Align
write_tag_align = cmds.checkBox("CoDMAYA_TAGALIGN", query=True, value=True)
# Frame Range
frame_range = range(int(frameStart/multiplier), int((frameEnd+1)/multiplier))
# Check if we want to reverse this anim.
if reverse:
frame_range = list(reversed(frame_range))
if frameStart < 0 or frameStart > frameEnd:
return "Error: Invalid frame range (start < 0 or start > end)"
if fps <= 0:
return "Error: Invalid FPS (fps < 0)"
if multiplier <= 0 or multiplier > 1:
return "Error: Invalid multiplier (multiplier < 0 && multiplier >= 1)"
# Set Progress bar to our frame length
cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue=len(frame_range) + 1)
# Create Directory/ies
try:
directory = os.path.dirname(filePath)
if not os.path.exists(directory):
os.makedirs(directory)
except OSError as e:
typex, value, traceback = sys.exc_info()
return "Unable to create file:\n\n%s" % value.strerror
# Create new xAnim
xanim = xAnim.Anim()
# Set Frame Rate
xanim.framerate = fps
# Add Joints
for i, joint in enumerate(joints[0]):
xanim.parts.append(xAnim.PartInfo(joint[1]))
# Export Tag Align (required for some anims)
if write_tag_align:
xanim.parts.append(xAnim.PartInfo("TAG_ALIGN"))
# Loop through frames
for n, i in enumerate(frame_range):
# Jump to frame
cmds.currentTime(i)
# Create Frame
frame = xAnim.Frame(n)
# Loop through joints
for j, joint in enumerate(joints[0]):
# Create Frame Part
frame_bone = xAnim.FramePart()
# Grab joint data for this part.
boneData = GetJointData(joint[2])
# Offset
frame_bone.offset = boneData[0]
# Rotation
frame_bone.matrix = boneData[1]
# Append it.
frame.parts.append(frame_bone)
# Export Tag Align (required for some anims)
if write_tag_align:
frame_bone = xAnim.FramePart()
frame_bone.offset = (0, 0, 0)
frame_bone.matrix = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
frame.parts.append(frame_bone)
# Add Frame
xanim.frames.append(frame)
# Increment Progress
ProgressBarStep()
# Get Notetracks for this Slot
slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
# Notes (seperated by comma)
notes = noteList.split(",")
# Run through note list
for note in notes:
# Split (frame : note string)
parts = note.split(":")
# Check for empty/bad string
if note.strip() == "" or len(parts) < 2:
continue
# Check for abc characters (allow rumble/sound notes prefixes)
name = "".join([c for c in parts[0] if c.isalnum() or c=="_"]).replace("sndnt", "sndnt#").replace("rmbnt", "rmbnt#")
if name == "":
continue
# Get Frame and attempt to parse it
frame=0
try:
frame = int(parts[1]) - frameStart
if(reverse):
frame = (len(frame_range) - 1) - frame
except ValueError:
continue
# Add to our notes list.
xanim.notes.append(xAnim.Note(frame, name))
# Get Extension
extension = os.path.splitext(filePath)[-1].lower()
# Export Bin
if(extension == ".xanim_bin"):
xanim.WriteFile_Bin(filePath, 3)
# Export Export
else:
xanim.WriteFile_Raw(filePath, 3)
# Refresh
cmds.refresh()
# Seperate Conversion via Export2Bin/ExportX
# Change variable at config at top to enable.
if USE_EXPORT_X:
if QueryToggableOption('E2B'):
try:
RunExport2Bin(filePath)
except:
MessageBox("The animation exported successfully however Export2Bin/ExportX failed to run, the animation will need to be converted manually.\n\nPlease check your paths.")
def WriteDummyTargetModelBoneRoot(outJSON, numframes):
# f.write("""
# "targetModelBoneRoots" : [
# {
# "name" : "TAG_ORIGIN",
# "animation" : [
# """)
outJSON["targetModelBoneRoots"] = [{
"name" : "TAG_ORIGIN",
"animation" : [],
},
{
"name" : "TAG_ALIGN",
"animation" : [],
}
]
for i in range(0,numframes):
outJSON["targetModelBoneRoots"][0]["animation"].append({
"frame" : i,
"offset" : [0.0,0.0,0.0],
"axis" : {
"x" : [0.0, -1.0, 0.0],
"y" : [1.0, 0.0, 0.0],
"z" : [0.0, 0.0, 1.0]
}
})
for i in range(0, numframes):
outJSON["targetModelBoneRoots"][1]["animation"].append({
"frame": i,
"offset": [0.0, 0.0, 0.0],
"axis": {
"x": [0.0, -1.0, 0.0],
"y": [1.0, 0.0, 0.0],
"z": [0.0, 0.0, 1.0]
}
})
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------- Export XCam --------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def ExportXCam(filePath):
# Progress bar
numSelectedObjects = len(cmds.ls(selection=True))
if numSelectedObjects == 0:
return "Error: No objects selected for export"
cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, maxValue=numSelectedObjects+1)
# Get data
cameras = GetCameraList()
if len(cameras) == 0:
return "Error: No cameras selected for export"
# Get settings
frameStart = cmds.intField(OBJECT_NAMES['xcam'][0]+"_FrameStartField", query=True, value=True)
frameEnd = cmds.intField(OBJECT_NAMES['xcam'][0]+"_FrameEndField", query=True, value=True)
fps = cmds.intField(OBJECT_NAMES['xcam'][0]+"_FPSField", query=True, value=True)
# QMultiplier = math.pow(2,cmds.intField(OBJECT_NAMES['xcam'][0]+"_qualityField", query=True, value=True))
#multiplier = 1/QMultiplier
multiplier = 1
fps = fps/multiplier
if frameStart < 0 or frameStart > frameEnd:
return "Error: Invalid frame range (start < 0 or start > end)"
if fps <= 0:
return "Error: Invalid FPS (fps < 0)"
if multiplier <= 0 or multiplier > 1:
return "Error: Invalid multiplier (multiplier < 0 && multiplier >= 1)"
# Open file
f = None
try:
# Create export directory if it doesn't exist
directory = os.path.dirname(filePath)
if not os.path.exists(directory):
os.makedirs(directory)
# Create files
f = open(filePath, 'w')
except (IOError, OSError) as e:
typex, value, traceback = sys.exc_info()
return "Unable to create files:\n\n%s" % value.strerror
fLength = ((frameEnd-frameStart+1) / multiplier)
# Write header
outputJSON = {
"version" : 1,
"framerate" : fps,
"numframes" : fLength
}
outputJSON["scene"] = os.path.normpath(os.path.abspath(cmds.file(query=True, sceneName=True))).encode('ascii', 'ignore').replace('\\','/')
outputJSON["align"] = {
"tag" : "tag_align",
"offset" : [0.0000, 0.0000, 0.0000],
"axis" : {
"x" : [0.0, -1.0, 0.0],
"y" : [1.0, 0.0, 0.0],
"z" : [0.0, 0.0, 1.0]
}
}
WriteDummyTargetModelBoneRoot(outputJSON, fLength)
# Write parts
outputJSON["cameras"] = []
currentFrame = cmds.currentTime(query=True)
for i, camera in enumerate(cameras):
name = camera[1].partialPathName().split("|")
name = name[len(name)-1].split(":") # Remove namespace prefixes
name = name[len(name)-1]
outputJSON["cameras"].append({
"name" : name,
"index" : i,
"type" : "Perspective",
"aperture" : "FOCAL_LENGTH"
});
WriteCameraData(True, outputJSON["cameras"][i], camera[1])
#outputJSON["cameras"][i]["aspectratio"] = 16.0/9.0
outputJSON["cameras"][i]["nearz"] = 4
outputJSON["cameras"][i]["farz"] = 4000
outputJSON["cameras"][i]["animation"] = []
for j in range(int(frameStart), int((frameEnd+1))):
cmds.currentTime(j)
outputJSON["cameras"][i]["animation"].append({
"frame" : j
})
WriteCameraData(False, outputJSON["cameras"][i]["animation"][j-frameStart], camera[1])
outputJSON["cameraSwitch"] = []
cmds.currentTime(currentFrame)
ProgressBarStep()
# Write notetrack
slotIndex = cmds.optionMenu(OBJECT_NAMES['xcam'][0]+"_SlotDropDown", query=True, select=True)
noteList = cmds.getAttr(OBJECT_NAMES['xcam'][2]+(".notetracks[%i]" % slotIndex)) or ""
notes = noteList.split(",")
cleanNotes = []
for note in notes:
parts = note.split(":")
if note.strip() == "" or len(parts) < 2:
continue
name = "".join([c for c in parts[0] if c.isalnum() or c=="_"])
if name == "":
continue
frame=0
try:
frame = int(parts[1])
except ValueError:
continue
cleanNotes.append((name, frame))
outputJSON["notetracks"] = []
for note in cleanNotes:
outputJSON["notetracks"].append({
"name" : note[0],
"frame" : note[1]
})
#f.write("{\n \"name\" : \"%s\",\n \"frame\" : %d\n},\n" % (note[0], note[1]))
json.dump(outputJSON, f, indent=4)
f.close()
ProgressBarStep()
cmds.refresh()
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------ Viewmodel Tools -------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def DoesObjectExist(name, type):
if not cmds.objExists(name):
MessageBox("Error: Missing %s '%s'" % (type, name))
return False
return True
def CreateNewGunsleeveMayaFile():
global WarningsDuringExport
# Save reminder
if not SaveReminder(False):
return
# Get paths
filePath = cmds.file(query=True, sceneName=True)
split1 = os.path.split(filePath)
split2 = os.path.splitext(split1[1])
exportPath = os.path.join(split1[0], "gunsleeves_" + split2[0] + ".xmodel_export")
# Create a new file and import models
cmds.file(force=True, newFile=True)
cmds.file(os.path.join(GetRootFolder(), "bin/maya/rigs/viewmodel/ViewModel_DefMesh.mb"), i=True, type="mayaBinary")
cmds.file(filePath, i=True, type="mayaBinary")
# Check to make sure objects exist
if not DoesObjectExist("J_Gun", "joint"): return
if not DoesObjectExist("tag_weapon", "tag"): return
if not DoesObjectExist("GunExport", "object set"): return
if not DoesObjectExist("DefViewSkeleton", "object set"): return
if not DoesObjectExist("tag_view", "tag"): return
if not cmds.objExists("viewmodelSleeves_OpForce") and not cmds.objExists("viewmodelSleeves_Marines"):
MessageBox("Error: Missing viewsleeves 'viewmodelSleeves_OpForce' or 'viewmodelSleeves_Marines'")
return
# Attach gun to rig
cmds.select("J_Gun", replace=True)
cmds.select("tag_weapon", add=True)
cmds.parent()
# Select things to export
cmds.select("GunExport", replace=True)
cmds.select("DefViewSkeleton", toggle=True)
cmds.select("tag_view", toggle=True)
if cmds.objExists("viewmodelSleeves_OpForce"):
cmds.select("viewmodelSleeves_OpForce", toggle=True, hierarchy=True)
else:
cmds.select("viewmodelSleeves_Marines", toggle=True, hierarchy=True)
# Export
if cmds.control("w"+OBJECT_NAMES['progress'][0], exists=True):
cmds.deleteUI("w"+OBJECT_NAMES['progress'][0])
progressWindow = cmds.window("w"+OBJECT_NAMES['progress'][0], title=OBJECT_NAMES['progress'][1], width=302, height=22, sizable=False)
cmds.columnLayout()
progressControl = cmds.progressBar(OBJECT_NAMES['progress'][0], width=300)
cmds.showWindow(progressWindow)
cmds.refresh() # Force the progress bar to be drawn
# Export
WarningsDuringExport = 0
response = None
try:
response = ExportXModel(exportPath)
except Exception as e:
response = "An unhandled error occurred during export:\n\n" + traceback.format_exc()
# Delete progress bar
cmds.deleteUI(progressWindow, window=True)
# Handle response
if type(response) == str:
MessageBox(response)
elif WarningsDuringExport > 0:
MessageBox("Warnings occurred during export. Check the script editor output for more details.")
if type(response) != str:
MessageBox("Export saved to\n\n" + os.path.normpath(exportPath))
def CreateNewViewmodelRigFile():
# Save reminder
if not SaveReminder(False):
return
# Get path
filePath = cmds.file(query=True, sceneName=True)
# Create a new file and import models
cmds.file(force=True, newFile=True)
cmds.file(os.path.join(GetRootFolder(), "bin/maya/rigs/viewmodel/ViewModel_Rig.mb"), reference=True, type="mayaBinary", namespace="rig", options="v=0")
cmds.file(filePath, reference=True, type="mayaBinary", namespace="VM_Gun")
# Check to make sure objects exist
if not DoesObjectExist("VM_Gun:J_Gun", "joint"): return
if not cmds.objExists("rig:DefMesh:tag_weapon") and not cmds.objExists("ConRig:DefMesh:tag_weapon"):
MessageBox("Error: Missing viewsleeves 'rig:DefMesh:tag_weapon' or 'ConRig:DefMesh:tag_weapon'")
return
# Connect gun to rig
if cmds.objExists("rig:DefMesh:tag_weapon"):
cmds.select("rig:DefMesh:tag_weapon", replace=True)
else:
cmds.select("ConRig:DefMesh:tag_weapon", replace=True)
cmds.select("VM_Gun:J_Gun", toggle=True)
cmds.parentConstraint(weight=1, name="VMParentConstraint")
cmds.select(clear=True)
def SwitchGunInCurrentRigFile():
# Save reminder
if not SaveReminder():
return
# Make sure the rig is correct
if not cmds.objExists("rig:DefMesh:tag_weapon") and not cmds.objExists("ConRig:DefMesh:tag_weapon"):
MessageBox("Error: Missing rig:DefMesh:tag_weapon' or 'ConRig:DefMesh:tag_weapon'")
return
if not DoesObjectExist("VM_Gun:J_Gun", "joint"): return
# Prompt user to select a new gun file
gunPath = cmds.fileDialog2(fileMode=1, fileFilter="Maya Files (*.ma *.mb)", caption="Select a New Gun File", startingDirectory=GetRootFolder())
if gunPath == None or len(gunPath) == 0 or gunPath[0].strip() == "":
return
gunPath = gunPath[0].strip()
# Delete the constraint
cmds.delete("VMParentConstraint")
# Delete any hand attachments
if cmds.objExists("rig:Hand_Extra_RI_GRP.Parent"):
parentRI = cmds.getAttr("rig:Hand_Extra_RI_GRP.Parent")
if parentRI != "":
cmds.delete(parentRI)
if cmds.objExists("rig:Hand_Extra_LE_GRP.Parent"):
parentLE = cmds.getAttr("rig:Hand_Extra_LE_GRP.Parent")
if parentLE != "":
cmds.delete(parentLE)
# Switch guns
cmds.file(gunPath, loadReference="VM_GunRN");
# Connect gun to rig
if cmds.objExists("rig:DefMesh:tag_weapon"):
cmds.select("rig:DefMesh:tag_weapon", replace=True)
else:
cmds.select("ConRig:DefMesh:tag_weapon", replace=True)
cmds.select("VM_Gun:J_Gun", toggle=True)
cmds.parentConstraint(weight=1, name="VMParentConstraint")
cmds.select(clear=True)
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------- XModel Export Window ----------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def CreateXModelWindow():
# Create window
if cmds.control(OBJECT_NAMES['xmodel'][0], exists=True):
cmds.deleteUI(OBJECT_NAMES['xmodel'][0])
cmds.window(OBJECT_NAMES['xmodel'][0], title=OBJECT_NAMES['xmodel'][1], width=340, height=1, retain=True, maximizeButton=False)
form = cmds.formLayout(OBJECT_NAMES['xmodel'][0]+"_Form")
# Controls
slotDropDown = cmds.optionMenu(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown", changeCommand="CoDMayaTools.RefreshXModelWindow()", annotation="Each slot contains different a export path, settings, and saved selection")
for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
cmds.menuItem(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown"+("_s%i" % i), label="Slot %i" % i)
separator1 = cmds.separator(style='in', height=16)
separator2 = cmds.separator(style='in')
saveToLabel = cmds.text(label="Save to:", annotation="This is where the .xmodel_export is saved to")
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")
fileBrowserButton = cmds.button(label="...", height=21, command="CoDMayaTools.GeneralWindow_FileBrowser('xmodel')", annotation="Open a file browser dialog")
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")
saveSelectionButton = cmds.button(label="Save Selection", command="CoDMayaTools.GeneralWindow_SaveSelection('xmodel')", annotation="Save the current object selection")
getSavedSelectionButton = cmds.button(label="Get Saved Selection", command="CoDMayaTools.GeneralWindow_GetSavedSelection('xmodel')", annotation="Reselect the saved selection")
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")
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")
setCosmeticParentbone = cmds.button(OBJECT_NAMES['xmodel'][0]+"_MarkCosmeticParent", label="Set selected as Cosmetic Parent", command="CoDMayaTools.SetCosmeticParent('xmodel')", annotation="Set this bone as our cosmetic parent. All bones under this will be cosmetic.")
RemoveCosmeticParent = cmds.button(OBJECT_NAMES['xmodel'][0]+"_ClearCosmeticParent", label="Clear Cosmetic Parent", command="CoDMayaTools.ClearCosmeticParent('xmodel')", annotation="Remove the cosmetic parent.")
# Setup form
cmds.formLayout(form, edit=True,
attachForm=[(slotDropDown, 'top', 6), (slotDropDown, 'left', 10), (slotDropDown, 'right', 10),
(separator1, 'left', 0), (separator1, 'right', 0),
(separator2, 'left', 0), (separator2, 'right', 0),
(saveToLabel, 'left', 12),
(fileBrowserButton, 'right', 10),
(exportMultipleSlotsButton, 'bottom', 6), (exportMultipleSlotsButton, 'left', 10),
(exportInMultiExportCheckbox, 'bottom', 9), (exportInMultiExportCheckbox, 'right', 6),
(exportSelectedButton, 'left', 10),
(saveSelectionButton, 'right', 10),
(setCosmeticParentbone, 'left', 10),
(RemoveCosmeticParent, 'left', 10)],
#(exportSelectedButton, 'bottom', 6), (exportSelectedButton, 'left', 10),
#(saveSelectionButton, 'bottom', 6), (saveSelectionButton, 'right', 10),
#(getSavedSelectionButton, 'bottom', 6)],
attachControl=[ (separator1, 'top', 0, slotDropDown),
(saveToLabel, 'bottom', 9, exportSelectedButton),
(saveToField, 'bottom', 5, exportSelectedButton), (saveToField, 'left', 5, saveToLabel), (saveToField, 'right', 5, fileBrowserButton),
(fileBrowserButton, 'bottom', 5, exportSelectedButton),
(exportSelectedButton, 'bottom', 5, separator2),
(saveSelectionButton, 'bottom', 5, separator2),
(setCosmeticParentbone, 'bottom', 5, separator2),
(RemoveCosmeticParent, 'bottom', 5, separator2),
(saveSelectionButton, 'bottom', 5, setCosmeticParentbone),
(exportSelectedButton, 'bottom', 5, setCosmeticParentbone),
(setCosmeticParentbone, 'bottom', 5, RemoveCosmeticParent),
(getSavedSelectionButton, 'bottom', 5, separator2), (getSavedSelectionButton, 'right', 10, saveSelectionButton),
(getSavedSelectionButton, 'bottom', 5, setCosmeticParentbone),
(separator2, 'bottom', 5, exportMultipleSlotsButton)])
def RefreshXModelWindow():
# Refresh/create node
if len(cmds.ls(OBJECT_NAMES['xmodel'][2])) == 0:
cmds.createNode("renderLayer", name=OBJECT_NAMES['xmodel'][2], skipSelect=True)
cmds.lockNode(OBJECT_NAMES['xmodel'][2], lock=False)
if not cmds.attributeQuery("slot", node=OBJECT_NAMES['xmodel'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="slot", attributeType='short', defaultValue=1)
if not cmds.attributeQuery("paths", node=OBJECT_NAMES['xmodel'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="paths", multi=True, dataType='string')
cmds.setAttr(OBJECT_NAMES['xmodel'][2]+".paths", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("selections", node=OBJECT_NAMES['xmodel'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="selections", multi=True, dataType='stringArray')
cmds.setAttr(OBJECT_NAMES['xmodel'][2]+".selections", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("useinmultiexport", node=OBJECT_NAMES['xmodel'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="useinmultiexport", multi=True, attributeType='bool', defaultValue=False)
cmds.setAttr(OBJECT_NAMES['xmodel'][2]+".useinmultiexport", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("Cosmeticbone", node=OBJECT_NAMES['xmodel'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xmodel'][2], longName="Cosmeticbone", dataType="string")
cmds.lockNode(OBJECT_NAMES['xmodel'][2], lock=True)
# Set values
slotIndex = cmds.optionMenu(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown", query=True, select=True)
path = cmds.getAttr(OBJECT_NAMES['xmodel'][2]+(".paths[%i]" % slotIndex))
cmds.setAttr(OBJECT_NAMES['xmodel'][2]+".slot", slotIndex)
cmds.textField(OBJECT_NAMES['xmodel'][0]+"_SaveToField", edit=True, fileName=path)
useInMultiExport = cmds.getAttr(OBJECT_NAMES['xmodel'][2]+(".useinmultiexport[%i]" % slotIndex))
cmds.checkBox(OBJECT_NAMES['xmodel'][0]+"_UseInMultiExportCheckBox", edit=True, value=useInMultiExport)
def SetCosmeticParent(reqarg):
selection = cmds.ls(selection = True, type = "joint")
if(len(selection) > 1):
MessageBox("Only 1 Cosmetic Parent is allowed.")
return
elif(len(selection) == 0):
MessageBox("No joint selected.")
return
cmds.setAttr(OBJECT_NAMES['xmodel'][2] + ".Cosmeticbone", selection[0], type="string")
MessageBox("\"%s\" has now been set as the cosmetic parent." % str(selection[0]))
def ClearCosmeticParent(reqarg):
cosmetic_bone = cmds.getAttr(OBJECT_NAMES["xmodel"][2]+ ".Cosmeticbone")
if cosmetic_bone is None:
cmds.error("No cosmetic bone set.")
cosmetic_bone = cosmetic_bone.split("|")[-1].split(":")[-1]
if cosmetic_bone != "" or cosmetic_bone is not None:
cmds.setAttr(OBJECT_NAMES['xmodel'][2] + ".Cosmeticbone", "", type="string")
MessageBox("Cosmetic Parent \"%s\" has now been removed." % cosmetic_bone)
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ----------------------------------------------------------------------- XAnim Export Window ----------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def CreateXAnimWindow():
# Create window
if cmds.control(OBJECT_NAMES['xanim'][0], exists=True):
cmds.deleteUI(OBJECT_NAMES['xanim'][0])
cmds.window(OBJECT_NAMES['xanim'][0], title=OBJECT_NAMES['xanim'][1], width=1, height=1, retain=True, maximizeButton=False)
form = cmds.formLayout(OBJECT_NAMES['xanim'][0]+"_Form")
# Controls
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")
for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
cmds.menuItem(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown"+("_s%i" % i), label="Slot %i" % i)
separator1 = cmds.separator(style='in')
separator2 = cmds.separator(style='in')
separator3 = cmds.separator(style='in')
framesLabel = cmds.text(label="Frames:", annotation="Range of frames to export")
framesStartField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", height=21, width=35, minValue=0, changeCommand="CoDMayaTools.UpdateFrameRange('xanim')", annotation="Starting frame to export (inclusive)")
framesToLabel = cmds.text(label="to")
framesEndField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", height=21, width=35, minValue=0, changeCommand="CoDMayaTools.UpdateFrameRange('xanim')", annotation="Ending frame to export (inclusive)")
GrabFrames = cmds.button(label="Grab Frames", width=75, command="CoDMayaTools.SetFrames('xanim')", annotation="Get frame end and start from scene.")
fpsLabel = cmds.text(label="FPS:")
fpsField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_FPSField", height=21, width=35, value=1, minValue=1, changeCommand="CoDMayaTools.UpdateFramerate('xanim')", annotation="Animation FPS")
qualityLabel = cmds.text(label="Quality (0-10)", annotation="Quality of the animation, higher values result in less jitter but produce larger files. Default is 0")
qualityField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_qualityField", height=21, width=35, value=0, minValue=0, maxValue=10, step=1, changeCommand="CoDMayaTools.UpdateMultiplier('xanim')", annotation="Quality of the animation, higher values result in less jitter but produce larger files.")
notetracksLabel = cmds.text(label="Notetrack:", annotation="Notetrack info for the animation")
noteList = cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", allowMultiSelection=False, selectCommand="CoDMayaTools.SelectNote('xanim')", annotation="List of notes in the notetrack")
addNoteButton = cmds.button(label="Add Note", width=75, command="CoDMayaTools.AddNote('xanim')", annotation="Add a note to the notetrack")
ReadNotesButton = cmds.button(label="Grab Notes", width=75, command="CoDMayaTools.ReadNotetracks('xanim')", annotation="Grab Notes from Notetrack in Outliner")
ClearNotes = cmds.button(label="Clear Notes", width=75, command="CoDMayaTools.ClearNotes('xanim')", annotation="Clear ALL notetracks.")
RenameNoteTrack = cmds.button(label="Rename Note", command="CoDMayaTools.RenameNotes('xanim')", annotation="Rename the currently selected note.")
removeNoteButton = cmds.button(label="Remove Note", command="CoDMayaTools.RemoveNote('xanim')", annotation="Remove the currently selected note from the notetrack")
noteFrameLabel = cmds.text(label="Frame:", annotation="The frame the currently selected note is applied to")
noteFrameField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_NoteFrameField", changeCommand="CoDMayaTools.UpdateNoteFrame('xanim')", height=21, width=30, minValue=0, annotation="The frame the currently selected note is applied to")
saveToLabel = cmds.text(label="Save to:", annotation="This is where .xanim_export is saved to")
saveToField = cmds.textField(OBJECT_NAMES['xanim'][0]+"_SaveToField", height=21, changeCommand="CoDMayaTools.GeneralWindow_SaveToField('xanim')", annotation="This is where .xanim_export is saved to")
fileBrowserButton = cmds.button(label="...", height=21, command="CoDMayaTools.GeneralWindow_FileBrowser('xanim', \"XAnim Intermediate File (*.xanim_export)\")", annotation="Open a file browser dialog")
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")
saveSelectionButton = cmds.button(label="Save Selection", command="CoDMayaTools.GeneralWindow_SaveSelection('xanim')", annotation="Save the current object selection")
getSavedSelectionButton = cmds.button(label="Get Saved Selection", command="CoDMayaTools.GeneralWindow_GetSavedSelection('xanim')", annotation="Reselect the saved selection")
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")
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")
ReverseAnimation = cmds.checkBox("CoDMAYA_ReverseAnim", label="Export Animation Reversed", annotation="Check this if you want to export the anim. backwards. Usefule for reversing to make opposite sprints, etc.", value=False)
TagAlignExport = cmds.checkBox("CoDMAYA_TAGALIGN", label="Export TAG_ALIGN", annotation="Check this if you want to export TAG_ALIGN with the animation, required for some animations (Not needed for Viewmodel Animations)", value=False)
# Setup form
cmds.formLayout(form, edit=True,
attachForm=[(slotDropDown, 'top', 6), (slotDropDown, 'left', 10), (slotDropDown, 'right', 10),
(separator1, 'left', 0), (separator1, 'right', 0),
(framesLabel, 'left', 10),
(fpsLabel, 'left', 10),
(qualityLabel, 'left', 10),
(notetracksLabel, 'left', 10),
(noteList, 'left', 10),
(ReverseAnimation, 'left', 10),
(TagAlignExport, 'left', 10),
(addNoteButton, 'right', 10),
(ReadNotesButton, 'right', 10),
(RenameNoteTrack, 'right', 10),
(ClearNotes, 'right', 10),
(removeNoteButton, 'right', 10),
(noteFrameField, 'right', 10),
(separator2, 'left', 0), (separator2, 'right', 0),
(saveToLabel, 'left', 12),
(fileBrowserButton, 'right', 10),
(exportMultipleSlotsButton, 'bottom', 6), (exportMultipleSlotsButton, 'left', 10),
(exportInMultiExportCheckbox, 'bottom', 9), (exportInMultiExportCheckbox, 'right', 6),
(exportSelectedButton, 'left', 10),
(saveSelectionButton, 'right', 10),
(separator3, 'left', 0), (separator3, 'right', 0)],
attachControl=[ (separator1, 'top', 6, slotDropDown),
(framesLabel, 'top', 8, separator1),
(framesStartField, 'top', 5, separator1), (framesStartField, 'left', 4, framesLabel),
(framesToLabel, 'top', 8, separator1), (framesToLabel, 'left', 4+35+4, framesLabel),
(framesEndField, 'top', 5, separator1), (framesEndField, 'left', 4, framesToLabel),
(GrabFrames, 'top', 5, separator1), (GrabFrames, 'left', 4, framesEndField),
(fpsLabel, 'top', 8, framesStartField),
(fpsField, 'top', 5, framesStartField), (fpsField, 'left', 21, fpsLabel),
(qualityLabel, 'top', 8, fpsField),
(qualityField, 'top', 5, fpsField), (qualityField, 'left', 21, qualityLabel),
(notetracksLabel, 'top', 5, qualityLabel),
(noteList, 'top', 5, notetracksLabel), (noteList, 'right', 10, removeNoteButton), (noteList, 'bottom', 60, separator2),
(ReverseAnimation, 'top', 10, noteList), (ReverseAnimation, 'right', 10, removeNoteButton),
(TagAlignExport, 'top', 5, ReverseAnimation),
(addNoteButton, 'top', 5, notetracksLabel),
(ReadNotesButton, 'top', 5, addNoteButton),
(RenameNoteTrack, 'top', 5, ReadNotesButton),
(ClearNotes, 'top', 5, RenameNoteTrack),
(removeNoteButton, 'top', 5, ClearNotes),
(noteFrameField, 'top', 5, removeNoteButton),
(noteFrameLabel, 'top', 8, removeNoteButton), (noteFrameLabel, 'right', 4, noteFrameField),
(separator2, 'bottom', 5, fileBrowserButton),
(saveToLabel, 'bottom', 10, exportSelectedButton),
(saveToField, 'bottom', 5, exportSelectedButton), (saveToField, 'left', 5, saveToLabel), (saveToField, 'right', 5, fileBrowserButton),
(fileBrowserButton, 'bottom', 5, exportSelectedButton),
(exportSelectedButton, 'bottom', 5, separator3),
(saveSelectionButton, 'bottom', 5, separator3),
(getSavedSelectionButton, 'bottom', 5, separator3), (getSavedSelectionButton, 'right', 10, saveSelectionButton),
(separator3, 'bottom', 5, exportMultipleSlotsButton)
])
def RefreshXAnimWindow():
# Refresh/create node
if len(cmds.ls(OBJECT_NAMES['xanim'][2])) == 0:
cmds.createNode("renderLayer", name=OBJECT_NAMES['xanim'][2], skipSelect=True)
cmds.lockNode(OBJECT_NAMES['xanim'][2], lock=False)
if not cmds.attributeQuery("slot", node=OBJECT_NAMES['xanim'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="slot", attributeType='short', defaultValue=1)
if not cmds.attributeQuery("paths", node=OBJECT_NAMES['xanim'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="paths", multi=True, dataType='string')
cmds.setAttr(OBJECT_NAMES['xanim'][2]+".paths", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("selections", node=OBJECT_NAMES['xanim'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="selections", multi=True, dataType='stringArray')
cmds.setAttr(OBJECT_NAMES['xanim'][2]+".selections", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("frameRanges", node=OBJECT_NAMES['xanim'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="frameRanges", multi=True, dataType='long2')
cmds.setAttr(OBJECT_NAMES['xanim'][2]+".frameRanges", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("framerate", node=OBJECT_NAMES['xanim'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="framerate", multi=True, attributeType='long', defaultValue=30)
cmds.setAttr(OBJECT_NAMES['xanim'][2]+".framerate", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("multiplier", node=OBJECT_NAMES['xanim'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="multiplier", multi=True, attributeType='long', defaultValue=30)
cmds.setAttr(OBJECT_NAMES['xanim'][2]+".multiplier", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("notetracks", node=OBJECT_NAMES['xanim'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="notetracks", multi=True, dataType='string') # Formatted as ":,:,..."
cmds.setAttr(OBJECT_NAMES['xanim'][2]+".notetracks", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("useinmultiexport", node=OBJECT_NAMES['xanim'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xanim'][2], longName="useinmultiexport", multi=True, attributeType='bool', defaultValue=False)
cmds.setAttr(OBJECT_NAMES['xanim'][2]+".useinmultiexport", size=EXPORT_WINDOW_NUMSLOTS)
cmds.lockNode(OBJECT_NAMES['xanim'][2], lock=True)
# Set values
slotIndex = cmds.optionMenu(OBJECT_NAMES['xanim'][0]+"_SlotDropDown", query=True, select=True)
cmds.setAttr(OBJECT_NAMES['xanim'][2]+".slot", slotIndex)
path = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".paths[%i]" % slotIndex))
cmds.textField(OBJECT_NAMES['xanim'][0]+"_SaveToField", edit=True, fileName=path)
frameRange = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".frameRanges[%i]" % slotIndex))
if frameRange == None:
cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".frameRanges[%i]" % slotIndex), 0, 0, type='long2')
cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", edit=True, value=0)
cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", edit=True, value=0)
else:
cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameStartField", edit=True, value=frameRange[0][0])
cmds.intField(OBJECT_NAMES['xanim'][0]+"_FrameEndField", edit=True, value=frameRange[0][1])
framerate = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".framerate[%i]" % slotIndex))
cmds.intField(OBJECT_NAMES['xanim'][0]+"_FPSField", edit=True, value=framerate)
noteFrameField = cmds.intField(OBJECT_NAMES['xanim'][0]+"_NoteFrameField", edit=True, value=0)
cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, removeAll=True)
noteList = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex)) or ""
notes = noteList.split(",")
for note in notes:
parts = note.split(":")
if note.strip() == "" or len(parts) == 0:
continue
name = "".join([c for c in parts[0] if c.isalnum() or c=="_"]).replace("sndnt", "sndnt#").replace("rmbnt", "rmbnt#")
if name == "":
continue
cmds.textScrollList(OBJECT_NAMES['xanim'][0]+"_NoteList", edit=True, append=name)
useInMultiExport = cmds.getAttr(OBJECT_NAMES['xanim'][2]+(".useinmultiexport[%i]" % slotIndex))
cmds.checkBox(OBJECT_NAMES['xanim'][0]+"_UseInMultiExportCheckBox", edit=True, value=useInMultiExport)
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ----------------------------------------------------------------------- XCam Export Window -----------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def CreateXCamWindow():
# Create window
if cmds.control(OBJECT_NAMES['xcam'][0], exists=True):
cmds.deleteUI(OBJECT_NAMES['xcam'][0])
cmds.window(OBJECT_NAMES['xcam'][0], title=OBJECT_NAMES['xcam'][1], width=1, height=1, retain=True, maximizeButton=False)
form = cmds.formLayout(OBJECT_NAMES['xcam'][0]+"_Form")
# Controls
slotDropDown = cmds.optionMenu(OBJECT_NAMES['xcam'][0]+"_SlotDropDown", changeCommand="CoDMayaTools.RefreshXCamWindow()", annotation="Each slot contains different a export path, frame range, notetrack, and saved selection")
for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
cmds.menuItem(OBJECT_NAMES['xmodel'][0]+"_SlotDropDown"+("_s%i" % i), label="Slot %i" % i)
separator1 = cmds.separator(style='in')
separator2 = cmds.separator(style='in')
separator3 = cmds.separator(style='in')
framesLabel = cmds.text(label="Frames:", annotation="Range of frames to export")
framesStartField = cmds.intField(OBJECT_NAMES['xcam'][0]+"_FrameStartField", height=21, width=35, minValue=0, changeCommand="CoDMayaTools.UpdateFrameRange('xcam')", annotation="Starting frame to export (inclusive)")
framesToLabel = cmds.text(label="to")
framesEndField = cmds.intField(OBJECT_NAMES['xcam'][0]+"_FrameEndField", height=21, width=35, minValue=0, changeCommand="CoDMayaTools.UpdateFrameRange('xcam')", annotation="Ending frame to export (inclusive)")
fpsLabel = cmds.text(label="FPS:")
fpsField = cmds.intField(OBJECT_NAMES['xcam'][0]+"_FPSField", height=21, width=35, value=1, minValue=1, changeCommand="CoDMayaTools.UpdateFramerate('xcam')", annotation="Animation FPS")
#qualityLabel = cmds.text(label="Quality (0-10)", annotation="Quality of the animation, higher values result in less jitter but produce larger files. Default is 0")
#qualityField = cmds.intField(OBJECT_NAMES['xcam'][0]+"_qualityField", height=21, width=35, value=0, minValue=0, maxValue=10, step=1, changeCommand=XCamWindow_UpdateMultiplier, annotation="Quality of the animation, higher values result in less jitter but produce larger files.")
notetracksLabel = cmds.text(label="Notetrack:", annotation="Notetrack info for the animation")
noteList = cmds.textScrollList(OBJECT_NAMES['xcam'][0]+"_NoteList", allowMultiSelection=False, selectCommand="CoDMayaTools.SelectNote('xcam')", annotation="List of notes in the notetrack")
addNoteButton = cmds.button(label="Add Note", width=75, command="CoDMayaTools.AddNote('xcam')", annotation="Add a note to the notetrack")
ReadNotesButton = cmds.button(label="Grab Notes", width=75, command="CoDMayaTools.ReadNotetracks('xcam')", annotation="Grab Notes from Notetrack in Outliner")
RenameNoteTrack = cmds.button(label="Rename Note", command="CoDMayaTools.RenameNotes('xcam')", annotation="Rename the currently selected note.")
removeNoteButton = cmds.button(label="Remove Note", command="CoDMayaTools.RemoveNote('xcam')", annotation="Remove the currently selected note from the notetrack")
noteFrameLabel = cmds.text(label="Frame:", annotation="The frame the currently selected note is applied to")
noteFrameField = cmds.intField(OBJECT_NAMES['xcam'][0]+"_NoteFrameField", changeCommand="CoDMayaTools.UpdateNoteFrame('xcam')", height=21, width=30, minValue=0, annotation="The frame the currently selected note is applied to")
GrabFrames = cmds.button(label="Grab Frames", width=75, command="CoDMayaTools.SetFrames('xcam')", annotation="Get frame end and start from scene.")
ClearNotes = cmds.button(label="Clear Notes", width=75, command="CoDMayaTools.ClearNotes('xcam')", annotation="Clear ALL notetracks.")
saveToLabel = cmds.text(label="Save to:", annotation="This is where .xcam_export is saved to")
saveToField = cmds.textField(OBJECT_NAMES['xcam'][0]+"_SaveToField", height=21, changeCommand="CoDMayaTools.GeneralWindow_SaveToField('xcam')", annotation="This is where .xcam_export is saved to")
fileBrowserButton = cmds.button(label="...", height=21, command="CoDMayaTools.GeneralWindow_FileBrowser('xcam', \"XCam Intermediate File (*.xcam_export)\")", annotation="Open a file browser dialog")
exportSelectedButton = cmds.button(label="Export Selected", command="CoDMayaTools.GeneralWindow_ExportSelected('xcam', False)", annotation="Export all currently selected joints from the scene (specified frames)\nWarning: Will automatically overwrite if the export path if it already exists")
saveSelectionButton = cmds.button(label="Save Selection", command="CoDMayaTools.GeneralWindow_SaveSelection('xcam')", annotation="Save the current object selection")
getSavedSelectionButton = cmds.button(label="Get Saved Selection", command="CoDMayaTools.GeneralWindow_GetSavedSelection('xcam')", annotation="Reselect the saved selection")
exportMultipleSlotsButton = cmds.button(label="Export Multiple Slots", command="CoDMayaTools.GeneralWindow_ExportMultiple('xcam')", annotation="Automatically export multiple slots at once, using each slot's saved selection")
exportInMultiExportCheckbox = cmds.checkBox(OBJECT_NAMES['xcam'][0]+"_UseInMultiExportCheckBox", label="Use current slot for Export Multiple", changeCommand="CoDMayaTools.GeneralWindow_ExportInMultiExport('xcam')", annotation="Check this make the 'Export Multiple Slots' button export this slot")
#ReverseAnimation = cmds.checkBox("CoDMAYA_ReverseAnim", label="Export Animation Reversed", annotation="Check this if you want to export the anim. backwards. Usefule for reversing to make opposite sprints, etc.", value=False)
# Setup form
cmds.formLayout(form, edit=True,
attachForm=[(slotDropDown, 'top', 6), (slotDropDown, 'left', 10), (slotDropDown, 'right', 10),
(separator1, 'left', 0), (separator1, 'right', 0),
(framesLabel, 'left', 10),
(fpsLabel, 'left', 10),
#(qualityLabel, 'left', 10),
(notetracksLabel, 'left', 10),
(noteList, 'left', 10),
#(ReverseAnimation, 'left', 10),
(addNoteButton, 'right', 10),
(ReadNotesButton, 'right', 10),
(RenameNoteTrack, 'right', 10),
(ClearNotes, 'right', 10),
(removeNoteButton, 'right', 10),
(noteFrameField, 'right', 10),
(separator2, 'left', 0), (separator2, 'right', 0),
(saveToLabel, 'left', 12),
(fileBrowserButton, 'right', 10),
(exportMultipleSlotsButton, 'bottom', 6), (exportMultipleSlotsButton, 'left', 10),
(exportInMultiExportCheckbox, 'bottom', 9), (exportInMultiExportCheckbox, 'right', 6),
(exportSelectedButton, 'left', 10),
(saveSelectionButton, 'right', 10),
(separator3, 'left', 0), (separator3, 'right', 0)],
attachControl=[ (separator1, 'top', 6, slotDropDown),
(framesLabel, 'top', 8, separator1),
(framesStartField, 'top', 5, separator1), (framesStartField, 'left', 4, framesLabel),
(framesToLabel, 'top', 8, separator1), (framesToLabel, 'left', 4+35+4, framesLabel),
(framesEndField, 'top', 5, separator1), (framesEndField, 'left', 4, framesToLabel),
(GrabFrames, 'top', 5, separator1), (GrabFrames, 'left', 4, framesEndField),
(fpsLabel, 'top', 8, framesStartField),
(fpsField, 'top', 5, framesStartField), (fpsField, 'left', 21, fpsLabel),
#(qualityLabel, 'top', 8, fpsField),
#(qualityField, 'top', 5, fpsField), (qualityField, 'left', 21, qualityLabel),
(notetracksLabel, 'top', 5, fpsField),
(noteList, 'top', 5, notetracksLabel), (noteList, 'right', 10, removeNoteButton), (noteList, 'bottom', 60, separator2),
#(ReverseAnimation, 'top', 10, noteList), (ReverseAnimation, 'right', 10, removeNoteButton),
(addNoteButton, 'top', 5, notetracksLabel),
(ReadNotesButton, 'top', 5, addNoteButton),
(RenameNoteTrack, 'top', 5, ReadNotesButton),
(ClearNotes, 'top', 5, RenameNoteTrack),
(removeNoteButton, 'top', 5, ClearNotes),
(noteFrameField, 'top', 5, removeNoteButton),
(noteFrameLabel, 'top', 8, removeNoteButton), (noteFrameLabel, 'right', 4, noteFrameField),
(separator2, 'bottom', 5, fileBrowserButton),
(saveToLabel, 'bottom', 10, exportSelectedButton),
(saveToField, 'bottom', 5, exportSelectedButton), (saveToField, 'left', 5, saveToLabel), (saveToField, 'right', 5, fileBrowserButton),
(fileBrowserButton, 'bottom', 5, exportSelectedButton),
(exportSelectedButton, 'bottom', 5, separator3),
(saveSelectionButton, 'bottom', 5, separator3),
(getSavedSelectionButton, 'bottom', 5, separator3), (getSavedSelectionButton, 'right', 10, saveSelectionButton),
(separator3, 'bottom', 5, exportMultipleSlotsButton)
])
def RefreshXCamWindow():
# Refresh/create node
if len(cmds.ls(OBJECT_NAMES['xcam'][2])) == 0:
cmds.createNode("renderLayer", name=OBJECT_NAMES['xcam'][2], skipSelect=True)
cmds.lockNode(OBJECT_NAMES['xcam'][2], lock=False)
if not cmds.attributeQuery("slot", node=OBJECT_NAMES['xcam'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xcam'][2], longName="slot", attributeType='short', defaultValue=1)
if not cmds.attributeQuery("paths", node=OBJECT_NAMES['xcam'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xcam'][2], longName="paths", multi=True, dataType='string')
cmds.setAttr(OBJECT_NAMES['xcam'][2]+".paths", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("selections", node=OBJECT_NAMES['xcam'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xcam'][2], longName="selections", multi=True, dataType='stringArray')
cmds.setAttr(OBJECT_NAMES['xcam'][2]+".selections", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("frameRanges", node=OBJECT_NAMES['xcam'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xcam'][2], longName="frameRanges", multi=True, dataType='long2')
cmds.setAttr(OBJECT_NAMES['xcam'][2]+".frameRanges", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("framerate", node=OBJECT_NAMES['xcam'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xcam'][2], longName="framerate", multi=True, attributeType='long', defaultValue=30)
cmds.setAttr(OBJECT_NAMES['xcam'][2]+".framerate", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("multiplier", node=OBJECT_NAMES['xcam'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xcam'][2], longName="multiplier", multi=True, attributeType='long', defaultValue=30)
cmds.setAttr(OBJECT_NAMES['xcam'][2]+".multiplier", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("notetracks", node=OBJECT_NAMES['xcam'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xcam'][2], longName="notetracks", multi=True, dataType='string') # Formatted as ":,:,..."
cmds.setAttr(OBJECT_NAMES['xcam'][2]+".notetracks", size=EXPORT_WINDOW_NUMSLOTS)
if not cmds.attributeQuery("useinmultiexport", node=OBJECT_NAMES['xcam'][2], exists=True):
cmds.addAttr(OBJECT_NAMES['xcam'][2], longName="useinmultiexport", multi=True, attributeType='bool', defaultValue=False)
cmds.setAttr(OBJECT_NAMES['xcam'][2]+".useinmultiexport", size=EXPORT_WINDOW_NUMSLOTS)
cmds.lockNode(OBJECT_NAMES['xcam'][2], lock=True)
# Set values
slotIndex = cmds.optionMenu(OBJECT_NAMES['xcam'][0]+"_SlotDropDown", query=True, select=True)
cmds.setAttr(OBJECT_NAMES['xcam'][2]+".slot", slotIndex)
path = cmds.getAttr(OBJECT_NAMES['xcam'][2]+(".paths[%i]" % slotIndex))
cmds.textField(OBJECT_NAMES['xcam'][0]+"_SaveToField", edit=True, fileName=path)
frameRange = cmds.getAttr(OBJECT_NAMES['xcam'][2]+(".frameRanges[%i]" % slotIndex))
if frameRange == None:
cmds.setAttr(OBJECT_NAMES['xcam'][2]+(".frameRanges[%i]" % slotIndex), 0, 0, type='long2')
cmds.intField(OBJECT_NAMES['xcam'][0]+"_FrameStartField", edit=True, value=0)
cmds.intField(OBJECT_NAMES['xcam'][0]+"_FrameEndField", edit=True, value=0)
else:
cmds.intField(OBJECT_NAMES['xcam'][0]+"_FrameStartField", edit=True, value=frameRange[0][0])
cmds.intField(OBJECT_NAMES['xcam'][0]+"_FrameEndField", edit=True, value=frameRange[0][1])
framerate = cmds.getAttr(OBJECT_NAMES['xcam'][2]+(".framerate[%i]" % slotIndex))
cmds.intField(OBJECT_NAMES['xcam'][0]+"_FPSField", edit=True, value=framerate)
noteFrameField = cmds.intField(OBJECT_NAMES['xcam'][0]+"_NoteFrameField", edit=True, value=0)
cmds.textScrollList(OBJECT_NAMES['xcam'][0]+"_NoteList", edit=True, removeAll=True)
noteList = cmds.getAttr(OBJECT_NAMES['xcam'][2]+(".notetracks[%i]" % slotIndex)) or ""
notes = noteList.split(",")
for note in notes:
parts = note.split(":")
if note.strip() == "" or len(parts) == 0:
continue
name = "".join([c for c in parts[0] if c.isalnum() or c=="_"])
if name == "":
continue
cmds.textScrollList(OBJECT_NAMES['xcam'][0]+"_NoteList", edit=True, append=name)
useInMultiExport = cmds.getAttr(OBJECT_NAMES['xcam'][2]+(".useinmultiexport[%i]" % slotIndex))
cmds.checkBox(OBJECT_NAMES['xcam'][0]+"_UseInMultiExportCheckBox", edit=True, value=useInMultiExport)
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------- xAnim/xCam Export Data --------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def SetFrames(windowID):
"""
Querys start and end frames and set thems for the window given by windowID
"""
start = cmds.playbackOptions(minTime=True, query=True)
end = cmds.playbackOptions(maxTime=True, query=True) # Query start and end froms.
cmds.intField(OBJECT_NAMES[windowID][0] + "_FrameStartField", edit=True, value=start)
cmds.intField(OBJECT_NAMES[windowID][0] + "_FrameEndField", edit=True, value=end)
UpdateFrameRange(windowID)
def UpdateFrameRange(windowID):
"""
Updates start and end frame when set by user or by other means.
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
start = cmds.intField(OBJECT_NAMES[windowID][0]+"_FrameStartField", query=True, value=True)
end = cmds.intField(OBJECT_NAMES[windowID][0]+"_FrameEndField", query=True, value=True)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".frameRanges[%i]" % slotIndex), start, end, type='long2')
def UpdateFramerate(windowID):
"""
Updates framerate when set by user or by other means.
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
fps = cmds.intField(OBJECT_NAMES[windowID][0]+"_FPSField", query=True, value=True)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".framerate[%i]" % slotIndex), fps)
def UpdateMultiplier(windowID):
"""
Updates multiplier when set by user or by other means.
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
fps = cmds.intField(OBJECT_NAMES[windowID][0]+"_qualityField", query=True, value=True)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".multiplier[%i]" % slotIndex), fps)
def AddNote(windowID):
"""
Add notetrack to window and attribute when user creates one.
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
if cmds.promptDialog(title="Add Note to Slot %i's Notetrack" % slotIndex, message="Enter the note's name:\t\t ") != "Confirm":
return
userInput = cmds.promptDialog(query=True, text=True)
noteName = "".join([c for c in userInput if c.isalnum() or c=="_"]).replace("sndnt", "sndnt#").replace("rmbnt", "rmbnt#") # Remove all non-alphanumeric characters
if noteName == "":
MessageBox("Invalid note name")
return
existingItems = cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", query=True, allItems=True)
if existingItems != None and noteName in existingItems:
MessageBox("A note with this name already exists")
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
noteList += "%s:%i," % (noteName, cmds.currentTime(query=True))
cmds.setAttr(OBJECT_NAMES['xanim'][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", edit=True, append=noteName, selectIndexedItem=len((existingItems or []))+1)
SelectNote(windowID)
def __get_notetracks__():
"""Loads all the notetracks in the scene"""
if not cmds.objExists("SENotes"):
cmds.rename(cmds.spaceLocator(), "SENotes")
if not cmds.objExists("SENotes.Notetracks"):
cmds.addAttr("SENotes", longName="Notetracks",
dataType="string", storable=True)
cmds.setAttr("SENotes.Notetracks", "{}", type="string")
# Load the existing notetracks buffer, then ensure we have this notetrack
return json.loads(cmds.getAttr("SENotes.Notetracks"))
def ReadNotetracks(windowID):
"""
Read notetracks from imported animations.
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
existingItems = cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", query=True, allItems=True)
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
# Get Notetracks
notetracks = __get_notetracks__()
# Add notetrack type prefix automatically
write_note_type = QueryToggableOption('PrefixNoteType')
for note, frames in notetracks.iteritems():
# Ignore end/loop_end
if note == "end" or note == "loop_end":
continue
# Check if we want to write notetype
# and if note is not already prefixed.
if(write_note_type and not "nt#" in note):
# Set Sound Note as Standard
note_type = "sndnt"
# Split notetrack's name
notesplit = note.split("_")
# Check is this a rumble (first word will be viewmodel/reload)
if(notesplit[0] == "viewmodel" or notesplit[0] == "reload"):
note_type = "rmbnt"
note = note.replace("viewmodel", "reload")
# Append
note = "#".join((note_type, note))
# Loop through note frames
for frame in frames:
# Append to list and scroll list
noteList += "%s:%i," % (note, frame)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", edit=True, append=note, selectIndexedItem=len((existingItems or []))+1)
# Set selected note
SelectNote(windowID)
def RenameNotes(windowID):
"""
Rename selected notetrack.
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
currentIndex = cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", query=True, selectIndexedItem=True)
if currentIndex != None and len(currentIndex) > 0 and currentIndex[0] >= 1:
if cmds.promptDialog(title="Rename NoteTrack in slot", message="Enter new notetrack name:\t\t ") != "Confirm":
return
userInput = cmds.promptDialog(query=True, text=True)
noteName = "".join([c for c in userInput if c.isalnum() or c=="_"]).replace("sndnt", "sndnt#").replace("rmbnt", "rmbnt#") # Remove all non-alphanumeric characters
if noteName == "":
MessageBox("Invalid note name")
return
currentIndex = currentIndex[0]
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
notes = noteList.split(",")
noteInfo = notes[currentIndex-1].split(":")
note = int(noteInfo[1])
NoteTrack = userInput
# REMOVE NOTE
cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", edit=True, removeIndexedItem=currentIndex)
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
notes = noteList.split(",")
del notes[currentIndex-1]
noteList = ",".join(notes)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
# REMOVE NOTE
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
noteList += "%s:%i," % (NoteTrack, note) # Add Notes to Aidan's list.
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", edit=True, append=NoteTrack, selectIndexedItem=currentIndex)
SelectNote(windowID)
def RemoveNote(windowID):
"""
Remove Note
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
currentIndex = cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", query=True, selectIndexedItem=True)
if currentIndex != None and len(currentIndex) > 0 and currentIndex[0] >= 1:
currentIndex = currentIndex[0]
cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", edit=True, removeIndexedItem=currentIndex)
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
notes = noteList.split(",")
del notes[currentIndex-1]
noteList = ",".join(notes)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
SelectNote(windowID)
def ClearNotes(windowID):
"""
Clear ALL notetracks.
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
notes = cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", query=True, allItems=True)
if notes is None:
return
for note in notes:
cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", edit=True, removeItem=note)
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
notetracks = noteList.split(",")
del notetracks
noteList = ""
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
SelectNote(windowID)
def UpdateNoteFrame(windowID):
"""
Update notetrack information.
"""
newFrame = cmds.intField(OBJECT_NAMES[windowID][0] + "_NoteFrameField", query = True, value = True)
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
currentIndex = cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", query=True, selectIndexedItem=True)
if currentIndex != None and len(currentIndex) > 0 and currentIndex[0] >= 1:
currentIndex = currentIndex[0]
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
notes = noteList.split(",")
parts = notes[currentIndex-1].split(":")
if len(parts) < 2:
parts("Error parsing notetrack string (A) at %i: %s" % (currentIndex, noteList))
notes[currentIndex-1] = "%s:%i" % (parts[0], newFrame)
noteList = ",".join(notes)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex), noteList, type='string')
def SelectNote(windowID):
"""
Select notetrack
"""
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
currentIndex = cmds.textScrollList(OBJECT_NAMES[windowID][0]+"_NoteList", query=True, selectIndexedItem=True)
if currentIndex != None and len(currentIndex) > 0 and currentIndex[0] >= 1:
currentIndex = currentIndex[0]
noteList = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".notetracks[%i]" % slotIndex)) or ""
notes = noteList.split(",")
parts = notes[currentIndex-1].split(":")
if len(parts) < 2:
error("Error parsing notetrack string (B) at %i: %s" % (currentIndex, noteList))
frame=0
try:
frame = int(parts[1])
except ValueError:
pass
noteFrameField = cmds.intField(OBJECT_NAMES[windowID][0]+"_NoteFrameField", edit=True, value=frame)
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------- General Export Window ---------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# GeneralWindow_... are callback functions that are used by both export windows
def GeneralWindow_SaveToField(windowID):
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
filePath = cmds.textField(OBJECT_NAMES[windowID][0]+"_SaveToField", query=True, fileName=True)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".paths[%i]" % slotIndex), filePath, type='string')
def GeneralWindow_FileBrowser(windowID, formatExtension="*"):
current_game = GetCurrentGame()
defaultFolder = GetRootFolder(None, current_game)
if windowID == 'xanim':
defaultFolder = defaultFolder + 'xanim_export/'
# Switch these around depending on title user has selected.
# and whether we're using ExportX
formatExtension = (
"XAnim Binary File (.xanim_bin) (*.xanim_bin);;"
"XAnim ASCII File (.xanim_export) (*.xanim_export)"
if GetCurrentGame() == "CoD12" and not USE_EXPORT_X else
"XAnim ASCII File (.xanim_export) (*.xanim_export);;"
"XAnim Binary File (.xanim_bin) (*.xanim_bin)")
elif windowID == 'xcam':
defaultFolder = defaultFolder + 'xanim_export/'
elif windowID == 'xmodel':
defaultFolder = defaultFolder + 'model_export/'
# Switch these around depending on title user has selected.
# and whether we're using ExportX
formatExtension = (
"XModel Binary File (.xmodel_bin) (*.xmodel_bin);;"
"XModel ASCII File (.xmodel_export) (*.xmodel_export)"
if GetCurrentGame() == "CoD12" and not USE_EXPORT_X else
"XModel ASCII File (.xmodel_export) (*.xmodel_export);;"
"XModel Binary File (.xmodel_bin) (*.xmodel_bin)")
saveTo = cmds.fileDialog2(fileMode=0, fileFilter=formatExtension, caption="Export To", startingDirectory=defaultFolder)
if saveTo == None or len(saveTo) == 0 or saveTo[0].strip() == "":
return
saveTo = saveTo[0].strip()
cmds.textField(OBJECT_NAMES[windowID][0]+"_SaveToField", edit=True, fileName=saveTo)
GeneralWindow_SaveToField(windowID)
def GeneralWindow_SaveSelection(windowID):
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
selection = cmds.ls(selection=True)
if selection == None or len(selection) == 0:
return
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".selections[%i]" % slotIndex), len(selection), *selection, type='stringArray')
def GeneralWindow_GetSavedSelection(windowID):
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
selection = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".selections[%i]" % slotIndex))
validSelection = []
for obj in selection:
if cmds.objExists(obj):
validSelection.append(obj)
# Remove non-existing objects from the saved list
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".selections[%i]" % slotIndex), len(validSelection), *validSelection, type='stringArray')
if validSelection == None or len(validSelection) == 0:
MessageBox("No selection saved to slot %i" % slotIndex)
return False
cmds.select(validSelection)
return True
def GeneralWindow_ExportSelected(windowID, exportingMultiple):
global WarningsDuringExport
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
# Get path
filePath = cmds.textField(OBJECT_NAMES[windowID][0]+"_SaveToField", query=True, fileName=True)
if filePath.strip() == "":
if exportingMultiple:
MessageBox("Invalid path on slot %i:\n\nPath is empty." % slotIndex)
else:
MessageBox("Invalid path:\n\nPath is empty.")
return
if os.path.isdir(filePath):
if exportingMultiple:
MessageBox("Invalid path on slot %i:\n\nPath points to an existing directory." % slotIndex)
else:
MessageBox("Invalid path:\n\nPath points to an existing directory.")
return
# Save reminder
if not exportingMultiple and not SaveReminder():
return
# Progress bar
if cmds.control("w"+OBJECT_NAMES['progress'][0], exists=True):
cmds.deleteUI("w"+OBJECT_NAMES['progress'][0])
progressWindow = cmds.window("w"+OBJECT_NAMES['progress'][0], title=OBJECT_NAMES['progress'][1], width=302, height=22, sizeable=False)
cmds.columnLayout()
progressControl = cmds.progressBar(OBJECT_NAMES['progress'][0], width=300)
if QueryToggableOption("PrintExport") and windowID == "xmodel":
cmds.scrollField("ExportLog", editable=False, wordWrap=False, width = 300)
cmds.showWindow(progressWindow)
cmds.refresh() # Force the progress bar to be drawn
# Export
if not exportingMultiple:
WarningsDuringExport = 0
response = None
try:
exec("response = %s(\"%s\")" % (OBJECT_NAMES[windowID][4], filePath))
except Exception as e:
response = "An unhandled error occurred during export:\n\n" + traceback.format_exc()
cmds.deleteUI(progressWindow, window=True)
# Handle response
if type(response) == str:
if exportingMultiple:
MessageBox("Slot %i\n\n%s" % (slotIndex, response))
else:
MessageBox(response)
elif WarningsDuringExport > 0 and not exportingMultiple:
MessageBox("Warnings occurred during export. Check the script editor output for more details.")
def GeneralWindow_ExportMultiple(windowID):
originalSlotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
any = False
for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
useInMultiExport = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".useinmultiexport[%i]" % i))
if useInMultiExport:
any = True
break
if not any:
MessageBox("No slots set to export.")
return
if not SaveReminder():
return
WarningsDuringExport = 0
originalSelection = cmds.ls(selection=True)
for i in range(1, EXPORT_WINDOW_NUMSLOTS+1):
useInMultiExport = cmds.getAttr(OBJECT_NAMES[windowID][2]+(".useinmultiexport[%i]" % i))
if useInMultiExport:
print ("Exporting slot %i in multiexport" % i)
cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", edit=True, select=i)
exec(OBJECT_NAMES[windowID][3] + "()") # Refresh window
if GeneralWindow_GetSavedSelection(windowID):
GeneralWindow_ExportSelected(windowID, True)
if originalSelection == None or len(originalSelection) == 0:
cmds.select(clear=True)
else:
cmds.select(originalSelection)
if WarningsDuringExport > 0:
MessageBox("Warnings occurred during export. Check the script editor output for more details.")
# Reset slot
cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", edit=True, select=originalSlotIndex)
exec(OBJECT_NAMES[windowID][3] + "()") # Refresh window
def GeneralWindow_ExportInMultiExport(windowID):
slotIndex = cmds.optionMenu(OBJECT_NAMES[windowID][0]+"_SlotDropDown", query=True, select=True)
useInMultiExport = cmds.checkBox(OBJECT_NAMES[windowID][0]+"_UseInMultiExportCheckBox", query=True, value=True)
cmds.setAttr(OBJECT_NAMES[windowID][2]+(".useinmultiexport[%i]" % slotIndex), useInMultiExport)
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------- General GUI --------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def SaveReminder(allowUnsaved=True):
if cmds.file(query=True, modified=True):
if cmds.file(query=True, exists=True):
result = cmds.confirmDialog(message="Save changes to %s?" % cmds.file(query=True, sceneName=True), button=["Yes", "No", "Cancel"], defaultButton="Yes", title="Save Changes")
if result == "Yes":
cmds.file(save=True)
elif result != "No":
return False
else: # The file has never been saved (has no name)
if allowUnsaved:
result = cmds.confirmDialog(message="The current scene is not saved. Continue?", button=["Yes", "No"], defaultButton="Yes", title="Save Changes")
if result != "Yes":
return False
else:
MessageBox("The scene needs to be saved first")
return False
return True
def PrintWarning(message):
global WarningsDuringExport
if WarningsDuringExport < MAX_WARNINGS_SHOWN:
print ("WARNING: %s" % message)
WarningsDuringExport += 1
elif WarningsDuringExport == MAX_WARNINGS_SHOWN:
print ("More warnings not shown because printing text is slow...\n")
WarningsDuringExport = MAX_WARNINGS_SHOWN+1
def MessageBox(message):
cmds.confirmDialog(message=message, button='OK', defaultButton='OK', title=OBJECT_NAMES['menu'][1])
def ShowWindow(windowID):
exec(OBJECT_NAMES[windowID][3] + "()") # Refresh window
cmds.showWindow(OBJECT_NAMES[windowID][0])
def ProgressBarStep():
cmds.progressBar(OBJECT_NAMES['progress'][0], edit=True, step=1)
def LogExport(text, isWarning = False):
if QueryToggableOption("PrintExport"):
if isWarning:
global WarningsDuringExport
if WarningsDuringExport < MAX_WARNINGS_SHOWN:
cmds.scrollField("ExportLog", edit = True, insertText = text)
WarningsDuringExport += 1
elif WarningsDuringExport == MAX_WARNINGS_SHOWN:
cmds.scrollField("ExportLog", edit = True, insertText = "More warnings not shown because printing text is slow...\n")
WarningsDuringExport = MAX_WARNINGS_SHOWN+1
else:
cmds.scrollField("ExportLog", edit = True, insertText = text)
def AboutWindow():
result = cmds.confirmDialog(message="Call of Duty Tools for Maya, created by Aidan Shafran (with assistance from The Internet).\nMaintained by Ray1235 (Maciej Zaremba) & Scobalula\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.\n\nVersion: %.2f" % FILE_VERSION, button=['OK', 'Visit Github Repo', 'CoD File Formats'], defaultButton='OK', title="About " + OBJECT_NAMES['menu'][1])
if result == "Visit Github Repo":
webbrowser.open("https://github.com/Ray1235/CoDMayaTools")
elif result == "CoD File Formats":
webbrowser.open("http://aidanshafran.com/codmayatools/codformats.html")
def LegacyWindow():
result = cmds.confirmDialog(message="""CoD1 mode exports models that are compatible with CoD1.
When this mode is disabled, the plugin will export models that are compatible with CoD2 and newer.
""", button=['OK'], defaultButton='OK', title="Legacy options")
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
# ----------------------------------------------------------------------- Get/Set Root Folder ----------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------
def SetRootFolder(msg=None, game="none"):
#if game == "none":
# game = currentGame
#if game == "none":
# res = cmds.confirmDialog(message="Please select the game you're working with", button=['OK'], defaultButton='OK', title="WARNING")
# return None
# Get current root folder (this also makes sure the reg key exists)
codRootPath = GetRootFolder(False, game)
# Open input box
#if cmds.promptDialog(title="Set Root Path", message=msg or "Change your root path:\t\t\t", text=codRootPath) != "Confirm":
# return None
codRootPath = cmds.fileDialog2(fileMode=3, dialogStyle=2)[0] + "/"
# Check to make sure the path exists
if not os.path.isdir(codRootPath):
MessageBox("Given root path does not exist")
return None
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_SET_VALUE)
reg.SetValueEx(storageKey, "%sRootPath" % game, 0, reg.REG_SZ, codRootPath)
reg.CloseKey(storageKey)
return codRootPath
def GetRootFolder(firstTimePrompt=False, game="none"):
codRootPath = ""
try:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
codRootPath = reg.QueryValueEx(storageKey, "%sRootPath" % game)[0]
reg.CloseKey(storageKey)
except WindowsError:
print(traceback.format_exc())
# First time, create key
storageKey = reg.CreateKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
reg.SetValueEx(storageKey, "RootPath", 0, reg.REG_SZ, "")
reg.CloseKey(storageKey)
if not os.path.isdir(codRootPath):
codRootPath = ""
# First-time prompt
if firstTimePrompt:
result = SetRootFolder("Your root folder path hasn't been confirmed yet. If the following is not\ncorrect, please fix it:", game)
if result:
codRootPath = result
return codRootPath
def RunExport2Bin(file):
p = GetExport2Bin()
directory = os.path.dirname(os.path.realpath(file))
if os.path.splitext(os.path.basename(p))[0] == "export2bin":
p = subprocess.Popen([p, "*"], cwd=directory)
elif os.path.splitext(os.path.basename(p))[0] == "exportx":
p = subprocess.Popen([p, "-f %s" % file])
p.wait()
if(QueryToggableOption('DeleteExport')):
os.remove(file)
def SetExport2Bin():
export2binpath = cmds.fileDialog2(fileMode=1, dialogStyle=2)[0]
# Check to make sure the path exists
if not os.path.isfile(export2binpath):
MessageBox("Given path does not exist")
return ""
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_SET_VALUE)
reg.SetValueEx(storageKey, "Export2BinPath", 0, reg.REG_SZ, export2binpath)
reg.CloseKey(storageKey)
return export2binpath
def GetExport2Bin(skipSet=True):
export2binpath = ""
try:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
export2binpath = reg.QueryValueEx(storageKey, "Export2BinPath")[0]
reg.CloseKey(storageKey)
except WindowsError:
# First time, create key
storageKey = reg.CreateKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
reg.SetValueEx(storageKey, "Export2BinPath", 0, reg.REG_SZ, "")
reg.CloseKey(storageKey)
if not os.path.isfile(export2binpath):
export2binpath = ""
if not skipSet:
result = SetExport2Bin()
if result:
export2binpath = result
return export2binpath
def CheckForUpdatesEXE():
# Check if we want updates
if QueryToggableOption("AutoUpdate"):
# Try run application
try:
p = ("%s -name %s -version %f -version_info_url %s"
% (os.path.join(WORKING_DIR, "autoUpdate.exe"),
"CoDMayaTools.py",
FILE_VERSION,
VERSION_CHECK_URL))
subprocess.Popen("%s %f" % (os.path.join(WORKING_DIR, "Updater.exe"), FILE_VERSION))
except:
# Failed, exit.
return
else:
return
#def SetGame(name):
# currentGame = name
##########################################################
# Ray's Animation Toolkit #
# #
# Credits: #
# Aidan - teaching me how to make plugins like this :) #
##########################################################
def GenerateCamAnim(reqarg=""):
useDefMesh = False
if (cmds.objExists(getObjectByAlias("camera")) == False):
print ("Camera doesn't exist")
return
if (cmds.objExists(getObjectByAlias("weapon")) == False):
print ("Weapon doesn't exist")
return
animStart = cmds.playbackOptions(query=True, minTime=True)
animEnd = cmds.playbackOptions(query=True, maxTime=True)
jointGun = cmds.xform(getObjectByAlias("weapon"), query=True, rotation=True)
jointGunPos = cmds.xform(getObjectByAlias("weapon"), query=True, translation=True)
GunMoveXorig = jointGunPos[0]*-0.025
GunRotYAddorig = jointGunPos[0]*-0.5
GunRotXAddorig = jointGunPos[1]*-0.25
progressW = cmds.progressWindow(minValue=animStart,maxValue=animEnd)
for i in range(int(animStart),int(animEnd+1)):
cmds.currentTime(i)
jointGun = cmds.xform(getObjectByAlias("weapon"), query=True, rotation=True)
jointGunPos = cmds.xform(getObjectByAlias("weapon"), query=True, translation=True)
GunMoveX = jointGunPos[0]*-0.025
GunRotYAdd = jointGunPos[0]*-0.5
GunRotXAdd = jointGunPos[1]*-0.25
GunRot = jointGun
GunRot[0] = jointGun[0]
GunRot[0] = GunRot[0] * 0.025
GunRot[1] = jointGun[1]
GunRot[1] = GunRot[1] * 0.025
GunRot[2] = jointGun[2]
GunRot[2] = GunRot[2] * 0.025
print (GunRot)
print (jointGun)
cmds.select(getObjectByAlias("camera"), replace=True)
# cmds.rotate(GunRot[0], GunRot[1], GunRot[2], rotateXYZ=True)
cmds.setKeyframe(v=(GunMoveX-GunMoveXorig),at='translateX')
cmds.setKeyframe(v=GunRot[0]+(GunRotXAdd-GunRotXAddorig),at='rotateX')
cmds.setKeyframe(v=(GunRot[1]+(GunRotYAdd-GunRotYAddorig)),at='rotateY')
cmds.setKeyframe(v=GunRot[2],at='rotateZ')
cmds.progressWindow(edit=True,step=1)
cmds.progressWindow(edit=True,endProgress=True)
def RemoveCameraKeys(reqarg=""):
if (cmds.objExists(getObjectByAlias("camera")) == False):
print ("ERROR: Camera doesn't exist")
return
else:
print ("Camera exists!")
jointCamera = cmds.joint(getObjectByAlias("camera"), query=True)
animStart = cmds.playbackOptions(query=True, minTime=True)
animEnd = cmds.playbackOptions(query=True, maxTime=True)
cmds.select(getObjectByAlias("camera"), replace=True)
#cmds.setAttr('tag_camera.translateX',0)
#cmds.setAttr('tag_camera.translateY',0)
#cmds.setAttr('tag_camera.translateZ',0)
#cmds.setAttr('tag_camera.rotateX',0)
#cmds.setAttr('tag_camera.rotateY',0)
#cmds.setAttr('tag_camera.rotateZ',0)
# cmds.rotate(GunRot[0], GunRot[1], GunRot[2], rotateXYZ=True)
cmds.cutKey(clear=True,time=(animStart,animEnd+1))
def RemoveCameraAnimData(reqarg=""):
if (cmds.objExists(getObjectByAlias("camera")) == False):
print ("ERROR: Camera doesn't exist")
return
else:
print ("Camera exists!")
jointCamera = cmds.joint(getObjectByAlias("camera"), query=True)
animStart = cmds.playbackOptions(query=True, animationStartTime=True)
animEnd = cmds.playbackOptions(query=True, animationEndTime=True)
cmds.cutKey(clear=True,time=(animStart,animEnd+1))
cmds.select(getObjectByAlias("camera"), replace=True)
cmds.setAttr(getObjectByAlias("camera")+'.translateX',0)
cmds.setAttr(getObjectByAlias("camera")+'.translateY',0)
cmds.setAttr(getObjectByAlias("camera")+'.translateZ',0)
cmds.setAttr(getObjectByAlias("camera")+'.rotateX',0)
cmds.setAttr(getObjectByAlias("camera")+'.rotateY',0)
cmds.setAttr(getObjectByAlias("camera")+'.rotateZ',0)
def setObjectAlias(aname):
if len(cmds.ls("CoDMayaTools")) == 0:
cmds.createNode("renderLayer", name="CoDMayaTools", skipSelect=True)
if not cmds.attributeQuery("objAlias%s" % aname, node="CoDMayaTools", exists=True):
cmds.addAttr("CoDMayaTools", longName="objAlias%s" % aname, dataType='string')
objects = cmds.ls(selection=True);
if len(objects) == 1:
print ("Marking selected object as %s" % aname)
else:
print ("Selected more than 1 object or none at all")
return
obj = objects[0]
cmds.setAttr("CoDMayaTools.objAlias%s" % aname, obj, type='string')
def getObjectByAlias(aname):
if len(cmds.ls("CoDMayaTools")) == 0:
cmds.createNode("renderLayer", name="CoDMayaTools", skipSelect=True)
if not cmds.attributeQuery("objAlias%s" % aname, node="CoDMayaTools", exists=True):
return ""
return cmds.getAttr("CoDMayaTools.objAlias%s" % aname) or ""
# Bind the weapon to hands
def WeaponBinder():
# Call of Duty specific
for x in xrange(0, len(GUN_BASE_TAGS)):
try:
# Select both tags and parent them
cmds.select(GUN_BASE_TAGS[x], replace = True)
cmds.select(VIEW_HAND_TAGS[x], toggle = True)
# Connect
cmds.connectJoint(connectMode = True)
# Parent
mel.eval("parent " + GUN_BASE_TAGS[x] + " " + VIEW_HAND_TAGS[x])
# Reset the positions of both bones
cmds.setAttr(GUN_BASE_TAGS[x] + ".t", 0, 0, 0)
cmds.setAttr(GUN_BASE_TAGS[x] + ".jo", 0, 0, 0)
cmds.setAttr(GUN_BASE_TAGS[x] + ".rotate", 0, 0, 0)
# Reset the rotation of the parent tag
cmds.setAttr(VIEW_HAND_TAGS[x] + ".jo", 0, 0, 0)
cmds.setAttr(VIEW_HAND_TAGS[x] + ".rotate", 0, 0, 0)
# Remove
cmds.select(clear = True)
except:
pass
def SetToggableOption(name="", val=0):
if not val:
val = int(cmds.menuItem(name, query=True, checkBox=True ))
try:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_ALL_ACCESS)
except WindowsError:
storageKey = reg.CreateKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_ALL_ACCESS)
reg.SetValueEx(storageKey, "Setting_%s" % name, 0, reg.REG_DWORD, val )
def QueryToggableOption(name=""):
try:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_ALL_ACCESS)
reg.QueryValueEx(storageKey, "Setting_%s" % name)[0]
except WindowsError:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1], 0, reg.KEY_ALL_ACCESS)
try:
reg.SetValueEx(storageKey, "Setting_%s" % name, 0, reg.REG_DWORD , 0 )
except:
return 1
return reg.QueryValueEx(storageKey, "Setting_%s" % name)[0]
# ---- Create windows ----
try:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
except WindowsError:
storageKey = reg.CreateKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1]) # Seems to fail because above in the bin function it tries to open the key but doesn't exist and stops there, so I heck it and added this.
try:
storageKey = reg.OpenKey(GLOBAL_STORAGE_REG_KEY[0], GLOBAL_STORAGE_REG_KEY[1])
codRootPath = reg.QueryValueEx(storageKey, "RootPath")[0]
reg.CloseKey(storageKey)
except WindowsError:
cmds.confirmDialog(message="It looks like this is your first time running CoD Maya Tools.\nYou will be asked to choose your game's root path.", button=['OK'], defaultButton='OK', title="First time configuration") #MessageBox("Please set your root path before starting to work with CoD Maya Tools")
result = cmds.confirmDialog(message="Which Game will you be working with? (Can be changed in settings)\n\nCoD4 = MW, CoD5 = WaW, CoD7 = BO1, CoD12 = Bo3", button=['CoD1', 'CoD2', 'CoD4', "CoD5", "CoD7", "CoD12"], defaultButton='OK', title="First time configuration") #MessageBox("Please set your root path before starting to work with CoD Maya Tools")
SetCurrentGame(result)
SetRootFolder(None, result)
res = cmds.confirmDialog(message="Enable Automatic Updates?", button=['Yes', 'No'], defaultButton='No', title="First time configuration")
if res == "Yes":
SetToggableOption(name="AutoUpdate", val=1)
else:
SetToggableOption(name="AutoUpdate", val=0)
cmds.confirmDialog(message="You're set! You can now export models and anims to any CoD!")
CheckForUpdatesEXE()
CreateMenu()
CreateXAnimWindow()
CreateXModelWindow()
CreateXCamWindow()
print ("CoDMayaTools initialized.")