MetaFusion/Reference/MSLiveLink/DHI/core/maya/adapter.py
2025-02-03 22:58:41 +08:00

568 lines
18 KiB
Python

'''
This module contains additional adapters which define how some objects
have to be additionally adapted for later use.
Adapter objects can be connected into chains. If so, object is being adapted by
first adapter in chain. Once first adapter finish adapting, it calls next
adapter in chain to do its adapting, and so on.
'''
from maya import cmds
class AdditionalAdapter:
'''
Base AdditionalAdapter class.
All adapters have to inherent this class and redefine method adapt().
Attributes:
nextAdapter (AdditionalAdapter): Next adapter in adapting chain.
'''
def __init__(self):
self.nextAdapter = None
def adapt(self, object):
'''
Changes given data in a way to adapt it for later use depending on context.
This method has to be overridden.
When method finish with adapting objects, it calls next adapter in chain.
@param object: Object which has to be adapted.
'''
if self.nextAdapter:
self.nextAdapter.adapt(object)
# ----------------------------
# Joint additional adapters
# ----------------------------
class MayaJointsDataScaleAA(AdditionalAdapter):
'''
Maya joint data additional adapter.
This adapter changes joints scale.
@see trilateral.api.maya.MayaJoint
@see trilateral.api.maya.MayaJointDataHolder
'''
def __init__(self, scale, pivot):
AdditionalAdapter.__init__(self)
self._scale = scale
self._pivot = pivot
def adapt(self, object):
'''
Adapt Maya joints scale based on scale coefficient and pivot.
@param object: Maya joints object. (trilateral.api.maya.MayaJointDataHolder)
'''
# adapt
if self._scale != 1.0:
object.scale(self._scale, self._pivot)
# call next in chain
if self.nextAdapter:
self.nextAdapter.adapt(object)
class MayaJointsDataOrientAA(AdditionalAdapter):
'''
Maya joint data additional adapter.
This adapter changes joints orient.
@see trilateral.api.maya.MayaJoint
@see trilateral.api.maya.MayaJointDataHolder
'''
def __init__(self, orient, translate):
AdditionalAdapter.__init__(self)
self._orient = orient
self._translate = translate
def adapt(self, joints):
'''
Adapt Maya joints orientation based on given orient.
@param object: Maya joints object. (trilateral.api.maya.MayaJointDataHolder)
'''
# adapt
for mayaJoint in joints:
# create joints
cmds.select(clear=True)
jointNode = cmds.joint(name="temp_root_jnt")
jointNode.jointOrient.set(mayaJoint.jointOrient.get())
jointNode.translate.set(mayaJoint.translate.get())
jointNode.rotate.set(mayaJoint.rotate.get())
jointNode.scale.set(mayaJoint.scale.get())
# rotate
locator = cmds.spaceLocator()
jointNode.setParent(locator)
if self._orient[0] != 0.0 or self._orient[1] != 0.0 or self._orient[2] != 0.0:
locator.rotate.set(self._orient)
if self._translate[0] != 0.0 or self._translate[1] != 0.0 or self._translate[2] != 0.0:
locator.translate.set(self._translate)
jointNode.setParent(None)
cmds.delete(locator)
# save changes
mayaJoint.translate.set(jointNode.translate.get())
mayaJoint.rotate.set(jointNode.rotate.get())
mayaJoint.scale.set(jointNode.scale.get())
mayaJoint.jointOrient.set(jointNode.jointOrient.get())
cmds.delete(jointNode)
# call next in chain
if self.nextAdapter:
self.nextAdapter.adapt(object)
# ----------------------------
# Mesh additional adapters
# ----------------------------
class MeshScaleAA(AdditionalAdapter):
'''
Mesh additional adapter.
This adapter changes mesh scale.
'''
def __init__(self, scale, pivot):
AdditionalAdapter.__init__(self)
self._scale = scale
self._pivot = pivot
def adapt(self, object):
'''
Adapt Mesh scale based on scale coefficient and pivot.
This method leave clean channels (translations and rotations at 0.0,
scale at 1.0).
@param object: Maya mesh node. (Mesh)
'''
# adapt
locator = cmds.spaceLocator()
locator.translate.set(self._pivot)
object.setParent(locator)
locator.scale.set([self._scale, self._scale, self._scale])
object.setParent(None)
cmds.delete(locator)
cmds.makeIdentity(object, apply=True, t=True, r=True, s=True, n=True)
cmds.polyNormalPerVertex(object, unFreezeNormal=True)
# call next in chain
if self.nextAdapter:
self.nextAdapter.adapt(object)
class MeshOrientAA(AdditionalAdapter):
'''
Mesh additional adapter.
This adapter changes mesh orientation.
'''
def __init__(self, orient, translate):
AdditionalAdapter.__init__(self)
self._orient = orient
self._translate = translate
def adapt(self, object):
'''
Adapt Mesh orientation based on given orient.
This method leave clean channels (translations and rotations at 0.0,
scale at 1.0).
@param object: Maya mesh node. (Mesh)
'''
# adapt
if self._orient[0] != 0.0 or self._orient[1] != 0.0 or self._orient[2] != 0.0:
cmds.xform(object, ro=self._orient, relative=True)
if self._translate[0] != 0.0 or self._translate[1] != 0.0 or self._translate[2] != 0.0:
cmds.xform(object, translation=self._translate, relative=True)
cmds.makeIdentity(object, apply=True, t=True, r=True, s=True, n=True)
cmds.polyNormalPerVertex(object, unFreezeNormal=True)
# call next in chain
if self.nextAdapter:
self.nextAdapter.adapt(object)
# ----------------------------
# GUI additional adapters
# ----------------------------
class GuiScaleAA(AdditionalAdapter):
'''
Gui additional adapter.
This adapter changes scale.
'''
def __init__(self, scale, pivot):
AdditionalAdapter.__init__(self)
self._scale = scale
self._pivot = pivot
def adapt(self, object):
'''
Adapt 2D gui scale based on scale coefficient and pivot.
This method does not leave clean channels (translations and rotations
can be different than 0.0, and scale different than 1.0).
@param object: List of root gui Maya nodes. (DagNode[])
Algorithm:
1. Creates helper locator.
2. Parent all root nodes under helper locator.
3. Moves and scales helper locator.
4. Unparent all root nodes.
5. Delete helper locator.
'''
# adapt
for rootNode in object:
locator = cmds.spaceLocator()
locator.translate.set(self._pivot)
parent = rootNode.getParent()
rootNode.setParent(locator)
locator.scale.set([self._scale, self._scale, self._scale])
rootNode.setParent(parent)
cmds.delete(locator)
# call next in chain
if self.nextAdapter:
self.nextAdapter.adapt(object)
class GuiOrientAA(AdditionalAdapter):
'''
Gui additional adapter.
This adapter changes 2D GUI orientation.
'''
def __init__(self, orient):
AdditionalAdapter.__init__(self)
self._orient = orient
def adapt(self, object):
'''
Adapt 2D gui orientation based on given orient.
This method does not leave clean channels (translations and rotations
can be different than 0.0, and scale different than 1.0).
@param object: List of root gui Maya nodes. (DagNode[])
Algorithm:
1. Creates helper locator.
2. Parent all root nodes under helper locator.
3. Rotates helper locator.
4. Unparent all root nodes.
5. Delete helper locator.
'''
# adapt
for rootNode in object:
locator = cmds.spaceLocator()
parent = rootNode.getParent()
rootNode.setParent(locator)
locator.rotate.set(self._orient)
rootNode.setParent(parent)
cmds.delete(locator)
# call next in chain
if self.nextAdapter:
self.nextAdapter.adapt(object)
# ----------------------------
# Controls additional adapters
# ----------------------------
class ControlsScaleAA(AdditionalAdapter):
'''
Analog controls additional adapter.
This adapter changes scale.
'''
def __init__(self, scale, pivot):
AdditionalAdapter.__init__(self)
self._scale = scale
self._pivot = pivot
def adapt(self, object):
'''
Adapt analog controls (or controls in general) scale based on scale
coefficient and pivot.
This method leave clean channels (translations and rotations at 0.0,
scale at 1.0).
@param object: List of root analog control Maya nodes. (DagNode[])
Algorithm:
1. Make list of all nodes sorted from parent to children.
2. Scale all objects:
- take each root node,
- create locator, move locator to pivot position, parent root node
under it,
- scale locator for scale coefficient,
- unparent root node,
- delete locator.
3. Unlock channels:
- for all nodes, save data which channels (translate, rotate, scale)
are locked,
- for all nodes unlock all channels,
4. Clean controls channels. For each node:
- disconnect node from parent and children,
- save node translate and rotate data,
- move node to 0, 0, 0,
- freeze transformations,
- return node to its previous position using saved translate
and rotate data,
- connect node with its previous parent and children.
5. Lock channels which were locked using saved data.
'''
# adapt
# get all nodes and scale them all
nodes = []
for rootNode in object:
nodes.extend(cmds.listRelatives(rootNode, allDescendents=True, type="transform"))
locator = cmds.spaceLocator()
locator.translate.set(self._pivot)
parent = rootNode.getParent()
rootNode.setParent(locator)
locator.scale.set([self._scale, self._scale, self._scale])
rootNode.setParent(parent)
cmds.delete(locator)
nodes.extend(object)
nodes.reverse()
# unlock translate, rotate, scale on all nodes
lockedDict = {}
for node in nodes:
locks = [node.translateX.isLocked(), node.translateY.isLocked(), node.translateZ.isLocked(),
node.rotateX.isLocked(), node.rotateY.isLocked(), node.rotateZ.isLocked(),
node.scaleX.isLocked(), node.scaleY.isLocked(), node.scaleZ.isLocked()]
lockedDict[node.longName()] = locks
node.translateX.setLocked(False)
node.translateY.setLocked(False)
node.translateZ.setLocked(False)
node.rotateX.setLocked(False)
node.rotateY.setLocked(False)
node.rotateZ.setLocked(False)
node.scaleX.setLocked(False)
node.scaleY.setLocked(False)
node.scaleZ.setLocked(False)
# clean transformations
for node in nodes:
# save data
parent = node.getParent()
children = node.getChildren(type="transform")
if parent:
node.setParent(None)
for child in children:
child.setParent(None)
translateValues = node.translate.get()
rotateValues = node.rotate.get()
# freeze
node.translate.set([0, 0, 0])
cmds.makeIdentity(node, apply=True, t=True, r=True, s=True, n=True)
if node.hasAttr("localScale"):
node.localScale.set([self._scale, self._scale, self._scale])
# parent and return previous state
node.translate.set(translateValues)
node.rotate.set(rotateValues)
if parent:
node.setParent(parent)
for child in children:
child.setParent(node)
# lock translate, rotate, scale on all nodes
for node in nodes:
locks = lockedDict[node.longName()]
node.translateX.setLocked(locks[0])
node.translateY.setLocked(locks[1])
node.translateZ.setLocked(locks[2])
node.rotateX.setLocked(locks[3])
node.rotateY.setLocked(locks[4])
node.rotateZ.setLocked(locks[5])
node.scaleX.setLocked(locks[6])
node.scaleY.setLocked(locks[7])
node.scaleZ.setLocked(locks[8])
# call next in chain
if self.nextAdapter:
self.nextAdapter.adapt(object)
class ControlsOrientAA(AdditionalAdapter):
'''
Gui additional adapter.
This adapter changes controls orientation.
'''
def __init__(self, orient):
AdditionalAdapter.__init__(self)
self._orient = orient
def adapt(self, object):
'''
Adapt analog controls (or controls in general) orientation based on
given orient.
This method does not leave clean channels (translations and rotations
can be different than 0.0, and scale different than 1.0).
@param object: List of root analog control Maya nodes. (DagNode[])
Algorithm:
1. Creates helper locator.
2. Parent all root nodes under helper locator.
3. Rotates helper locator.
4. Unparent all root nodes.
5. Delete helper locator.
'''
# adapt
for rootNode in object:
locator = cmds.spaceLocator()
parent = rootNode.getParent()
rootNode.setParent(locator)
locator.rotate.set(self._orient)
rootNode.setParent(parent)
cmds.delete(locator)
# call next in chain
if self.nextAdapter:
self.nextAdapter.adapt(object)
# ----------------------------
# Addition adapter builders
# ----------------------------
class AdditionalAdapterBuilder:
'''
This class contains static methods for building addition adapter chains
depending on its usage.
@see AdditionalAdapter
'''
@staticmethod
def buildMayaJointsDataAA(scale, pivot, orient, translate):
'''
Builds additional adapters chain for Maya joint data adaptation.
@param scale: Scale factor (float)
@param pivor: Scale pivot position. ([float, float, float])
@param orient: Orientation rotations. ([float, float, float])
'''
firstAA = None
lastAA = None
if scale != 1.0:
aa = MayaJointsDataScaleAA(scale, pivot)
firstAA = aa
lastAA = aa
if orient[0] != 0.0 or orient[1] != 0.0 or orient[2] != 0.0 or translate[0] != 0.0 or translate[1] != 0.0 or translate[2] != 0.0:
aa = MayaJointsDataOrientAA(orient, translate)
if lastAA:
lastAA.nextAdapter = aa
else:
firstAA = aa
lastAA = aa
return firstAA
@staticmethod
def buildMeshAA(scale, pivot, orient, translate):
'''
Builds additional adapters chain for mesh adaptation.
@param scale: Scale factor (float)
@param pivot: Scale pivot position. ([float, float, float])
@param orient: Orientation rotations. ([float, float, float])
@param translate: Translate position. ([float, float, float])
'''
firstAA = None
lastAA = None
if scale != 1.0:
aa = MeshScaleAA(scale, pivot)
firstAA = aa
lastAA = aa
if orient[0] != 0.0 or orient[1] != 0.0 or orient[2] != 0.0 or translate[0] != 0.0 or translate[1] != 0.0 or translate[2] != 0.0:
aa = MeshOrientAA(orient, translate)
if lastAA:
lastAA.nextAdapter = aa
else:
firstAA = aa
lastAA = aa
return firstAA
@staticmethod
def buildGuiAA(scale, pivot, orient):
'''
Builds additional adapters chain for 2D GUI adaptation.
@param scale: Scale factor (float)
@param pivor: Scale pivot position. ([float, float, float])
@param orient: Orientation rotations. ([float, float, float])
'''
firstAA = None
lastAA = None
if scale != 1.0:
aa = GuiScaleAA(scale, pivot)
firstAA = aa
lastAA = aa
if orient[0] != 0.0 or orient[1] != 0.0 or orient[2] != 0.0:
aa = GuiOrientAA(orient)
if lastAA:
lastAA.nextAdapter = aa
else:
firstAA = aa
lastAA = aa
return firstAA
@staticmethod
def buildControlsAA(scale, pivot, orient):
'''
Builds additional adapters chain for controls adaptation.
@param scale: Scale factor (float)
@param pivor: Scale pivot position. ([float, float, float])
@param orient: Orientation rotations. ([float, float, float])
'''
firstAA = None
lastAA = None
if scale != 1.0:
aa = ControlsScaleAA(scale, pivot)
firstAA = aa
lastAA = aa
if orient[0] != 0.0 or orient[1] != 0.0 or orient[2] != 0.0:
aa = ControlsOrientAA(orient)
if lastAA:
lastAA.nextAdapter = aa
else:
firstAA = aa
lastAA = aa
return firstAA