Skip to content

Commit

Permalink
Fixed a bug related to the Mirror Control Shape tool. reported by remicc
Browse files Browse the repository at this point in the history
  • Loading branch information
joji2468ng committed Aug 13, 2023
1 parent 0f0cd1d commit 73e0f69
Showing 1 changed file with 118 additions and 100 deletions.
218 changes: 118 additions & 100 deletions release/scripts/mgear/rigbits/mirror_controls.py
Original file line number Diff line number Diff line change
@@ -1,116 +1,134 @@
import pymel.core as pc
import pymel.core as pm
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

import mgear
from mgear.vendor.Qt import QtCore, QtWidgets


def mirror_selection():
pairs = []
for source in pc.selected():
target = get_opposite_control(source)
if target:
pairs.append([source, target])

mirror_pairs(pairs)


def get_controls_without_string(exclusion_string):
nodes = []

# Find controllers set
rig_models = [
item for item in pc.ls(transforms=True) if item.hasAttr("is_rig")
]
controllers_set = None
for node in rig_models[0].rigGroups.inputs():
if node.name().endswith("controllers_grp"):
controllers_set = node
break

# Collect all transforms from set and subsets.
nodes = []
for node in controllers_set.members():
if node.nodeType() == "objectSet":
nodes.extend(node.members())
else:
nodes.append(node)

# Find all nodes without exclusion_string
nodes = [x for x in nodes if exclusion_string not in x.name()]

return nodes


def get_opposite_control(node):
target_name = mgear.core.string.convertRLName(node.name())
target = None
if pc.objExists(target_name):
target = pc.PyNode(target_name)
return target


def mirror_left_to_right():
pairs = []
for source in get_controls_without_string("_R"):
target = get_opposite_control(source)
if target:
pairs.append([source, target])

mirror_pairs(pairs)


def mirror_right_to_left():
pairs = []
for source in get_controls_without_string("_L"):
target = get_opposite_control(source)
if target:
pairs.append([source, target])

mirror_pairs(pairs)
from mgear.vendor.Qt import QtCore, QtWidgets


def mirror_pairs(pairs):
# Modify control shapes
for source, target in pairs:
# Copy shapes
source_copy = pc.duplicate(source)[0]
class MirrorController:
def __init__(self):
pass

@staticmethod
def get_opposite_control(node):
side_l = node.attr("L_custom_side_label").get()
side_r = node.attr("R_custom_side_label").get()
side = node.attr("side_label").get()

target_name = None
if side == "L":
target_name = node.name().replace(side_l, side_r)
elif side == "R":
target_name = node.name().replace(side_r, side_l)

if target_name and pm.objExists(target_name):
return pm.PyNode(target_name)
return None

@staticmethod
def get_specific_side_controls(side="L"):
# Find controllers set
rig_models = [item for item in pm.ls(transforms=True) if item.hasAttr("is_rig")]
controllers_set = None
for node in rig_models[0].rigGroups.inputs():
if node.name().endswith("controllers_grp"):
controllers_set = node
break

# Collect all transforms from set and subsets.
nodes = []
for node in controllers_set.members():
if node.nodeType() == "objectSet":
nodes.extend(node.members())
else:
nodes.append(node)

side_name = None
side_ctl = [node for node in nodes if node.hasAttr("{}_custom_side_label".format(side))]
if side_ctl:
side_name = side_ctl[0].attr("{}_custom_side_label".format(side)).get()

# Find all nodes without exclusion_string
nodes = [x for x in nodes if side_name in x.name()]
return nodes

@staticmethod
def copy_and_prepare_source(source):
source_copy = pm.duplicate(source)[0]
mgear.core.attribute.setKeyableAttributes(
source_copy,
["tx", "ty", "tz", "ro", "rx", "ry", "rz", "sx", "sy", "sz"]
)

# Delete children except shapes
for child in source_copy.getChildren():
if child.nodeType() != "nurbsCurve":
pc.delete(child)
pm.delete(child)
return source_copy

def mirror_pairs(self, pairs):
# Modify control shapes
for source, target in pairs:
source_copy = self.copy_and_prepare_source(source)

pm.select(clear=True)

# Mirror a source copy under a group node
grp = pm.group(world=True)
pm.parent(source_copy, grp)
grp.scaleX.set(-1)

# Re-parent, freeze transforms and match color
pm.parent(source_copy, target)
pm.makeIdentity(source_copy, apply=True, t=1, r=1, s=1, n=0)
pm.parent(source_copy, target.getParent())

target_color = mgear.core.curve.get_color(target)
if target_color:
mgear.core.curve.set_color(source_copy, target_color)

# Replace shape
mgear.rigbits.replaceShape(source_copy, [target])

# Clean up
pm.delete(grp)
pm.delete(source_copy)

def mirror_selection(self):
pairs = []
for source in pm.selected():
target = self.get_opposite_control(source)
if not target:
continue
pairs.append([source, target])

self.mirror_pairs(pairs)

# Mirror
pc.select(clear=True)
grp = pc.group(world=True)
pc.parent(source_copy, grp)
grp.scaleX.set(-1)
def mirror_left_to_right(self):
pairs = []
for source in self.get_specific_side_controls(side="L"):
target = self.get_opposite_control(source)
if target:
pairs.append([source, target])

# Reparent, freeze transforms and match color
pc.parent(source_copy, target)
pc.makeIdentity(source_copy, apply=True, t=1, r=1, s=1, n=0)
pc.parent(source_copy, target.getParent())
targetColor = mgear.core.curve.get_color(target)
if targetColor:
mgear.core.curve.set_color(source_copy, targetColor)
self.mirror_pairs(pairs)

# Replace shape
mgear.rigbits.replaceShape(source_copy, [target])
def mirror_right_to_left(self):
pairs = []
for source in self.get_specific_side_controls(side="R"):
target = self.get_opposite_control(source)
if target:
pairs.append([source, target])

# Clean up
pc.delete(grp)
pc.delete(source_copy)
self.mirror_pairs(pairs)


class mirror_controls_ui(MayaQWidgetDockableMixin, QtWidgets.QDialog):
class MirrorControlsUi(MayaQWidgetDockableMixin, QtWidgets.QDialog):

def __init__(self, parent=None):
super(mirror_controls_ui, self).__init__(parent)
super(MirrorControlsUi, self).__init__(parent)

self.func = MirrorController()

self.setWindowTitle("Mirror Controls")
self.setWindowFlags(QtCore.Qt.Window)
Expand All @@ -134,26 +152,26 @@ def __init__(self, parent=None):
self.mirror_button.clicked.connect(self.mirror_button_pressed)

def mirror_button_pressed(self):
pc.system.undoInfo(openChunk=True)
pm.system.undoInfo(openChunk=True)

# Store selection
selection = pc.selected()
selection = pm.selected()

if self.selection_button.isChecked():
mirror_selection()
self.func.mirror_selection()
if self.left_to_right_button.isChecked():
mirror_left_to_right()
self.func.mirror_left_to_right()
if self.right_to_left_button.isChecked():
mirror_right_to_left()
self.func.mirror_right_to_left()

# Restore selection
pc.select(selection)
pm.select(selection)

pc.system.undoInfo(closeChunk=True)
pm.system.undoInfo(closeChunk=True)


def show(*args):
mgear.core.pyqt.showDialog(mirror_controls_ui)
mgear.core.pyqt.showDialog(MirrorControlsUi)


if __name__ == "__main__":
Expand Down

0 comments on commit 73e0f69

Please sign in to comment.