Update
This commit is contained in:
184
2023/scripts/rigging_tools/ngskintools2/api/mirror.py
Normal file
184
2023/scripts/rigging_tools/ngskintools2/api/mirror.py
Normal file
@@ -0,0 +1,184 @@
|
||||
import itertools
|
||||
|
||||
from maya import cmds
|
||||
|
||||
from ngSkinTools2.api import influenceMapping, internals, plugin, target_info
|
||||
from ngSkinTools2.api.cmd_wrappers import get_source_node
|
||||
from ngSkinTools2.api.layers import Layers
|
||||
from ngSkinTools2.api.log import getLogger
|
||||
from ngSkinTools2.api.python_compatibility import Object
|
||||
|
||||
log = getLogger("mirror")
|
||||
|
||||
|
||||
class Mirror(Object):
|
||||
"""
|
||||
query and configure mirror options for provided target
|
||||
"""
|
||||
|
||||
axis = internals.make_editable_property('mirrorAxis')
|
||||
seam_width = internals.make_editable_property('mirrorWidth')
|
||||
vertex_transfer_mode = internals.make_editable_property('vertexTransferMode')
|
||||
|
||||
def __init__(self, target):
|
||||
"""
|
||||
:type target: skin target (skinCluster or mesh)
|
||||
"""
|
||||
self.target = target
|
||||
self.__skin_cluster__ = None
|
||||
self.__data_node__ = None
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
def __query__(self, **kwargs):
|
||||
return plugin.ngst2Layers(self.target, q=True, **kwargs)
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
def __edit__(self, **kwargs):
|
||||
plugin.ngst2Layers(self.target, configureMirrorMapping=True, **kwargs)
|
||||
self.recalculate_influences_mapping()
|
||||
|
||||
def __mapper_config_attr(self):
|
||||
return self.__get_data_node__() + ".influenceMappingOptions"
|
||||
|
||||
def build_influences_mapper(self, defaults=None):
|
||||
mapper = influenceMapping.InfluenceMapping()
|
||||
layers = Layers(self.target)
|
||||
mapper.influences = layers.list_influences()
|
||||
|
||||
mapper.config.load_json(cmds.getAttr(self.__mapper_config_attr()))
|
||||
mapper.config.mirror_axis = self.axis
|
||||
|
||||
return mapper
|
||||
|
||||
def save_influences_mapper(self, mapper):
|
||||
"""
|
||||
:type mapper: influenceMapping.InfluenceMapping
|
||||
"""
|
||||
self.set_mirror_config(mapper.config.as_json())
|
||||
|
||||
def set_mirror_config(self, config_as_json):
|
||||
cmds.setAttr(self.__mapper_config_attr(), config_as_json, type='string')
|
||||
|
||||
def set_influences_mapping(self, mapping):
|
||||
"""
|
||||
:type mapping: map[int] -> int
|
||||
"""
|
||||
log.info("mapping updated: %r", mapping)
|
||||
|
||||
mapping_as_string = ','.join(str(k) + "," + str(v) for (k, v) in list(mapping.items()))
|
||||
plugin.ngst2Layers(self.target, configureMirrorMapping=True, influencesMapping=mapping_as_string)
|
||||
|
||||
def recalculate_influences_mapping(self):
|
||||
"""
|
||||
loads current influence mapping settings, and update influences mapping with these values
|
||||
"""
|
||||
m = self.build_influences_mapper().calculate()
|
||||
self.set_influences_mapping(influenceMapping.InfluenceMapping.asIntIntMapping(m))
|
||||
|
||||
def mirror(self, options):
|
||||
"""
|
||||
:type options: MirrorOptions
|
||||
"""
|
||||
plugin.ngst2Layers(
|
||||
self.target,
|
||||
mirrorLayerWeights=options.mirrorWeights,
|
||||
mirrorLayerMask=options.mirrorMask,
|
||||
mirrorLayerDq=options.mirrorDq,
|
||||
mirrorDirection=options.direction,
|
||||
)
|
||||
|
||||
def set_reference_mesh(self, mesh_shape):
|
||||
dest = self.__get_data_node__() + ".mirrorMesh"
|
||||
if mesh_shape:
|
||||
cmds.connectAttr(mesh_shape + ".outMesh", dest)
|
||||
else:
|
||||
for i in cmds.listConnections(dest, source=True, plugs=True):
|
||||
cmds.disconnectAttr(i, dest)
|
||||
|
||||
def get_reference_mesh(self):
|
||||
return get_source_node(self.__get_data_node__() + '.mirrorMesh')
|
||||
|
||||
def __get_skin_cluster__(self):
|
||||
if self.__skin_cluster__ is None:
|
||||
self.__skin_cluster__ = target_info.get_related_skin_cluster(self.target)
|
||||
return self.__skin_cluster__
|
||||
|
||||
def __get_data_node__(self):
|
||||
if self.__data_node__ is None:
|
||||
self.__data_node__ = target_info.get_related_data_node(self.target)
|
||||
return self.__data_node__
|
||||
|
||||
# noinspection PyStatementEffect
|
||||
def build_reference_mesh(self):
|
||||
sc = self.__get_skin_cluster__()
|
||||
dn = self.__get_data_node__()
|
||||
|
||||
if sc is None or dn is None:
|
||||
return
|
||||
|
||||
existing_ref_mesh = self.get_reference_mesh()
|
||||
if existing_ref_mesh:
|
||||
cmds.select(existing_ref_mesh)
|
||||
raise Exception("symmetry mesh already configured for %s: %s" % (str(sc), existing_ref_mesh))
|
||||
|
||||
def get_shape(node):
|
||||
return cmds.listRelatives(node, shapes=True)[0]
|
||||
|
||||
result, _ = cmds.polyCube()
|
||||
g = cmds.group(empty=True, name="ngskintools_mirror_mesh_setup")
|
||||
cmds.parent(result, g)
|
||||
result = cmds.rename(g + "|" + result, "mirror_reference_mesh")
|
||||
|
||||
cmds.delete(result, ch=True)
|
||||
cmds.connectAttr(sc + ".input[0].inputGeometry", get_shape(result) + ".inMesh")
|
||||
cmds.delete(result, ch=True)
|
||||
|
||||
(mirrored,) = cmds.duplicate(result)
|
||||
mirrored = cmds.rename(g + "|" + mirrored, 'flipped_preview')
|
||||
mirrored_shape = get_shape(mirrored)
|
||||
|
||||
cmds.setAttr(mirrored + ".sx", -1)
|
||||
cmds.setAttr(mirrored + ".overrideEnabled", 1)
|
||||
cmds.setAttr(mirrored + ".overrideDisplayType", 2)
|
||||
cmds.setAttr(mirrored + ".overrideShading", 0)
|
||||
cmds.setAttr(mirrored + ".overrideTexturing", 1)
|
||||
|
||||
(blend,) = cmds.blendShape(result, mirrored_shape)
|
||||
cmds.setAttr(blend + ".weight[0]", 1.0)
|
||||
|
||||
# lock accidental transformations
|
||||
for c, t, m in itertools.product('xyz', 'trs', (result, mirrored)):
|
||||
cmds.setAttr(m + "." + t + c, lock=True)
|
||||
|
||||
# shift setup to the right by slightly more than bounding box width
|
||||
|
||||
bb = cmds.exactWorldBoundingBox(g)
|
||||
cmds.move((bb[3] - bb[0]) * 1.2, 0, 0, g, r=True)
|
||||
|
||||
self.set_reference_mesh(str(result))
|
||||
cmds.select(result)
|
||||
return result
|
||||
|
||||
|
||||
class MirrorOptions(Object):
|
||||
directionNegativeToPositive = 0
|
||||
directionPositiveToNegative = 1
|
||||
directionGuess = 2
|
||||
directionFlip = 3
|
||||
|
||||
def __init__(self):
|
||||
self.mirrorWeights = True
|
||||
self.mirrorMask = True
|
||||
self.mirrorDq = True
|
||||
self.direction = MirrorOptions.directionPositiveToNegative
|
||||
|
||||
|
||||
def set_reference_mesh_from_selection():
|
||||
selection = cmds.ls(sl=True, long=True)
|
||||
|
||||
if len(selection) != 2:
|
||||
log.debug("wrong selection size")
|
||||
return
|
||||
|
||||
m = Mirror(selection[1])
|
||||
m.set_reference_mesh(selection[0])
|
||||
Reference in New Issue
Block a user