Start of the Major Overhaul
I decided to go through each function and UI section one by one, improving and overhauling things. Each function and section is going to be fully tested and not rushed out. This is the best way to catch things, but also include the code base as much as possible.
This commit is contained in:
@@ -1,182 +0,0 @@
|
||||
from bpy.types import UIList, Panel, UILayout, Object, Context, Material, Operator
|
||||
import bpy
|
||||
from math import sqrt
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from ..core.common import SceneMatClass, MaterialListBool, get_selected_armature
|
||||
from ..functions.atlas_materials import AvatarToolKit_OT_AtlasMaterials
|
||||
from ..core.translations import t
|
||||
|
||||
class AvatarToolKit_OT_SelectAllMaterials(Operator):
|
||||
bl_idname = 'avatar_toolkit.select_all_materials'
|
||||
bl_label = "Select All"
|
||||
bl_description = "Select all materials for atlas"
|
||||
|
||||
def execute(self, context):
|
||||
for item in context.scene.avatar_toolkit.materials:
|
||||
item.mat.avatar_toolkit.include_in_atlas = True
|
||||
return {'FINISHED'}
|
||||
|
||||
class AvatarToolKit_OT_SelectNoneMaterials(Operator):
|
||||
bl_idname = 'avatar_toolkit.select_none_materials'
|
||||
bl_label = "Select None"
|
||||
bl_description = "Deselect all materials"
|
||||
|
||||
def execute(self, context):
|
||||
for item in context.scene.avatar_toolkit.materials:
|
||||
item.mat.avatar_toolkit.include_in_atlas = False
|
||||
return {'FINISHED'}
|
||||
|
||||
class AvatarToolKit_OT_ExpandAllMaterials(Operator):
|
||||
bl_idname = 'avatar_toolkit.expand_all_materials'
|
||||
bl_label = "Expand All"
|
||||
bl_description = "Expand all material settings"
|
||||
|
||||
def execute(self, context):
|
||||
for item in context.scene.avatar_toolkit.materials:
|
||||
item.mat.avatar_toolkit.material_expanded = True
|
||||
return {'FINISHED'}
|
||||
|
||||
class AvatarToolKit_OT_CollapseAllMaterials(Operator):
|
||||
bl_idname = 'avatar_toolkit.collapse_all_materials'
|
||||
bl_label = "Collapse All"
|
||||
bl_description = "Collapse all material settings"
|
||||
|
||||
def execute(self, context):
|
||||
for item in context.scene.avatar_toolkit.materials:
|
||||
item.mat.avatar_toolkit.material_expanded = False
|
||||
return {'FINISHED'}
|
||||
|
||||
class AvatarToolKit_OT_ExpandSectionMaterials(Operator):
|
||||
bl_idname = 'avatar_toolkit.expand_section_materials'
|
||||
bl_label = ""
|
||||
bl_description = ""
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: Context) -> bool:
|
||||
return True
|
||||
|
||||
def execute(self, context: Context) -> set:
|
||||
if not context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown:
|
||||
context.scene.avatar_toolkit.materials.clear()
|
||||
newlist: list[Material] = []
|
||||
for obj in context.scene.objects:
|
||||
if len(obj.material_slots)>0:
|
||||
for mat_slot in obj.material_slots:
|
||||
if mat_slot.material:
|
||||
if mat_slot.material not in newlist:
|
||||
newlist.append(mat_slot.material)
|
||||
newitem: SceneMatClass = context.scene.avatar_toolkit.materials.add()
|
||||
newitem.mat = mat_slot.material
|
||||
MaterialListBool.old_list[context.scene.name] = newlist
|
||||
else:
|
||||
context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown = False
|
||||
return {'FINISHED'}
|
||||
|
||||
class AvatarToolKit_UL_MaterialTextureAtlasProperties(UIList):
|
||||
bl_label = t("TextureAtlas.material_list_label")
|
||||
bl_idname = "Material_UL_avatar_toolkit_texture_atlas_mat_list_mat"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
|
||||
def draw_header(self, context):
|
||||
layout = self.layout
|
||||
row = layout.row(align=True)
|
||||
|
||||
row.operator("avatar_toolkit.select_all_materials", text="", icon='CHECKBOX_HLT')
|
||||
row.operator("avatar_toolkit.select_none_materials", text="", icon='CHECKBOX_DEHLT')
|
||||
row.operator("avatar_toolkit.expand_all_materials", text="", icon='DISCLOSURE_TRI_DOWN')
|
||||
row.operator("avatar_toolkit.collapse_all_materials", text="", icon='DISCLOSURE_TRI_RIGHT')
|
||||
row.prop(context.scene.avatar_toolkit, "material_search_filter", text="", icon='VIEWZOOM')
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text=f"Estimated Atlas Size: {self.calculate_atlas_size(context)}px")
|
||||
|
||||
def draw_item(self, context: Context, layout: UILayout, data: Object, item: SceneMatClass, icon, active_data, active_propname, index):
|
||||
if context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown:
|
||||
if context.scene.avatar_toolkit.material_search_filter and context.scene.avatar_toolkit.material_search_filter.lower() not in item.mat.name.lower():
|
||||
return
|
||||
|
||||
row = layout.row()
|
||||
|
||||
row.prop(item.mat.avatar_toolkit, "include_in_atlas", text="", icon='CHECKBOX_HLT' if item.mat.avatar_toolkit.include_in_atlas else 'CHECKBOX_DEHLT')
|
||||
|
||||
row.prop(item.mat.avatar_toolkit, "material_expanded",
|
||||
text=item.mat.name,
|
||||
icon='DOWNARROW_HLT' if item.mat.avatar_toolkit.material_expanded else 'RIGHTARROW',
|
||||
emboss=False)
|
||||
|
||||
if item.mat.avatar_toolkit.material_expanded and item.mat.avatar_toolkit.include_in_atlas:
|
||||
box = layout.box()
|
||||
col = box.column(align=True)
|
||||
self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_albedo", "IMAGE_RGB")
|
||||
self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_normal", "NORMALS_FACE")
|
||||
self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_emission", "LIGHT")
|
||||
self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_ambient_occlusion", "SHADING_SOLID")
|
||||
self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_height", "IMAGE_ZDEPTH")
|
||||
self.draw_texture_row(col, item.mat.avatar_toolkit, "texture_atlas_roughness", "MATERIAL")
|
||||
|
||||
col.separator(factor=0.5)
|
||||
|
||||
def draw_texture_row(self, layout, material, prop_name, icon):
|
||||
row = layout.row()
|
||||
row.prop(material, prop_name, icon=icon)
|
||||
if getattr(material, prop_name):
|
||||
row.label(text="", icon='CHECKMARK')
|
||||
else:
|
||||
row.label(text="", icon='X')
|
||||
|
||||
def calculate_atlas_size(self, context):
|
||||
total_size = 0
|
||||
for mat in context.scene.avatar_toolkit.materials:
|
||||
if mat.mat.avatar_toolkit.include_in_atlas:
|
||||
if mat.mat.avatar_toolkit.texture_atlas_albedo:
|
||||
img = bpy.data.images[mat.mat.avatar_toolkit.texture_atlas_albedo]
|
||||
total_size += img.size[0] * img.size[1]
|
||||
return f"{int(sqrt(total_size))}x{int(sqrt(total_size))}"
|
||||
|
||||
class AvatarToolKit_PT_TextureAtlasPanel(Panel):
|
||||
bl_label = t("TextureAtlas.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_texture_atlas"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 6
|
||||
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
armature = get_selected_armature(context)
|
||||
|
||||
if armature:
|
||||
layout.label(text=t("TextureAtlas.label"), icon='TEXTURE')
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
direction_icon = 'RIGHTARROW' if not context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT'
|
||||
row.operator(AvatarToolKit_OT_ExpandSectionMaterials.bl_idname,
|
||||
text=(t("TextureAtlas.reload_list") if not context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown else t("TextureAtlas.loaded_list")),
|
||||
icon=direction_icon)
|
||||
|
||||
if context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown:
|
||||
row = box.row()
|
||||
row.template_list(AvatarToolKit_UL_MaterialTextureAtlasProperties.bl_idname,
|
||||
'material_list',
|
||||
context.scene.avatar_toolkit,
|
||||
'materials',
|
||||
context.scene.avatar_toolkit,
|
||||
'texture_atlas_material_index',
|
||||
rows=12,
|
||||
type='DEFAULT')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
row = layout.row()
|
||||
row.scale_y = 1.5
|
||||
row.operator(AvatarToolKit_OT_AtlasMaterials.bl_idname,
|
||||
text=t("TextureAtlas.atlas_materials"),
|
||||
icon='NODE_TEXTURE')
|
||||
else:
|
||||
layout.label(text=t("Tools.select_armature"), icon='ERROR')
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import bpy
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from ..core.translations import t
|
||||
from ..core.common import open_web_after_delay_multi_threaded
|
||||
|
||||
class AvatarToolkit_PT_CreditsSupport(bpy.types.Panel):
|
||||
bl_label = t("CreditsSupport.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_credits_support"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 10
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.label(text=t("CreditsSupport.credits_title"))
|
||||
box = layout.box()
|
||||
column = box.column(align=True)
|
||||
column.scale_y = 0.7
|
||||
column.label(text=t("CreditsSupport.credits_text1"))
|
||||
column.label(text=t("CreditsSupport.credits_text2"))
|
||||
column.label(text=t("CreditsSupport.credits_text3"))
|
||||
column.label(text=t("CreditsSupport.credits_text4"))
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.label(text=t("CreditsSupport.support_title"))
|
||||
box = layout.box()
|
||||
column = box.column(align=True)
|
||||
column.scale_y = 0.7
|
||||
column.label(text=t("CreditsSupport.support_text1"))
|
||||
column.label(text=t("CreditsSupport.support_text2"))
|
||||
row = column.row()
|
||||
row.scale_y = 1.5
|
||||
row.operator("wm.url_open", text=t("CreditsSupport.support_button")).url = "https://neoneko.xyz/supportus.html"
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.label(text=t("CreditsSupport.help_title"))
|
||||
box = layout.box()
|
||||
column = box.column(align=True)
|
||||
column.scale_y = 0.7
|
||||
column.label(text=t("CreditsSupport.help_text1"))
|
||||
column.label(text=t("CreditsSupport.help_text2"))
|
||||
row = column.row()
|
||||
row.scale_y = 1.5
|
||||
row.operator("wm.url_open", text=t("CreditsSupport.wiki_button")).url = "https://github.com/teamneoneko/Avatar-Toolkit"
|
||||
row = column.row()
|
||||
row.scale_y = 1.5
|
||||
row.operator("wm.url_open", text=t("CreditsSupport.discord_button")).url = "https://discord.catsblenderplugin.xyz"
|
||||
|
||||
+26
-9
@@ -1,21 +1,38 @@
|
||||
import bpy
|
||||
from typing import Optional
|
||||
from bpy.types import Panel, Context, UILayout
|
||||
from ..core.translations import t
|
||||
|
||||
CATEGORY_NAME = "Avatar Toolkit"
|
||||
CATEGORY_NAME: str = "Avatar Toolkit"
|
||||
|
||||
def draw_title(self: bpy.types.Panel):
|
||||
layout = self.layout
|
||||
layout.label(text=t("AvatarToolkit.desc1"))
|
||||
layout.label(text=t("AvatarToolkit.desc2"))
|
||||
layout.label(text=t("AvatarToolkit.desc3"))
|
||||
def draw_title(self: Panel) -> None:
|
||||
"""Draw the main panel title and description"""
|
||||
layout: UILayout = self.layout
|
||||
box: UILayout = layout.box()
|
||||
col: UILayout = box.column(align=True)
|
||||
|
||||
# Add a nice header
|
||||
row: UILayout = col.row()
|
||||
row.scale_y = 1.2
|
||||
row.label(text=t("AvatarToolkit.label"), icon='ARMATURE_DATA')
|
||||
|
||||
# Description as a flowing paragraph
|
||||
desc_col: UILayout = col.column()
|
||||
desc_col.scale_y = 0.6
|
||||
desc_col.label(text=t("AvatarToolkit.desc1"))
|
||||
desc_col.label(text=t("AvatarToolkit.desc2"))
|
||||
desc_col.label(text=t("AvatarToolkit.desc3"))
|
||||
col.separator()
|
||||
|
||||
class AvatarToolKit_PT_AvatarToolkitPanel(bpy.types.Panel):
|
||||
class AvatarToolKit_PT_AvatarToolkitPanel(Panel):
|
||||
"""Main panel for Avatar Toolkit containing general information and settings"""
|
||||
bl_label = t("AvatarToolkit.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self: bpy.types.Panel, context: bpy.types.Context):
|
||||
def draw(self, context: Context) -> None:
|
||||
"""Draw the main panel layout"""
|
||||
draw_title(self)
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import bpy
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from bpy.types import Panel, Context
|
||||
from ..core.common import get_selected_armature
|
||||
from ..core.translations import t
|
||||
from ..functions.armature_modifying import AvatarToolkit_OT_MergeArmatures
|
||||
|
||||
class AvatarToolkit_PT_MergeArmaturesPanel(Panel):
|
||||
bl_label = t("MergeArmatures.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_merge_armatures"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 5
|
||||
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
armature = get_selected_armature(context)
|
||||
|
||||
if armature:
|
||||
layout.label(text=t("MergeArmatures.title.label"), icon='ARMATURE_DATA')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
box = layout.box()
|
||||
col = box.column(align=True)
|
||||
|
||||
col.prop(context.scene.avatar_toolkit, "selected_armature", text=t("MergeArmatures.target_armature.label"), icon="ARMATURE_DATA")
|
||||
col.prop(context.scene.avatar_toolkit, "merge_armature_source", icon="OUTLINER_OB_ARMATURE")
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(context.scene.avatar_toolkit, "merge_armature_align_bones", icon="BONE_DATA")
|
||||
col.prop(context.scene.avatar_toolkit, "merge_armature_apply_transforms", icon="OBJECT_ORIGIN")
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
row = layout.row()
|
||||
row.scale_y = 1.5
|
||||
row.operator(operator=AvatarToolkit_OT_MergeArmatures.bl_idname, icon="ARMATURE_DATA")
|
||||
|
||||
else:
|
||||
layout.label(text=t("MergeArmatures.select_armature"), icon='ERROR')
|
||||
@@ -1,48 +0,0 @@
|
||||
import bpy
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from ..core.translations import t
|
||||
from ..functions.mmd_functions import *
|
||||
from ..functions.mesh_tools import AvatarToolKit_OT_JoinAllMeshes
|
||||
from ..functions.combine_materials import AvatarToolKit_OT_CombineMaterials
|
||||
from ..functions.additional_tools import AvatarToolKit_OT_ApplyTransforms
|
||||
|
||||
class AvatarToolkit_PT_MMDOptionsPanel(bpy.types.Panel):
|
||||
bl_label = t("MMDOptions.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_mmd_options"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 4
|
||||
|
||||
def draw(self, context: bpy.types.Context) -> None:
|
||||
layout = self.layout
|
||||
|
||||
layout.label(text=t("MMDOptions.title"), icon='OUTLINER_OB_ARMATURE')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.scale_y = 1.2
|
||||
col.operator(AvatarToolKit_OT_CleanupMesh.bl_idname, icon='BRUSH_DATA')
|
||||
col.operator(AvatarToolKit_OT_JoinAllMeshes.bl_idname, icon='OBJECT_DATAMODE')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.scale_y = 1.2
|
||||
col.operator(AvatarToolKit_OT_OptimizeWeights.bl_idname, icon='MOD_VERTEX_WEIGHT')
|
||||
col.operator(AvatarToolKit_OT_OptimizeArmature.bl_idname, icon='ARMATURE_DATA')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.2
|
||||
row.operator(AvatarToolKit_OT_ApplyTransforms.bl_idname, icon='OBJECT_ORIGIN')
|
||||
row.operator(AvatarToolKit_OT_CombineMaterials.bl_idname, icon='MATERIAL')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
row = layout.row()
|
||||
row.scale_y = 1.2
|
||||
row.operator(AvatarToolKit_OT_ConvertMaterials.bl_idname, icon='SHADING_TEXTURE')
|
||||
@@ -1,46 +0,0 @@
|
||||
import bpy
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from ..core.translations import t
|
||||
from ..functions.remove_doubles_safely import AvatarToolKit_OT_RemoveDoublesSafely, AvatarToolKit_OT_RemoveDoublesSafelyAdvanced
|
||||
from ..core.common import get_selected_armature
|
||||
from ..functions.mesh_tools import AvatarToolKit_OT_JoinAllMeshes, AvatarToolKit_OT_JoinSelectedMeshes
|
||||
from ..functions.combine_materials import AvatarToolKit_OT_CombineMaterials
|
||||
|
||||
class AvatarToolkit_PT_OptimizationPanel(bpy.types.Panel):
|
||||
bl_label = t("Optimization.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_optimization"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 2
|
||||
|
||||
def draw(self, context: bpy.types.Context):
|
||||
layout = self.layout
|
||||
armature = get_selected_armature(context)
|
||||
|
||||
if armature:
|
||||
layout.label(text=t("Optimization.options.label"), icon='SETTINGS')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.2
|
||||
row.operator(AvatarToolKit_OT_CombineMaterials.bl_idname, text=t("Optimization.combine_materials.label"), icon='MATERIAL')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.2
|
||||
row.operator(AvatarToolKit_OT_RemoveDoublesSafely.bl_idname, text=t("Optimization.remove_doubles_safely.label"), icon='SNAP_VERTEX')
|
||||
row.operator(AvatarToolKit_OT_RemoveDoublesSafelyAdvanced.bl_idname, text=t("Optimization.remove_doubles_safely_advanced.label"), icon="ACTION")
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
layout.label(text=t("Optimization.joinmeshes.label"), icon='OBJECT_DATA')
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.2
|
||||
row.operator(AvatarToolKit_OT_JoinAllMeshes.bl_idname, text=t("Optimization.join_all_meshes.label"), icon='OUTLINER_OB_MESH')
|
||||
row.operator(AvatarToolKit_OT_JoinSelectedMeshes.bl_idname, text=t("Optimization.join_selected_meshes.label"), icon='STICKY_UVS_LOC')
|
||||
|
||||
else:
|
||||
layout.label(text=t("Optimization.select_armature"), icon='ERROR')
|
||||
@@ -1,89 +0,0 @@
|
||||
import bpy
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from ..core.exporters.export_resonite import AvatarToolKit_OT_ExportResonite
|
||||
from bpy.types import Context, Mesh, Panel, Operator
|
||||
from ..core.translations import t
|
||||
from ..core.common import get_selected_armature
|
||||
from ..functions.import_anything import AvatarToolKit_OT_ImportAnyModel
|
||||
from ..functions.armature_modifying import (AvatarToolkit_OT_StartPoseMode,
|
||||
AvatarToolkit_OT_StopPoseMode,
|
||||
AvatarToolkit_OT_ApplyPoseAsRest,
|
||||
AvatarToolkit_OT_ApplyPoseAsShapekey)
|
||||
|
||||
class AvatarToolkitQuickAccessPanel(Panel):
|
||||
bl_label = t("Quick_Access.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_quick_access"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 1
|
||||
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
layout.label(text=t("Quick_Access.options"), icon='TOOL_SETTINGS')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
layout.label(text=t("Quick_Access.select_armature"), icon='ARMATURE_DATA')
|
||||
layout.prop(context.scene.avatar_toolkit, "selected_armature", text="")
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
layout.label(text=t("Quick_Access.import_export.label"), icon='IMPORT')
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.5
|
||||
row.operator(AvatarToolKit_OT_ImportAnyModel.bl_idname, text=t("Quick_Access.import"), icon='IMPORT')
|
||||
row.operator(AVATAR_TOOLKIT_OT_ExportMenu.bl_idname, text=t("Quick_Access.export"), icon='EXPORT')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
if get_selected_armature(context) != None:
|
||||
if(context.mode == "POSE"):
|
||||
col = layout.column(align=True)
|
||||
col.scale_y = 1.2
|
||||
col.operator(AvatarToolkit_OT_StopPoseMode.bl_idname, text=t("Quick_Access.stop_pose_mode.label"), icon='POSE_HLT')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.scale_y = 1.2
|
||||
col.operator(AvatarToolkit_OT_ApplyPoseAsRest.bl_idname, text=t("Quick_Access.apply_pose_as_rest.label"), icon='MOD_ARMATURE')
|
||||
col.operator(AvatarToolkit_OT_ApplyPoseAsShapekey.bl_idname, text=t("Quick_Access.apply_pose_as_shapekey.label"), icon='MOD_ARMATURE')
|
||||
else:
|
||||
row = layout.row()
|
||||
row.scale_y = 1.2
|
||||
row.operator(AvatarToolkit_OT_StartPoseMode.bl_idname, text=t("Quick_Access.start_pose_mode.label"), icon='POSE_HLT')
|
||||
|
||||
class AVATAR_TOOLKIT_OT_ExportMenu(bpy.types.Operator):
|
||||
bl_idname = "avatar_toolkit.export_menu"
|
||||
bl_label = t("Quick_Access.export_menu.label")
|
||||
bl_description = t("Quick_Access.export_menu.desc")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return any(obj.type == 'MESH' for obj in context.scene.objects)
|
||||
|
||||
def execute(self, context: Context) -> set[str]:
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context: Context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_popup(self, width=200)
|
||||
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
layout.label(text=t("Quick_Access.select_export.label"), icon='EXPORT')
|
||||
layout.operator(AvatarToolKit_OT_ExportResonite.bl_idname, text=t("Quick_Access.select_export_resonite.label"), icon='SCENE_DATA')
|
||||
layout.operator(AVATAR_TOOLKIT_OT_ExportFbx.bl_idname, text=t("Quick_Access.export_fbx.label"), icon='OBJECT_DATA')
|
||||
|
||||
class AVATAR_TOOLKIT_OT_ExportFbx(bpy.types.Operator):
|
||||
bl_idname = 'avatar_toolkit.export_fbx'
|
||||
bl_label = t("Quick_Access.export_fbx.label")
|
||||
bl_description = t("Quick_Access.export_fbx.desc")
|
||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||
|
||||
def execute(self, context) -> set[str]:
|
||||
bpy.ops.export_scene.fbx('INVOKE_DEFAULT')
|
||||
return {'FINISHED'}
|
||||
@@ -0,0 +1,132 @@
|
||||
import bpy
|
||||
from typing import Set, Optional, List, Tuple
|
||||
from bpy.types import Operator, Panel, Menu, Context, UILayout
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from ..core.translations import t
|
||||
from ..core.common import (
|
||||
get_active_armature,
|
||||
clear_default_objects,
|
||||
validate_armature,
|
||||
get_armature_list,
|
||||
get_armature_stats
|
||||
)
|
||||
from ..core.importers.importer import import_types, imports
|
||||
from ..functions.pose_mode import (
|
||||
AvatarToolkit_OT_StartPoseMode,
|
||||
AvatarToolkit_OT_StopPoseMode,
|
||||
AvatarToolkit_OT_ApplyPoseAsShapekey,
|
||||
AvatarToolkit_OT_ApplyPoseAsRest
|
||||
)
|
||||
|
||||
class AvatarToolKit_OT_Import(Operator):
|
||||
"""Import FBX files into Blender with Avatar Toolkit settings"""
|
||||
bl_idname = "avatar_toolkit.import"
|
||||
bl_label = t("QuickAccess.import")
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
clear_default_objects()
|
||||
bpy.ops.import_scene.fbx('INVOKE_DEFAULT', filter_glob=imports)
|
||||
return {'FINISHED'}
|
||||
|
||||
class AvatarToolKit_OT_ExportFBX(Operator):
|
||||
"""Export selected objects as FBX"""
|
||||
bl_idname = "avatar_toolkit.export_fbx"
|
||||
bl_label = 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 = "AVATAR_TOOLKIT_MT_export_menu"
|
||||
bl_label = 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 = "avatar_toolkit.export"
|
||||
bl_label = t("QuickAccess.export")
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
bpy.ops.wm.call_menu(name=AvatarToolKit_MT_ExportMenu.bl_idname)
|
||||
return {'FINISHED'}
|
||||
|
||||
class AvatarToolKit_PT_QuickAccessPanel(Panel):
|
||||
"""Quick access panel for common Avatar Toolkit operations"""
|
||||
bl_label = t("QuickAccess.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_quick_access"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 0
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: Context) -> bool:
|
||||
"""Only show panel in Object or Pose mode"""
|
||||
return context.mode in {'OBJECT', 'POSE'}
|
||||
|
||||
def draw(self, context: Context) -> None:
|
||||
"""Draw the panel layout"""
|
||||
layout: UILayout = self.layout
|
||||
|
||||
# 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 = get_active_armature(context)
|
||||
if active_armature:
|
||||
is_valid: bool
|
||||
message: str
|
||||
is_valid, message = validate_armature(active_armature)
|
||||
|
||||
if is_valid:
|
||||
info_box: UILayout = col.box()
|
||||
row: UILayout = info_box.row()
|
||||
split: UILayout = row.split(factor=0.6)
|
||||
split.label(text=t("QuickAccess.valid_armature"), icon='CHECKMARK')
|
||||
stats: dict = 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:
|
||||
col.separator(factor=0.5)
|
||||
col.label(text=message, icon='ERROR')
|
||||
|
||||
# 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')
|
||||
@@ -1,38 +0,0 @@
|
||||
import bpy
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from ..core.translations import t
|
||||
|
||||
class AvatarToolkitSettingsPanel(bpy.types.Panel):
|
||||
bl_label = t("Settings.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_settings"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 8
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.label(text=t("Settings.language.label"))
|
||||
layout.prop(context.scene.avatar_toolkit, "language", text="", icon='WORLD')
|
||||
|
||||
class AVATAR_TOOLKIT_OT_translation_restart_popup(bpy.types.Operator):
|
||||
bl_idname = "avatar_toolkit.translation_restart_popup"
|
||||
bl_label = t("Settings.translation_restart_popup.label")
|
||||
bl_description = t("Settings.translation_restart_popup.description")
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
def execute(self, context):
|
||||
if context.scene.avatar_toolkit.language_changed:
|
||||
bpy.ops.script.reload()
|
||||
context.scene.avatar_toolkit.language_changed = False
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self, width=300)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.label(text=t("Settings.translation_restart_popup.message1"), icon='INFO')
|
||||
layout.label(text=t("Settings.translation_restart_popup.message2"), icon='FILE_REFRESH')
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
import bpy
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from bpy.types import Context
|
||||
from ..functions.digitigrade_legs import AvatarToolKit_OT_CreateDigitigradeLegs
|
||||
from ..functions.resonite_functions import AvatarToolKit_OT_ConvertToResonite
|
||||
from ..core.translations import t
|
||||
from ..core.common import get_selected_armature
|
||||
from ..functions.mesh_tools import AvatarToolkit_OT_RemoveUnusedShapekeys
|
||||
from ..functions.additional_tools import (AvatarToolKit_OT_ApplyTransforms,
|
||||
AvatarToolKit_OT_ConnectBones,
|
||||
AvatarToolKit_OT_DeleteBoneConstraints,
|
||||
AvatarToolKit_OT_SeparateByMaterials,
|
||||
AvatarToolKit_OT_SeparateByLooseParts)
|
||||
from ..functions.armature_modifying import (AvatarToolkit_OT_RemoveZeroWeightBones,
|
||||
AvatarToolkit_OT_MergeBonesToActive,
|
||||
AvatarToolkit_OT_MergeBonesToParents)
|
||||
|
||||
class AvatarToolkit_PT_ToolsPanel(bpy.types.Panel):
|
||||
bl_label = t("Tools.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_tools"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 3
|
||||
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
armature = get_selected_armature(context)
|
||||
|
||||
if armature:
|
||||
layout.label(text=t("Tools.tools_title.label"), icon='TOOL_SETTINGS')
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.5
|
||||
row.operator(AvatarToolKit_OT_ConvertToResonite.bl_idname, text=t("Tools.convert_to_resonite.label"), icon='SCENE_DATA')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
layout.label(text=t("Tools.separate_by.label"), icon='MESH_DATA')
|
||||
row = layout.row(align=True)
|
||||
row.operator(AvatarToolKit_OT_SeparateByMaterials.bl_idname, text=t("Tools.separate_by_materials.label"), icon='MATERIAL')
|
||||
row.operator(AvatarToolKit_OT_SeparateByLooseParts.bl_idname, text=t("Tools.separate_by_loose_parts.label"), icon='OUTLINER_OB_MESH')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
layout.label(text=t("Tools.bone_tools.label"), icon='BONE_DATA')
|
||||
row = layout.row(align=True)
|
||||
row.operator(AvatarToolKit_OT_CreateDigitigradeLegs.bl_idname, text=t("Tools.create_digitigrade_legs.label"), icon='BONE_DATA')
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator(AvatarToolkit_OT_RemoveZeroWeightBones.bl_idname, text=t("Tools.remove_zero_weight_bones.label"), icon='BONE_DATA')
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator(AvatarToolkit_OT_MergeBonesToActive.bl_idname, text=t("Tools.merge_bones_to_active.label"), icon='BONE_DATA')
|
||||
row.operator(AvatarToolkit_OT_MergeBonesToParents.bl_idname, text=t("Tools.merge_bones_to_parents.label"), icon='BONE_DATA')
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator(AvatarToolKit_OT_ConnectBones.bl_idname, text=t("Tools.connect_bones.label"), icon='BONE_DATA')
|
||||
row.operator(AvatarToolKit_OT_DeleteBoneConstraints.bl_idname, text=t("Tools.delete_bone_constraints.label"), icon='CONSTRAINT_BONE')
|
||||
|
||||
row = layout.row()
|
||||
row.prop(context.scene.avatar_toolkit, "merge_twist_bones")
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
layout.label(text=t("Tools.additional_tools.label"), icon='TOOL_SETTINGS')
|
||||
row = layout.row(align=True)
|
||||
row.operator(AvatarToolKit_OT_ApplyTransforms.bl_idname, text=t("Tools.apply_transforms.label"), icon='OBJECT_ORIGIN')
|
||||
row.operator(AvatarToolkit_OT_RemoveUnusedShapekeys.bl_idname, text=t("Tools.remove_unused_shapekeys.label"), icon='SHAPEKEY_DATA')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
else:
|
||||
layout.label(text=t("Tools.select_armature"), icon='ERROR')
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import bpy
|
||||
from ..core.translations import t
|
||||
from .main_panel import draw_title
|
||||
|
||||
class UVTools_PT_MainPanel(bpy.types.Panel):
|
||||
bl_label = t("AvatarToolkit.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_uv"
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Avatar Toolkit"
|
||||
|
||||
def draw(self: bpy.types.Panel, context: bpy.types.Context):
|
||||
layout = self.layout
|
||||
draw_title(self)
|
||||
@@ -1,19 +0,0 @@
|
||||
import bpy
|
||||
from ..core.translations import t
|
||||
from ..functions.uv_tools import AvatarToolkit_OT_AlignUVEdgesToTarget
|
||||
from .main_panel import draw_title
|
||||
from .uv_panel import UVTools_PT_MainPanel
|
||||
|
||||
class UVTools_PT_Tools(bpy.types.Panel):
|
||||
bl_label = t("Tools.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_uv_tools"
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Avatar Toolkit"
|
||||
bl_parent_id = UVTools_PT_MainPanel.bl_idname
|
||||
bl_order = 3
|
||||
|
||||
def draw(self, context: bpy.types.Context):
|
||||
layout = self.layout
|
||||
row = layout.row(align=True)
|
||||
row.operator(AvatarToolkit_OT_AlignUVEdgesToTarget.bl_idname, text=t("avatar_toolkit.align_uv_edges_to_target.label"), icon='GP_MULTIFRAME_EDITING')
|
||||
@@ -1,53 +0,0 @@
|
||||
import bpy
|
||||
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||
from ..functions.viseme import AvatarToolKit_OT_AutoVisemeButton
|
||||
from ..core.translations import t
|
||||
from ..core.common import get_selected_armature
|
||||
|
||||
class AvatarToolkitVisemePanel(bpy.types.Panel):
|
||||
bl_label = t("VisemePanel.label")
|
||||
bl_idname = "OBJECT_PT_avatar_toolkit_viseme"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = CATEGORY_NAME
|
||||
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
|
||||
bl_order = 7
|
||||
|
||||
def draw(self, context: bpy.types.Context) -> None:
|
||||
layout = self.layout
|
||||
|
||||
armature = get_selected_armature(context)
|
||||
if armature:
|
||||
layout.label(text=t("VisemePanel.label"), icon='SOUND')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
layout.prop(context.scene.avatar_toolkit, "selected_mesh", text=t("VisemePanel.select_mesh"), icon='OUTLINER_OB_MESH')
|
||||
|
||||
mesh = bpy.data.objects.get(context.scene.avatar_toolkit.selected_mesh)
|
||||
if mesh and mesh.type == 'MESH':
|
||||
if mesh.data.shape_keys:
|
||||
box = layout.box()
|
||||
col = box.column(align=True)
|
||||
col.prop_search(context.scene.avatar_toolkit, "mouth_a", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_a.label'), icon='SHAPEKEY_DATA')
|
||||
col.prop_search(context.scene.avatar_toolkit, "mouth_o", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_o.label'), icon='SHAPEKEY_DATA')
|
||||
col.prop_search(context.scene.avatar_toolkit, "mouth_ch", mesh.data.shape_keys, "key_blocks", text=t('VisemePanel.mouth_ch.label'), icon='SHAPEKEY_DATA')
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
layout.prop(context.scene.avatar_toolkit, 'shape_intensity', text=t('VisemePanel.shape_intensity'), icon='FORCE_LENNARDJONES')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
|
||||
row = layout.row()
|
||||
row.scale_y = 1.5
|
||||
row.operator(AvatarToolKit_OT_AutoVisemeButton.bl_idname, text=t('VisemePanel.create_visemes'), icon='TRIA_RIGHT')
|
||||
else:
|
||||
layout.label(text=t('VisemePanel.error.noShapekeys'), icon='ERROR')
|
||||
else:
|
||||
layout.label(text=t('VisemePanel.error.selectMesh'), icon='INFO')
|
||||
else:
|
||||
layout.label(text=t('VisemePanel.error.noArmature'), icon='ERROR')
|
||||
|
||||
layout.separator(factor=1.0)
|
||||
layout.label(text=t('VisemePanel.info.selectMesh'), icon='HELP')
|
||||
Reference in New Issue
Block a user