6412b6f619
- Added standard list. - Added bone_hierarchy list - Added bone_hierarchy - Better checks. - Better UI. This is the first part, still needs alot of work, but this is better then before. Need to add some more standards and then we will be golden.
187 lines
7.9 KiB
Python
187 lines
7.9 KiB
Python
import bpy
|
|
from typing import Set, Dict, List, Optional, Tuple
|
|
from bpy.types import (
|
|
Operator,
|
|
Panel,
|
|
Menu,
|
|
Context,
|
|
UILayout,
|
|
WindowManager,
|
|
Object
|
|
)
|
|
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
|
from ..core.translations import t
|
|
from ..core.common import (
|
|
get_active_armature,
|
|
clear_default_objects,
|
|
get_armature_list,
|
|
get_armature_stats
|
|
)
|
|
from ..functions.pose_mode import (
|
|
AvatarToolkit_OT_StartPoseMode,
|
|
AvatarToolkit_OT_StopPoseMode,
|
|
AvatarToolkit_OT_ApplyPoseAsShapekey,
|
|
AvatarToolkit_OT_ApplyPoseAsRest
|
|
)
|
|
from ..core.armature_validation import validate_armature
|
|
|
|
class AvatarToolKit_OT_ExportFBX(Operator):
|
|
"""Export selected objects as FBX"""
|
|
bl_idname: str = "avatar_toolkit.export_fbx"
|
|
bl_label: str = t("QuickAccess.export_fbx")
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
bpy.ops.export_scene.fbx('INVOKE_DEFAULT')
|
|
return {'FINISHED'}
|
|
|
|
class AvatarToolKit_MT_ExportMenu(Menu):
|
|
"""Export menu containing various export options"""
|
|
bl_idname: str = "AVATAR_TOOLKIT_MT_export_menu"
|
|
bl_label: str = t("QuickAccess.export")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
layout: UILayout = self.layout
|
|
layout.operator("avatar_toolkit.export_fbx", text=t("QuickAccess.export_fbx"))
|
|
layout.operator("avatar_toolkit.export_resonite", text=t("QuickAccess.export_resonite"))
|
|
|
|
class AvatarToolKit_OT_ExportMenu(Operator):
|
|
"""Open the export menu"""
|
|
bl_idname: str = "avatar_toolkit.export"
|
|
bl_label: str = t("QuickAccess.export")
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return get_active_armature(context) is not None
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
bpy.context.window_manager.popup_menu(AvatarToolKit_MT_ExportMenu.draw)
|
|
return {'FINISHED'}
|
|
|
|
class AvatarToolKit_PT_QuickAccessPanel(Panel):
|
|
"""Quick access panel for common Avatar Toolkit operations"""
|
|
bl_label: str = t("QuickAccess.label")
|
|
bl_idname: str = "OBJECT_PT_avatar_toolkit_quick_access"
|
|
bl_space_type: str = 'VIEW_3D'
|
|
bl_region_type: str = 'UI'
|
|
bl_category: str = CATEGORY_NAME
|
|
bl_parent_id: str = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
|
bl_order: int = 0
|
|
|
|
def draw(self, context: Context) -> None:
|
|
"""Draw the panel layout"""
|
|
layout: UILayout = self.layout
|
|
props = context.scene.avatar_toolkit
|
|
|
|
# Armature Selection Box
|
|
armature_box: UILayout = layout.box()
|
|
col: UILayout = armature_box.column(align=True)
|
|
col.label(text=t("QuickAccess.select_armature"), icon='ARMATURE_DATA')
|
|
col.separator(factor=0.5)
|
|
|
|
# Armature Selection
|
|
col.prop(context.scene.avatar_toolkit, "active_armature", text="")
|
|
|
|
# Armature Validation
|
|
active_armature: Optional[Object] = get_active_armature(context)
|
|
if active_armature:
|
|
is_valid, messages = validate_armature(active_armature)
|
|
|
|
info_box = col.box()
|
|
|
|
if is_valid:
|
|
row = info_box.row()
|
|
split = row.split(factor=0.6)
|
|
split.label(text=t("QuickAccess.valid_armature"), icon='CHECKMARK')
|
|
stats = get_armature_stats(active_armature)
|
|
split.label(text=t("QuickAccess.bones_count", count=stats['bone_count']))
|
|
|
|
if stats['has_pose']:
|
|
info_box.label(text=t("QuickAccess.pose_bones_available"), icon='POSE_HLT')
|
|
else:
|
|
# Found Bones section
|
|
validation_box = info_box.box()
|
|
row = validation_box.row()
|
|
row.prop(props, "show_found_bones", text=t("Validation.section.found_bones"), icon='TRIA_DOWN' if props.show_found_bones else 'TRIA_RIGHT', emboss=False)
|
|
if props.show_found_bones:
|
|
for line in messages[0].split('\n'):
|
|
validation_box.label(text=line)
|
|
|
|
# Main validation status
|
|
validation_box = info_box.box()
|
|
row = validation_box.row()
|
|
row.alert = True
|
|
row.label(text=t("Validation.status.failed"))
|
|
|
|
# Detailed validation message
|
|
validation_box = info_box.box()
|
|
row = validation_box.row()
|
|
row.alert = True
|
|
row.label(text=t("Validation.message.failed.line1"))
|
|
row = validation_box.row()
|
|
row.alert = True
|
|
row.label(text=t("Validation.message.failed.line2"))
|
|
row = validation_box.row()
|
|
row.alert = True
|
|
row.label(text=t("Validation.message.failed.line3"))
|
|
|
|
# Non-Standard Bones section
|
|
validation_box = info_box.box()
|
|
row = validation_box.row()
|
|
row.alert = True
|
|
row.prop(props, "show_non_standard", text=t("Validation.section.non_standard"), icon='TRIA_DOWN' if props.show_non_standard else 'TRIA_RIGHT', emboss=False)
|
|
if props.show_non_standard:
|
|
for line in messages[1].split('\n'):
|
|
sub_row = validation_box.row()
|
|
sub_row.alert = True
|
|
sub_row.label(text=line)
|
|
|
|
# Hierarchy Issues section
|
|
validation_box = info_box.box()
|
|
row = validation_box.row()
|
|
row.alert = True
|
|
row.prop(props, "show_hierarchy", text=t("Validation.section.hierarchy"), icon='TRIA_DOWN' if props.show_hierarchy else 'TRIA_RIGHT', emboss=False)
|
|
if props.show_hierarchy:
|
|
for message in messages[2:]:
|
|
sub_row = validation_box.row()
|
|
sub_row.alert = True
|
|
sub_row.label(text=message)
|
|
|
|
# Validation Mode Warnings - always show in info box
|
|
validation_mode = context.scene.avatar_toolkit.validation_mode
|
|
if validation_mode == 'BASIC':
|
|
warning_row = info_box.box()
|
|
warning_row.alert = True
|
|
warning_row.label(text=t("QuickAccess.validation_basic_warning"), icon='INFO')
|
|
warning_row.label(text=t("QuickAccess.validation_basic_details"))
|
|
elif validation_mode == 'NONE':
|
|
warning_row = info_box.box()
|
|
warning_row.alert = True
|
|
warning_row.label(text=t("QuickAccess.validation_none_warning"), icon='ERROR')
|
|
warning_row.label(text=t("QuickAccess.validation_none_details"))
|
|
|
|
# Pose Mode Controls
|
|
pose_box: UILayout = layout.box()
|
|
col = pose_box.column(align=True)
|
|
col.label(text=t("QuickAccess.pose_controls"), icon='ARMATURE_DATA')
|
|
col.separator(factor=0.5)
|
|
|
|
if context.mode == "POSE":
|
|
col.operator(AvatarToolkit_OT_StopPoseMode.bl_idname, icon='POSE_HLT')
|
|
col.separator(factor=0.5)
|
|
col.operator(AvatarToolkit_OT_ApplyPoseAsRest.bl_idname, icon='MOD_ARMATURE')
|
|
col.operator(AvatarToolkit_OT_ApplyPoseAsShapekey.bl_idname, icon='MOD_ARMATURE')
|
|
else:
|
|
col.operator(AvatarToolkit_OT_StartPoseMode.bl_idname, icon='POSE_HLT')
|
|
|
|
# Import/Export Box
|
|
import_box: UILayout = layout.box()
|
|
col = import_box.column(align=True)
|
|
col.label(text=t("QuickAccess.import_export"), icon='IMPORT')
|
|
col.separator(factor=0.5)
|
|
|
|
# Import/Export Buttons
|
|
button_row: UILayout = col.row(align=True)
|
|
button_row.scale_y = 1.5
|
|
button_row.operator("avatar_toolkit.import", text=t("QuickAccess.import"), icon='IMPORT')
|
|
button_row.operator("avatar_toolkit.export", text=t("QuickAccess.export"), icon='EXPORT')
|