diff --git a/2024/README.md b/2024/README.md deleted file mode 100644 index 06c65ed..0000000 --- a/2024/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Nexus Maya 2023 Plugin - -## 目录结构 - -- **shelves/** - 工具架文件 (.mel 格式) - - shelf_Nexus_Modeling.mel - 建模工具架 - - shelf_Nexus_Rigging.mel - 绑定工具架 - - shelf_Nexus_Animation.mel - 动画工具架 - -- **scripts/** - Python/MEL 脚本 - - userSetup.py - Maya 启动时自动执行 - - nexus_test.py - 测试脚本 - - modeling_tools/ - 建模工具包 - - rigging_tools/ - 绑定工具包 - - animation_tools/ - 动画工具包 - -- **plug-ins/** - Maya 插件文件 (.py 或 .mll) - - nexus_plugin.py - Nexus 插件 - -- **icons/** - 工具图标 - - 工具架按钮使用的图标文件 - -## 使用方法 - -1. 在 NexusLauncher 的 config.json 中配置: - ```json - "maya_plugin_path": "E:/Zoroot/Dev/NexusLauncher/template/plugins/Nexus" - ``` - -2. 启动 Maya 2023,系统将自动: - - 加载 Nexus 工具架(Nexus_Modeling, Nexus_Rigging, Nexus_Animation) - - 执行 userSetup.py - - 设置环境变量 - -3. 测试: - - 检查是否出现 Nexus 工具架 - - 点击测试按钮 - - 应出现确认对话框 - -## 环境变量 - -启动时自动设置: -- MAYA_SHELF_PATH - 指向 shelves 目录 -- MAYA_SCRIPT_PATH - 指向 scripts 目录 -- PYTHONPATH - 指向 scripts 目录 -- MAYA_PLUG_IN_PATH - 指向 plug-ins 目录 -- XBMLANGPATH - 指向 icons 目录 diff --git a/2024/RELOAD_SHELF.py b/2024/RELOAD_SHELF.py deleted file mode 100644 index 81677ec..0000000 --- a/2024/RELOAD_SHELF.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Shelf Reload Script -Use this script to reload Nexus shelves without restarting Maya -""" - -import maya.cmds as cmds -import maya.mel as mel -import os - -SHELF_NAMES = ["Nexus_Modeling", "Nexus_Rigging", "Nexus_Animation"] - -def reload_nexus_shelves(): - """Reload all Nexus shelves""" - print("=" * 60) - print("[Nexus] Reloading Nexus shelves...") - print("=" * 60) - - # Get shelf path - shelf_paths = os.environ.get('MAYA_SHELF_PATH', '') - - if not shelf_paths: - print("[Nexus] MAYA_SHELF_PATH not set") - try: - script_dir = os.path.dirname(os.path.abspath(__file__)) - shelf_paths = os.path.join(script_dir, "shelves") - print(f"[Nexus] Using script directory: {shelf_paths}") - except: - print("[Nexus] Could not determine shelf path") - return - - path_separator = ';' if os.name == 'nt' else ':' - shelf_path_list = shelf_paths.split(path_separator) - - for shelf_name in SHELF_NAMES: - # Find shelf file - shelf_file_found = None - for shelf_path in shelf_path_list: - shelf_path = shelf_path.strip() - if not shelf_path: - continue - - shelf_file = os.path.join(shelf_path, f"shelf_{shelf_name}.mel") - shelf_file = shelf_file.replace("\\", "/") - - if os.path.exists(shelf_file): - shelf_file_found = shelf_file - break - - if not shelf_file_found: - print(f"[Nexus] Could not find shelf_{shelf_name}.mel") - continue - - # Delete old shelf if exists - if cmds.shelfLayout(shelf_name, exists=True): - print(f"[Nexus] Deleting old shelf: {shelf_name}") - try: - cmds.deleteUI(shelf_name, layout=True) - except Exception as e: - print(f"[Nexus] Warning: Could not delete old shelf: {e}") - - # Load shelf using proper MEL method - print(f"[Nexus] Loading shelf: {shelf_name}") - try: - # Disable auto-save - mel.eval('optionVar -intValue "saveLastLoadedShelf" 0;') - - # Create shelf layout - mel.eval(f''' - global string $gShelfTopLevel; - if (`shelfLayout -exists {shelf_name}`) {{ - deleteUI -layout {shelf_name}; - }} - setParent $gShelfTopLevel; - shelfLayout -cellWidth 35 -cellHeight 34 {shelf_name}; - ''') - print(f"[Nexus] ✓ Created shelf layout: {shelf_name}") - - # Set parent and execute shelf script - mel.eval(f'setParent {shelf_name};') - mel.eval(f'source "{shelf_file_found}";') - mel.eval(f'shelf_{shelf_name}();') - print(f"[Nexus] ✓ Executed shelf script: shelf_{shelf_name}()") - - # Verify shelf - if cmds.shelfLayout(shelf_name, exists=True): - buttons = cmds.shelfLayout(shelf_name, query=True, childArray=True) or [] - if buttons: - print(f"[Nexus] ✓ Shelf loaded with {len(buttons)} button(s): {shelf_name}") - else: - print(f"[Nexus] ⚠ Shelf created but no buttons: {shelf_name}") - - # Remove auto-saved config - try: - maya_version = cmds.about(version=True).split()[0] - maya_app_dir = os.environ.get('MAYA_APP_DIR', '') - if maya_app_dir: - shelf_config = os.path.join(maya_app_dir, maya_version, "prefs", "shelves", f"shelf_{shelf_name}.mel") - if os.path.exists(shelf_config): - os.remove(shelf_config) - print(f"[Nexus] ✓ Removed auto-saved config: {shelf_name}") - except Exception as e: - print(f"[Nexus] Warning: {e}") - else: - print(f"[Nexus] ✗ Shelf layout not created: {shelf_name}") - - except Exception as e: - print(f"[Nexus] Error loading shelf {shelf_name}: {e}") - import traceback - traceback.print_exc() - continue - - print("=" * 60) - print("[Nexus] Shelf reload complete!") - print("=" * 60) - - -if __name__ == "__main__": - reload_nexus_shelves() diff --git a/2024/icons/README.md b/2024/icons/README.md deleted file mode 100644 index 438368a..0000000 --- a/2024/icons/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Nexus Icons Directory - -存放工具架按钮使用的图标文件。 - -## 图标格式 - -- 支持格式:PNG, XPM, BMP -- 推荐格式:PNG(支持透明) -- 推荐尺寸:32x32 或 64x64 像素 - -## 命名规范 - -- 使用小写字母和下划线 -- 例如:`modeling_tool.png`, `rigging_helper.png` - -## 使用方法 - -在 MEL 工具架文件中引用: -```mel --image "your_icon.png" --image1 "your_icon.png" -``` - -Maya 会自动在 XBMLANGPATH 环境变量指定的目录中查找图标文件。 diff --git a/2024/icons/batchextrusion.png b/2024/icons/batchextrusion.png deleted file mode 100644 index e74527d..0000000 Binary files a/2024/icons/batchextrusion.png and /dev/null differ diff --git a/2024/plug-ins/nexus_plugin.py b/2024/plug-ins/nexus_plugin.py deleted file mode 100644 index d85d6e6..0000000 --- a/2024/plug-ins/nexus_plugin.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Plugin -Maya Python API 2.0 plugin -""" - -import sys -import maya.api.OpenMaya as om - -def maya_useNewAPI(): - """Tell Maya to use Python API 2.0""" - pass - - -class NexusCmd(om.MPxCommand): - """Nexus command""" - - kPluginCmdName = "nexusCmd" - - def __init__(self): - om.MPxCommand.__init__(self) - - def doIt(self, args): - """Execute the command""" - print("[Nexus] Nexus Plugin command executed!") - om.MGlobal.displayInfo("Nexus Plugin is working!") - - -def cmdCreator(): - """Create command instance""" - return NexusCmd() - - -def initializePlugin(mobject): - """Initialize plugin""" - mplugin = om.MFnPlugin(mobject, "Nexus", "1.0", "Any") - try: - mplugin.registerCommand( - NexusCmd.kPluginCmdName, - cmdCreator - ) - print("[Nexus] Plugin initialized: nexus_plugin.py") - except: - sys.stderr.write(f"Failed to register command: {NexusCmd.kPluginCmdName}\n") - raise - - -def uninitializePlugin(mobject): - """Uninitialize plugin""" - mplugin = om.MFnPlugin(mobject) - try: - mplugin.deregisterCommand(NexusCmd.kPluginCmdName) - print("[Nexus] Plugin uninitialized: nexus_plugin.py") - except: - sys.stderr.write(f"Failed to deregister command: {NexusCmd.kPluginCmdName}\n") - raise diff --git a/2024/scripts/animation_tools/__init__.py b/2024/scripts/animation_tools/__init__.py deleted file mode 100644 index b553fc0..0000000 --- a/2024/scripts/animation_tools/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Animation Tools Package -Animation utilities and helpers -""" - -__all__ = [] diff --git a/2024/scripts/modeling_tools/__init__.py b/2024/scripts/modeling_tools/__init__.py deleted file mode 100644 index cb40630..0000000 --- a/2024/scripts/modeling_tools/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Modeling Tools Package -General modeling utilities -""" - -from .batchextrusion import show_batch_extrusion_ui - -__all__ = [ - 'show_batch_extrusion_ui' -] diff --git a/2024/scripts/modeling_tools/__pycache__/__init__.cpython-39.pyc b/2024/scripts/modeling_tools/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index dd05daf..0000000 Binary files a/2024/scripts/modeling_tools/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/2024/scripts/modeling_tools/__pycache__/batchextrusion.cpython-39.pyc b/2024/scripts/modeling_tools/__pycache__/batchextrusion.cpython-39.pyc deleted file mode 100644 index 1ad3e46..0000000 Binary files a/2024/scripts/modeling_tools/__pycache__/batchextrusion.cpython-39.pyc and /dev/null differ diff --git a/2024/scripts/modeling_tools/batchextrusion.py b/2024/scripts/modeling_tools/batchextrusion.py deleted file mode 100644 index 350c0c7..0000000 --- a/2024/scripts/modeling_tools/batchextrusion.py +++ /dev/null @@ -1,761 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# @Site : CGNICO Games -# @Author : Jeffrey Tsai - -""" -Maya Batch Extrusion Shell Mesh Tool - -Features: -1. Copy selected model to a specified number of layers -2. Extrude each layer, automatically deleting original layers to prevent overlap -3. Set vertex colors increasing from the inside out -4. Support model merging -""" - -import maya.cmds as cmds -import maya.mel as mel - -# UI Style configurations - -MESSAGE_BUTTON_STYLE = """ - QPushButton { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #D97706, stop:1 #B45309); - color: white; - border: 1px solid #92400E; - border-radius: 8px; - padding: 10px 16px; - font-weight: 600; - font-size: 12px; - min-width: 100px; - } - QPushButton:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #F59E0B, stop:1 #D97706); - border-color: #D97706; - } - QPushButton:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B45309, stop:1 #92400E); - border-color: #78350F; - } -""" - -SUCCESS_BUTTON_STYLE = """ - QPushButton { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #3B82F6, stop:1 #2563EB); - color: white; - border: none; - border-radius: 10px; - padding: 12px 20px; - font-weight: 600; - font-size: 13px; - min-width: 110px; - } - QPushButton:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #60A5FA, stop:1 #3B82F6); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); - } - QPushButton:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2563EB, stop:1 #1D4ED8); - transform: translateY(0px); - } -""" - -WARNING_BUTTON_STYLE = """ - QPushButton { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #F59E0B, stop:1 #D97706); - color: white; - border: none; - border-radius: 10px; - padding: 12px 20px; - font-weight: 600; - font-size: 13px; - min-width: 110px; - } - QPushButton:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FBBF24, stop:1 #F59E0B); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3); - } - QPushButton:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #D97706, stop:1 #B45309); - transform: translateY(0px); - } -""" - -class BatchExtrusion: - def __init__(self): - self.window_name = "BatchExtrusionWindow" - self.layers = 7 # Default number of layers - self.thickness = 0.1 # Default thickness - self.min_color = [0.0, 0.0, 0.0] # Inner layer color (black) - self.max_color = [1.0, 1.0, 1.0] # Outer layer color (white) - self.merge_layers = False # Whether to merge all layers - self.original_objects = [] # Original object list - self.generated_objects = [] # Generated object list - self.is_updating = False # Prevent recursive updates - self.button_info = [] # Store button information for delayed style application - self.qt_available = self.check_qt_availability() # Check Qt availability - - def check_qt_availability(self): - """Check Qt and related module availability""" - try: - maya_version = int(cmds.about(version=True)) - print(f"Maya version: {maya_version}") - - # Try PySide6 first (Maya 2022+) - try: - from PySide6 import QtWidgets, QtCore - import shiboken6 as shiboken - print("Using PySide6 (Maya 2022+)") - return "PySide6" - except ImportError: - try: - from PySide2 import QtWidgets, QtCore - import shiboken2 as shiboken - print("Using PySide2 (Maya 2020-2021)") - return "PySide2" - except ImportError: - print("PySide module is not available, using Maya native style") - return None - - except Exception as e: - print(f"Qt availability check failed: {str(e)}") - return None - - def create_ui(self): - """Create user interface""" - # If the window already exists, delete it - if cmds.window(self.window_name, exists=True): - cmds.deleteUI(self.window_name) - # Reset window preferences to avoid obscuring buttons due to old window dimensions - if cmds.windowPref(self.window_name, exists=True): - cmds.windowPref(self.window_name, remove=True) - - # Create window - cmds.window(self.window_name, title="Batch Extrusion Shell Mesh", widthHeight=(420, 600), sizeable=True) - - # Create main layout (using scroll layout to prevent small window obscuring bottom buttons) - scroll = cmds.scrollLayout(horizontalScrollBarThickness=0, verticalScrollBarThickness=12) - main_layout = cmds.columnLayout(adjustableColumn=True, rowSpacing=10, columnOffset=('both', 10)) - - # Title - cmds.text(label="Batch Extrusion Shell Mesh", font="boldLabelFont", height=30) - cmds.separator(height=10) - - # Number of layers control - cmds.text(label="Layers:", align="left", font="boldLabelFont") - self.layers_slider = cmds.intSliderGrp( - label="Layers: ", - field=True, - minValue=1, - maxValue=20, - value=self.layers, - step=1, - changeCommand=self.on_layers_changed - ) - - # Thickness control - cmds.text(label="Thickness:", align="left", font="boldLabelFont") - self.thickness_slider = cmds.floatSliderGrp( - label="Thickness: ", - field=True, - minValue=0.000001, - maxValue=10.0, - value=self.thickness, - precision=4, - step=0.01, - changeCommand=self.on_thickness_changed - ) - - # Vertex color control - cmds.text(label="Vertex Color:", align="left", font="boldLabelFont") - self.min_color_field = cmds.colorSliderGrp( - label="Inner layer: ", - rgb=self.min_color, - changeCommand=self.on_color_changed - ) - self.max_color_field = cmds.colorSliderGrp( - label="Outer layer: ", - rgb=self.max_color, - changeCommand=self.on_color_changed - ) - - # Merge options - cmds.text(label="Merge:", align="left", font="boldLabelFont") - self.merge_checkbox = cmds.checkBox( - label="Merge All Extruded Layers", - value=self.merge_layers, - changeCommand=self.on_merge_changed - ) - - # Preview area - cmds.text(label="Vertex Color Setting Preview:", align="left", font="boldLabelFont") - self.preview_text = cmds.scrollField( - editable=False, - wordWrap=True, - height=100, - backgroundColor=[0.2, 0.2, 0.2] - ) - - cmds.separator(height=10) - - # Operation buttons - self.create_styled_button("Extrude Shell Mesh", self.select_base_objects, SUCCESS_BUTTON_STYLE) - - self.create_styled_button("Clear All Extruded Layers", self.clear_all_layers, WARNING_BUTTON_STYLE) - - # Delay style application to ensure Qt controls are fully created - cmds.evalDeferred(self.apply_delayed_styles) - - # Show window - cmds.showWindow(self.window_name) - - # Initial preview - self.preview_colors() - - def create_styled_button(self, label, command, style): - """Create styled button""" - # Set base color based on style type - base_color = [0.4, 0.4, 0.4] # Default gray - if "SUCCESS" in style: - base_color = [0.2, 0.5, 0.8] # Blue - elif "WARNING" in style: - base_color = [0.8, 0.6, 0.2] # Orange - elif "MESSAGE" in style: - base_color = [0.7, 0.4, 0.1] # Deep orange - - # Create Maya button, set base color first - button_name = cmds.button( - label=label, - command=command, - height=35, - backgroundColor=base_color - ) - - # Store button info for delayed style application - self.button_info.append({ - 'name': button_name, - 'label': label, - 'style': style - }) - - # Immediately try to apply Qt style - self.apply_style_to_button(button_name, label, style) - - return button_name - - def apply_style_to_button(self, button_name, label, style): - """Apply style to a single button""" - # If Qt is not available, return False - if not self.qt_available: - print(f"Qt is not available, skipping style application: {label}") - return False - - try: - # Import corresponding modules based on detected Qt version - if self.qt_available == "PySide6": - from PySide6 import QtWidgets, QtCore - import shiboken6 as shiboken - elif self.qt_available == "PySide2": - from PySide2 import QtWidgets, QtCore - import shiboken2 as shiboken - else: - return False - - import maya.OpenMayaUI as omui - - # Get the Qt object of the button - button_ptr = omui.MQtUtil.findControl(button_name) - if button_ptr: - qt_button = shiboken.wrapInstance(int(button_ptr), QtWidgets.QPushButton) - if qt_button: - qt_button.setStyleSheet(style) - print(f"✓ Successfully applied Qt style: {label}") - return True - else: - print(f"✗ Failed to get Qt button object: {label}") - else: - print(f"✗ Failed to find button control: {label}") - - except Exception as e: - print(f"✗ Qt style application failed ({label}): {str(e)}") - - return False - - def apply_delayed_styles(self): - """Delay style application to all buttons""" - print("=== Starting delayed style application ===") - success_count = 0 - for button_info in self.button_info: - if cmds.control(button_info['name'], exists=True): - if self.apply_style_to_button( - button_info['name'], - button_info['label'], - button_info['style'] - ): - success_count += 1 - else: - print(f"✗ Button control does not exist: {button_info['label']}") - - print(f"=== Style application completed: {success_count}/{len(self.button_info)} successful ===") - - def show_dialog(self, title, message): - """Show styled confirmation dialog""" - dialog_name = "StyledConfirmDialog" - - # If the dialog already exists, delete it - if cmds.window(dialog_name, exists=True): - cmds.deleteUI(dialog_name) - - # Create dialog window - cmds.window(dialog_name, title=title, widthHeight=(300, 120), sizeable=False) - - # Main layout - main_layout = cmds.columnLayout(adjustableColumn=True, rowSpacing=10, columnOffset=('both', 15)) - - # Message text - cmds.text(label=message, align="center", wordWrap=True, height=40) - - cmds.separator(height=5) - - # Confirm button - self.create_styled_button("Yes", lambda *args: self.close_dialog(dialog_name), MESSAGE_BUTTON_STYLE) - - # Show dialog - cmds.showWindow(dialog_name) - - def close_dialog(self, dialog_name): - """Close dialog""" - if cmds.window(dialog_name, exists=True): - cmds.deleteUI(dialog_name) - - def calculate_vertex_colors(self, total_layers): - """Calculate vertex colors for each layer""" - if total_layers <= 1: - return [[1.0, 1.0, 1.0]] - - colors = [] - for i in range(total_layers): - # Calculate interpolation factor (0.0 to 1.0) - factor = i / (total_layers - 1) if total_layers > 1 else 0.0 - - # Interpolate between minimum and maximum colors - r = self.min_color[0] + (self.max_color[0] - self.min_color[0]) * factor - g = self.min_color[1] + (self.max_color[1] - self.min_color[1]) * factor - b = self.min_color[2] + (self.max_color[2] - self.min_color[2]) * factor - - colors.append([r, g, b]) - - return colors - - def on_layers_changed(self, *args): - """Callback function when the number of layers changes - Real-time update""" - if self.is_updating: - return - - self.layers = cmds.intSliderGrp(self.layers_slider, query=True, value=True) - self.preview_colors() - - # Update logic - if self.original_objects: # If there are base models - if self.merge_layers: - print("The models have been merged, please manually clean up and re-extrude!") - else: - print(f"Number of layers updated to {self.layers}, regenerating shell layers...") - # Clean up existing layers - self.clear_generated_objects() - # Regenerate layers - self.generate_layers() - - def on_thickness_changed(self, *args): - """Callback function when thickness changes - Real-time update""" - if self.is_updating: - return - - self.thickness = cmds.floatSliderGrp(self.thickness_slider, query=True, value=True) - self.preview_colors() - - # Update logic - if self.original_objects and self.generated_objects: # If there are base models and generated layers - if self.merge_layers: - print("The models have been merged, please manually clean up and re-extrude!") - else: - print(f"Thickness updated to {self.thickness}, updating thickness for all layers...") - self.update_thickness() - - def on_merge_changed(self, *args): - """Callback function when merge option changes""" - if self.is_updating: - return - self.merge_layers = cmds.checkBox(self.merge_checkbox, query=True, value=True) - self.preview_colors() - - def on_color_changed(self, *args): - """Callback function when colors change - Real-time update""" - if self.is_updating: - return - - self.min_color = cmds.colorSliderGrp(self.min_color_field, query=True, rgb=True) - self.max_color = cmds.colorSliderGrp(self.max_color_field, query=True, rgb=True) - self.preview_colors() - - # Update logic - if self.original_objects and self.generated_objects: # If there are base models and generated layers - if self.merge_layers: - print("The models have been merged, please manually clean up and re-extrude!") - else: - print("Colors updated, updating vertex colors for all layers...") - self.update_colors_for_all_layers() - - def preview_colors(self, *args): - """Preview color distribution""" - layers = self.layers - total_layers = layers + 1 # Include original layer - colors = self.calculate_vertex_colors(total_layers) - - preview_text = f"Colors preview ({total_layers} layers):\n\n" - - for i, color in enumerate(colors): - r, g, b = color - if i == 0: - layer_name = "Original layer" - else: - layer_name = f"Layer {i}" - - preview_text += f"{layer_name}: RGB({r:.2f}, {g:.2f}, {b:.2f})\n" - - cmds.scrollField(self.preview_text, edit=True, text=preview_text) - - def select_base_objects(self, *args): - """Select base objects and start extrusion""" - selected = cmds.ls(selection=True, type='transform') - if not selected: - cmds.warning("Please select the models to process first") - return - - # Clean up existing layers - self.clear_generated_objects() - - # Save original objects - self.original_objects = selected[:] - - # Generate layers immediately - self.generate_layers() - - self.show_dialog("Completed", f"Processed {len(selected)} objects, generated {self.layers} shell layers") - - def clear_all_layers(self, *args): - """Clear all generated layers""" - # Check if merge option is checked - if self.merge_layers: - print("The models have been merged, please manually clean up and re-extrude!") - return - self.clear_generated_objects() - self.show_dialog("Completed", "All generated layers have been cleared") - - def clear_generated_objects(self): - """Clear generated objects""" - for obj in self.generated_objects: - if cmds.objExists(obj): - cmds.delete(obj) - self.generated_objects = [] - - def generate_layers(self): - """Generate shell layers""" - if not self.original_objects: - print("Error: No original objects selected") - return - - try: - print(f"=== Starting generation of {self.layers} shell layers ===") - - created_objects = [] - - for obj_index, original_obj in enumerate(self.original_objects): - print(f"Processing object {obj_index + 1}: {original_obj}") - - # Collect all layer objects (including original object) - all_layers = [original_obj] - - # Create layers one by one (always from the original object) - for layer_num in range(1, self.layers + 1): - print(f" Creating layer {layer_num}...") - - # Key: Always copy from the original object, not from the previous layer - new_layer = self.extrude_layer_correct(original_obj, layer_num, self.thickness) - if new_layer: - all_layers.append(new_layer) - created_objects.append(new_layer) - print(f" ✓ Layer {layer_num} completed") - else: - print(f" ✗ Layer {layer_num} failed, stopping") - break - - # Set vertex colors - print(f" Setting vertex colors for {len(all_layers)} layers...") - self.apply_colors(all_layers) - - # Save results - self.generated_objects = created_objects - print(f"=== Completed! Created {len(created_objects)} layers ===") - - # Check if merge option is checked - if self.merge_layers and len(created_objects) > 0: - print("=== Starting simple merge ===") - self.simple_merge_objects() - else: - print("=== Skipping merge (not checked or no objects) ===") - - except Exception as e: - print(f"Generation failed: {str(e)}") - # Cleanup - for obj in created_objects: - if cmds.objExists(obj): - try: - cmds.delete(obj) - except: - pass - - def simple_merge_objects(self): - """Simple merge method to avoid complex operations""" - try: - all_objects = self.original_objects + self.generated_objects - print(f"Simple merge {len(all_objects)} objects") - - if len(all_objects) > 1: - # Only do basic merge - existing_objects = [obj for obj in all_objects if cmds.objExists(obj)] - - if len(existing_objects) > 1: - cmds.select(existing_objects, replace=True) - merged = cmds.polyUnite(existing_objects, name="BatchExtruded_Simple")[0] - print(f"Simple merge completed: {merged}") - - # Update object list - self.original_objects = [merged] - self.generated_objects = [] - else: - print("Not enough objects to merge") - else: - print("Not enough objects to merge") - - except Exception as e: - print(f"Simple merge failed: {str(e)}") - - def extrude_layer_correct(self, obj, layer_index, thickness): - """Follow the correct Maya workflow: duplicate -> extrude -> delete by inverse selection""" - duplicated = None - try: - print(f" === Correct workflow for layer {layer_index} ===") - - # 1. Verify object - if not cmds.objExists(obj): - print(f" Error: Object {obj} does not exist") - return None - - # 2. Copy model - duplicated = cmds.duplicate(obj, name=f"Layer_{layer_index}")[0] - print(f" Copy completed: {duplicated}") - - # 3. Switch to face mode and select all faces - print(f" Switching to face mode and selecting all faces...") - - # Switch to component mode - cmds.selectMode(component=True) - cmds.selectType(facet=True) - - # Select all faces directly (more reliable method) - try: - cmds.select(f"{duplicated}.f[*]", replace=True) - selected_faces = cmds.ls(selection=True, flatten=True) - print(f" Selected faces count: {len(selected_faces)}") - - if len(selected_faces) == 0: - # Backup method 1: Select object first then convert - print(f" Direct selection failed, trying conversion method...") - cmds.selectMode(object=True) - cmds.select(duplicated, replace=True) - cmds.selectMode(component=True) - cmds.selectType(facet=True) - - # Use mel command to convert - try: - mel.eval("ConvertSelectionToFaces;") - selected_faces = cmds.ls(selection=True, flatten=True) - print(f" Selected faces count: {len(selected_faces)}") - except Exception as mel_error: - print(f" MEL conversion failed: {str(mel_error)}") - - # Backup method 2: Use polyEvaluate to get face count, then select - try: - face_count = cmds.polyEvaluate(duplicated, face=True) - if face_count > 0: - face_range = f"{duplicated}.f[0:{face_count-1}]" - cmds.select(face_range, replace=True) - selected_faces = cmds.ls(selection=True, flatten=True) - print(f" Selected faces count: {len(selected_faces)}") - except Exception as backup_error: - print(f" Backup method also failed: {str(backup_error)}") - - except Exception as select_error: - print(f" Face selection failed: {str(select_error)}") - raise select_error - - # Verify face selection success - if len(selected_faces) == 0: - print(f" Error: No faces selected") - return None - - print(f" Selected {len(selected_faces)} faces, starting extrusion...") - - # Execute extrusion (thickness=0) - extrude_result = cmds.polyExtrudeFacet( - constructionHistory=True, - keepFacesTogether=True, - divisions=1, - twist=0, - taper=1, - off=0, - thickness=0, - smoothingAngle=30 - ) - print(f" Extrusion command completed: {extrude_result}") - - # Set cumulative thickness (each layer's thickness is cumulative) - if extrude_result: - extrude_node = extrude_result[0] - # Cumulative thickness = base thickness * layer number - cumulative_thickness = thickness * layer_index - cmds.setAttr(f"{extrude_node}.thickness", cumulative_thickness) - print(f" Set cumulative thickness: {cumulative_thickness} (Layer {layer_index})") - - # 4. Invert selection and delete internal faces - print(f" Inverting selection and deleting internal faces...") - - # After extrusion, the original faces are still selected - # We need to keep these original faces and delete the newly generated faces - original_selected_faces = cmds.ls(selection=True, flatten=True) - print(f" Original selected faces count: {len(original_selected_faces)}") - - # Get all faces after extrusion - all_faces_after_extrude = cmds.ls(f"{duplicated}.f[*]", flatten=True) - print(f" Total faces after extrusion: {len(all_faces_after_extrude)}") - - # Calculate newly generated faces (faces to delete) - if len(all_faces_after_extrude) > len(original_selected_faces): - # New faces = All faces - Original faces - new_faces = [face for face in all_faces_after_extrude if face not in original_selected_faces] - - if new_faces: - print(f" Deleting {len(new_faces)} newly generated faces") - cmds.select(new_faces, replace=True) - cmds.delete() - print(f" Deleted successfully, keeping original faces as shell") - else: - print(f" No newly generated faces found") - else: - print(f" No faces added, skipping deletion") - - # 5. Return to object mode - cmds.selectMode(object=True) - cmds.select(clear=True) - - print(f" === Layer {layer_index} completed ===") - return duplicated - - except Exception as e: - print(f" Layer {layer_index} failed: {str(e)}") - if duplicated and cmds.objExists(duplicated): - try: - cmds.delete(duplicated) - except: - pass - return None - - def apply_colors(self, layer_objects): - """Simple vertex color application""" - try: - total_layers = len(layer_objects) - colors = self.calculate_vertex_colors(total_layers) - - print(f" Setting colors for {total_layers} layers...") - - for i, obj in enumerate(layer_objects): - if cmds.objExists(obj) and i < len(colors): - color = colors[i] - print(f" Layer {i}: {obj} -> RGB{color}") - - # Set vertex colors - try: - # Select all vertices - cmds.select(f"{obj}.vtx[*]", replace=True) - - # Apply color - cmds.polyColorPerVertex(rgb=color, colorDisplayOption=True) - - # Enable vertex color display - cmds.setAttr(f"{obj}.displayColors", 1) - - except Exception as color_error: - print(f" Setting vertex colors failed: {str(color_error)}") - - # Clear selection - cmds.select(clear=True) - print(f" Vertex colors set successfully") - except Exception as e: - print(f" Vertex color application failed: {str(e)}") - - def update_thickness(self): - """Update thickness for all layers""" - try: - print(f"=== Updating thickness to {self.thickness} ===") - - for layer_index, obj in enumerate(self.generated_objects, 1): - if cmds.objExists(obj): - print(f" Updating layer {layer_index}: {obj}") - - # Find extrude node - history = cmds.listHistory(obj, pruneDagObjects=True) - extrude_nodes = [node for node in history if cmds.nodeType(node) == 'polyExtrudeFace'] - - if extrude_nodes: - extrude_node = extrude_nodes[0] # Take the latest extrude node - # Set cumulative thickness - cumulative_thickness = self.thickness * layer_index - cmds.setAttr(f"{extrude_node}.thickness", cumulative_thickness) - print(f" ✓ Updated thickness: {cumulative_thickness}") - else: - print(f" ✗ No extrude node found") - - print(f"=== Thickness updated successfully ===") - - except Exception as e: - print(f"Thickness update failed: {str(e)}") - - def update_colors_for_all_layers(self): - """Update colors for all layers""" - try: - print(f"=== Updating colors for all layers ===") - - # Collect all layer objects (including original objects) - all_layers = self.original_objects + self.generated_objects - - # Reapply colors - self.apply_colors(all_layers) - - print(f"=== Colors updated successfully ===") - - except Exception as e: - print(f"Colors update failed: {str(e)}") - -# Create and display UI function -def show_batch_extrusion_ui(): - """Show batch extrusion UI""" - batch_extrusion = BatchExtrusion() - batch_extrusion.create_ui() - -# If run this script directly -if __name__ == "__main__": - show_batch_extrusion_ui() diff --git a/2024/scripts/nexus_test.py b/2024/scripts/nexus_test.py deleted file mode 100644 index 8a8eb10..0000000 --- a/2024/scripts/nexus_test.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Test Script -Simple test to verify the Nexus plugin system is working -""" - -import maya.cmds as cmds - - -def run_test(): - """Run a simple test""" - print("[Nexus Test] Running test...") - - # Show confirmation dialog - result = cmds.confirmDialog( - title='Nexus Test', - message='Nexus Plugin System is working correctly!', - button=['OK'], - defaultButton='OK', - cancelButton='OK', - dismissString='OK' - ) - - print(f"[Nexus Test] Test completed: {result}") - return result - - -if __name__ == "__main__": - run_test() diff --git a/2024/scripts/rigging_tools/__init__.py b/2024/scripts/rigging_tools/__init__.py deleted file mode 100644 index a987049..0000000 --- a/2024/scripts/rigging_tools/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Rigging Tools Package -Character rigging utilities -""" - -__all__ = [] diff --git a/2024/scripts/userSetup.py b/2024/scripts/userSetup.py deleted file mode 100644 index 5d41610..0000000 --- a/2024/scripts/userSetup.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Maya 2023 - User Setup Script -Automatically executed when Maya starts -""" - -import maya.cmds as cmds -import maya.mel as mel -import maya.utils -import os -import sys - -SHELF_NAMES = ["Nexus_Modeling", "Nexus_Rigging", "Nexus_Animation"] - -def load_nexus_shelves(): - """Load all Nexus shelves (force refresh)""" - try: - shelf_paths = os.environ.get('MAYA_SHELF_PATH', '') - - if not shelf_paths: - print("[Nexus] MAYA_SHELF_PATH not set, trying alternative method...") - try: - script_dir = os.path.dirname(os.path.abspath(__file__)) - shelf_paths = os.path.join(os.path.dirname(script_dir), "shelves") - except: - print("[Nexus] Could not determine shelf path, skipping shelf load") - return - - path_separator = ';' if os.name == 'nt' else ':' - shelf_path_list = shelf_paths.split(path_separator) - - for shelf_name in SHELF_NAMES: - shelf_file_found = None - for shelf_path in shelf_path_list: - shelf_path = shelf_path.strip() - if not shelf_path: - continue - - shelf_file = os.path.join(shelf_path, f"shelf_{shelf_name}.mel") - shelf_file = shelf_file.replace("\\", "/") - - if os.path.exists(shelf_file): - shelf_file_found = shelf_file - print(f"[Nexus] Found shelf file: {shelf_file}") - break - - if not shelf_file_found: - print(f"[Nexus] Could not find shelf_{shelf_name}.mel") - continue - - # Delete old shelf if exists - if cmds.shelfLayout(shelf_name, exists=True): - print(f"[Nexus] Deleting old shelf: {shelf_name}") - try: - cmds.deleteUI(shelf_name, layout=True) - except Exception as e: - print(f"[Nexus] Warning: Could not delete old shelf: {e}") - - # Load shelf using proper MEL method - print(f"[Nexus] Loading shelf: {shelf_name}") - try: - # Disable auto-save - mel.eval('optionVar -intValue "saveLastLoadedShelf" 0;') - - # Create shelf layout - mel.eval(f''' - global string $gShelfTopLevel; - if (`shelfLayout -exists {shelf_name}`) {{ - deleteUI -layout {shelf_name}; - }} - setParent $gShelfTopLevel; - shelfLayout -cellWidth 35 -cellHeight 34 {shelf_name}; - ''') - print(f"[Nexus] ✓ Created shelf layout: {shelf_name}") - - # Set parent and execute shelf script - mel.eval(f'setParent {shelf_name};') - mel.eval(f'source "{shelf_file_found}";') - mel.eval(f'shelf_{shelf_name}();') - print(f"[Nexus] ✓ Executed shelf script: shelf_{shelf_name}()") - - # Verify shelf - if cmds.shelfLayout(shelf_name, exists=True): - buttons = cmds.shelfLayout(shelf_name, query=True, childArray=True) or [] - if buttons: - print(f"[Nexus] ✓ Shelf loaded with {len(buttons)} button(s): {shelf_name}") - else: - print(f"[Nexus] ⚠ Shelf created but no buttons: {shelf_name}") - - # Remove auto-saved config - try: - maya_version = cmds.about(version=True).split()[0] - maya_app_dir = os.environ.get('MAYA_APP_DIR', '') - if maya_app_dir: - shelf_config = os.path.join(maya_app_dir, maya_version, "prefs", "shelves", f"shelf_{shelf_name}.mel") - if os.path.exists(shelf_config): - os.remove(shelf_config) - print(f"[Nexus] ✓ Removed auto-saved config: {shelf_name}") - except Exception as e: - print(f"[Nexus] Warning: {e}") - else: - print(f"[Nexus] ✗ Shelf layout not created: {shelf_name}") - - except Exception as e: - print(f"[Nexus] Error loading shelf {shelf_name}: {e}") - import traceback - traceback.print_exc() - continue - - except Exception as e: - print(f"[Nexus] Error in load_nexus_shelves: {e}") - import traceback - traceback.print_exc() - - -def load_nexus_plugins(): - """Load Nexus plugins""" - try: - plugin_paths = os.environ.get('MAYA_PLUG_IN_PATH', '') - - if not plugin_paths: - print("[Nexus] MAYA_PLUG_IN_PATH not set") - return - - path_separator = ';' if os.name == 'nt' else ':' - plugin_path_list = plugin_paths.split(path_separator) - - plugins_to_load = ["nexus_plugin.py"] - - for plugin_name in plugins_to_load: - plugin_found = False - for plugin_path in plugin_path_list: - plugin_path = plugin_path.strip() - if not plugin_path: - continue - - plugin_file = os.path.join(plugin_path, plugin_name) - if os.path.exists(plugin_file): - plugin_found = True - break - - if not plugin_found: - print(f"[Nexus] Plugin not found: {plugin_name}") - continue - - # Load plugin - try: - if not cmds.pluginInfo(plugin_name, query=True, loaded=True): - cmds.loadPlugin(plugin_name) - print(f"[Nexus] ✓ Plugin loaded: {plugin_name}") - else: - print(f"[Nexus] Plugin already loaded: {plugin_name}") - except Exception as e: - print(f"[Nexus] Error loading plugin {plugin_name}: {e}") - - except Exception as e: - print(f"[Nexus] Error in load_nexus_plugins: {e}") - import traceback - traceback.print_exc() - - -# Deferred execution -def initialize_nexus(): - """Initialize Nexus plugin system""" - print("=" * 80) - print("[Nexus] Initializing Nexus Maya 2023 Plugin System...") - print("=" * 80) - - load_nexus_shelves() - load_nexus_plugins() - - print("=" * 80) - print("[Nexus] Nexus Plugin System Initialized!") - print("=" * 80) - - -# Execute after Maya is fully loaded -maya.utils.executeDeferred(initialize_nexus) - - -# Cleanup on exit -def cleanup_on_exit(): - """Cleanup when Maya exits""" - print("[Nexus] Cleaning up...") - - # Unload plugins - plugins_to_unload = ["nexus_plugin.py"] - for plugin_name in plugins_to_unload: - if cmds.pluginInfo(plugin_name, query=True, loaded=True): - try: - cmds.unloadPlugin(plugin_name) - print(f"[Nexus] Plugin unloaded: {plugin_name}") - except: - pass - - -import atexit -atexit.register(cleanup_on_exit) diff --git a/2024/shelves/shelf_Nexus_Animation.mel b/2024/shelves/shelf_Nexus_Animation.mel deleted file mode 100644 index a7ff9a1..0000000 --- a/2024/shelves/shelf_Nexus_Animation.mel +++ /dev/null @@ -1,43 +0,0 @@ -global proc shelf_Nexus_Animation () { - global string $gBuffStr; - global string $gBuffStr0; - global string $gBuffStr1; - - - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Nexus Test - Verify plugin system" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "Test" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -imageOverlayLabel "Test" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "commandButton.png" - -image1 "commandButton.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "import nexus_test\nnexus_test.run_test()" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - -} diff --git a/2024/shelves/shelf_Nexus_Modeling.mel b/2024/shelves/shelf_Nexus_Modeling.mel deleted file mode 100644 index 590ff7f..0000000 --- a/2024/shelves/shelf_Nexus_Modeling.mel +++ /dev/null @@ -1,43 +0,0 @@ -global proc shelf_Nexus_Modeling () { - global string $gBuffStr; - global string $gBuffStr0; - global string $gBuffStr1; - - - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Batch Extrusion - Shell mesh tool with vertex colors" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "BatchExt" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -imageOverlayLabel "Extrude" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "batchextrusion.png" - -image1 "batchextrusion.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "import modeling_tools\nmodeling_tools.show_batch_extrusion_ui()" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - -} diff --git a/2024/shelves/shelf_Nexus_Rigging.mel b/2024/shelves/shelf_Nexus_Rigging.mel deleted file mode 100644 index 778a59f..0000000 --- a/2024/shelves/shelf_Nexus_Rigging.mel +++ /dev/null @@ -1,43 +0,0 @@ -global proc shelf_Nexus_Rigging () { - global string $gBuffStr; - global string $gBuffStr0; - global string $gBuffStr1; - - - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Nexus Test - Verify plugin system" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "Test" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -imageOverlayLabel "Test" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "commandButton.png" - -image1 "commandButton.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "import nexus_test\nnexus_test.run_test()" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - -} diff --git a/2025/README.md b/2025/README.md deleted file mode 100644 index 06c65ed..0000000 --- a/2025/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Nexus Maya 2023 Plugin - -## 目录结构 - -- **shelves/** - 工具架文件 (.mel 格式) - - shelf_Nexus_Modeling.mel - 建模工具架 - - shelf_Nexus_Rigging.mel - 绑定工具架 - - shelf_Nexus_Animation.mel - 动画工具架 - -- **scripts/** - Python/MEL 脚本 - - userSetup.py - Maya 启动时自动执行 - - nexus_test.py - 测试脚本 - - modeling_tools/ - 建模工具包 - - rigging_tools/ - 绑定工具包 - - animation_tools/ - 动画工具包 - -- **plug-ins/** - Maya 插件文件 (.py 或 .mll) - - nexus_plugin.py - Nexus 插件 - -- **icons/** - 工具图标 - - 工具架按钮使用的图标文件 - -## 使用方法 - -1. 在 NexusLauncher 的 config.json 中配置: - ```json - "maya_plugin_path": "E:/Zoroot/Dev/NexusLauncher/template/plugins/Nexus" - ``` - -2. 启动 Maya 2023,系统将自动: - - 加载 Nexus 工具架(Nexus_Modeling, Nexus_Rigging, Nexus_Animation) - - 执行 userSetup.py - - 设置环境变量 - -3. 测试: - - 检查是否出现 Nexus 工具架 - - 点击测试按钮 - - 应出现确认对话框 - -## 环境变量 - -启动时自动设置: -- MAYA_SHELF_PATH - 指向 shelves 目录 -- MAYA_SCRIPT_PATH - 指向 scripts 目录 -- PYTHONPATH - 指向 scripts 目录 -- MAYA_PLUG_IN_PATH - 指向 plug-ins 目录 -- XBMLANGPATH - 指向 icons 目录 diff --git a/2025/RELOAD_SHELF.py b/2025/RELOAD_SHELF.py deleted file mode 100644 index 81677ec..0000000 --- a/2025/RELOAD_SHELF.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Shelf Reload Script -Use this script to reload Nexus shelves without restarting Maya -""" - -import maya.cmds as cmds -import maya.mel as mel -import os - -SHELF_NAMES = ["Nexus_Modeling", "Nexus_Rigging", "Nexus_Animation"] - -def reload_nexus_shelves(): - """Reload all Nexus shelves""" - print("=" * 60) - print("[Nexus] Reloading Nexus shelves...") - print("=" * 60) - - # Get shelf path - shelf_paths = os.environ.get('MAYA_SHELF_PATH', '') - - if not shelf_paths: - print("[Nexus] MAYA_SHELF_PATH not set") - try: - script_dir = os.path.dirname(os.path.abspath(__file__)) - shelf_paths = os.path.join(script_dir, "shelves") - print(f"[Nexus] Using script directory: {shelf_paths}") - except: - print("[Nexus] Could not determine shelf path") - return - - path_separator = ';' if os.name == 'nt' else ':' - shelf_path_list = shelf_paths.split(path_separator) - - for shelf_name in SHELF_NAMES: - # Find shelf file - shelf_file_found = None - for shelf_path in shelf_path_list: - shelf_path = shelf_path.strip() - if not shelf_path: - continue - - shelf_file = os.path.join(shelf_path, f"shelf_{shelf_name}.mel") - shelf_file = shelf_file.replace("\\", "/") - - if os.path.exists(shelf_file): - shelf_file_found = shelf_file - break - - if not shelf_file_found: - print(f"[Nexus] Could not find shelf_{shelf_name}.mel") - continue - - # Delete old shelf if exists - if cmds.shelfLayout(shelf_name, exists=True): - print(f"[Nexus] Deleting old shelf: {shelf_name}") - try: - cmds.deleteUI(shelf_name, layout=True) - except Exception as e: - print(f"[Nexus] Warning: Could not delete old shelf: {e}") - - # Load shelf using proper MEL method - print(f"[Nexus] Loading shelf: {shelf_name}") - try: - # Disable auto-save - mel.eval('optionVar -intValue "saveLastLoadedShelf" 0;') - - # Create shelf layout - mel.eval(f''' - global string $gShelfTopLevel; - if (`shelfLayout -exists {shelf_name}`) {{ - deleteUI -layout {shelf_name}; - }} - setParent $gShelfTopLevel; - shelfLayout -cellWidth 35 -cellHeight 34 {shelf_name}; - ''') - print(f"[Nexus] ✓ Created shelf layout: {shelf_name}") - - # Set parent and execute shelf script - mel.eval(f'setParent {shelf_name};') - mel.eval(f'source "{shelf_file_found}";') - mel.eval(f'shelf_{shelf_name}();') - print(f"[Nexus] ✓ Executed shelf script: shelf_{shelf_name}()") - - # Verify shelf - if cmds.shelfLayout(shelf_name, exists=True): - buttons = cmds.shelfLayout(shelf_name, query=True, childArray=True) or [] - if buttons: - print(f"[Nexus] ✓ Shelf loaded with {len(buttons)} button(s): {shelf_name}") - else: - print(f"[Nexus] ⚠ Shelf created but no buttons: {shelf_name}") - - # Remove auto-saved config - try: - maya_version = cmds.about(version=True).split()[0] - maya_app_dir = os.environ.get('MAYA_APP_DIR', '') - if maya_app_dir: - shelf_config = os.path.join(maya_app_dir, maya_version, "prefs", "shelves", f"shelf_{shelf_name}.mel") - if os.path.exists(shelf_config): - os.remove(shelf_config) - print(f"[Nexus] ✓ Removed auto-saved config: {shelf_name}") - except Exception as e: - print(f"[Nexus] Warning: {e}") - else: - print(f"[Nexus] ✗ Shelf layout not created: {shelf_name}") - - except Exception as e: - print(f"[Nexus] Error loading shelf {shelf_name}: {e}") - import traceback - traceback.print_exc() - continue - - print("=" * 60) - print("[Nexus] Shelf reload complete!") - print("=" * 60) - - -if __name__ == "__main__": - reload_nexus_shelves() diff --git a/2025/icons/README.md b/2025/icons/README.md deleted file mode 100644 index 438368a..0000000 --- a/2025/icons/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Nexus Icons Directory - -存放工具架按钮使用的图标文件。 - -## 图标格式 - -- 支持格式:PNG, XPM, BMP -- 推荐格式:PNG(支持透明) -- 推荐尺寸:32x32 或 64x64 像素 - -## 命名规范 - -- 使用小写字母和下划线 -- 例如:`modeling_tool.png`, `rigging_helper.png` - -## 使用方法 - -在 MEL 工具架文件中引用: -```mel --image "your_icon.png" --image1 "your_icon.png" -``` - -Maya 会自动在 XBMLANGPATH 环境变量指定的目录中查找图标文件。 diff --git a/2025/icons/batchextrusion.png b/2025/icons/batchextrusion.png deleted file mode 100644 index e74527d..0000000 Binary files a/2025/icons/batchextrusion.png and /dev/null differ diff --git a/2025/plug-ins/nexus_plugin.py b/2025/plug-ins/nexus_plugin.py deleted file mode 100644 index d85d6e6..0000000 --- a/2025/plug-ins/nexus_plugin.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Plugin -Maya Python API 2.0 plugin -""" - -import sys -import maya.api.OpenMaya as om - -def maya_useNewAPI(): - """Tell Maya to use Python API 2.0""" - pass - - -class NexusCmd(om.MPxCommand): - """Nexus command""" - - kPluginCmdName = "nexusCmd" - - def __init__(self): - om.MPxCommand.__init__(self) - - def doIt(self, args): - """Execute the command""" - print("[Nexus] Nexus Plugin command executed!") - om.MGlobal.displayInfo("Nexus Plugin is working!") - - -def cmdCreator(): - """Create command instance""" - return NexusCmd() - - -def initializePlugin(mobject): - """Initialize plugin""" - mplugin = om.MFnPlugin(mobject, "Nexus", "1.0", "Any") - try: - mplugin.registerCommand( - NexusCmd.kPluginCmdName, - cmdCreator - ) - print("[Nexus] Plugin initialized: nexus_plugin.py") - except: - sys.stderr.write(f"Failed to register command: {NexusCmd.kPluginCmdName}\n") - raise - - -def uninitializePlugin(mobject): - """Uninitialize plugin""" - mplugin = om.MFnPlugin(mobject) - try: - mplugin.deregisterCommand(NexusCmd.kPluginCmdName) - print("[Nexus] Plugin uninitialized: nexus_plugin.py") - except: - sys.stderr.write(f"Failed to deregister command: {NexusCmd.kPluginCmdName}\n") - raise diff --git a/2025/scripts/animation_tools/__init__.py b/2025/scripts/animation_tools/__init__.py deleted file mode 100644 index b553fc0..0000000 --- a/2025/scripts/animation_tools/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Animation Tools Package -Animation utilities and helpers -""" - -__all__ = [] diff --git a/2025/scripts/modeling_tools/__init__.py b/2025/scripts/modeling_tools/__init__.py deleted file mode 100644 index cb40630..0000000 --- a/2025/scripts/modeling_tools/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Modeling Tools Package -General modeling utilities -""" - -from .batchextrusion import show_batch_extrusion_ui - -__all__ = [ - 'show_batch_extrusion_ui' -] diff --git a/2025/scripts/modeling_tools/__pycache__/__init__.cpython-39.pyc b/2025/scripts/modeling_tools/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index dd05daf..0000000 Binary files a/2025/scripts/modeling_tools/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/2025/scripts/modeling_tools/__pycache__/batchextrusion.cpython-39.pyc b/2025/scripts/modeling_tools/__pycache__/batchextrusion.cpython-39.pyc deleted file mode 100644 index 1ad3e46..0000000 Binary files a/2025/scripts/modeling_tools/__pycache__/batchextrusion.cpython-39.pyc and /dev/null differ diff --git a/2025/scripts/modeling_tools/batchextrusion.py b/2025/scripts/modeling_tools/batchextrusion.py deleted file mode 100644 index 350c0c7..0000000 --- a/2025/scripts/modeling_tools/batchextrusion.py +++ /dev/null @@ -1,761 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# @Site : CGNICO Games -# @Author : Jeffrey Tsai - -""" -Maya Batch Extrusion Shell Mesh Tool - -Features: -1. Copy selected model to a specified number of layers -2. Extrude each layer, automatically deleting original layers to prevent overlap -3. Set vertex colors increasing from the inside out -4. Support model merging -""" - -import maya.cmds as cmds -import maya.mel as mel - -# UI Style configurations - -MESSAGE_BUTTON_STYLE = """ - QPushButton { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #D97706, stop:1 #B45309); - color: white; - border: 1px solid #92400E; - border-radius: 8px; - padding: 10px 16px; - font-weight: 600; - font-size: 12px; - min-width: 100px; - } - QPushButton:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #F59E0B, stop:1 #D97706); - border-color: #D97706; - } - QPushButton:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B45309, stop:1 #92400E); - border-color: #78350F; - } -""" - -SUCCESS_BUTTON_STYLE = """ - QPushButton { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #3B82F6, stop:1 #2563EB); - color: white; - border: none; - border-radius: 10px; - padding: 12px 20px; - font-weight: 600; - font-size: 13px; - min-width: 110px; - } - QPushButton:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #60A5FA, stop:1 #3B82F6); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); - } - QPushButton:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2563EB, stop:1 #1D4ED8); - transform: translateY(0px); - } -""" - -WARNING_BUTTON_STYLE = """ - QPushButton { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #F59E0B, stop:1 #D97706); - color: white; - border: none; - border-radius: 10px; - padding: 12px 20px; - font-weight: 600; - font-size: 13px; - min-width: 110px; - } - QPushButton:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FBBF24, stop:1 #F59E0B); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3); - } - QPushButton:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #D97706, stop:1 #B45309); - transform: translateY(0px); - } -""" - -class BatchExtrusion: - def __init__(self): - self.window_name = "BatchExtrusionWindow" - self.layers = 7 # Default number of layers - self.thickness = 0.1 # Default thickness - self.min_color = [0.0, 0.0, 0.0] # Inner layer color (black) - self.max_color = [1.0, 1.0, 1.0] # Outer layer color (white) - self.merge_layers = False # Whether to merge all layers - self.original_objects = [] # Original object list - self.generated_objects = [] # Generated object list - self.is_updating = False # Prevent recursive updates - self.button_info = [] # Store button information for delayed style application - self.qt_available = self.check_qt_availability() # Check Qt availability - - def check_qt_availability(self): - """Check Qt and related module availability""" - try: - maya_version = int(cmds.about(version=True)) - print(f"Maya version: {maya_version}") - - # Try PySide6 first (Maya 2022+) - try: - from PySide6 import QtWidgets, QtCore - import shiboken6 as shiboken - print("Using PySide6 (Maya 2022+)") - return "PySide6" - except ImportError: - try: - from PySide2 import QtWidgets, QtCore - import shiboken2 as shiboken - print("Using PySide2 (Maya 2020-2021)") - return "PySide2" - except ImportError: - print("PySide module is not available, using Maya native style") - return None - - except Exception as e: - print(f"Qt availability check failed: {str(e)}") - return None - - def create_ui(self): - """Create user interface""" - # If the window already exists, delete it - if cmds.window(self.window_name, exists=True): - cmds.deleteUI(self.window_name) - # Reset window preferences to avoid obscuring buttons due to old window dimensions - if cmds.windowPref(self.window_name, exists=True): - cmds.windowPref(self.window_name, remove=True) - - # Create window - cmds.window(self.window_name, title="Batch Extrusion Shell Mesh", widthHeight=(420, 600), sizeable=True) - - # Create main layout (using scroll layout to prevent small window obscuring bottom buttons) - scroll = cmds.scrollLayout(horizontalScrollBarThickness=0, verticalScrollBarThickness=12) - main_layout = cmds.columnLayout(adjustableColumn=True, rowSpacing=10, columnOffset=('both', 10)) - - # Title - cmds.text(label="Batch Extrusion Shell Mesh", font="boldLabelFont", height=30) - cmds.separator(height=10) - - # Number of layers control - cmds.text(label="Layers:", align="left", font="boldLabelFont") - self.layers_slider = cmds.intSliderGrp( - label="Layers: ", - field=True, - minValue=1, - maxValue=20, - value=self.layers, - step=1, - changeCommand=self.on_layers_changed - ) - - # Thickness control - cmds.text(label="Thickness:", align="left", font="boldLabelFont") - self.thickness_slider = cmds.floatSliderGrp( - label="Thickness: ", - field=True, - minValue=0.000001, - maxValue=10.0, - value=self.thickness, - precision=4, - step=0.01, - changeCommand=self.on_thickness_changed - ) - - # Vertex color control - cmds.text(label="Vertex Color:", align="left", font="boldLabelFont") - self.min_color_field = cmds.colorSliderGrp( - label="Inner layer: ", - rgb=self.min_color, - changeCommand=self.on_color_changed - ) - self.max_color_field = cmds.colorSliderGrp( - label="Outer layer: ", - rgb=self.max_color, - changeCommand=self.on_color_changed - ) - - # Merge options - cmds.text(label="Merge:", align="left", font="boldLabelFont") - self.merge_checkbox = cmds.checkBox( - label="Merge All Extruded Layers", - value=self.merge_layers, - changeCommand=self.on_merge_changed - ) - - # Preview area - cmds.text(label="Vertex Color Setting Preview:", align="left", font="boldLabelFont") - self.preview_text = cmds.scrollField( - editable=False, - wordWrap=True, - height=100, - backgroundColor=[0.2, 0.2, 0.2] - ) - - cmds.separator(height=10) - - # Operation buttons - self.create_styled_button("Extrude Shell Mesh", self.select_base_objects, SUCCESS_BUTTON_STYLE) - - self.create_styled_button("Clear All Extruded Layers", self.clear_all_layers, WARNING_BUTTON_STYLE) - - # Delay style application to ensure Qt controls are fully created - cmds.evalDeferred(self.apply_delayed_styles) - - # Show window - cmds.showWindow(self.window_name) - - # Initial preview - self.preview_colors() - - def create_styled_button(self, label, command, style): - """Create styled button""" - # Set base color based on style type - base_color = [0.4, 0.4, 0.4] # Default gray - if "SUCCESS" in style: - base_color = [0.2, 0.5, 0.8] # Blue - elif "WARNING" in style: - base_color = [0.8, 0.6, 0.2] # Orange - elif "MESSAGE" in style: - base_color = [0.7, 0.4, 0.1] # Deep orange - - # Create Maya button, set base color first - button_name = cmds.button( - label=label, - command=command, - height=35, - backgroundColor=base_color - ) - - # Store button info for delayed style application - self.button_info.append({ - 'name': button_name, - 'label': label, - 'style': style - }) - - # Immediately try to apply Qt style - self.apply_style_to_button(button_name, label, style) - - return button_name - - def apply_style_to_button(self, button_name, label, style): - """Apply style to a single button""" - # If Qt is not available, return False - if not self.qt_available: - print(f"Qt is not available, skipping style application: {label}") - return False - - try: - # Import corresponding modules based on detected Qt version - if self.qt_available == "PySide6": - from PySide6 import QtWidgets, QtCore - import shiboken6 as shiboken - elif self.qt_available == "PySide2": - from PySide2 import QtWidgets, QtCore - import shiboken2 as shiboken - else: - return False - - import maya.OpenMayaUI as omui - - # Get the Qt object of the button - button_ptr = omui.MQtUtil.findControl(button_name) - if button_ptr: - qt_button = shiboken.wrapInstance(int(button_ptr), QtWidgets.QPushButton) - if qt_button: - qt_button.setStyleSheet(style) - print(f"✓ Successfully applied Qt style: {label}") - return True - else: - print(f"✗ Failed to get Qt button object: {label}") - else: - print(f"✗ Failed to find button control: {label}") - - except Exception as e: - print(f"✗ Qt style application failed ({label}): {str(e)}") - - return False - - def apply_delayed_styles(self): - """Delay style application to all buttons""" - print("=== Starting delayed style application ===") - success_count = 0 - for button_info in self.button_info: - if cmds.control(button_info['name'], exists=True): - if self.apply_style_to_button( - button_info['name'], - button_info['label'], - button_info['style'] - ): - success_count += 1 - else: - print(f"✗ Button control does not exist: {button_info['label']}") - - print(f"=== Style application completed: {success_count}/{len(self.button_info)} successful ===") - - def show_dialog(self, title, message): - """Show styled confirmation dialog""" - dialog_name = "StyledConfirmDialog" - - # If the dialog already exists, delete it - if cmds.window(dialog_name, exists=True): - cmds.deleteUI(dialog_name) - - # Create dialog window - cmds.window(dialog_name, title=title, widthHeight=(300, 120), sizeable=False) - - # Main layout - main_layout = cmds.columnLayout(adjustableColumn=True, rowSpacing=10, columnOffset=('both', 15)) - - # Message text - cmds.text(label=message, align="center", wordWrap=True, height=40) - - cmds.separator(height=5) - - # Confirm button - self.create_styled_button("Yes", lambda *args: self.close_dialog(dialog_name), MESSAGE_BUTTON_STYLE) - - # Show dialog - cmds.showWindow(dialog_name) - - def close_dialog(self, dialog_name): - """Close dialog""" - if cmds.window(dialog_name, exists=True): - cmds.deleteUI(dialog_name) - - def calculate_vertex_colors(self, total_layers): - """Calculate vertex colors for each layer""" - if total_layers <= 1: - return [[1.0, 1.0, 1.0]] - - colors = [] - for i in range(total_layers): - # Calculate interpolation factor (0.0 to 1.0) - factor = i / (total_layers - 1) if total_layers > 1 else 0.0 - - # Interpolate between minimum and maximum colors - r = self.min_color[0] + (self.max_color[0] - self.min_color[0]) * factor - g = self.min_color[1] + (self.max_color[1] - self.min_color[1]) * factor - b = self.min_color[2] + (self.max_color[2] - self.min_color[2]) * factor - - colors.append([r, g, b]) - - return colors - - def on_layers_changed(self, *args): - """Callback function when the number of layers changes - Real-time update""" - if self.is_updating: - return - - self.layers = cmds.intSliderGrp(self.layers_slider, query=True, value=True) - self.preview_colors() - - # Update logic - if self.original_objects: # If there are base models - if self.merge_layers: - print("The models have been merged, please manually clean up and re-extrude!") - else: - print(f"Number of layers updated to {self.layers}, regenerating shell layers...") - # Clean up existing layers - self.clear_generated_objects() - # Regenerate layers - self.generate_layers() - - def on_thickness_changed(self, *args): - """Callback function when thickness changes - Real-time update""" - if self.is_updating: - return - - self.thickness = cmds.floatSliderGrp(self.thickness_slider, query=True, value=True) - self.preview_colors() - - # Update logic - if self.original_objects and self.generated_objects: # If there are base models and generated layers - if self.merge_layers: - print("The models have been merged, please manually clean up and re-extrude!") - else: - print(f"Thickness updated to {self.thickness}, updating thickness for all layers...") - self.update_thickness() - - def on_merge_changed(self, *args): - """Callback function when merge option changes""" - if self.is_updating: - return - self.merge_layers = cmds.checkBox(self.merge_checkbox, query=True, value=True) - self.preview_colors() - - def on_color_changed(self, *args): - """Callback function when colors change - Real-time update""" - if self.is_updating: - return - - self.min_color = cmds.colorSliderGrp(self.min_color_field, query=True, rgb=True) - self.max_color = cmds.colorSliderGrp(self.max_color_field, query=True, rgb=True) - self.preview_colors() - - # Update logic - if self.original_objects and self.generated_objects: # If there are base models and generated layers - if self.merge_layers: - print("The models have been merged, please manually clean up and re-extrude!") - else: - print("Colors updated, updating vertex colors for all layers...") - self.update_colors_for_all_layers() - - def preview_colors(self, *args): - """Preview color distribution""" - layers = self.layers - total_layers = layers + 1 # Include original layer - colors = self.calculate_vertex_colors(total_layers) - - preview_text = f"Colors preview ({total_layers} layers):\n\n" - - for i, color in enumerate(colors): - r, g, b = color - if i == 0: - layer_name = "Original layer" - else: - layer_name = f"Layer {i}" - - preview_text += f"{layer_name}: RGB({r:.2f}, {g:.2f}, {b:.2f})\n" - - cmds.scrollField(self.preview_text, edit=True, text=preview_text) - - def select_base_objects(self, *args): - """Select base objects and start extrusion""" - selected = cmds.ls(selection=True, type='transform') - if not selected: - cmds.warning("Please select the models to process first") - return - - # Clean up existing layers - self.clear_generated_objects() - - # Save original objects - self.original_objects = selected[:] - - # Generate layers immediately - self.generate_layers() - - self.show_dialog("Completed", f"Processed {len(selected)} objects, generated {self.layers} shell layers") - - def clear_all_layers(self, *args): - """Clear all generated layers""" - # Check if merge option is checked - if self.merge_layers: - print("The models have been merged, please manually clean up and re-extrude!") - return - self.clear_generated_objects() - self.show_dialog("Completed", "All generated layers have been cleared") - - def clear_generated_objects(self): - """Clear generated objects""" - for obj in self.generated_objects: - if cmds.objExists(obj): - cmds.delete(obj) - self.generated_objects = [] - - def generate_layers(self): - """Generate shell layers""" - if not self.original_objects: - print("Error: No original objects selected") - return - - try: - print(f"=== Starting generation of {self.layers} shell layers ===") - - created_objects = [] - - for obj_index, original_obj in enumerate(self.original_objects): - print(f"Processing object {obj_index + 1}: {original_obj}") - - # Collect all layer objects (including original object) - all_layers = [original_obj] - - # Create layers one by one (always from the original object) - for layer_num in range(1, self.layers + 1): - print(f" Creating layer {layer_num}...") - - # Key: Always copy from the original object, not from the previous layer - new_layer = self.extrude_layer_correct(original_obj, layer_num, self.thickness) - if new_layer: - all_layers.append(new_layer) - created_objects.append(new_layer) - print(f" ✓ Layer {layer_num} completed") - else: - print(f" ✗ Layer {layer_num} failed, stopping") - break - - # Set vertex colors - print(f" Setting vertex colors for {len(all_layers)} layers...") - self.apply_colors(all_layers) - - # Save results - self.generated_objects = created_objects - print(f"=== Completed! Created {len(created_objects)} layers ===") - - # Check if merge option is checked - if self.merge_layers and len(created_objects) > 0: - print("=== Starting simple merge ===") - self.simple_merge_objects() - else: - print("=== Skipping merge (not checked or no objects) ===") - - except Exception as e: - print(f"Generation failed: {str(e)}") - # Cleanup - for obj in created_objects: - if cmds.objExists(obj): - try: - cmds.delete(obj) - except: - pass - - def simple_merge_objects(self): - """Simple merge method to avoid complex operations""" - try: - all_objects = self.original_objects + self.generated_objects - print(f"Simple merge {len(all_objects)} objects") - - if len(all_objects) > 1: - # Only do basic merge - existing_objects = [obj for obj in all_objects if cmds.objExists(obj)] - - if len(existing_objects) > 1: - cmds.select(existing_objects, replace=True) - merged = cmds.polyUnite(existing_objects, name="BatchExtruded_Simple")[0] - print(f"Simple merge completed: {merged}") - - # Update object list - self.original_objects = [merged] - self.generated_objects = [] - else: - print("Not enough objects to merge") - else: - print("Not enough objects to merge") - - except Exception as e: - print(f"Simple merge failed: {str(e)}") - - def extrude_layer_correct(self, obj, layer_index, thickness): - """Follow the correct Maya workflow: duplicate -> extrude -> delete by inverse selection""" - duplicated = None - try: - print(f" === Correct workflow for layer {layer_index} ===") - - # 1. Verify object - if not cmds.objExists(obj): - print(f" Error: Object {obj} does not exist") - return None - - # 2. Copy model - duplicated = cmds.duplicate(obj, name=f"Layer_{layer_index}")[0] - print(f" Copy completed: {duplicated}") - - # 3. Switch to face mode and select all faces - print(f" Switching to face mode and selecting all faces...") - - # Switch to component mode - cmds.selectMode(component=True) - cmds.selectType(facet=True) - - # Select all faces directly (more reliable method) - try: - cmds.select(f"{duplicated}.f[*]", replace=True) - selected_faces = cmds.ls(selection=True, flatten=True) - print(f" Selected faces count: {len(selected_faces)}") - - if len(selected_faces) == 0: - # Backup method 1: Select object first then convert - print(f" Direct selection failed, trying conversion method...") - cmds.selectMode(object=True) - cmds.select(duplicated, replace=True) - cmds.selectMode(component=True) - cmds.selectType(facet=True) - - # Use mel command to convert - try: - mel.eval("ConvertSelectionToFaces;") - selected_faces = cmds.ls(selection=True, flatten=True) - print(f" Selected faces count: {len(selected_faces)}") - except Exception as mel_error: - print(f" MEL conversion failed: {str(mel_error)}") - - # Backup method 2: Use polyEvaluate to get face count, then select - try: - face_count = cmds.polyEvaluate(duplicated, face=True) - if face_count > 0: - face_range = f"{duplicated}.f[0:{face_count-1}]" - cmds.select(face_range, replace=True) - selected_faces = cmds.ls(selection=True, flatten=True) - print(f" Selected faces count: {len(selected_faces)}") - except Exception as backup_error: - print(f" Backup method also failed: {str(backup_error)}") - - except Exception as select_error: - print(f" Face selection failed: {str(select_error)}") - raise select_error - - # Verify face selection success - if len(selected_faces) == 0: - print(f" Error: No faces selected") - return None - - print(f" Selected {len(selected_faces)} faces, starting extrusion...") - - # Execute extrusion (thickness=0) - extrude_result = cmds.polyExtrudeFacet( - constructionHistory=True, - keepFacesTogether=True, - divisions=1, - twist=0, - taper=1, - off=0, - thickness=0, - smoothingAngle=30 - ) - print(f" Extrusion command completed: {extrude_result}") - - # Set cumulative thickness (each layer's thickness is cumulative) - if extrude_result: - extrude_node = extrude_result[0] - # Cumulative thickness = base thickness * layer number - cumulative_thickness = thickness * layer_index - cmds.setAttr(f"{extrude_node}.thickness", cumulative_thickness) - print(f" Set cumulative thickness: {cumulative_thickness} (Layer {layer_index})") - - # 4. Invert selection and delete internal faces - print(f" Inverting selection and deleting internal faces...") - - # After extrusion, the original faces are still selected - # We need to keep these original faces and delete the newly generated faces - original_selected_faces = cmds.ls(selection=True, flatten=True) - print(f" Original selected faces count: {len(original_selected_faces)}") - - # Get all faces after extrusion - all_faces_after_extrude = cmds.ls(f"{duplicated}.f[*]", flatten=True) - print(f" Total faces after extrusion: {len(all_faces_after_extrude)}") - - # Calculate newly generated faces (faces to delete) - if len(all_faces_after_extrude) > len(original_selected_faces): - # New faces = All faces - Original faces - new_faces = [face for face in all_faces_after_extrude if face not in original_selected_faces] - - if new_faces: - print(f" Deleting {len(new_faces)} newly generated faces") - cmds.select(new_faces, replace=True) - cmds.delete() - print(f" Deleted successfully, keeping original faces as shell") - else: - print(f" No newly generated faces found") - else: - print(f" No faces added, skipping deletion") - - # 5. Return to object mode - cmds.selectMode(object=True) - cmds.select(clear=True) - - print(f" === Layer {layer_index} completed ===") - return duplicated - - except Exception as e: - print(f" Layer {layer_index} failed: {str(e)}") - if duplicated and cmds.objExists(duplicated): - try: - cmds.delete(duplicated) - except: - pass - return None - - def apply_colors(self, layer_objects): - """Simple vertex color application""" - try: - total_layers = len(layer_objects) - colors = self.calculate_vertex_colors(total_layers) - - print(f" Setting colors for {total_layers} layers...") - - for i, obj in enumerate(layer_objects): - if cmds.objExists(obj) and i < len(colors): - color = colors[i] - print(f" Layer {i}: {obj} -> RGB{color}") - - # Set vertex colors - try: - # Select all vertices - cmds.select(f"{obj}.vtx[*]", replace=True) - - # Apply color - cmds.polyColorPerVertex(rgb=color, colorDisplayOption=True) - - # Enable vertex color display - cmds.setAttr(f"{obj}.displayColors", 1) - - except Exception as color_error: - print(f" Setting vertex colors failed: {str(color_error)}") - - # Clear selection - cmds.select(clear=True) - print(f" Vertex colors set successfully") - except Exception as e: - print(f" Vertex color application failed: {str(e)}") - - def update_thickness(self): - """Update thickness for all layers""" - try: - print(f"=== Updating thickness to {self.thickness} ===") - - for layer_index, obj in enumerate(self.generated_objects, 1): - if cmds.objExists(obj): - print(f" Updating layer {layer_index}: {obj}") - - # Find extrude node - history = cmds.listHistory(obj, pruneDagObjects=True) - extrude_nodes = [node for node in history if cmds.nodeType(node) == 'polyExtrudeFace'] - - if extrude_nodes: - extrude_node = extrude_nodes[0] # Take the latest extrude node - # Set cumulative thickness - cumulative_thickness = self.thickness * layer_index - cmds.setAttr(f"{extrude_node}.thickness", cumulative_thickness) - print(f" ✓ Updated thickness: {cumulative_thickness}") - else: - print(f" ✗ No extrude node found") - - print(f"=== Thickness updated successfully ===") - - except Exception as e: - print(f"Thickness update failed: {str(e)}") - - def update_colors_for_all_layers(self): - """Update colors for all layers""" - try: - print(f"=== Updating colors for all layers ===") - - # Collect all layer objects (including original objects) - all_layers = self.original_objects + self.generated_objects - - # Reapply colors - self.apply_colors(all_layers) - - print(f"=== Colors updated successfully ===") - - except Exception as e: - print(f"Colors update failed: {str(e)}") - -# Create and display UI function -def show_batch_extrusion_ui(): - """Show batch extrusion UI""" - batch_extrusion = BatchExtrusion() - batch_extrusion.create_ui() - -# If run this script directly -if __name__ == "__main__": - show_batch_extrusion_ui() diff --git a/2025/scripts/nexus_test.py b/2025/scripts/nexus_test.py deleted file mode 100644 index 8a8eb10..0000000 --- a/2025/scripts/nexus_test.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Test Script -Simple test to verify the Nexus plugin system is working -""" - -import maya.cmds as cmds - - -def run_test(): - """Run a simple test""" - print("[Nexus Test] Running test...") - - # Show confirmation dialog - result = cmds.confirmDialog( - title='Nexus Test', - message='Nexus Plugin System is working correctly!', - button=['OK'], - defaultButton='OK', - cancelButton='OK', - dismissString='OK' - ) - - print(f"[Nexus Test] Test completed: {result}") - return result - - -if __name__ == "__main__": - run_test() diff --git a/2025/scripts/rigging_tools/__init__.py b/2025/scripts/rigging_tools/__init__.py deleted file mode 100644 index a987049..0000000 --- a/2025/scripts/rigging_tools/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Rigging Tools Package -Character rigging utilities -""" - -__all__ = [] diff --git a/2025/scripts/userSetup.py b/2025/scripts/userSetup.py deleted file mode 100644 index 5d41610..0000000 --- a/2025/scripts/userSetup.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Nexus Maya 2023 - User Setup Script -Automatically executed when Maya starts -""" - -import maya.cmds as cmds -import maya.mel as mel -import maya.utils -import os -import sys - -SHELF_NAMES = ["Nexus_Modeling", "Nexus_Rigging", "Nexus_Animation"] - -def load_nexus_shelves(): - """Load all Nexus shelves (force refresh)""" - try: - shelf_paths = os.environ.get('MAYA_SHELF_PATH', '') - - if not shelf_paths: - print("[Nexus] MAYA_SHELF_PATH not set, trying alternative method...") - try: - script_dir = os.path.dirname(os.path.abspath(__file__)) - shelf_paths = os.path.join(os.path.dirname(script_dir), "shelves") - except: - print("[Nexus] Could not determine shelf path, skipping shelf load") - return - - path_separator = ';' if os.name == 'nt' else ':' - shelf_path_list = shelf_paths.split(path_separator) - - for shelf_name in SHELF_NAMES: - shelf_file_found = None - for shelf_path in shelf_path_list: - shelf_path = shelf_path.strip() - if not shelf_path: - continue - - shelf_file = os.path.join(shelf_path, f"shelf_{shelf_name}.mel") - shelf_file = shelf_file.replace("\\", "/") - - if os.path.exists(shelf_file): - shelf_file_found = shelf_file - print(f"[Nexus] Found shelf file: {shelf_file}") - break - - if not shelf_file_found: - print(f"[Nexus] Could not find shelf_{shelf_name}.mel") - continue - - # Delete old shelf if exists - if cmds.shelfLayout(shelf_name, exists=True): - print(f"[Nexus] Deleting old shelf: {shelf_name}") - try: - cmds.deleteUI(shelf_name, layout=True) - except Exception as e: - print(f"[Nexus] Warning: Could not delete old shelf: {e}") - - # Load shelf using proper MEL method - print(f"[Nexus] Loading shelf: {shelf_name}") - try: - # Disable auto-save - mel.eval('optionVar -intValue "saveLastLoadedShelf" 0;') - - # Create shelf layout - mel.eval(f''' - global string $gShelfTopLevel; - if (`shelfLayout -exists {shelf_name}`) {{ - deleteUI -layout {shelf_name}; - }} - setParent $gShelfTopLevel; - shelfLayout -cellWidth 35 -cellHeight 34 {shelf_name}; - ''') - print(f"[Nexus] ✓ Created shelf layout: {shelf_name}") - - # Set parent and execute shelf script - mel.eval(f'setParent {shelf_name};') - mel.eval(f'source "{shelf_file_found}";') - mel.eval(f'shelf_{shelf_name}();') - print(f"[Nexus] ✓ Executed shelf script: shelf_{shelf_name}()") - - # Verify shelf - if cmds.shelfLayout(shelf_name, exists=True): - buttons = cmds.shelfLayout(shelf_name, query=True, childArray=True) or [] - if buttons: - print(f"[Nexus] ✓ Shelf loaded with {len(buttons)} button(s): {shelf_name}") - else: - print(f"[Nexus] ⚠ Shelf created but no buttons: {shelf_name}") - - # Remove auto-saved config - try: - maya_version = cmds.about(version=True).split()[0] - maya_app_dir = os.environ.get('MAYA_APP_DIR', '') - if maya_app_dir: - shelf_config = os.path.join(maya_app_dir, maya_version, "prefs", "shelves", f"shelf_{shelf_name}.mel") - if os.path.exists(shelf_config): - os.remove(shelf_config) - print(f"[Nexus] ✓ Removed auto-saved config: {shelf_name}") - except Exception as e: - print(f"[Nexus] Warning: {e}") - else: - print(f"[Nexus] ✗ Shelf layout not created: {shelf_name}") - - except Exception as e: - print(f"[Nexus] Error loading shelf {shelf_name}: {e}") - import traceback - traceback.print_exc() - continue - - except Exception as e: - print(f"[Nexus] Error in load_nexus_shelves: {e}") - import traceback - traceback.print_exc() - - -def load_nexus_plugins(): - """Load Nexus plugins""" - try: - plugin_paths = os.environ.get('MAYA_PLUG_IN_PATH', '') - - if not plugin_paths: - print("[Nexus] MAYA_PLUG_IN_PATH not set") - return - - path_separator = ';' if os.name == 'nt' else ':' - plugin_path_list = plugin_paths.split(path_separator) - - plugins_to_load = ["nexus_plugin.py"] - - for plugin_name in plugins_to_load: - plugin_found = False - for plugin_path in plugin_path_list: - plugin_path = plugin_path.strip() - if not plugin_path: - continue - - plugin_file = os.path.join(plugin_path, plugin_name) - if os.path.exists(plugin_file): - plugin_found = True - break - - if not plugin_found: - print(f"[Nexus] Plugin not found: {plugin_name}") - continue - - # Load plugin - try: - if not cmds.pluginInfo(plugin_name, query=True, loaded=True): - cmds.loadPlugin(plugin_name) - print(f"[Nexus] ✓ Plugin loaded: {plugin_name}") - else: - print(f"[Nexus] Plugin already loaded: {plugin_name}") - except Exception as e: - print(f"[Nexus] Error loading plugin {plugin_name}: {e}") - - except Exception as e: - print(f"[Nexus] Error in load_nexus_plugins: {e}") - import traceback - traceback.print_exc() - - -# Deferred execution -def initialize_nexus(): - """Initialize Nexus plugin system""" - print("=" * 80) - print("[Nexus] Initializing Nexus Maya 2023 Plugin System...") - print("=" * 80) - - load_nexus_shelves() - load_nexus_plugins() - - print("=" * 80) - print("[Nexus] Nexus Plugin System Initialized!") - print("=" * 80) - - -# Execute after Maya is fully loaded -maya.utils.executeDeferred(initialize_nexus) - - -# Cleanup on exit -def cleanup_on_exit(): - """Cleanup when Maya exits""" - print("[Nexus] Cleaning up...") - - # Unload plugins - plugins_to_unload = ["nexus_plugin.py"] - for plugin_name in plugins_to_unload: - if cmds.pluginInfo(plugin_name, query=True, loaded=True): - try: - cmds.unloadPlugin(plugin_name) - print(f"[Nexus] Plugin unloaded: {plugin_name}") - except: - pass - - -import atexit -atexit.register(cleanup_on_exit) diff --git a/2025/shelves/shelf_Nexus_Animation.mel b/2025/shelves/shelf_Nexus_Animation.mel deleted file mode 100644 index a7ff9a1..0000000 --- a/2025/shelves/shelf_Nexus_Animation.mel +++ /dev/null @@ -1,43 +0,0 @@ -global proc shelf_Nexus_Animation () { - global string $gBuffStr; - global string $gBuffStr0; - global string $gBuffStr1; - - - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Nexus Test - Verify plugin system" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "Test" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -imageOverlayLabel "Test" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "commandButton.png" - -image1 "commandButton.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "import nexus_test\nnexus_test.run_test()" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - -} diff --git a/2025/shelves/shelf_Nexus_Modeling.mel b/2025/shelves/shelf_Nexus_Modeling.mel deleted file mode 100644 index 590ff7f..0000000 --- a/2025/shelves/shelf_Nexus_Modeling.mel +++ /dev/null @@ -1,43 +0,0 @@ -global proc shelf_Nexus_Modeling () { - global string $gBuffStr; - global string $gBuffStr0; - global string $gBuffStr1; - - - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Batch Extrusion - Shell mesh tool with vertex colors" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "BatchExt" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -imageOverlayLabel "Extrude" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "batchextrusion.png" - -image1 "batchextrusion.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "import modeling_tools\nmodeling_tools.show_batch_extrusion_ui()" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - -} diff --git a/2025/shelves/shelf_Nexus_Rigging.mel b/2025/shelves/shelf_Nexus_Rigging.mel deleted file mode 100644 index 778a59f..0000000 --- a/2025/shelves/shelf_Nexus_Rigging.mel +++ /dev/null @@ -1,43 +0,0 @@ -global proc shelf_Nexus_Rigging () { - global string $gBuffStr; - global string $gBuffStr0; - global string $gBuffStr1; - - - shelfButton - -enableCommandRepeat 1 - -flexibleWidthType 3 - -flexibleWidthValue 32 - -enable 1 - -width 35 - -height 34 - -manage 1 - -visible 1 - -preventOverride 0 - -annotation "Nexus Test - Verify plugin system" - -enableBackground 0 - -backgroundColor 0 0 0 - -highlightColor 0.321569 0.521569 0.65098 - -align "center" - -label "Test" - -labelOffset 0 - -rotation 0 - -flipX 0 - -flipY 0 - -useAlpha 1 - -font "plainLabelFont" - -imageOverlayLabel "Test" - -overlayLabelColor 0.8 0.8 0.8 - -overlayLabelBackColor 0 0 0 0.5 - -image "commandButton.png" - -image1 "commandButton.png" - -style "iconOnly" - -marginWidth 0 - -marginHeight 1 - -command "import nexus_test\nnexus_test.run_test()" - -sourceType "python" - -commandRepeatable 1 - -flat 1 - ; - -}