Update
This commit is contained in:
295
2023/scripts/rigging_tools/ngskintools2/operations/tools.py
Normal file
295
2023/scripts/rigging_tools/ngskintools2/operations/tools.py
Normal file
@@ -0,0 +1,295 @@
|
||||
from maya import cmds
|
||||
|
||||
from ngSkinTools2 import api, signal
|
||||
from ngSkinTools2.api.log import getLogger
|
||||
from ngSkinTools2.api.python_compatibility import Object
|
||||
from ngSkinTools2.api.session import Session
|
||||
from ngSkinTools2.decorators import undoable
|
||||
from ngSkinTools2.observableValue import ObservableValue
|
||||
from ngSkinTools2.operations import layers
|
||||
from ngSkinTools2.ui import dialogs
|
||||
|
||||
logger = getLogger("operation/tools")
|
||||
|
||||
|
||||
def __create_tool_action__(parent, session, action_name, action_tooltip, exec_handler, enabled_handler=None):
|
||||
"""
|
||||
:type session: Session
|
||||
"""
|
||||
|
||||
from ngSkinTools2.ui import actions
|
||||
|
||||
def execute():
|
||||
if not session.active():
|
||||
return
|
||||
|
||||
exec_handler()
|
||||
|
||||
result = actions.define_action(parent, action_name, callback=execute, tooltip=action_tooltip)
|
||||
|
||||
@signal.on(session.events.targetChanged, session.events.currentLayerChanged)
|
||||
def update_state():
|
||||
enabled = session.state.layersAvailable and session.state.currentLayer.layer is not None
|
||||
if enabled and enabled_handler is not None:
|
||||
enabled = enabled_handler(session.state.currentLayer.layer)
|
||||
result.setEnabled(enabled)
|
||||
|
||||
update_state()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class ClosestJointOptions(Object):
|
||||
def __init__(self):
|
||||
self.create_new_layer = ObservableValue(False)
|
||||
self.all_influences = ObservableValue(True)
|
||||
|
||||
|
||||
def create_action__from_closest_joint(parent, session):
|
||||
options = ClosestJointOptions()
|
||||
|
||||
def exec_handler():
|
||||
layer = session.state.currentLayer.layer
|
||||
influences = None
|
||||
if not options.all_influences():
|
||||
influences = layer.paint_targets
|
||||
if not influences:
|
||||
dialogs.info("Select one or more influences in Influences list")
|
||||
return
|
||||
|
||||
if options.create_new_layer():
|
||||
layer = layers.addLayer()
|
||||
|
||||
api.assign_from_closest_joint(
|
||||
session.state.selectedSkinCluster,
|
||||
layer,
|
||||
influences=influences,
|
||||
)
|
||||
session.events.currentLayerChanged.emitIfChanged()
|
||||
session.events.influencesListUpdated.emit()
|
||||
|
||||
if layer.paint_target is None:
|
||||
used_influences = layer.get_used_influences()
|
||||
if used_influences:
|
||||
layer.paint_target = min(used_influences)
|
||||
|
||||
return (
|
||||
__create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Assign From Closest Joint",
|
||||
action_tooltip="Assign 1.0 weight for closest influence per each vertex in selected layer",
|
||||
exec_handler=exec_handler,
|
||||
),
|
||||
options,
|
||||
)
|
||||
|
||||
|
||||
class UnifyWeightsOptions(Object):
|
||||
overall_effect = ObservableValue(1.0)
|
||||
single_cluster_mode = ObservableValue(False)
|
||||
|
||||
|
||||
def create_action__unify_weights(parent, session):
|
||||
options = UnifyWeightsOptions()
|
||||
|
||||
def exec_handler():
|
||||
api.unify_weights(
|
||||
session.state.selectedSkinCluster,
|
||||
session.state.currentLayer.layer,
|
||||
overall_effect=options.overall_effect(),
|
||||
single_cluster_mode=options.single_cluster_mode(),
|
||||
)
|
||||
|
||||
return (
|
||||
__create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Unify Weights",
|
||||
action_tooltip="For selected vertices, make verts the same for all verts",
|
||||
exec_handler=exec_handler,
|
||||
),
|
||||
options,
|
||||
)
|
||||
|
||||
|
||||
def create_action__merge_layers(parent, session):
|
||||
"""
|
||||
:param parent: UI parent for this action
|
||||
:type session: Session
|
||||
"""
|
||||
|
||||
def exec_handler():
|
||||
api.merge_layers(layers=session.context.selected_layers(default=[]))
|
||||
session.events.layerListChanged.emitIfChanged()
|
||||
session.events.currentLayerChanged.emitIfChanged()
|
||||
|
||||
def enabled_handler(layer):
|
||||
return layer is not None and layer.index > 0
|
||||
|
||||
return __create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Merge",
|
||||
action_tooltip="Merge contents of this layer into underlying layer. Pre-effects weights will be used for this",
|
||||
exec_handler=exec_handler,
|
||||
enabled_handler=enabled_handler,
|
||||
)
|
||||
|
||||
|
||||
def create_action__duplicate_layer(parent, session):
|
||||
"""
|
||||
:param parent: UI parent for this action
|
||||
:type session: Session
|
||||
"""
|
||||
|
||||
@undoable
|
||||
def exec_handler():
|
||||
with api.suspend_updates(session.state.selectedSkinCluster):
|
||||
for source in session.context.selected_layers(default=[]):
|
||||
api.duplicate_layer(layer=source)
|
||||
|
||||
session.events.layerListChanged.emitIfChanged()
|
||||
session.events.currentLayerChanged.emitIfChanged()
|
||||
|
||||
return __create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Duplicate",
|
||||
action_tooltip="Duplicate selected layer(s)",
|
||||
exec_handler=exec_handler,
|
||||
)
|
||||
|
||||
|
||||
def create_action__fill_transparency(parent, session):
|
||||
"""
|
||||
:param parent: UI parent for this action
|
||||
:type session: Session
|
||||
"""
|
||||
|
||||
@undoable
|
||||
def exec_handler():
|
||||
with api.suspend_updates(session.state.selectedSkinCluster):
|
||||
for source in session.context.selected_layers(default=[]):
|
||||
api.fill_transparency(layer=source)
|
||||
|
||||
return __create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Fill Transparency",
|
||||
action_tooltip="All transparent vertices in the selected layer(s) receive weights from their closest non-empty neighbour vertex",
|
||||
exec_handler=exec_handler,
|
||||
)
|
||||
|
||||
|
||||
def create_action__copy_component_weights(parent, session):
|
||||
"""
|
||||
:param parent: UI parent for this action
|
||||
:type session: Session
|
||||
"""
|
||||
|
||||
def exec_handler():
|
||||
for source in session.context.selected_layers(default=[]):
|
||||
api.copy_component_weights(layer=source)
|
||||
|
||||
return __create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Copy Component Weights",
|
||||
action_tooltip="Store components weights in memory for further component-based paste actions",
|
||||
exec_handler=exec_handler,
|
||||
)
|
||||
|
||||
|
||||
def create_action__paste_average_component_weight(parent, session):
|
||||
"""
|
||||
:param parent: UI parent for this action
|
||||
:type session: Session
|
||||
"""
|
||||
|
||||
def exec_handler():
|
||||
for l in session.context.selected_layers(default=[]):
|
||||
api.paste_average_component_weights(layer=l)
|
||||
|
||||
return __create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Paste Average Component Weight",
|
||||
action_tooltip="Compute average of copied component weights and set that value to currently selected components",
|
||||
exec_handler=exec_handler,
|
||||
)
|
||||
|
||||
|
||||
def create_action__add_influences(parent, session):
|
||||
"""
|
||||
:param parent: UI parent for this action
|
||||
:type session: Session
|
||||
"""
|
||||
|
||||
def exec_handler():
|
||||
selection = cmds.ls(sl=True, l=True)
|
||||
if len(selection) < 2:
|
||||
logger.info("invalid selection: %s", selection)
|
||||
return
|
||||
api.add_influences(selection[:-1], selection[-1])
|
||||
cmds.select(selection[-1])
|
||||
session.events.influencesListUpdated.emit()
|
||||
|
||||
return __create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Add Influences",
|
||||
action_tooltip="Add selected influences to current skin cluster.",
|
||||
exec_handler=exec_handler,
|
||||
)
|
||||
|
||||
|
||||
def create_action__select_affected_vertices(parent, session):
|
||||
"""
|
||||
:param parent: UI parent for this action
|
||||
:type session: Session
|
||||
"""
|
||||
|
||||
def exec_handler():
|
||||
selected_layers = session.context.selected_layers(default=[])
|
||||
if not selected_layers:
|
||||
return
|
||||
|
||||
if not session.state.currentLayer.layer:
|
||||
return
|
||||
|
||||
influences = session.state.currentLayer.layer.paint_targets
|
||||
if not influences:
|
||||
return
|
||||
|
||||
non_zero_weights = []
|
||||
for layer in selected_layers:
|
||||
for i in influences:
|
||||
weights = layer.get_weights(i)
|
||||
if weights:
|
||||
non_zero_weights.append(weights)
|
||||
|
||||
if not non_zero_weights:
|
||||
return
|
||||
|
||||
current_selection = cmds.ls(sl=True, o=True, l=True)
|
||||
if len(current_selection) != 1:
|
||||
return
|
||||
|
||||
# we're not sure - this won't work if skin cluster is selected directly
|
||||
selected_mesh_probably = current_selection[0]
|
||||
|
||||
combined_weights = [sum(i) for i in zip(*non_zero_weights)]
|
||||
indexes = [selected_mesh_probably + ".vtx[%d]" % index for index, i in enumerate(combined_weights) if i > 0.00001]
|
||||
try:
|
||||
cmds.select(indexes)
|
||||
except:
|
||||
pass
|
||||
|
||||
return __create_tool_action__(
|
||||
parent,
|
||||
session,
|
||||
action_name=u"Select Affected Vertices",
|
||||
action_tooltip="Select vertices that have non-zero weight for current influence.",
|
||||
exec_handler=exec_handler,
|
||||
)
|
||||
Reference in New Issue
Block a user