MetaBox/Scripts/Modeling/Edit/PlugIt/Tools/BakeTransformations.py

1027 lines
33 KiB
Python
Raw Normal View History

2025-01-14 02:17:16 +08:00
import maya.cmds as cmds
import maya.mel as mel
import maya.api.OpenMaya as om
from six.moves import range
from collections import Counter, OrderedDict
from functools import partial
from webbrowser import open_new_tab
import six, math
def GetSelection(*args, **kwargs):
type = kwargs["type"] if "type" in kwargs else "None"
highlight = kwargs["highlight"] if "highlight" in kwargs else False
flatten = kwargs["flatten"] if "flatten" in kwargs else False
allParents = kwargs["allParents"] if "allParents" in kwargs else False
# Check if there is any args
if len(args) > 0:
selection, objects = [], []
# Get selection & objects from args
for arg in args:
selection.extend(arg)
objects.extend(arg)
else:
# Get selection
if flatten:
selection = cmds.ls(sl=True, l=True, fl=True)
else:
selection = cmds.ls(sl=True, l=True)
objects = list(selection)
# Get highlighted objects
if highlight:
objects.extend(cmds.ls(hl=True, l=True))
# Get shapes
if type == "mesh":
shapes = cmds.ls(objects, l=True, dag=True, ni=True, o=True, typ="mesh")
elif type == "geometry" or type == "geo":
shapes = cmds.ls(objects, l=True, dag=True, ni=True, o=True, g=True)
elif type == "nurbsCurve" or type == "curve":
shapes = cmds.ls(objects, l=True, dag=True, ni=True, typ="nurbsCurve")
elif type == "transform":
shapes = cmds.ls(objects, l=True, dag=True, ni=True, o=True, typ="transform")
else:
shapes = cmds.ls(objects, l=True, dag=True, ni=True, o=True)
# Get objects
if allParents:
objects = cmds.listRelatives(shapes, f=True, ap=True, ni=True, typ="transform")
else:
objects = cmds.listRelatives(shapes, f=True, p=True, ni=True, typ="transform")
# Remove intermediate objects
objects = cmds.ls(objects, l=True, ni=True)
# Remove duplicates objects
if objects is not None:
objects = list(OrderedDict.fromkeys(objects))
return selection, shapes, objects
def GetComponentType(*args, **kwargs):
# Create default args
selection = None
# Load arguments (override default args)
for i, arg in enumerate(args):
if i == 0:
selection = arg
# Load keyworded arguments (override *args)
for key, value in six.iteritems(kwargs):
if key == "selection" or key == "sel":
selection = value
selList = om.MSelectionList()
if selection is not None:
for each in selection:
try:
selList.add(each)
except:
pass
else:
selList = om.MGlobal.getActiveSelectionList()
filterDict = OrderedDict([("v", om.MFn.kMeshVertComponent), ("eg", om.MFn.kMeshEdgeComponent), ("fc", om.MFn.kMeshPolygonComponent), ("puv", om.MFn.kMeshMapComponent), ("pvf", om.MFn.kMeshVtxFaceComponent), ("cv", om.MFn.kCurveCVComponent)])
for key, filter in six.iteritems(filterDict):
iter = om.MItSelectionList(selList, filter)
try:
if iter.hasComponents() is True:
return key
except:
pass
return None
def Eval(msg, mode='PRINT', deferred=False):
if deferred is False:
if mode == 'PRINT':
from sys import stdout; stdout.write("{}\n".format(msg))
elif mode == 'INFO':
om.MGlobal.displayInfo(msg)
elif mode == 'WARNING':
om.MGlobal.displayWarning(msg)
elif mode == 'ERROR':
om.MGlobal.displayError(msg)
else:
if mode == 'PRINT':
cmds.evalDeferred(r'from sys import stdout; stdout.write("{}\n")'.format(msg))
elif mode == 'INFO':
cmds.evalDeferred(r'import maya.api.OpenMaya as om; om.MGlobal.displayInfo("'+msg+'")')
elif mode == 'WARNING':
cmds.evalDeferred(r'import maya.api.OpenMaya as om; om.MGlobal.displayWarning("'+msg+'")')
elif mode == 'ERROR':
cmds.evalDeferred(r'import maya.api.OpenMaya as om; om.MGlobal.displayError("'+msg+'")')
def Print(object, mode='PRINT', deferred=False):
if type(object) is not list:
Eval(object, mode, deferred)
else:
for obj in object:
Eval(obj, mode, deferred)
def IsInstanced(object):
isInstanced = False
shapes = cmds.listRelatives(object, f=True, s=True, ni=True)
if shapes:
parents = cmds.listRelatives(shapes, f=True, ap=True) or []
if len(parents) > 1:
isInstanced = True
return isInstanced
def getOutlinerList(object=None):
# If object has a parent, get a list of the childrens
if object:
parent = cmds.listRelatives(object, parent=True, fullPath=True)
if parent:
return cmds.listRelatives(parent, children=True, type="transform", fullPath=True)
# Otherwise, get list of assemblies in the scene
return cmds.ls(long=True, assemblies=True)
def getOutlinerPosition(obj):
# Check if objects exists
if cmds.objExists(obj):
# Make sure it's long names
obj = cmds.ls(obj, long=True)[0]
# Get a list of the outliner
outlinerList = getOutlinerList(obj)
# Get the position of the object in the outliner
sourceObjPosition = outlinerList.index(obj)+1
# Get the position of the object from the bottom of the outliner
return sourceObjPosition - len(outlinerList)
def crossProduct(v1, v2):
vector = om.MVector(*v1) ^ om.MVector(*v2)
return [vector.x, vector.y, vector.z]
def normalizeVector(v1=[1, 0, 0]):
if isinstance(v1, (list, tuple)) is True and len(v1) >= 3:
v1 = om.MVector(v1[0], v1[1], v1[2])
v1.normalize()
v1 = [v1.x, v1.y, v1.z]
return v1
def vectorBetweenPoints(point1, point2):
return normalizeVector([point2[i] - point1[i] for i in range(len(point1))])
def vectorAvg(vectors):
sum = om.MVector()
for vector in vectors:
sum += vector
return sum / len(vectors)
def cubicRoot(number):
return number ** (1 / 3)
def GetVertexPosition(vertex):
id = int(vertex.split("[")[-1][:-1])
vtxList = om.MSelectionList().add(vertex)
dagPath = vtxList.getDagPath(0)
vtx_iter = om.MItMeshVertex(dagPath)
vtx_iter.setIndex(id)
# Get vertex position
position = vtx_iter.position(space=om.MSpace.kWorld)
return om.MVector(position)
def GetEdgePosition(edge):
id = int(edge.split("[")[-1][:-1])
edgeList = om.MSelectionList().add(edge)
dagPath = edgeList.getDagPath(0)
edge_iter = om.MItMeshEdge(dagPath)
edge_iter.setIndex(id)
# Get edge position
position = edge_iter.center(space=om.MSpace.kWorld)
return om.MVector(position)
def GetFacePosition(face):
id = int(face.split("[")[-1][:-1])
faceList = om.MSelectionList().add(face)
dagPath = faceList.getDagPath(0)
face_iter = om.MItMeshPolygon(dagPath)
face_iter.setIndex(id)
# Get edge position
position = face_iter.center(space=om.MSpace.kWorld)
return om.MVector(position)
def GetVertexVectors(vertex):
space = om.MSpace.kWorld
id = int(vertex.split("[")[-1][:-1])
vtxList = om.MSelectionList().add(vertex)
dagPath = vtxList.getDagPath(0)
vtx_iter = om.MItMeshVertex(dagPath)
vtx_iter.setIndex(id)
# Get vertex normal & position
normal = vtx_iter.getNormal(space=space)
position = vtx_iter.position(space=space)
# Get all connected vertices
cvertices = list(vtx_iter.getConnectedVertices())
vertices_dict = {}
cvtx_iter = om.MItMeshVertex(dagPath)
while len(cvertices):
if cvertices[0] not in vertices_dict:
cvtx_iter.setIndex(cvertices[0])
cvtx_position = cvtx_iter.position(space=space)
edge_v = vectorBetweenPoints(list(position), list(cvtx_position))
vertices_dict[cvertices[0]] = tuple([round(v, 3) for v in edge_v])
del cvertices[0]
# Get the most common vector
tangent, vector_count = Counter(vertices_dict.values()).most_common(1)[0]
# If no common vector is found, check for parallel vectors
if vector_count < 2:
all_vectors = list(vertices_dict.values())
parallel_vectors = {k: 1 for k in all_vectors}
while len(all_vectors) > 0:
omV = om.MVector(all_vectors[0])
for ov in all_vectors:
if ov == all_vectors[0]:
continue
if omV.isParallel(om.MVector(ov)):
parallel_vectors[all_vectors[0]] += 1
del parallel_vectors[ov]
del all_vectors[0]
v = max(parallel_vectors, key=parallel_vectors.get)
if parallel_vectors[v] > 1:
tangent, vector_count = v, parallel_vectors[v]
# If no parallel vector is found, use first two points
if vector_count < 2:
vtx_id = list(vertices_dict.keys())[0]
else:
vtx_id = list(vertices_dict.keys())[list(vertices_dict.values()).index(tangent)]
# Calculate tangent
cvtx_iter.setIndex(vtx_id)
cvtx_position = cvtx_iter.position(space=space)
tangent = vectorBetweenPoints(list(position), list(cvtx_position))
return list(normal), tangent
def GetEdgeVectors(edge):
space = om.MSpace.kWorld
# Calculate normal from face vertices
vtxFaces = cmds.polyListComponentConversion(edge, fe=True, tvf=True)
vtxFaceList = om.MSelectionList()
for vtxFace in vtxFaces:
vtxFaceList.add(vtxFace)
normal = om.MVector()
count = 0
dagPath, component = vtxFaceList.getComponent(0)
fnMesh = om.MFnMesh(dagPath)
vtxFace_itr = om.MItMeshFaceVertex(dagPath, component)
while not vtxFace_itr.isDone():
faceId = vtxFace_itr.faceId()
vertexId = vtxFace_itr.vertexId()
normal += fnMesh.getFaceVertexNormal(faceId, vertexId, space=space)
count += 1
vtxFace_itr.next()
normal = normal / count
# Get tangent
id = int(edge.split("[")[-1][:-1])
edgeList = om.MSelectionList().add(edge)
dagPath = edgeList.getDagPath(0)
edge_iter = om.MItMeshEdge(dagPath)
edge_iter.setIndex(id)
position = om.MPoint()
vertices = [edge_iter.vertexId(0), edge_iter.vertexId(1)]
vtx_iter = om.MItMeshVertex(dagPath)
for vtx in vertices:
vtx_iter.setIndex(vtx)
position += vtx_iter.position(space=space)
avgPos = position / len(vertices)
tangent = vectorBetweenPoints(list(avgPos), list(edge_iter.point(1, space)))
return list(normal), list(tangent)
def GetFaceVectors(face):
space = om.MSpace.kWorld
id = int(face.split("[")[-1][:-1])
faceList = om.MSelectionList().add(face)
dagPath = faceList.getDagPath(0)
face_iter = om.MItMeshPolygon(dagPath)
face_iter.setIndex(id)
# Get face normal
normal = face_iter.getNormal(space=space)
# Get all connected faces that have similar normal
cfaces = list(face_iter.getConnectedFaces())
faces_dict = {id: True}
cface_iter = om.MItMeshPolygon(dagPath)
while len(cfaces):
if cfaces[0] not in faces_dict:
cface_iter.setIndex(cfaces[0])
cface_normal = cface_iter.getNormal(space=space)
if normal.isEquivalent(cface_normal, 0.01):
faces_dict[cfaces[0]] = True
for f in cface_iter.getConnectedFaces():
if f not in faces_dict:
cfaces.append(f)
else:
faces_dict[cfaces[0]] = False
del cfaces[0]
# Get edge vectors from connected faces
cfaces = [f for f, value in six.iteritems(faces_dict) if value is True]
edges_dict = {}
cedge_iter = om.MItMeshEdge(dagPath)
for f in cfaces:
cface_iter.setIndex(f)
cedges = list(cface_iter.getEdges())
for edge in cedges:
if edge not in edges_dict:
cedge_iter.setIndex(edge)
edge_v = vectorBetweenPoints(list(cedge_iter.point(0, space)), list(cedge_iter.point(1, space)))
edges_dict[edge] = tuple([round(v, 3) for v in edge_v])
# Get the most common vector
tangent, vector_count = Counter(edges_dict.values()).most_common(1)[0]
# if no common vector is found, check for parallel vectors
if vector_count < 2:
all_vectors = list(edges_dict.values())
parallel_vectors = {k: 1 for k in all_vectors}
while len(all_vectors) > 0:
omV = om.MVector(all_vectors[0])
for ov in all_vectors:
if ov == all_vectors[0]:
continue
if omV.isParallel(om.MVector(ov)):
parallel_vectors[all_vectors[0]] += 1
del parallel_vectors[ov]
del all_vectors[0]
v = max(parallel_vectors, key=parallel_vectors.get)
if parallel_vectors[v] > 1:
tangent, vector_count = v, parallel_vectors[v]
# if no parallel vector is found, use first two points
if vector_count < 2:
# get face-relative vertices positions
vertices_position = face_iter.getPoints(space=space)
vertices_dict = {}
# convert face-relative indices to object-relative indices
for i in range(face_iter.polygonVertexCount()):
vertices_dict[int(face_iter.vertexIndex(i))] = vertices_position[i]
vertices_dict = OrderedDict(sorted(vertices_dict.items()))
vertices_position = []
for pos in six.itervalues(vertices_dict):
vertices_position.append(pos)
if len(vertices_position) == 2:
break
tangent = vectorBetweenPoints(list(vertices_position[0]), list(vertices_position[1]))
else:
# Get back the original vector since we needed to round the values for comparing the vectors
edge_id = list(edges_dict.keys())[list(edges_dict.values()).index(tangent)]
cedge_iter.setIndex(edge_id)
tangent = vectorBetweenPoints(list(cedge_iter.point(0, space)), list(cedge_iter.point(1, space)))
return list(normal), tangent
def GetOrientationFromVectors(normal, tangent):
binormal = normalizeVector(crossProduct(normal, tangent))
position = [0, 0, 0]
tMatrix = normal + [0] + tangent + [0] + binormal + [0] + position + [1]
mMatrix = om.MMatrix(tMatrix)
tmMatrix = om.MTransformationMatrix(mMatrix)
rotation = tmMatrix.rotation(False).reorder(om.MEulerRotation.kXYZ)
rotation = [math.degrees(x + 0) for x in list(rotation)[:3]]
return rotation
def SignedVolumeOfTriangle(p1, p2, p3):
volume = (p1 * (p2 ^ p3)) / 6
return volume
def GetObjectVolume(object):
# Create face list
faces = object + ".f[*]"
faceList = om.MSelectionList().add(faces)
dagPath, component = faceList.getComponent(0)
# Get ids list
compFn = om.MFnSingleIndexedComponent(component)
ids = compFn.getElements()
# Get all triangle volumes
volumes = []
border = False
# Looping through each face
face_iter = om.MItMeshPolygon(dagPath, component)
for id in ids:
face_iter.setIndex(id)
numTriangles = face_iter.numTriangles()
# Loop each face triangle
for num in range(numTriangles):
triangle = face_iter.getTriangle(num, space=om.MSpace.kWorld)[0]
volume = SignedVolumeOfTriangle(om.MVector(triangle[0]), om.MVector(triangle[1]), om.MVector(triangle[2]))
volumes.append(volume)
# Check if face is on open border
if border is False:
border = face_iter.onBoundary()
# Calcule sum of every triangle volumes
volume = abs(sum(volumes))
# Print warning if object has open borders
if border is True:
Print.Print("{} has open borders, which could result in unexpected scale values.".format(object), mode='WARNING', deferred=True)
# Return volume cubic root
return cubicRoot(volume)
def GetEdgeScale(edge):
id = int(edge.split("[")[-1][:-1])
edgeList = om.MSelectionList().add(edge)
dagPath = edgeList.getDagPath(0)
edge_iter = om.MItMeshEdge(dagPath)
edge_iter.setIndex(id)
# Get edge position
scale = edge_iter.length(space=om.MSpace.kWorld)
return scale
def GetFaceScale(face):
id = int(face.split("[")[-1][:-1])
faceList = om.MSelectionList().add(face)
dagPath = faceList.getDagPath(0)
face_iter = om.MItMeshPolygon(dagPath)
face_iter.setIndex(id)
# Get edge position
scale = face_iter.getArea(space=om.MSpace.kWorld)
return math.sqrt(scale)
# Create empty MEL function for proper undo/redo operations
mel.eval('proc BakeTransformations(){}')
# Main function
def PerformBakeTransformations(bakeMode=1, setObjSpc=1, clearSel=1, bakePos=1, bakeRot=1, bakeScale=1):
# Open undo chunk
cmds.undoInfo(ock=True, cn="BakeTransformations")
# Call empty MEL function for proper undo/redo operations
mel.eval('BakeTransformations')
# Get selection
selection = GetSelection(highlight=False, flatten=True)[0]
# Check that something is selected
if not len(selection):
Print("Nothing selected!", mode='ERROR')
cmds.undoInfo(cck=True)
# Get object from selection
component = selection[-1]
selectType = GetComponentType([component])
object = GetSelection([component], type="mesh")[2][-1] if selectType else component
# Check that a component is selected
if bakeMode == 1 and not selectType:
Print("No component selected!", mode='ERROR')
cmds.undoInfo(cck=True)
return
# Check that object is not an instance
if IsInstanced(object):
Print("Selected object is an instance and cannot be baked.", mode='ERROR')
cmds.undoInfo(cck=True)
return
# Get object parent and outliner position
parent = cmds.listRelatives(object, f=True, p=True)
currentPosition = getOutlinerPosition(object)
# Get pivot manipulator context and mode
if bakeMode == 2:
currentCtx = cmds.currentCtx()
if currentCtx == "moveSuperContext":
manipMode = cmds.manipMoveContext("Move", q=True, m=True)
elif currentCtx == "RotateSuperContext":
manipMode = cmds.manipRotateContext("Rotate", q=True, m=True)
if manipMode == 1:
manipMode = 2
elif manipMode == 2:
manipMode = 0
elif manipMode == 3:
manipMode = 6
elif currentCtx == "scaleSuperContext":
manipMode = cmds.manipScaleContext("Scale", q=True, m=True)
# Store current units and temporarily change for centimeters
units = cmds.currentUnit(q=True, l=True)
cmds.currentUnit(l="cm")
# Get translation
if bakePos or not clearSel:
# From Component
if bakeMode == 1:
if selectType == "v":
# Get vertex position
position = GetVertexPosition(component)
if selectType == "eg":
# Get edge position
position = GetEdgePosition(component)
if selectType == "fc":
# Get face position
position = GetFacePosition(component)
# From Pivot
elif bakeMode == 2:
# Case 1: Get move tool position
if currentCtx == "moveSuperContext" and cmds.manipMoveContext("Move", q=True, vis=True):
position = cmds.manipMoveContext("Move", q=True, p=True)
elif currentCtx == "moveSuperContext" and cmds.manipMoveContext("Move", q=True, epm=True):
position = cmds.manipMoveContext("Move", q=True, epp=True)
# Case 2: Get rotate tool position
elif currentCtx == "RotateSuperContext" and cmds.manipRotateContext("Rotate", q=True, vis=True):
position = cmds.manipRotateContext("Rotate", q=True, p=True)
elif currentCtx == "RotateSuperContext" and cmds.manipRotateContext("Rotate", q=True, epm=True):
position = cmds.manipRotateContext("Rotate", q=True, epp=True)
# Case 3: Get scale tool position
elif currentCtx == "scaleSuperContext" and cmds.manipScaleContext("Scale", q=True, vis=True):
position = cmds.manipScaleContext("Scale", q=True, p=True)
elif currentCtx == "scaleSuperContext" and cmds.manipScaleContext("Scale", q=True, epm=True):
position = cmds.manipScaleContext("Scale", q=True, epp=True)
# Case 4: Get object pivot position
else:
rotatePivot = om.MVector(cmds.xform(object, q=True, a=True, ws=True, rp=True))
scalePivot = om.MVector(cmds.xform(object, q=True, a=True, ws=True, sp=True))
position = vectorAvg([rotatePivot, scalePivot])
# Get rotation
if bakeRot or not clearSel:
# From Component
if bakeMode == 1:
if selectType == "v":
# Get vertex normal and tangent
normal, tangent = GetVertexVectors(component)
elif selectType == "eg":
# Get edge normal and tangent
normal, tangent = GetEdgeVectors(component)
elif selectType == "fc":
# Get face normal and tangent
normal, tangent = GetFaceVectors(component)
# Get rotation from the component normal and corresponding edge vector
rotation = GetOrientationFromVectors(normal, tangent)
# From Pivot
elif bakeMode == 2:
rotation = cmds.manipPivot(q=True, o=True)[0]
if any([currentCtx == "moveSuperContext", currentCtx == "RotateSuperContext", currentCtx == "scaleSuperContext"]):
if any([manipMode == 0, manipMode == 4, manipMode == 10]):
rotation = cmds.xform(object, q=True, a=True, eu=True, ro=True)
elif manipMode == 1:
if parent is not None:
rotation = cmds.xform(parent[0], q=True, a=True, eu=True, ro=True)
elif manipMode == 5:
live = cmds.ls(l=True, lv=True)
if len(live):
live = cmds.listRelatives(live, f=True, p=True, ni=True, typ="transform")[0]
rotation = cmds.xform(live, q=True, a=True, eu=True, ro=True)
# Get scale
if bakeScale:
# From Component
if bakeMode == 1:
if selectType == "v":
# Get vertex scale
scale = GetObjectVolume(object)
elif selectType == "eg":
# Get edge scale
scale = GetEdgeScale(component)
elif selectType == "fc":
# Get face scale
scale = GetFaceScale(component)
# From Pivot
elif bakeMode == 2:
# Get pivot scale
scale = GetObjectVolume(object)
# Store components selection
cmds.selectMode(co=True)
cmds.selectType(pv=True)
vertices = cmds.ls(sl=True, l=True)
cmds.selectType(pe=True)
edges = cmds.ls(sl=True, l=True)
cmds.selectType(pf=True)
faces = cmds.ls(sl=True, l=True)
# Create null and apply transformations
null = cmds.ls(cmds.group(em=True), l=True)[0]
cmds.matchTransform(null, object)
# Set translation
if bakePos:
# Move null
cmds.xform(null, a=True, ws=True, t=position)
# Move pivot
cmds.xform(object, a=True, ws=True, piv=position)
# Set rotation
if bakeRot:
# Rotate null
cmds.xform(null, a=True, eu=True, roo="xyz", ro=rotation)
# Set scale
if bakeScale:
# Scale null
cmds.xform(null, a=True, ws=True, s=[scale, scale, scale])
# Freeze null transformations
cmds.makeIdentity(null, a=True, t=1-bakePos, r=1-bakeRot, s=1-bakeScale, n=False, pn=True)
# Parent object to null
if parent is not None:
null = cmds.ls(cmds.parent(null, parent), l=True)[0]
object = cmds.ls(cmds.parent(object, null), l=True)[0]
# Freeze object transformations
cmds.makeIdentity(object, a=True, t=bakePos, r=bakeRot, s=bakeScale, n=False, pn=True)
# Unparent object from null
if parent is not None:
object = cmds.ls(cmds.parent(object, parent), l=True)[0]
else:
object = cmds.ls(cmds.parent(object, w=True), l=True)[0]
# Delete null
cmds.delete(null)
# Restore outliner position
cmds.reorder(object, relative=currentPosition)
# Restore components selection
cmds.selectMode(co=True)
cmds.selectType(pv=True)
cmds.select(vertices, r=True)
cmds.selectType(pe=True)
cmds.select(edges, r=True)
cmds.selectType(pf=True)
cmds.select(faces, r=True)
# Select object
if clearSel:
cmds.selectMode(o=True)
cmds.select(object, r=True)
elif selectType is not None:
mel.eval("selectType -"+selectType+" 1;")
cmds.select(selection, r=True)
# Set tools axis to object space
if setObjSpc:
cmds.manipMoveContext("Move", e=True, m=0)
cmds.manipRotateContext("Rotate", e=True, m=0)
cmds.manipScaleContext("Scale", e=True, m=0)
elif bakeMode == 2:
cmds.manipPivot(p=position, o=rotation)
# Recover units
cmds.currentUnit(l=units)
# Close undo chunk
cmds.undoInfo(cck=True)
# Create a new partial class to print the correct command name when undoing
class rpartial(partial):
def __repr__(self):
return __name__.split(".")[-1]
# Create window
class BakeTransformations(object):
def __init__(self, optionBox=False, *args, **kwargs):
# Create default variables
self.pfx = "BT_"
self.windowName = self.pfx + "Window"
self.windowSizeMenuItem = self.pfx + "SaveWindowSize_menuItem"
self.windowTitle = "Bake Transformations"
self.runLabel = "Bake"
self.windowSize = [546, 350]
# Create option variables
self.saveWindowSize = self.pfx + "SaveWindowSize"
self.bakeMode = self.pfx + "BakeMode"
self.setObjSpc = self.pfx + "SetObjSpc"
self.clearSel = self.pfx + "ClearSel"
self.bakePos = self.pfx + "BakePos"
self.bakeRot = self.pfx + "BakeRot"
self.bakeScale = self.pfx + "BakeScale"
# Create window or run command
if optionBox is True:
self.createWindow()
else:
self.runCmd(closeWindow=False, **kwargs)
# Help command
def helpCmd(self, *args):
open_new_tab("https://gabrielnadeau.com/pages/gn-baketransformations")
# Run command
def runCmd(self, closeWindow, **kwargs):
# Get option variables
self.defaultSettings(reset=False)
if "bakeMode" in kwargs:
bakeMode = kwargs["bakeMode"]
else:
bakeMode = cmds.optionVar(q=self.bakeMode)
if "setObjSpc" in kwargs:
setObjSpc = kwargs["setObjSpc"]
else:
setObjSpc = cmds.optionVar(q=self.setObjSpc)
if "clearSel" in kwargs:
clearSel = kwargs["clearSel"]
else:
clearSel = cmds.optionVar(q=self.clearSel)
if "bakePos" in kwargs:
bakePos = kwargs["bakePos"]
else:
bakePos = cmds.optionVar(q=self.bakePos)
if "bakeRot" in kwargs:
bakeRot = kwargs["bakeRot"]
else:
bakeRot = cmds.optionVar(q=self.bakeRot)
if "bakeScale" in kwargs:
bakeScale = kwargs["bakeScale"]
else:
bakeScale = cmds.optionVar(q=self.bakeScale)
# Bake Transformations
PerformBakeTransformations(bakeMode, setObjSpc, clearSel, bakePos, bakeRot, bakeScale)
# Close window if specified
if closeWindow is True:
self.closeWindow()
# Close window
def closeWindow(self):
if cmds.window(self.windowName, ex=True) is True:
cmds.evalDeferred('import maya.cmds as cmds; cmds.deleteUI("'+self.windowName+'", wnd=True)')
# Window size
def resizeWindow(self):
if cmds.optionVar(q=self.saveWindowSize) == 0:
cmds.window(self.windowName, e=True, wh=self.windowSize)
# Default settings
def defaultSettings(self, reset=False):
if cmds.optionVar(ex=self.saveWindowSize) == 0:
cmds.optionVar(iv=(self.saveWindowSize, 1))
if reset is True or cmds.optionVar(ex=self.bakeMode) == 0:
cmds.optionVar(iv=(self.bakeMode, 1))
if reset is True or cmds.optionVar(ex=self.setObjSpc) == 0:
cmds.optionVar(iv=(self.setObjSpc, 1))
if reset is True or cmds.optionVar(ex=self.clearSel) == 0:
cmds.optionVar(iv=(self.clearSel, 1))
if reset is True or cmds.optionVar(ex=self.bakePos) == 0:
cmds.optionVar(iv=(self.bakePos, 1))
if reset is True or cmds.optionVar(ex=self.bakeRot) == 0:
cmds.optionVar(iv=(self.bakeRot, 1))
if reset is True or cmds.optionVar(ex=self.bakeScale) == 0:
cmds.optionVar(iv=(self.bakeScale, 1))
# Reset settings
def resetSettings(self, *args):
self.defaultSettings(reset=True)
self.windowSetup()
# Save settings
def saveSettings(self, *args):
cmds.optionVar(iv=(self.saveWindowSize, cmds.menuItem(self.windowSizeMenuItem, q=True, cb=True)))
cmds.optionVar(iv=(self.bakeMode, cmds.radioButtonGrp(self.bakeMode, q=True, sl=True)))
cmds.optionVar(iv=(self.setObjSpc, cmds.checkBoxGrp(self.setObjSpc, q=True, v1=True)))
cmds.optionVar(iv=(self.clearSel, cmds.checkBoxGrp(self.clearSel, q=True, v1=True)))
cmds.optionVar(iv=(self.bakePos, cmds.checkBoxGrp(self.bakePos, q=True, v1=True)))
cmds.optionVar(iv=(self.bakeRot, cmds.checkBoxGrp(self.bakeRot, q=True, v1=True)))
cmds.optionVar(iv=(self.bakeScale, cmds.checkBoxGrp(self.bakeScale, q=True, v1=True)))
# Setup window
def windowSetup(self):
cmds.menuItem(self.windowSizeMenuItem, e=True, cb=cmds.optionVar(q=self.saveWindowSize))
cmds.radioButtonGrp(self.bakeMode, e=True, sl=cmds.optionVar(q=self.bakeMode))
cmds.checkBoxGrp(self.setObjSpc, e=True, v1=cmds.optionVar(q=self.setObjSpc))
cmds.checkBoxGrp(self.clearSel, e=True, v1=cmds.optionVar(q=self.clearSel))
cmds.checkBoxGrp(self.bakePos, e=True, v1=cmds.optionVar(q=self.bakePos))
cmds.checkBoxGrp(self.bakeRot, e=True, v1=cmds.optionVar(q=self.bakeRot))
cmds.checkBoxGrp(self.bakeScale, e=True, v1=cmds.optionVar(q=self.bakeScale))
self.saveSettings()
def initializeWindow(self):
cmds.showWindow(self.windowName)
self.resizeWindow()
cmds.setFocus(self.windowName)
# Create window
def createWindow(self):
# If window already opened, set focus on it
if cmds.window(self.windowName, ex=True) is True:
self.initializeWindow()
return
# Window
cmds.window(self.windowName, t=self.windowTitle, mb=True, wh=self.windowSize)
# Edit Menu
cmds.menu(l="Edit")
cmds.menuItem(l="Save Settings", c=partial(self.saveSettings), ecr=False)
cmds.menuItem(l="Reset Settings", c=partial(self.resetSettings), ecr=False)
cmds.menuItem(d=True)
cmds.menuItem(self.windowSizeMenuItem, l="Save Window Size", c=partial(self.saveSettings), cb=False, ecr=False)
cmds.setParent("..")
# Help Menu
cmds.menu(l="Help", helpMenu=True)
cmds.menuItem(l="Help on "+self.windowTitle, i="help.png", c=partial(self.helpCmd), ecr=False)
cmds.setParent("..")
# Window Form (START)
cmds.formLayout(self.pfx+"windowForm")
# Tab Layout (START)
cmds.tabLayout(self.pfx+"tabLayout", tv=False)
cmds.formLayout(self.pfx+"tabForm")
# Scroll Layout (START)
cmds.scrollLayout(self.pfx+"scrollLayout", cr=True)
cmds.formLayout(self.pfx+"scrollForm")
# Description Frame (START)
cmds.frameLayout(self.pfx+"Description_frameLayout", cll=True, l="Description", li=5, bgs=True, mh=4, mw=0)
# Description Column (START)
cmds.columnLayout(cat=("left", 20), adj=1)
# Description
cmds.text(l="Bakes selected component or current tool pivot transformations on object.", al="left")
cmds.setParent("..")
# Description Column (END)
cmds.setParent("..")
# Settings Frame (START)
cmds.frameLayout(self.pfx+"Settings_frameLayout", cll=True, l="Settings", li=5, bgs=True, mh=4, mw=0)
# Settings Column (START)
cmds.columnLayout(cat=("left", 0), adj=1)
# Space
cmds.checkBoxGrp(self.setObjSpc, l1="Set tools axis to object space", cat=(1, "left", 143), cc=partial(self.saveSettings))
# Clear
cmds.checkBoxGrp(self.clearSel, l1="Set selection type to object", cat=(1, "left", 143), cc=partial(self.saveSettings))
# Separation
cmds.rowLayout(h=3)
cmds.setParent("..")
cmds.rowLayout(h=2, bgc=[0.266, 0.266, 0.266])
cmds.setParent("..")
cmds.rowLayout(h=3)
cmds.setParent("..")
# Bake
cmds.rowLayout(nc=2, cw=(1, 134), cat=(1, "right", 0), ct2=("left", "left"))
cmds.text(l="Bake:")
cmds.radioButtonGrp(self.bakeMode, nrb=2, l="", la2=("Component", "Pivot"), cw3=(4, 110, 110), cc=partial(self.saveSettings))
cmds.setParent("..")
# Transformations
cmds.rowLayout(nc=2, cw=(1, 134), cat=(1, "right", 0), ct2=("left", "left"))
cmds.text(l="Transformations:")
cmds.checkBoxGrp(self.bakePos, l1="Translation", cat=(1, "left", 6), cc=partial(self.saveSettings))
cmds.setParent("..")
cmds.checkBoxGrp(self.bakeRot, l1="Rotation", cat=(1, "left", 143), cc=partial(self.saveSettings))
cmds.checkBoxGrp(self.bakeScale, l1="Scale", cat=(1, "left", 143), cc=partial(self.saveSettings))
# Settings Column (END)
cmds.setParent("..")
# Settings Frame (END)
cmds.setParent("..")
# Scroll Layout (END)
cmds.setParent("..")
cmds.setParent("..")
# Tab Layout (END)
cmds.setParent("..")
cmds.setParent("..")
# Buttons
cmds.formLayout(self.pfx+"Buttons_formLayout", nd=150)
if int(cmds.about(mjv=True)) < 2020:
cmds.iconTextButton(self.pfx+"ApplyAndClose_button", st="textOnly", l=self.runLabel, c=rpartial(self.runCmd, closeWindow=True), fla=False, h=26, rpt=True)
cmds.iconTextButton(self.pfx+"Apply_button", st="textOnly", l="Apply", c=rpartial(self.runCmd, closeWindow=False), fla=False, h=26, rpt=True)
else:
cmds.iconTextButton(self.pfx+"ApplyAndClose_button", st="textOnly", l=self.runLabel, c=partial(self.runCmd, closeWindow=True), fla=False, h=26, rpt=True)
cmds.iconTextButton(self.pfx+"Apply_button", st="textOnly", l="Apply", c=partial(self.runCmd, closeWindow=False), fla=False, h=26, rpt=True)
cmds.iconTextButton(self.pfx+"Close_button", st="textOnly", l="Close", c=partial(self.closeWindow), fla=False, h=26, rpt=True)
cmds.setParent("..")
# Window Form (END)
cmds.setParent("..")
# Window Form Layout
cmds.formLayout(self.pfx+"windowForm", e=True,
af=[(self.pfx+"tabLayout", "top", 0), (self.pfx+"tabLayout", "left", 0), (self.pfx+"tabLayout", "right", 0), (self.pfx+"tabLayout", "bottom", 36)])
cmds.formLayout(self.pfx+"windowForm", e=True,
ac=(self.pfx+"Buttons_formLayout", "top", 5, self.pfx+"tabLayout"),
af=[(self.pfx+"Buttons_formLayout", "left", 5), (self.pfx+"Buttons_formLayout", "right", 5)],
an=(self.pfx+"Buttons_formLayout", "bottom"))
# Tabs Form Layout
cmds.formLayout(self.pfx+"tabForm", e=True,
af=[(self.pfx+"scrollLayout", "top", 2), (self.pfx+"scrollLayout", "left", 2), (self.pfx+"scrollLayout", "right", 2), (self.pfx+"scrollLayout", "bottom", 2)])
# Scroll Form Layout
cmds.formLayout(self.pfx+"scrollForm", e=True,
af=[(self.pfx+"Description_frameLayout", "top", 0), (self.pfx+"Description_frameLayout", "left", 0), (self.pfx+"Description_frameLayout", "right", 0)],
an=(self.pfx+"Description_frameLayout", "bottom"))
cmds.formLayout(self.pfx+"scrollForm", e=True,
ac=(self.pfx+"Settings_frameLayout", "top", 0, self.pfx+"Description_frameLayout"),
af=[(self.pfx+"Settings_frameLayout", "left", 0), (self.pfx+"Settings_frameLayout", "right", 0)],
an=(self.pfx+"Settings_frameLayout", "bottom"))
# Buttons Form Layout
cmds.formLayout(self.pfx+"Buttons_formLayout", e=True,
af=[(self.pfx+"ApplyAndClose_button", "top", 0), (self.pfx+"ApplyAndClose_button", "left", 0)],
ap=(self.pfx+"ApplyAndClose_button", "right", 2, 50),
an=(self.pfx+"ApplyAndClose_button", "bottom"))
cmds.formLayout(self.pfx+"Buttons_formLayout", e=True,
af=(self.pfx+"Apply_button", "top", 0),
ap=[(self.pfx+"Apply_button", "left", 2, 50), (self.pfx+"Apply_button", "right", 2, 100)],
an=(self.pfx+"Apply_button", "bottom"))
cmds.formLayout(self.pfx+"Buttons_formLayout", e=True,
af=[(self.pfx+"Close_button", "top", 0), (self.pfx+"Close_button", "right", 0)],
ap=(self.pfx+"Close_button", "left", 2, 100),
an=(self.pfx+"Close_button", "bottom"))
# Window Setup
self.defaultSettings(reset=False)
self.windowSetup()
self.initializeWindow()