Skip to content

Instantly share code, notes, and snippets.

@Pakmanv
Created May 2, 2025 23:58
Show Gist options
  • Select an option

  • Save Pakmanv/2375d3ceb79b3bb2509e859718c55358 to your computer and use it in GitHub Desktop.

Select an option

Save Pakmanv/2375d3ceb79b3bb2509e859718c55358 to your computer and use it in GitHub Desktop.
vvweightweaver
import maya.cmds as cmds
import random
import time
import getpass # Added to get the current username
# Recursive function to get all joints in the hierarchy
def get_joint_hierarchy(root_joint):
joints = [root_joint]
children = cmds.listRelatives(root_joint, children=True, type="joint", fullPath=False) or []
for child in children:
joints.extend(get_joint_hierarchy(child))
return joints
# Function to load selection into a specified list
def load_selection(list_widget, is_joint=False, *args):
selection = cmds.ls(selection=True, long=False)
if selection:
cmds.textScrollList(list_widget, edit=True, removeAll=True)
if is_joint:
all_joints = []
for item in selection:
if cmds.objectType(item, isType="joint"):
all_joints.extend(get_joint_hierarchy(item))
selection = all_joints
cmds.select(selection, replace=True)
for joint in selection:
depth = joint.count("|")
cmds.textScrollList(list_widget, edit=True, append=" " * depth + joint)
else:
for item in selection:
cmds.textScrollList(list_widget, edit=True, append=item)
update_box_label(list_widget)
else:
cmds.warning("Nothing selected!")
# Function to update the box label with the object count
def update_box_label(list_widget):
parent_layout = cmds.textScrollList(list_widget, query=True, parent=True)
label = cmds.frameLayout(parent_layout, query=True, label=True)
count = cmds.textScrollList(list_widget, query=True, numberOfItems=True)
cmds.frameLayout(parent_layout, edit=True, label=f"{label.split(' (')[0]} ({count})")
# Function to find the longest common substring between two strings
def longest_common_substring(str1, str2):
max_len = 0
end_pos = 0
matrix = [[0] * (len(str2) + 1) for _ in range(len(str1) + 1)]
for i in range(1, len(str1) + 1):
for j in range(1, len(str2) + 1):
if str1[i - 1] == str2[j - 1]:
matrix[i][j] = matrix[i - 1][j - 1] + 1
if matrix[i][j] > max_len:
max_len = matrix[i][j]
end_pos = i
return str1[end_pos - max_len:end_pos]
# Function to compare source and target geometry
def compare_geometry(*args):
source_geo = cmds.textScrollList(source_geo_list, query=True, allItems=True) or []
target_geo = cmds.textScrollList(target_geo_list, query=True, allItems=True) or []
cmds.textScrollList(comparison_list, edit=True, removeAll=True)
for source in source_geo:
source_vertex_count = cmds.polyEvaluate(source, vertex=True)
best_match = None
best_match_score = -1
for target in target_geo:
target_vertex_count = cmds.polyEvaluate(target, vertex=True)
common_substring = longest_common_substring(source, target)
name_similarity = len(common_substring)
score = name_similarity * 1000 - abs(source_vertex_count - target_vertex_count)
if score > best_match_score:
best_match = target
best_match_score = score
if best_match:
cmds.textScrollList(comparison_list, edit=True, append=f"{source} -> {best_match}")
else:
cmds.textScrollList(comparison_list, edit=True, append=f"{source} -> No match")
update_box_label(comparison_list)
# Function to get influencing joints for a geometry
def get_influencing_joints(geometry):
skin_cluster = cmds.ls(cmds.listHistory(geometry), type="skinCluster") or []
if skin_cluster:
return cmds.skinCluster(skin_cluster[0], query=True, influence=True)
return []
# Function to get the skin cluster for a geometry
def get_skin_cluster(geometry):
skin_cluster = cmds.ls(cmds.listHistory(geometry), type="skinCluster") or []
return skin_cluster[0] if skin_cluster else None
# Function to handle selection in the comparison list
def select_comparison(*args):
selected_items = cmds.textScrollList(comparison_list, query=True, selectItem=True) or []
cmds.select(clear=True)
if selected_items:
for item in selected_items:
source, target = item.split(" -> ")
cmds.select(source, add=True)
if target != "No match":
cmds.select(target, add=True)
update_joint_influence_lists(source, target)
# Function to update joint influence lists
def update_joint_influence_lists(source, target):
cmds.textScrollList(source_joint_influence_list, edit=True, removeAll=True)
cmds.textScrollList(target_joint_influence_list, edit=True, removeAll=True)
source_joints = get_influencing_joints(source)
for joint in source_joints:
cmds.textScrollList(source_joint_influence_list, edit=True, append=joint)
if target != "No match":
target_joints = get_influencing_joints(target)
for joint in target_joints:
cmds.textScrollList(target_joint_influence_list, edit=True, append=joint)
update_box_label(source_joint_influence_list)
update_box_label(target_joint_influence_list)
compare_joint_influences()
# Function to compare joint influence counts
def compare_joint_influences(*args):
geo_comparisons = cmds.textScrollList(comparison_list, query=True, allItems=True) or []
cmds.textScrollList(joint_comparison_list, edit=True, removeAll=True)
if not geo_comparisons:
cmds.textScrollList(joint_comparison_list, edit=True, append="No geometry comparisons available")
update_box_label(joint_comparison_list)
return
all_matched = True
for comparison in geo_comparisons:
source_geo, target_geo = comparison.split(" -> ")
if target_geo == "No match":
cmds.textScrollList(joint_comparison_list, edit=True, append=f"{source_geo} -> No target match")
all_matched = False
continue
source_joints = get_influencing_joints(source_geo)
target_joints = get_influencing_joints(target_geo)
source_count = len(source_joints) if source_joints else 0
target_count = len(target_joints) if target_joints else 0
if source_count == target_count:
cmds.textScrollList(joint_comparison_list, edit=True, append=f"{source_geo} -> {target_geo}: Match ({source_count} influences)")
else:
cmds.textScrollList(joint_comparison_list, edit=True, append=f"{source_geo} -> {target_geo}: Source={source_count}, Target={target_count}")
all_matched = False
if all_matched and geo_comparisons:
print("All joint influence counts matched successfully!")
elif geo_comparisons:
cmds.warning("Differences found in joint influence counts!")
else:
print("No geometry pairs to compare.")
update_box_label(joint_comparison_list)
# Function to compare joints based on name, short name, and chain position
def compare_joints(*args):
source_joints = cmds.textScrollList(source_joints_list, query=True, allItems=True) or []
target_joints = cmds.textScrollList(target_joints_list, query=True, allItems=True) or []
cmds.textScrollList(joint_matching_list, edit=True, removeAll=True)
if not source_joints and not target_joints:
cmds.textScrollList(joint_matching_list, edit=True, append="No joints to compare")
update_box_label(joint_matching_list)
return
source_short_names = [cmds.ls(j, shortNames=True)[0] for j in source_joints]
target_short_names = [cmds.ls(j, shortNames=True)[0] for j in target_joints]
matches = []
source_unmatched = set(source_joints)
target_unmatched = set(target_joints)
for source, ssn in zip(source_joints, source_short_names):
best_match = None
best_score = -1
for target, tsn in zip(target_joints, target_short_names):
name_similarity = len(longest_common_substring(ssn, tsn))
source_depth = source.count("|")
target_depth = target.count("|")
depth_diff = abs(source_depth - target_depth)
score = name_similarity * 100 - depth_diff
if score > best_score:
best_match = target
best_score = score
if best_match:
matches.append(f"Match: {source} --> {best_match}")
source_unmatched.discard(source)
target_unmatched.discard(best_match)
for match in matches:
cmds.textScrollList(joint_matching_list, edit=True, append=match)
for source in source_unmatched:
cmds.textScrollList(joint_matching_list, edit=True, append=f"Source Only: {source} --> No match")
for target in target_unmatched:
cmds.textScrollList(joint_matching_list, edit=True, append=f"Target Only: No match --> {target}")
if source_unmatched or target_unmatched:
cmds.warning("Differences found between source and target joints!")
elif matches:
print("All joints matched successfully!")
else:
print("No joints to compare.")
update_box_label(joint_matching_list)
# Function to bind target geometry using the source joint influences
def bind_target_to_matched_joints(*args):
geo_comparisons = cmds.textScrollList(comparison_list, query=True, allItems=True) or []
if not geo_comparisons:
cmds.warning("Please run geometry comparison first!")
return
joint_matches = cmds.textScrollList(joint_matching_list, query=True, allItems=True) or []
if not joint_matches:
cmds.warning("No joint matches found! Please run 'Compare Joints' first.")
return
joint_map = {}
for match in joint_matches:
if "Match:" in match:
source, target = match.split("-->")
source = source.replace("Match:", "").strip()
target = target.strip()
joint_map[source] = target
if not joint_map:
cmds.warning("No valid joint matches found in the joint matching list!")
return
for comparison in geo_comparisons:
source_geo, target_geo = comparison.split(" -> ")
if target_geo == "No match":
continue
source_influences = get_influencing_joints(source_geo)
if not source_influences:
cmds.warning(f"No influences found for source geometry: {source_geo}")
continue
matched_target_joints = []
for source_joint in source_influences:
if source_joint in joint_map:
matched_target_joints.append(joint_map[source_joint])
else:
cmds.warning(f"No matching target joint found for source joint: {source_joint}")
if not matched_target_joints:
cmds.warning(f"No matching target joints found for {source_geo}. Binding skipped.")
continue
existing_skin = get_skin_cluster(target_geo)
if existing_skin:
cmds.delete(existing_skin)
try:
cmds.skinCluster(
matched_target_joints,
target_geo,
toSelectedBones=True,
bindMethod=0,
skinMethod=0,
normalizeWeights=1,
weightDistribution=0,
maximumInfluences=len(matched_target_joints)
)
print(f"Successfully bound {target_geo} to {len(matched_target_joints)} joints: {matched_target_joints}")
new_target_influences = get_influencing_joints(target_geo)
if set(new_target_influences) != set(matched_target_joints):
cmds.warning(f"Post-binding mismatch for {target_geo}: {new_target_influences} vs {matched_target_joints}")
else:
print(f"Influences match confirmed for {target_geo}")
cmds.textScrollList(target_joint_influence_list, edit=True, removeAll=True)
for joint in matched_target_joints:
cmds.textScrollList(target_joint_influence_list, edit=True, append=joint)
update_box_label(target_joint_influence_list)
except Exception as e:
cmds.warning(f"Failed to bind {target_geo}: {str(e)}")
compare_joint_influences()
# Function to snap source joints to target joints and apply red Lambert to source geometries
def snap_source_to_target(*args):
joint_matches = cmds.textScrollList(joint_matching_list, query=True, allItems=True) or []
source_geos = cmds.textScrollList(source_geo_list, query=True, allItems=True) or []
if not joint_matches:
cmds.warning("No joint matches found! Please run 'Compare Joints' first.")
return
if not source_geos:
cmds.warning("No source geometries loaded!")
return
# Create or get the red Lambert shader
shader_name = "source_transfer_shader"
if not cmds.objExists(shader_name):
shader = cmds.shadingNode("lambert", asShader=True, name=shader_name)
cmds.setAttr(f"{shader}.color", 1, 0, 0, type="double3") # Set to red
shader_sg = cmds.sets(renderable=True, noSurfaceShader=True, empty=True, name=f"{shader_name}SG")
cmds.connectAttr(f"{shader}.outColor", f"{shader_sg}.surfaceShader", force=True)
else:
shader = shader_name
shader_sg = f"{shader_name}SG"
# Snap joints
for match in joint_matches:
if "Match:" in match:
source, target = match.split("-->")
source = source.replace("Match:", "").strip()
target = target.strip()
try:
# Get target position
target_pos = cmds.xform(target, query=True, worldSpace=True, translation=True)
# Move source joint to target position
cmds.xform(source, worldSpace=True, translation=target_pos)
print(f"Snapped {source} to {target} at position {target_pos}")
except Exception as e:
cmds.warning(f"Failed to snap {source} to {target}: {str(e)}")
# Apply red Lambert shader to all source geometries
for geo in source_geos:
if cmds.objExists(geo):
try:
cmds.sets(geo, edit=True, forceElement=shader_sg)
print(f"Applied red shader to {geo}")
except Exception as e:
cmds.warning(f"Failed to apply shader to {geo}: {str(e)}")
# Function to delete source joints and geometry including the shader
def delete_source_joints_and_geo(*args):
source_joints = cmds.textScrollList(source_joints_list, query=True, allItems=True) or []
source_geo = cmds.textScrollList(source_geo_list, query=True, allItems=True) or []
if not source_joints and not source_geo:
cmds.warning("No source joints or geometry to delete!")
return
if cmds.confirmDialog(
title='Confirm Deletion',
message='Are you sure you want to delete all source joints, geometry, and source_transfer_shader?',
button=['Yes', 'No'],
defaultButton='No',
cancelButton='No',
dismissString='No'
) == 'Yes':
# Delete source joints
for joint in source_joints:
if cmds.objExists(joint):
try:
cmds.delete(joint)
except Exception as e:
cmds.warning(f"Failed to delete joint {joint}: {str(e)}")
# Delete source geometry
for geo in source_geo:
if cmds.objExists(geo):
try:
cmds.delete(geo)
except Exception as e:
cmds.warning(f"Failed to delete geometry {geo}: {str(e)}")
# Delete the source_transfer_shader and its shading group
shader_name = "source_transfer_shader"
if cmds.objExists(shader_name):
try:
shader_sg = f"{shader_name}SG"
if cmds.objExists(shader_sg):
cmds.delete(shader_sg)
cmds.delete(shader_name)
print(f"Deleted {shader_name} and its shading group")
except Exception as e:
cmds.warning(f"Failed to delete shader {shader_name}: {str(e)}")
# Clear the source lists
cmds.textScrollList(source_joints_list, edit=True, removeAll=True)
cmds.textScrollList(source_geo_list, edit=True, removeAll=True)
update_box_label(source_joints_list)
update_box_label(source_geo_list)
print("Source joints, geometry, and shader deleted successfully")
else:
print("Deletion cancelled")
# Function to copy skin weights with success popup
def copy_skin_weights(*args):
geo_comparisons = cmds.textScrollList(comparison_list, query=True, allItems=True) or []
if not geo_comparisons:
cmds.warning("Please run geometry comparison first!")
return
success = True
for comparison in geo_comparisons:
source_geo, target_geo = comparison.split(" -> ")
if target_geo == "No match":
continue
source_skin = get_skin_cluster(source_geo)
target_skin = get_skin_cluster(target_geo)
if not source_skin:
cmds.warning(f"No skin cluster found on source geometry: {source_geo}")
success = False
continue
if not target_skin:
cmds.warning(f"No skin cluster found on target geometry: {target_geo}. Bind target first!")
success = False
continue
try:
cmds.copySkinWeights(
sourceSkin=source_skin,
destinationSkin=target_skin,
noMirror=True,
surfaceAssociation="closestPoint",
influenceAssociation=["closestJoint", "oneToOne"],
normalize=True
)
print(f"Successfully copied skin weights from {source_geo} to {target_geo}")
except Exception as e:
cmds.warning(f"Failed to copy skin weights from {source_geo} to {target_geo}: {str(e)}")
success = False
# Update influence lists after copying weights
if geo_comparisons:
source_geo, target_geo = geo_comparisons[0].split(" -> ")
update_joint_influence_lists(source_geo, target_geo)
# Show success popup if no issues occurred
if success and geo_comparisons:
cmds.confirmDialog(
title="Weight Transfer Complete",
message="Weights transferred successfully with no issues!",
button=["OK"],
defaultButton="OK"
)
# Function to run matching test with uniform joint rotation
def run_matching_test(*args):
geo_comparisons = cmds.textScrollList(comparison_list, query=True, allItems=True) or []
if not geo_comparisons:
cmds.warning("Please run geometry comparison first!")
return
joint_matches = cmds.textScrollList(joint_matching_list, query=True, allItems=True) or []
if not joint_matches:
cmds.warning("No joint matches found! Please run 'Compare Joints' first.")
return
# Create joint mapping
joint_map = {}
for match in joint_matches:
if "Match:" in match:
source, target = match.split("-->")
source = source.replace("Match:", "").strip()
target = target.strip()
joint_map[source] = target
if not joint_map:
cmds.warning("No valid joint matches found!")
return
# Store original rotations
original_rotations = {}
for source, target in joint_map.items():
original_rotations[source] = cmds.getAttr(f"{source}.rotate")[0]
original_rotations[target] = cmds.getAttr(f"{target}.rotate")[0]
start_time = time.time()
alignment_count = 0
test_duration = 4.0 # 4 seconds
samples = 10 # Number of tests
while time.time() - start_time < test_duration:
# Generate random rotation
rot_x = random.uniform(-45, 45)
rot_y = random.uniform(-45, 45)
rot_z = random.uniform(-45, 45)
# Apply same rotation to matched joints
for source, target in joint_map.items():
cmds.setAttr(f"{source}.rotate", rot_x, rot_y, rot_z)
cmds.setAttr(f"{target}.rotate", rot_x, rot_y, rot_z)
cmds.refresh() # Update the scene
# Check vertex alignment
all_aligned = True
for comparison in geo_comparisons:
source_geo, target_geo = comparison.split(" -> ")
if target_geo == "No match":
continue
source_verts = cmds.polyEvaluate(source_geo, vertex=True)
target_verts = cmds.polyEvaluate(target_geo, vertex=True)
if source_verts != target_verts:
all_aligned = False
break
for i in range(source_verts):
source_pos = cmds.pointPosition(f"{source_geo}.vtx[{i}]")
target_pos = cmds.pointPosition(f"{target_geo}.vtx[{i}]")
if not all(abs(source_pos[j] - target_pos[j]) < 0.001 for j in range(3)):
all_aligned = False
break
if not all_aligned:
break
if all_aligned:
alignment_count += 1
time.sleep(test_duration / samples) # Spread samples over 4 seconds
# Restore original rotations
for joint, rot in original_rotations.items():
cmds.setAttr(f"{joint}.rotate", rot[0], rot[1], rot[2])
cmds.refresh()
# If aligned in most samples, show success message
if alignment_count >= samples * 0.7: # 70% threshold
cmds.confirmDialog(
title="Matching Test Result",
message="Weighting is aligned between source and target mesh!",
button=["OK"],
defaultButton="OK"
)
print("Weighting alignment confirmed")
else:
cmds.warning(f"Weighting alignment test failed: {alignment_count}/{samples} samples aligned")
# Function to run all commands in sequence (for Napalm button)
def run_all_commands(*args):
bind_target_to_matched_joints()
snap_source_to_target()
copy_skin_weights()
run_matching_test()
delete_source_joints_and_geo()
# Function to clear all lists and reset memory
def refresh_ui(*args):
global source_joints_list, source_geo_list, target_joints_list, target_geo_list
global comparison_list, source_joint_influence_list, target_joint_influence_list, joint_comparison_list, joint_matching_list
# Clear all textScrollLists
cmds.textScrollList(source_joints_list, edit=True, removeAll=True)
cmds.textScrollList(source_geo_list, edit=True, removeAll=True)
cmds.textScrollList(target_joints_list, edit=True, removeAll=True)
cmds.textScrollList(target_geo_list, edit=True, removeAll=True)
cmds.textScrollList(comparison_list, edit=True, removeAll=True)
cmds.textScrollList(source_joint_influence_list, edit=True, removeAll=True)
cmds.textScrollList(target_joint_influence_list, edit=True, removeAll=True)
cmds.textScrollList(joint_comparison_list, edit=True, removeAll=True)
cmds.textScrollList(joint_matching_list, edit=True, removeAll=True)
# Update labels to reflect cleared state
update_box_label(source_joints_list)
update_box_label(source_geo_list)
update_box_label(target_joints_list)
update_box_label(target_geo_list)
update_box_label(comparison_list)
update_box_label(source_joint_influence_list)
update_box_label(target_joint_influence_list)
update_box_label(joint_comparison_list)
update_box_label(joint_matching_list)
# Clear selection in the scene
cmds.select(clear=True)
print("UI refreshed and memory cleared")
# Create the UI with scrollable layout, 50% width for side-by-side windows, and welcome message
def create_vv_weight_weaver_ui():
global source_joints_list, source_geo_list, target_joints_list, target_geo_list
global comparison_list, source_joint_influence_list, target_joint_influence_list, joint_comparison_list, joint_matching_list
ui_width = 860 # Define UI width
half_width = ui_width // 2 # 50% of UI width = 430px
if cmds.window("vvWeightWeaverTool", exists=True):
cmds.deleteUI("vvWeightWeaverTool")
window = cmds.window("vvWeightWeaverTool", title="VV Weight Weaver", widthHeight=(ui_width, 600))
main_layout = cmds.scrollLayout(horizontalScrollBarThickness=0, verticalScrollBarThickness=16) # No horizontal scroll
scroll_column = cmds.columnLayout(adjustableColumn=True, width=ui_width)
# Welcome message with username
username = getpass.getuser()
cmds.text(label=f"Welcome, {username}!",
align="center",
parent=scroll_column,
width=ui_width,
height=30)
# Note above Refresh button
cmds.text(label="Note: Source and target joints should have similar naming and hierarchy.\nGeometry should have matching vertex order.",
align="center",
parent=scroll_column,
width=ui_width,
height=40)
# Refresh button
cmds.button(label="Refresh", command=refresh_ui, parent=scroll_column, width=ui_width, height=30)
# Source Joints and Target Joints side by side (50% each)
cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1, half_width), (2, half_width)], parent=scroll_column)
cmds.frameLayout(label="Source Joints (0)", collapsable=True, width=half_width)
source_joints_list = cmds.textScrollList(allowMultiSelection=True, width=half_width, height=150, selectCommand=lambda *args: cmds.select(cmds.textScrollList(source_joints_list, query=True, selectItem=True)))
cmds.button(label="Load Selected Joints", command=lambda *args: load_selection(source_joints_list, is_joint=True), width=half_width)
cmds.setParent("..")
cmds.frameLayout(label="Target Joints (0)", collapsable=True, width=half_width)
target_joints_list = cmds.textScrollList(allowMultiSelection=True, width=half_width, height=150, selectCommand=lambda *args: cmds.select(cmds.textScrollList(target_joints_list, query=True, selectItem=True)))
cmds.button(label="Load Selected Joints", command=lambda *args: load_selection(target_joints_list, is_joint=True), width=half_width)
cmds.setParent("..")
cmds.setParent("..")
# Source Geometry and Target Geometry side by side (50% each)
cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1, half_width), (2, half_width)], parent=scroll_column)
cmds.frameLayout(label="Source Geometry (0)", collapsable=True, width=half_width)
source_geo_list = cmds.textScrollList(allowMultiSelection=True, width=half_width, height=150, selectCommand=lambda *args: cmds.select(cmds.textScrollList(source_geo_list, query=True, selectItem=True)))
cmds.button(label="Load Selected Geometry", command=lambda *args: load_selection(source_geo_list), width=half_width)
cmds.setParent("..")
cmds.frameLayout(label="Target Geometry (0)", collapsable=True, width=half_width)
target_geo_list = cmds.textScrollList(allowMultiSelection=True, width=half_width, height=150, selectCommand=lambda *args: cmds.select(cmds.textScrollList(target_geo_list, query=True, selectItem=True)))
cmds.button(label="Load Selected Geometry", command=lambda *args: load_selection(target_geo_list), width=half_width)
cmds.setParent("..")
cmds.setParent("..")
# Geometry Comparison (capped at 800px)
cmds.frameLayout(label="Geometry Comparison (0)", collapsable=True, parent=scroll_column, width=800)
comparison_list = cmds.textScrollList(allowMultiSelection=True, width=800, height=150, selectCommand=select_comparison)
cmds.button(label="Compare Source and Target Geometry", command=compare_geometry, width=800)
cmds.setParent("..")
# Source Joint Influence and Target Joint Influence side by side (50% each)
cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1, half_width), (2, half_width)], parent=scroll_column)
cmds.frameLayout(label="Source Joint Influence (0)", collapsable=True, width=half_width)
source_joint_influence_list = cmds.textScrollList(allowMultiSelection=True, width=half_width, height=150)
cmds.setParent("..")
cmds.frameLayout(label="Target Joint Influence (0)", collapsable=True, width=half_width)
target_joint_influence_list = cmds.textScrollList(allowMultiSelection=True, width=half_width, height=150)
cmds.setParent("..")
cmds.setParent("..")
# Joint Influence Comparison (capped at 800px)
cmds.frameLayout(label="Joint Influence Comparison (0)", collapsable=True, parent=scroll_column, width=800)
joint_comparison_list = cmds.textScrollList(allowMultiSelection=True, width=800, height=150)
cmds.setParent("..")
# Joint Matching (capped at 800px)
cmds.frameLayout(label="Joint Matching (0)", collapsable=True, parent=scroll_column, width=800)
joint_matching_list = cmds.textScrollList(allowMultiSelection=True, width=800, height=150)
cmds.button(label="Compare Joints", command=compare_joints, width=800)
cmds.setParent("..")
# Buttons (single column)
cmds.button(label="Bind Target Geometry with Target Joints", command=bind_target_to_matched_joints, parent=scroll_column, width=ui_width)
cmds.button(label="Snap Source to Target", command=snap_source_to_target, parent=scroll_column, width=ui_width)
cmds.button(label="Copy Skin Weights", command=copy_skin_weights, parent=scroll_column, width=ui_width)
cmds.button(label="Run Matching Test", command=run_matching_test, parent=scroll_column, width=ui_width, backgroundColor=(0.3, 0.3, 0.3))
cmds.button(label="Delete Source Joints and Geo", command=delete_source_joints_and_geo, parent=scroll_column, width=ui_width, backgroundColor=(1.0, 0.0, 0.0))
cmds.button(label="Napalm", command=run_all_commands, parent=scroll_column, width=ui_width, backgroundColor=(1.0, 0.5, 0.0))
# Centered text at the bottom
cmds.text(label="This is intended for transferring weights between multiple objects\nthat have similar joint structure and geometry. Also similar name.\nPlease do not share.",
align="center",
parent=scroll_column,
width=ui_width,
height=60)
cmds.setParent("..")
cmds.setParent("..")
cmds.showWindow(window)
print("UI created successfully")
# Run the UI
if __name__ == "__main__":
create_vv_weight_weaver_ui()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment