230 lines
7.0 KiB
Python
230 lines
7.0 KiB
Python
import random
|
|
|
|
from maya import cmds
|
|
|
|
import ngSkinTools2.api
|
|
from ngSkinTools2 import signal
|
|
from ngSkinTools2.api import Mirror
|
|
from ngSkinTools2.api.layers import generate_layer_name
|
|
from ngSkinTools2.api.log import getLogger
|
|
from ngSkinTools2.api.pyside import QAction, QtCore
|
|
from ngSkinTools2.api.session import session, withSession
|
|
from ngSkinTools2.decorators import undoable
|
|
from ngSkinTools2.ui import dialogs, qt
|
|
from ngSkinTools2.ui.action import Action
|
|
from ngSkinTools2.ui.options import config
|
|
|
|
logger = getLogger("layer operations")
|
|
|
|
|
|
@withSession
|
|
@undoable
|
|
def initializeLayers(createFirstLayer=True):
|
|
if session.state.layersAvailable:
|
|
return # ignore
|
|
|
|
target = session.state.selectedSkinCluster
|
|
|
|
layers = ngSkinTools2.api.init_layers(target)
|
|
with ngSkinTools2.api.suspend_updates(target):
|
|
if createFirstLayer:
|
|
layer = layers.add("Base weights")
|
|
layer.set_current()
|
|
Mirror(target).set_mirror_config(config.mirrorInfluencesDefaults)
|
|
|
|
session.events.targetChanged.emitIfChanged()
|
|
|
|
if ngSkinTools2.api.is_slow_mode_skin_cluster(target):
|
|
dialogs.info(
|
|
"ngSkinTools switched to slow maya API for setting skin cluster weights for this skinCluster, to workaround a Maya bug when skinCluster uses dg nodes as inputs"
|
|
)
|
|
|
|
|
|
@undoable
|
|
def addLayer():
|
|
layers = ngSkinTools2.api.Layers(session.state.selectedSkinCluster)
|
|
|
|
def guessParent():
|
|
currentLayer = layers.current_layer()
|
|
if currentLayer is None:
|
|
return None
|
|
|
|
# current layer is a parent?
|
|
if currentLayer.num_children > 0:
|
|
return currentLayer
|
|
|
|
return currentLayer.parent
|
|
|
|
with ngSkinTools2.api.suspend_updates(layers.mesh):
|
|
new_layer = layers.add(generate_layer_name(session.state.all_layers, "New Layer"))
|
|
new_layer.parent = guessParent()
|
|
|
|
session.events.layerListChanged.emitIfChanged()
|
|
setCurrentLayer(new_layer)
|
|
|
|
return new_layer
|
|
|
|
|
|
def build_action_initialize_layers(session, parent):
|
|
from ngSkinTools2.ui import actions
|
|
|
|
from . import import_v1_actions
|
|
|
|
def do_initialize():
|
|
if import_v1_actions.can_import(session):
|
|
q = (
|
|
"Skinning layers from previous version of ngSkinTools are present on this mesh. This operation will initialize "
|
|
"skinning layers from scratch, discarding previous layers information. Do you want to continue?"
|
|
)
|
|
if not dialogs.yesNo(q):
|
|
return
|
|
|
|
initializeLayers()
|
|
|
|
result = actions.define_action(parent, "Initialize Skinning Layers", callback=do_initialize)
|
|
|
|
@signal.on(session.events.nodeSelectionChanged)
|
|
def update():
|
|
result.setEnabled(session.state.selectedSkinCluster is not None)
|
|
|
|
update()
|
|
|
|
return result
|
|
|
|
|
|
def buildAction_createLayer(session, parent):
|
|
from ngSkinTools2.ui import actions
|
|
|
|
result = actions.define_action(parent, "Create Layer", callback=addLayer, icon=":/newLayerEmpty.png", shortcut=QtCore.Qt.Key_Insert)
|
|
|
|
@signal.on(session.events.targetChanged)
|
|
def update_to_target():
|
|
result.setEnabled(session.state.layersAvailable)
|
|
|
|
update_to_target()
|
|
|
|
return result
|
|
|
|
|
|
def buildAction_deleteLayer(session, parent):
|
|
from ngSkinTools2.ui import actions
|
|
|
|
result = actions.define_action(parent, "Delete Layer", callback=deleteSelectedLayers, shortcut=QtCore.Qt.Key_Delete)
|
|
|
|
@signal.on(session.context.selected_layers.changed, session.events.targetChanged, qtParent=parent)
|
|
def update_to_target():
|
|
result.setEnabled(session.state.layersAvailable and bool(session.context.selected_layers(default=[])))
|
|
|
|
update_to_target()
|
|
|
|
return result
|
|
|
|
|
|
@undoable
|
|
def setCurrentLayer(layer):
|
|
"""
|
|
:type layer: ngSkinTools2.api.layers.Layer
|
|
"""
|
|
if not session.active():
|
|
logger.info("didn't set current layer: no session")
|
|
|
|
if not session.state.layersAvailable:
|
|
logger.info("didn't set current layer: layers not enabled")
|
|
|
|
logger.info("setting current layer to %r on %r", layer, session.state.selectedSkinCluster)
|
|
layer.set_current()
|
|
session.events.currentLayerChanged.emitIfChanged()
|
|
|
|
|
|
def getCurrentLayer():
|
|
layers = ngSkinTools2.api.Layers(session.state.selectedSkinCluster)
|
|
return layers.current_layer()
|
|
|
|
|
|
@undoable
|
|
def renameLayer(layer, newName):
|
|
layer.name = newName
|
|
cmds.evalDeferred(session.events.layerListChanged.emitIfChanged)
|
|
|
|
|
|
@undoable
|
|
def deleteSelectedLayers():
|
|
layers = ngSkinTools2.api.Layers(session.state.selectedSkinCluster)
|
|
for i in session.context.selected_layers(default=[]):
|
|
layers.delete(i)
|
|
|
|
session.events.layerListChanged.emitIfChanged()
|
|
session.events.currentLayerChanged.emitIfChanged()
|
|
|
|
|
|
class ToggleEnabledAction(Action):
|
|
name = "Enabled"
|
|
checkable = True
|
|
|
|
def __init__(self, session):
|
|
Action.__init__(self, session)
|
|
|
|
def checked(self):
|
|
"""
|
|
return true if most of selected layers are enabled
|
|
:return:
|
|
"""
|
|
layers = session.context.selected_layers(default=[])
|
|
if not layers:
|
|
return True
|
|
|
|
enabled_disabled_balance = 0
|
|
for layer in layers:
|
|
try:
|
|
# eat up the exception if layer id is invalid
|
|
enabled_disabled_balance += 1 if layer.enabled else -1
|
|
except:
|
|
pass
|
|
|
|
return enabled_disabled_balance >= 0
|
|
|
|
def run(self):
|
|
enabled = not self.checked()
|
|
selected_layers = session.context.selected_layers()
|
|
if not selected_layers:
|
|
return
|
|
|
|
for i in selected_layers:
|
|
i.enabled = enabled
|
|
|
|
logger.info("layers toggled: %r", selected_layers)
|
|
|
|
session.events.layerListChanged.emitIfChanged()
|
|
|
|
def enabled(self):
|
|
return session.state.layersAvailable and bool(session.context.selected_layers(default=[]))
|
|
|
|
def update_on_signals(self):
|
|
return [session.context.selected_layers.changed, session.events.layerListChanged, session.events.targetChanged]
|
|
|
|
|
|
def build_action_randomize_influences_colors(session, parent):
|
|
"""
|
|
builds a UI action for randomly choosing new colors for influences
|
|
:type session: ngSkinTools2.api.session.Session
|
|
"""
|
|
|
|
result = QAction("Randomize colors", parent)
|
|
result.setToolTip("Choose random colors for each influence, selecting from Maya's pallete of indexed colors")
|
|
|
|
def color_filter(c):
|
|
brightness = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]
|
|
return brightness > 0.001 and brightness < 0.99 # just a fancy way to skip white and black
|
|
|
|
colors = set([tuple(cmds.colorIndex(i, q=True)) for i in range(1, 30)])
|
|
colors = [c for c in colors if color_filter(c)]
|
|
|
|
@qt.on(result.triggered)
|
|
def triggered():
|
|
if session.state.selectedSkinCluster is None:
|
|
return
|
|
layers = ngSkinTools2.api.Layers(session.state.selectedSkinCluster)
|
|
layers.config.influence_colors = {i.logicalIndex: random.choice(colors) for i in layers.list_influences()}
|
|
|
|
return result
|