Created
May 2, 2025 17:52
-
-
Save Pakmanv/9e9cca5785917c2e8f6411a4748756ee to your computer and use it in GitHub Desktop.
Scene Unit and Finaling VV
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 os | |
| class SceneUnitConverter: | |
| def __init__(self): | |
| self.window_name = "sceneUnitVVWindow" | |
| self.maya_units = { | |
| "Millimeter": "mm", | |
| "Centimeter": "cm", | |
| "Meter": "m", | |
| "Inch": "inch", | |
| "Foot": "ft", | |
| "Yard": "yd" | |
| } | |
| self.current_unit = "Foot" | |
| self.grid_size = 12 | |
| self.grid_spacing = 5 | |
| self.grid_subdivisions = 5 | |
| self.distance_feet = 6 | |
| self.distance_inches = 6 | |
| self.reference_path = "O:\\productions\\inviz\\SKTL\\00_cg\\scenes\\default_setup\\sizecomp_joint_referernce_VV_Realworld_UE_Scales.ma" | |
| self.is_referenced = False | |
| self.joint_size = 0.01 | |
| self.current_file = cmds.file(query=True, sceneName=True) or "Untitled.ma" | |
| self.create_ui() | |
| def create_ui(self): | |
| if cmds.window(self.window_name, exists=True): | |
| cmds.deleteUI(self.window_name) | |
| self.window = cmds.window(self.window_name, title="Scene Unit and Finaling VV 4.0", | |
| widthHeight=(300, 750)) | |
| self.scroll_layout = cmds.scrollLayout(horizontalScrollBarThickness=16, | |
| verticalScrollBarThickness=16) | |
| self.main_layout = cmds.columnLayout(adjustableColumn=True, rowSpacing=5) | |
| # Working Units Section | |
| self.working_frame = cmds.frameLayout(label="Working Units", collapsable=True, collapse=False) | |
| cmds.text(label="Current Unit:") | |
| self.current_unit_field = cmds.text(label=f"{self.current_unit}") | |
| cmds.text(label="Select Unit:") | |
| self.unit_menu = cmds.optionMenu() | |
| for unit in self.maya_units.keys(): | |
| cmds.menuItem(label=unit) | |
| cmds.optionMenu(self.unit_menu, edit=True, value="Foot") | |
| cmds.button(label="Change Unit", command=self.change_unit, backgroundColor=(1.0, 0.8, 0.8)) | |
| cmds.setParent("..") | |
| # Grid Settings Section | |
| self.grid_frame = cmds.frameLayout(label="Grid Settings", collapsable=True, collapse=False) | |
| cmds.text(label="Size:") | |
| self.size_field = cmds.floatField(value=self.grid_size, precision=2) | |
| cmds.text(label="Grid Lines Every:") | |
| self.spacing_field = cmds.floatField(value=self.grid_spacing, precision=2) | |
| cmds.text(label="Subdivisions:") | |
| self.subdiv_field = cmds.intField(value=self.grid_subdivisions, minValue=1) | |
| cmds.button(label="Commit Grid", command=self.update_grid, backgroundColor=(0.6, 0.8, 1.0)) | |
| cmds.setParent("..") | |
| # Distance Tool Section | |
| self.distance_frame = cmds.frameLayout(label="Distance Tool", collapsable=True, collapse=False) | |
| cmds.rowLayout(numberOfColumns=6, columnWidth6=(60, 60, 30, 60, 30, 80)) | |
| cmds.text(label="Height:") | |
| self.feet_field = cmds.intField(value=self.distance_feet, width=60) | |
| cmds.text(label="ft") | |
| self.inches_field = cmds.intField(value=self.distance_inches, width=60) | |
| cmds.text(label="in") | |
| self.conversion_label = cmds.text(label="= 0.0 units") | |
| cmds.setParent("..") | |
| cmds.rowLayout(numberOfColumns=3, columnWidth3=(100, 80, 80)) | |
| cmds.text(label="Convert to:") | |
| self.conversion_unit_menu = cmds.optionMenu(width=80) | |
| for unit in self.maya_units.keys(): | |
| cmds.menuItem(label=unit) | |
| cmds.optionMenu(self.conversion_unit_menu, edit=True, value="Foot") | |
| cmds.button(label="Convert", command=self.convert_distance) | |
| cmds.setParent("..") | |
| cmds.button(label="Create Distance", command=self.create_distance, backgroundColor=(1.0, 1.0, 0.6)) | |
| cmds.setParent("..") | |
| # Reference Toggle Section | |
| self.reference_frame = cmds.frameLayout(label="Reference Toggle", collapsable=True, collapse=False) | |
| cmds.text(label="Reference Path:") | |
| self.reference_path_field = cmds.text(label=self.reference_path, wordWrap=True, height=40) | |
| cmds.button(label="Change Reference Path", command=self.change_reference_path) | |
| self.toggle_button = cmds.button(label="Load Reference", command=self.toggle_reference) | |
| self.status_field = cmds.text(label="Reference not loaded") | |
| cmds.setParent("..") | |
| # Joint Size Section | |
| self.joint_frame = cmds.frameLayout(label="Joint Size Changer", collapsable=True, collapse=False) | |
| cmds.text(label="Set all joint sizes in scene", height=20) | |
| self.joint_size_field = cmds.floatFieldGrp(label="Joint Size", value1=self.joint_size, precision=3) | |
| cmds.button(label="Apply Joint Size", command=self.change_joint_size, backgroundColor=(0.9, 0.8, 1.0)) | |
| cmds.setParent("..") | |
| # Geometry Tools Section | |
| self.geometry_frame = cmds.frameLayout(label="Geometry Tools", collapsable=True, collapse=False) | |
| cmds.button(label="Freeze All Geometry", command=self.freeze_geometry, backgroundColor=(0.7, 0.9, 0.9)) | |
| cmds.button(label="Freeze Selection", command=self.freeze_selection, backgroundColor=(0.8, 1.0, 0.8)) | |
| cmds.button(label="Reset Root Pivot to Origin", command=self.reset_root_pivot) | |
| cmds.button(label="Center Pivot", command=self.center_pivot) | |
| cmds.button(label="Unhide All Nodes", command=self.unhide_all_nodes) | |
| cmds.setParent("..") | |
| # Camera Clip Planes Section (Updated) | |
| self.camera_frame = cmds.frameLayout(label="Camera Clip Planes", collapsable=True, collapse=False) | |
| current_camera = self.get_active_camera() | |
| near_clip, far_clip = self.get_camera_clip_values(current_camera) | |
| cmds.text(label=f"Current Camera: {current_camera}") | |
| self.near_clip_field = cmds.floatFieldGrp(label="Near Clip", value1=near_clip, precision=2) | |
| self.far_clip_field = cmds.floatFieldGrp(label="Far Clip", value1=far_clip, precision=2) | |
| cmds.button(label="Update Clip Planes", command=self.update_clip_planes, backgroundColor=(0.7, 0.9, 0.85)) | |
| cmds.button(label="Refresh Camera", command=self.refresh_camera_info, backgroundColor=(0.7, 0.9, 0.85)) | |
| cmds.setParent("..") | |
| # Notes Section | |
| self.notes_frame = cmds.frameLayout(label="Scene Notes", collapsable=True, collapse=False) | |
| self.notes_field = cmds.scrollField(editable=True, height=100, wordWrap=True) | |
| cmds.button(label="Apply Notes to Root", command=self.apply_notes) | |
| cmds.setParent("..") | |
| # File Management Section | |
| self.file_frame = cmds.frameLayout(label="File Management", collapsable=True, collapse=False) | |
| cmds.text(label="Current File:") | |
| self.file_name_field = cmds.textField(text=self.current_file, width=280) | |
| cmds.button(label="Open", command=self.open_file, backgroundColor=(0.7, 0.9, 1.0)) | |
| cmds.button(label="Save", command=self.save_file, backgroundColor=(1.0, 0.7, 0.7)) | |
| cmds.button(label="Save As", command=self.save_as_file, backgroundColor=(1.0, 0.85, 0.6)) | |
| cmds.text(label="Tool created by Vypac Voeur and shouldn't be distributed.", | |
| align="center", height=20) | |
| cmds.setParent("..") | |
| cmds.setParent("..") | |
| cmds.setParent("..") | |
| cmds.showWindow(self.window) | |
| def get_active_camera(self): | |
| """Get the camera from the active viewport""" | |
| visible_panels = cmds.getPanel(visiblePanels=True) or [] | |
| for panel in visible_panels: | |
| if cmds.getPanel(typeOf=panel) == "modelPanel": | |
| cam = cmds.modelPanel(panel, query=True, camera=True) | |
| if cam: | |
| return cam | |
| return "persp" # Fallback | |
| def get_camera_clip_values(self, camera): | |
| """Get near and far clip values from the camera's shape node""" | |
| shapes = cmds.listRelatives(camera, shapes=True, fullPath=True) or [camera] | |
| cam_shape = shapes[0] | |
| near_clip = cmds.getAttr(f"{cam_shape}.nearClipPlane") | |
| far_clip = cmds.getAttr(f"{cam_shape}.farClipPlane") | |
| return near_clip, far_clip | |
| def change_unit(self, *args): | |
| """Update Maya's working unit preference and UI display""" | |
| self.current_unit = cmds.optionMenu(self.unit_menu, query=True, value=True) | |
| maya_unit = self.maya_units[self.current_unit] | |
| cmds.optionVar(stringValue=("linearUnit", maya_unit)) | |
| cmds.currentUnit(linear=maya_unit) | |
| cmds.text(self.current_unit_field, edit=True, label=f"{self.current_unit}") | |
| def update_grid(self, *args): | |
| """Update Maya's grid settings""" | |
| try: | |
| self.grid_size = cmds.floatField(self.size_field, query=True, value=True) | |
| self.grid_spacing = cmds.floatField(self.spacing_field, query=True, value=True) | |
| self.grid_subdivisions = cmds.intField(self.subdiv_field, query=True, value=True) | |
| cmds.grid(size=self.grid_size, | |
| spacing=self.grid_spacing, | |
| divisions=self.grid_subdivisions) | |
| except Exception as e: | |
| cmds.warning(f"Failed to update grid: {str(e)}") | |
| def create_distance(self, *args): | |
| """Create distance measurement with bright yellow locators using feet and inches in scene units""" | |
| try: | |
| feet = cmds.intField(self.feet_field, query=True, value=True) | |
| inches = cmds.intField(self.inches_field, query=True, value=True) | |
| total_feet = feet + (inches / 12.0) | |
| conversion_rates = { | |
| "Foot": 1.0, | |
| "Inch": 12.0, | |
| "Millimeter": 304.8, | |
| "Centimeter": 30.48, | |
| "Meter": 0.3048, | |
| "Yard": 0.333333 | |
| } | |
| current_maya_unit = self.maya_units[self.current_unit] | |
| distance_in_scene_units = total_feet * conversion_rates[self.current_unit] | |
| loc1 = cmds.spaceLocator(position=(0, 0, 0), name="distanceLoc1")[0] | |
| loc2 = cmds.spaceLocator(position=(0, distance_in_scene_units, 0), | |
| name="distanceLoc2")[0] | |
| for loc in [loc1, loc2]: | |
| shape = cmds.listRelatives(loc, shapes=True, fullPath=True)[0] | |
| cmds.setAttr(f"{shape}.overrideEnabled", 1) | |
| cmds.setAttr(f"{shape}.overrideRGBColors", 1) | |
| cmds.setAttr(f"{shape}.overrideColorRGB", 1.0, 1.0, 0.0) # Bright yellow | |
| distance_obj = cmds.distanceDimension(sp=(0, 0, 0), | |
| ep=(0, distance_in_scene_units, 0)) | |
| cmds.connectAttr(f"{loc1}.worldPosition[0]", f"{distance_obj}.startPoint") | |
| cmds.connectAttr(f"{loc2}.worldPosition[0]", f"{distance_obj}.endPoint") | |
| cmds.select(distance_obj) | |
| except Exception as e: | |
| cmds.warning(f"Failed to create distance: {str(e)}") | |
| def convert_distance(self, *args): | |
| """Convert feet and inches to selected unit and update label""" | |
| try: | |
| feet = cmds.intField(self.feet_field, query=True, value=True) | |
| inches = cmds.intField(self.inches_field, query=True, value=True) | |
| total_feet = feet + (inches / 12.0) | |
| conversion_rates = { | |
| "Foot": 1.0, | |
| "Inch": 12.0, | |
| "Millimeter": 304.8, | |
| "Centimeter": 30.48, | |
| "Meter": 0.3048, | |
| "Yard": 0.333333 | |
| } | |
| convert_to_unit = cmds.optionMenu(self.conversion_unit_menu, query=True, value=True) | |
| distance_in_target_unit = total_feet * conversion_rates[convert_to_unit] | |
| cmds.text(self.conversion_label, edit=True, | |
| label=f"= {distance_in_target_unit:.2f} {self.maya_units[convert_to_unit]}") | |
| except Exception as e: | |
| cmds.warning(f"Failed to convert distance: {str(e)}") | |
| def toggle_reference(self, *args): | |
| """Toggle reference file loading""" | |
| try: | |
| if not self.is_referenced: | |
| cmds.file( | |
| self.reference_path, | |
| reference=True, | |
| options="v=0;p=17;f=0", | |
| ignoreVersion=True | |
| ) | |
| self.is_referenced = True | |
| cmds.button(self.toggle_button, edit=True, label="Remove Reference") | |
| cmds.text(self.status_field, edit=True, label="Reference loaded") | |
| else: | |
| ref_node = cmds.referenceQuery(self.reference_path, referenceNode=True) | |
| if ref_node: | |
| cmds.file(self.reference_path, removeReference=True) | |
| self.is_referenced = False | |
| cmds.button(self.toggle_button, edit=True, label="Load Reference") | |
| cmds.text(self.status_field, edit=True, label="Reference removed") | |
| except Exception as e: | |
| cmds.warning(f"Failed to toggle reference: {str(e)}") | |
| def change_reference_path(self, *args): | |
| """Change the reference file path""" | |
| try: | |
| new_path = cmds.fileDialog2(fileMode=1, caption="Select Reference File", | |
| fileFilter="Maya Files (*.ma *.mb)") | |
| if new_path: | |
| self.reference_path = new_path[0] | |
| cmds.text(self.reference_path_field, edit=True, label=self.reference_path) | |
| if self.is_referenced: | |
| cmds.file(self.reference_path, removeReference=True, force=True) | |
| cmds.file(self.reference_path, reference=True, options="v=0;p=17;f=0", | |
| ignoreVersion=True) | |
| cmds.text(self.status_field, edit=True, label="Reference updated") | |
| print(f"Reference path updated to: {self.reference_path}") | |
| except Exception as e: | |
| cmds.warning(f"Failed to change reference path: {str(e)}") | |
| def change_joint_size(self, *args): | |
| """Change size of all joints in the scene""" | |
| try: | |
| new_size = cmds.floatFieldGrp(self.joint_size_field, query=True, value1=True) | |
| all_joints = cmds.ls(type="joint") | |
| if not all_joints: | |
| cmds.warning("No joints found in the scene!") | |
| return | |
| for joint in all_joints: | |
| cmds.setAttr(f"{joint}.radius", new_size) | |
| print(f"Changed {len(all_joints)} joints to size {new_size}") | |
| except Exception as e: | |
| cmds.warning(f"Failed to change joint size: {str(e)}") | |
| def freeze_geometry(self, *args): | |
| """Freeze transformations on all geometry objects only""" | |
| try: | |
| geometry = cmds.ls(type=["mesh", "nurbsSurface"], geometry=True) | |
| if not geometry: | |
| cmds.warning("No geometry found in scene!") | |
| return | |
| transforms = cmds.listRelatives(geometry, parent=True, fullPath=True) or [] | |
| transforms = list(set(transforms)) | |
| geo_transforms = [t for t in transforms if cmds.nodeType(t) == "transform"] | |
| if not geo_transforms: | |
| cmds.warning("No geometry transform nodes found!") | |
| return | |
| cmds.makeIdentity(geo_transforms, apply=True, translate=True, | |
| rotate=True, scale=True, normal=0) | |
| print(f"Froze transformations on {len(geo_transforms)} geometry objects") | |
| except Exception as e: | |
| cmds.warning(f"Failed to freeze geometry: {str(e)}") | |
| def freeze_selection(self, *args): | |
| """Freeze transformations on any selected transform objects""" | |
| try: | |
| selected = cmds.ls(selection=True, long=True) | |
| if not selected: | |
| cmds.warning("No objects selected!") | |
| return | |
| transforms = [obj for obj in selected if cmds.nodeType(obj) == "transform"] | |
| if not transforms: | |
| cmds.warning("No transform nodes selected to freeze!") | |
| return | |
| cmds.makeIdentity(transforms, apply=True, translate=True, | |
| rotate=True, scale=True, normal=0) | |
| print(f"Froze transformations on {len(transforms)} selected objects") | |
| except Exception as e: | |
| cmds.warning(f"Failed to freeze selection: {str(e)}") | |
| def reset_root_pivot(self, *args): | |
| """Move pivot of root-level groups to origin (0,0,0)""" | |
| try: | |
| root_nodes = cmds.ls(assemblies=True, long=True) | |
| if not root_nodes: | |
| cmds.warning("No root nodes found in scene!") | |
| return | |
| count = 0 | |
| for node in root_nodes: | |
| if cmds.nodeType(node) == "transform": | |
| cmds.xform(node, pivots=(0, 0, 0), worldSpace=True) | |
| count += 1 | |
| print(f"Reset pivots to origin for {count} root nodes") | |
| except Exception as e: | |
| cmds.warning(f"Failed to reset pivots: {str(e)}") | |
| def center_pivot(self, *args): | |
| """Center pivot for each geometry object individually""" | |
| try: | |
| geometry = cmds.ls(type=["mesh", "nurbsSurface"], geometry=True) | |
| if not geometry: | |
| cmds.warning("No geometry found in scene!") | |
| return | |
| transforms = cmds.listRelatives(geometry, parent=True, fullPath=True) or [] | |
| transforms = list(set(transforms)) | |
| geo_transforms = [t for t in transforms if cmds.nodeType(t) == "transform"] | |
| if not geo_transforms: | |
| cmds.warning("No geometry transform nodes found!") | |
| return | |
| count = 0 | |
| for transform in geo_transforms: | |
| cmds.xform(transform, centerPivots=True) | |
| count += 1 | |
| print(f"Centered pivots for {count} geometry objects") | |
| except Exception as e: | |
| cmds.warning(f"Failed to center pivots: {str(e)}") | |
| def unhide_all_nodes(self, *args): | |
| """Unhide all hidden nodes in the scene that have a visibility attribute""" | |
| try: | |
| all_nodes = cmds.ls(long=True) | |
| hidden_nodes = [node for node in all_nodes | |
| if cmds.attributeQuery("visibility", node=node, exists=True) | |
| and cmds.getAttr(f"{node}.visibility") == 0] | |
| if not hidden_nodes: | |
| print("No hidden nodes found in the scene.") | |
| return | |
| for node in hidden_nodes: | |
| cmds.setAttr(f"{node}.visibility", 1) | |
| print(f"Unhidden {len(hidden_nodes)} nodes in the scene.") | |
| except Exception as e: | |
| cmds.warning(f"Failed to unhide nodes: {str(e)}") | |
| def update_clip_planes(self, *args): | |
| """Update the near and far clip planes of the current camera""" | |
| try: | |
| current_camera = self.get_active_camera() | |
| near_clip = cmds.floatFieldGrp(self.near_clip_field, query=True, value1=True) | |
| far_clip = cmds.floatFieldGrp(self.far_clip_field, query=True, value1=True) | |
| if near_clip <= 0: | |
| cmds.warning("Near Clip Plane must be greater than 0!") | |
| return | |
| if far_clip <= near_clip: | |
| cmds.warning("Far Clip Plane must be greater than Near Clip Plane!") | |
| return | |
| shapes = cmds.listRelatives(current_camera, shapes=True, fullPath=True) or [current_camera] | |
| cam_shape = shapes[0] | |
| cmds.setAttr(f"{cam_shape}.nearClipPlane", near_clip) | |
| cmds.setAttr(f"{cam_shape}.farClipPlane", far_clip) | |
| print(f"Updated clip planes for {current_camera}: Near = {near_clip}, Far = {far_clip}") | |
| except Exception as e: | |
| cmds.warning(f"Failed to update clip planes: {str(e)}") | |
| def refresh_camera_info(self, *args): | |
| """Refresh the camera clip plane UI with current camera values""" | |
| current_camera = self.get_active_camera() | |
| near_clip, far_clip = self.get_camera_clip_values(current_camera) | |
| cmds.text(self.camera_frame, edit=True, label=f"Camera Clip Planes - {current_camera}") | |
| cmds.floatFieldGrp(self.near_clip_field, edit=True, value1=near_clip) | |
| cmds.floatFieldGrp(self.far_clip_field, edit=True, value1=far_clip) | |
| def apply_notes(self, *args): | |
| """Apply notes to root transform attributes""" | |
| try: | |
| notes = cmds.scrollField(self.notes_field, query=True, text=True) | |
| root_nodes = cmds.ls(assemblies=True, long=True) | |
| if not root_nodes: | |
| cmds.warning("No root nodes found to apply notes!") | |
| return | |
| count = 0 | |
| for node in root_nodes: | |
| if cmds.nodeType(node) == "transform": | |
| if not cmds.attributeQuery("notes", node=node, exists=True): | |
| cmds.addAttr(node, longName="notes", dataType="string") | |
| cmds.setAttr(f"{node}.notes", notes, type="string") | |
| count += 1 | |
| print(f"Applied notes to {count} root nodes") | |
| except Exception as e: | |
| cmds.warning(f"Failed to apply notes: {str(e)}") | |
| def save_file(self, *args): | |
| """Save the current file""" | |
| try: | |
| file_name = cmds.textField(self.file_name_field, query=True, text=True) | |
| if not file_name: | |
| cmds.warning("No file name specified!") | |
| return | |
| cmds.file(rename=file_name) | |
| cmds.file(save=True, type="mayaAscii") | |
| self.current_file = file_name | |
| cmds.textField(self.file_name_field, edit=True, text=self.current_file) | |
| print(f"Saved file as: {self.current_file}") | |
| except Exception as e: | |
| cmds.warning(f"Failed to save file: {str(e)}") | |
| def save_as_file(self, *args): | |
| """Save the file with a new name""" | |
| try: | |
| new_file = cmds.fileDialog2(fileMode=0, caption="Save As", | |
| fileFilter="Maya ASCII (*.ma)") | |
| if new_file: | |
| new_file_path = new_file[0] | |
| cmds.file(rename=new_file_path) | |
| cmds.file(save=True, type="mayaAscii") | |
| self.current_file = new_file_path | |
| cmds.textField(self.file_name_field, edit=True, text=self.current_file) | |
| print(f"Saved as: {self.current_file}") | |
| except Exception as e: | |
| cmds.warning(f"Failed to save as: {str(e)}") | |
| def open_file(self, *args): | |
| """Open a new scene file and update save location""" | |
| try: | |
| new_file = cmds.fileDialog2(fileMode=1, caption="Open File", | |
| fileFilter="Maya Files (*.ma *.mb)") | |
| if new_file: | |
| new_file_path = new_file[0] | |
| cmds.file(new_file_path, open=True, force=True) | |
| self.current_file = cmds.file(query=True, sceneName=True) or "Untitled.ma" | |
| cmds.textField(self.file_name_field, edit=True, text=self.current_file) | |
| print(f"Opened file: {self.current_file}") | |
| except Exception as e: | |
| cmds.warning(f"Failed to open file: {str(e)}") | |
| def main(): | |
| cmds.optionVar(stringValue=("linearUnit", "ft")) | |
| cmds.currentUnit(linear="ft") | |
| cmds.grid(size=12, spacing=5, divisions=5, reset=True) | |
| SceneUnitConverter() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment