Files
Nexus/2023/scripts/rigging_tools/ngskintools2/ui/tabSetWeights.py
2025-11-24 08:27:50 +08:00

229 lines
8.4 KiB
Python

from ngSkinTools2 import signal
from ngSkinTools2.api import PaintMode, PaintModeSettings, flood_weights
from ngSkinTools2.api.log import getLogger
from ngSkinTools2.api.pyside import QAction, QActionGroup, QtWidgets
from ngSkinTools2.api.python_compatibility import Object
from ngSkinTools2.api.session import session
from ngSkinTools2.signal import Signal
from ngSkinTools2.ui import qt, widgets
from ngSkinTools2.ui.layout import TabSetup, createTitledRow
from ngSkinTools2.ui.ui_lock import UiLock
log = getLogger("tab set weights")
def make_presets():
presets = {m: PaintModeSettings() for m in PaintMode.all()}
for k, v in presets.items():
v.mode = k
presets[PaintMode.smooth].intensity = 0.3
presets[PaintMode.scale].intensity = 0.3
presets[PaintMode.add].intensity = 0.1
presets[PaintMode.scale].intensity = 0.95
return presets
class Model(Object):
def __init__(self):
self.mode_changed = Signal("mode changed")
self.presets = make_presets()
self.current_settings = None
self.set_mode(PaintMode.replace)
def set_mode(self, mode):
self.current_settings = self.presets[mode]
self.mode_changed.emit()
def apply(self):
flood_weights(session.state.currentLayer.layer, influences=session.state.currentLayer.layer.paint_targets, settings=self.current_settings)
def build_ui(parent):
model = Model()
ui_lock = UiLock()
def build_mode_settings_group():
def mode_row():
row = QtWidgets.QVBoxLayout()
group = QActionGroup(parent)
actions = {}
def create_mode_button(toolbar, mode, label, tooltip):
a = QAction(label, parent)
a.setToolTip(tooltip)
a.setStatusTip(tooltip)
a.setCheckable(True)
actions[mode] = a
group.addAction(a)
@qt.on(a.toggled)
@ui_lock.skip_if_updating
def toggled(checked):
if checked:
model.set_mode(mode)
update_ui()
toolbar.addAction(a)
t = QtWidgets.QToolBar()
create_mode_button(t, PaintMode.replace, "Replace", "")
create_mode_button(t, PaintMode.add, "Add", "")
create_mode_button(t, PaintMode.scale, "Scale", "")
row.addWidget(t)
t = QtWidgets.QToolBar()
create_mode_button(t, PaintMode.smooth, "Smooth", "")
create_mode_button(t, PaintMode.sharpen, "Sharpen", "")
row.addWidget(t)
actions[model.current_settings.mode].setChecked(True)
return row
influences_limit = widgets.NumberSliderGroup(value_type=int, min_value=0, max_value=10)
@signal.on(influences_limit.valueChanged)
@ui_lock.skip_if_updating
def influences_limit_changed():
for _, v in model.presets.items():
v.influences_limit = influences_limit.value()
update_ui()
intensity = widgets.NumberSliderGroup()
@signal.on(intensity.valueChanged, qtParent=parent)
@ui_lock.skip_if_updating
def intensity_edited():
model.current_settings.intensity = intensity.value()
update_ui()
iterations = widgets.NumberSliderGroup(value_type=int, min_value=1, max_value=100)
@signal.on(iterations.valueChanged, qtParent=parent)
@ui_lock.skip_if_updating
def iterations_edited():
model.current_settings.iterations = iterations.value()
update_ui()
fixed_influences = QtWidgets.QCheckBox("Only adjust existing vertex influences")
fixed_influences.setToolTip(
"When this option is enabled, smooth will only adjust existing influences per vertex, "
"and won't include other influences from nearby vertices"
)
volume_neighbours = QtWidgets.QCheckBox("Smooth across gaps and thin surfaces")
volume_neighbours.setToolTip(
"Use all nearby neighbours, regardless if they belong to same surface. "
"This will allow for smoothing to happen across gaps and thin surfaces."
)
limit_to_component_selection = QtWidgets.QCheckBox("Limit to component selection")
limit_to_component_selection.setToolTip("When this option is enabled, smoothing will only happen between selected components")
@qt.on(fixed_influences.stateChanged)
@ui_lock.skip_if_updating
def fixed_influences_changed(*_):
model.current_settings.fixed_influences_per_vertex = fixed_influences.isChecked()
@qt.on(limit_to_component_selection.stateChanged)
@ui_lock.skip_if_updating
def limit_to_component_selection_changed(*_):
model.current_settings.limit_to_component_selection = limit_to_component_selection.isChecked()
def update_ui():
with ui_lock:
widgets.set_paint_expo(intensity, model.current_settings.mode)
intensity.set_value(model.current_settings.intensity)
iterations.set_value(model.current_settings.iterations)
iterations.set_enabled(model.current_settings.mode in [PaintMode.smooth, PaintMode.sharpen])
fixed_influences.setEnabled(model.current_settings.mode in [PaintMode.smooth])
fixed_influences.setChecked(model.current_settings.fixed_influences_per_vertex)
limit_to_component_selection.setChecked(model.current_settings.limit_to_component_selection)
limit_to_component_selection.setEnabled(fixed_influences.isEnabled())
influences_limit.set_value(model.current_settings.influences_limit)
volume_neighbours.setChecked(model.current_settings.use_volume_neighbours)
volume_neighbours.setEnabled(model.current_settings.mode == PaintMode.smooth)
settings_group = QtWidgets.QGroupBox("Mode Settings")
layout = QtWidgets.QVBoxLayout()
layout.addLayout(createTitledRow("Mode:", mode_row()))
layout.addLayout(createTitledRow("Intensity:", intensity.layout()))
layout.addLayout(createTitledRow("Iterations:", iterations.layout()))
layout.addLayout(createTitledRow("Influences limit:", influences_limit.layout()))
layout.addLayout(createTitledRow("Weight bleeding:", fixed_influences))
layout.addLayout(createTitledRow("Volume smoothing:", volume_neighbours))
layout.addLayout(createTitledRow("Isolation:", limit_to_component_selection))
settings_group.setLayout(layout)
update_ui()
return settings_group
def common_settings():
layout = QtWidgets.QVBoxLayout()
mirror = QtWidgets.QCheckBox("Mirror")
layout.addLayout(createTitledRow("", mirror))
@qt.on(mirror.stateChanged)
@ui_lock.skip_if_updating
def mirror_changed(*_):
for _, v in model.presets.items():
v.mirror = mirror.isChecked()
redistribute_removed_weight = QtWidgets.QCheckBox("Distribute to other influences")
layout.addLayout(createTitledRow("Removed weight:", redistribute_removed_weight))
@qt.on(redistribute_removed_weight.stateChanged)
def redistribute_removed_weight_changed():
for _, v in model.presets.items():
v.distribute_to_other_influences = redistribute_removed_weight.isChecked()
@signal.on(model.mode_changed, qtParent=layout)
def update_ui():
mirror.setChecked(model.current_settings.mirror)
redistribute_removed_weight.setChecked(model.current_settings.distribute_to_other_influences)
group = QtWidgets.QGroupBox("Common Settings")
group.setLayout(layout)
update_ui()
return group
def apply_button():
btn = QtWidgets.QPushButton("Apply")
btn.setToolTip("Apply selected operation to vertex")
@qt.on(btn.clicked)
def clicked():
model.apply()
return btn
tab = TabSetup()
tab.innerLayout.addWidget(build_mode_settings_group())
tab.innerLayout.addWidget(common_settings())
tab.innerLayout.addStretch()
tab.lowerButtonsRow.addWidget(apply_button())
@signal.on(session.events.targetChanged, qtParent=tab.tabContents)
def update_tab_enabled():
tab.tabContents.setEnabled(session.state.layersAvailable)
update_tab_enabled()
return tab.tabContents