Created
May 2, 2025 23:58
-
-
Save Pakmanv/2375d3ceb79b3bb2509e859718c55358 to your computer and use it in GitHub Desktop.
vvweightweaver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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