Merge branch 'main' into UVAligner

This commit is contained in:
Yusarina
2024-09-11 01:41:05 +01:00
committed by GitHub
22 changed files with 479 additions and 93 deletions
+30 -8
View File
@@ -21,7 +21,7 @@ class SceneMatClass(PropertyGroup):
register_class(SceneMatClass)
class material_list_bool:
class MaterialListBool:
#For the love that is holy do not ever touch these. If this was java I would make these private
#They should only be accessed via context.scene.texture_atlas_Has_Mat_List_Shown
#This is so we know if the materials are up to date. messing with these variables directly will make the thing blow up.
@@ -31,9 +31,9 @@ class material_list_bool:
bool_material_list_expand: dict[str,bool] = {}
def set_bool(self, value: bool) -> None:
material_list_bool.bool_material_list_expand[bpy.context.scene.name] = value
MaterialListBool.bool_material_list_expand[bpy.context.scene.name] = value
if value == False:
material_list_bool.old_list[bpy.context.scene.name] = []
MaterialListBool.old_list[bpy.context.scene.name] = []
def get_bool(self) -> bool:
newlist: list[Material] = []
@@ -45,20 +45,20 @@ class material_list_bool:
newlist.append(mat_slot.material)
still_the_same: bool = True
if bpy.context.scene.name in material_list_bool.old_list:
if bpy.context.scene.name in MaterialListBool.old_list:
for item in newlist:
if item not in material_list_bool.old_list[bpy.context.scene.name]:
if item not in MaterialListBool.old_list[bpy.context.scene.name]:
still_the_same = False
break
for item in material_list_bool.old_list[bpy.context.scene.name]:
for item in MaterialListBool.old_list[bpy.context.scene.name]:
if item not in newlist:
still_the_same = False
break
else:
still_the_same = False
material_list_bool.bool_material_list_expand[bpy.context.scene.name] = still_the_same
MaterialListBool.bool_material_list_expand[bpy.context.scene.name] = still_the_same
return material_list_bool.bool_material_list_expand[bpy.context.scene.name]
return MaterialListBool.bool_material_list_expand[bpy.context.scene.name]
### Clean up material names in the given mesh by removing the '.001' suffix.
@@ -262,3 +262,25 @@ def update_progress(self, context, message):
def finish_progress(context):
context.window_manager.progress_end()
context.area.header_text_set(None)
def transfer_vertex_weights(context: Context, obj: bpy.types.Object, source_group: str, target_group: str, delete_source_group: bool = True) -> bool:
modifier: bpy.types.VertexWeightMixModifier = obj.modifiers.new(name="merge_weights",type="VERTEX_WEIGHT_MIX")
modifier.mix_set = 'B'
modifier.vertex_group_a = target_group
modifier.vertex_group_b = source_group
modifier.mask_constant = 1.0
bpy.ops.object.mode_set(mode='OBJECT')
prev_obj: bpy.types.Object = context.view_layer.objects.active
context.view_layer.objects.active = obj
bpy.ops.object.modifier_apply(modifier=modifier.name)
if delete_source_group:
obj.vertex_groups.remove(obj.vertex_groups.get(source_group))
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')
context.view_layer.objects.active = prev_obj
return True
+1 -1
View File
@@ -9,7 +9,7 @@ from ..functions.translations import t
@register_wrap
class ExportResonite(Operator):
class AvatarToolKit_OT_ExportResonite(Operator):
bl_idname = 'avatar_toolkit.export_resonite'
bl_label = t("Importer.export_resonite.label")
bl_description = t("Importer.export_resonite.desc")
+3 -3
View File
@@ -4,7 +4,7 @@ from ..core.register import register_property
from bpy.types import Scene, Object, Material, Context
from bpy.props import BoolProperty, EnumProperty, IntProperty, CollectionProperty, StringProperty, FloatVectorProperty, PointerProperty
from ..core.addon_preferences import get_preference
from ..core.common import SceneMatClass, material_list_bool, get_armatures, get_mesh_items
from ..core.common import SceneMatClass, MaterialListBool, get_armatures, get_mesh_items
def register() -> None:
default_language = get_preference("language", 0)
@@ -106,8 +106,8 @@ def register() -> None:
register_property((Scene, "texture_atlas_Has_Mat_List_Shown", BoolProperty(
default=False,
get=material_list_bool.get_bool,
set=material_list_bool.set_bool)))
get=MaterialListBool.get_bool,
set=MaterialListBool.set_bool)))
def unregister() -> None:
+1 -1
View File
@@ -5,7 +5,7 @@ from ..core.common import get_selected_armature, is_valid_armature, get_all_mesh
from ..functions.translations import t
@register_wrap
class ApplyTransforms(Operator):
class AvatarToolKit_OT_ApplyTransforms(Operator):
bl_idname = "avatar_toolkit.apply_transforms"
bl_label = t("Tools.apply_transforms.label")
bl_description = t("Tools.apply_transforms.desc")
+158
View File
@@ -0,0 +1,158 @@
import bpy
from ..core import common
from bpy.types import Operator, Context, Mesh, Armature, EditBone
from ..core.register import register_wrap
from .translations import t
@register_wrap
class AvatarToolkit_OT_RemoveZeroWeightBones(Operator):
bl_idname = "avatar_toolkit.remove_zero_weight_bones"
bl_label = t("Tools.remove_zero_weight_bones.label")
bl_description = t("Tools.remove_zero_weight_bones.desc")
bl_options = {'REGISTER', 'UNDO'}
threshold: bpy.props.FloatProperty(
default=0.01,
name=t("Tools.remove_zero_weight_bones.threshold.label"),
description=t("Tools.remove_zero_weight_bones.threshold.desc"),
min=0.0000001,
max=0.9999999)
@classmethod
def poll(cls, context: Context) -> bool:
return common.get_selected_armature(context) is not None
def execute(self, context: Context) -> set[str]:
armature = common.get_selected_armature(context)
if not common.is_valid_armature(armature):
self.report({'ERROR'}, t("Tools.apply_transforms.invalid_armature"))
return {'CANCELLED'}
weighted_bones: list[str] = []
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
armature.select_set(True)
context.view_layer.objects.active = armature
meshes = common.get_all_meshes(context)
for mesh in meshes:
mesh_data: Mesh = mesh.data
for vertex in mesh_data.vertices:
for group in vertex.groups:
if group.weight > self.threshold:
weighted_bones.append(mesh.vertex_groups[group.group].name) #add bone name to list of bones that are greater than the weight threshold
bpy.ops.object.mode_set(mode='EDIT')
amature_data: Armature = armature.data
unweighted_bones: list[str] = []
#doing 2 loops to prevent modification of array during iteration
for bone in amature_data.edit_bones:
if bone.name not in weighted_bones:
unweighted_bones.append(bone.name) #add bones that arent in the list of bones that have weight into the list of bones that don't
for bone_name in unweighted_bones:
for edit_bone in amature_data.edit_bones[bone_name].children:
edit_bone.use_connect = False #to fix randomly moving bones
edit_bone.parent = amature_data.edit_bones[bone_name].parent #to fix unparented bones.
amature_data.edit_bones.remove(amature_data.edit_bones[bone_name]) #delete list of unweighted bones from the armature
self.report({'INFO'}, t("Tools.remove_zero_weight_bones.success"))
return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_MergeBonesToActive(Operator):
bl_idname = "avatar_toolkit.merge_bones_to_active"
bl_label = t("Tools.merge_bones_to_active.label")
bl_description = t("Tools.merge_bones_to_active.desc")
bl_options = {'REGISTER', 'UNDO'}
delete_old: bpy.props.BoolProperty(name=t("Tools.merge_bones_to_active.delete_old.label"), description=t("Tools.merge_bones_to_active.delete_old.desc"), default=False)
@classmethod
def poll(cls, context: Context) -> bool:
if common.get_selected_armature(context) is not None:
if common.get_selected_armature(context) == context.view_layer.objects.active:
if context.mode == "POSE":
return len(context.selected_pose_bones) > 1
elif context.mode == "EDIT_ARMATURE":
return len(context.selected_bones) > 1
return False
def execute(cls, context: Context) -> set[str]:
prev_mode: str = "EDIT"
if context.mode == "POSE":
prev_mode = "POSE"
#get active bone and a list of all other selected bones
bpy.ops.object.mode_set(mode='EDIT')
target_bone: str = context.active_bone.name
armature_data: Armature = context.view_layer.objects.active.data
bones: list[str] = [i.name for i in context.selected_bones]
bones.remove(target_bone)
for obj in common.get_all_meshes(context):
for bone in bones:
bone_name: str = armature_data.edit_bones[bone].name
common.transfer_vertex_weights(context=context,obj=obj,source_group=bone_name,target_group=armature_data.edit_bones[target_bone].name)
bpy.ops.object.mode_set(mode='EDIT')
for bone in bones:
if cls.delete_old:
for bone_child in armature_data.edit_bones[bone].children:
bone_child.parent = armature_data.edit_bones[bone].parent
armature_data.edit_bones.remove(armature_data.edit_bones[bone])
bpy.ops.object.mode_set(mode=prev_mode)
return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_MergeBonesToParents(Operator):
bl_idname = "avatar_toolkit.merge_bones_to_parents"
bl_label = t("Tools.merge_bones_to_parents.label")
bl_description = t("Tools.merge_bones_to_parents.desc")
bl_options = {'REGISTER', 'UNDO'}
delete_old: bpy.props.BoolProperty(name=t("Tools.merge_bones_to_parents.delete_old.label"), description=t("Tools.merge_bones_to_parents.delete_old.desc"), default=False)
@classmethod
def poll(cls, context: Context) -> bool:
if common.get_selected_armature(context) is not None:
if common.get_selected_armature(context) == context.view_layer.objects.active:
if context.mode == "POSE":
return len(context.selected_pose_bones) > 0
elif context.mode == "EDIT_ARMATURE":
return len(context.selected_bones) > 0
return False
def execute(cls, context: Context) -> set[str]:
prev_mode: str = "EDIT"
if context.mode == "POSE":
prev_mode = "POSE"
#get active bone and a list of all other selected bones
bpy.ops.object.mode_set(mode='EDIT')
armature_data: Armature = context.view_layer.objects.active.data
for obj in common.get_all_meshes(context):
for bone in [i.name for i in context.selected_bones]:
if armature_data.edit_bones[bone].parent != None:
bone_name: str = armature_data.edit_bones[bone].name
common.transfer_vertex_weights(context=context,obj=obj,source_group=bone_name,target_group=armature_data.edit_bones[bone].parent.name)
bpy.ops.object.mode_set(mode='EDIT')
for bone in [i.name for i in context.selected_bones]:
if cls.delete_old:
for bone_child in armature_data.edit_bones[bone].children:
bone_child.parent = armature_data.edit_bones[bone].parent
armature_data.edit_bones.remove(armature_data.edit_bones[bone])
bpy.ops.object.mode_set(mode=prev_mode)
return {'FINISHED'}
+2 -4
View File
@@ -2,13 +2,11 @@ from pathlib import Path
import numpy
import bpy
import re
import os
from typing import List, Tuple, Optional
from mathutils import Vector
from bpy.types import Material, Operator, Context, Object, Image, Mesh, MeshUVLoopLayer, Float2AttributeValue, ShaderNodeTexImage, ShaderNodeBsdfPrincipled, ShaderNodeNormalMap
from ..core.register import register_wrap
from ..core.common import SceneMatClass, material_list_bool
from ..core.common import SceneMatClass, MaterialListBool
from ..core.packer.rectangle_packer import MaterialImageList, BinPacker
from ..functions.translations import t
@@ -113,7 +111,7 @@ def prep_images_in_scene(context: Context) -> list[MaterialImageList]:
@register_wrap
class Atlas_Materials(Operator):
class AvatarToolKit_OT_AtlasMaterials(Operator):
bl_idname = "avatar_toolkit.atlas_materials"
bl_label = t("TextureAtlas.atlas_materials")
+1 -1
View File
@@ -53,7 +53,7 @@ def get_base_name(name: str) -> str:
return mat_match.group(1) if mat_match else name
@register_wrap
class CombineMaterials(Operator):
class AvatarToolKit_OT_CombineMaterials(Operator):
bl_idname = "avatar_toolkit.combine_materials"
bl_label = t("Optimization.combine_materials.label")
bl_description = t("Optimization.combine_materials.desc")
+2 -2
View File
@@ -6,8 +6,8 @@ import re
@register_wrap
class CreateDigitigradeLegs(bpy.types.Operator):
bl_idname = "avatar_toolkit.createdigitigradelegs"
class AvatarToolKit_OT_CreateDigitigradeLegs(bpy.types.Operator):
bl_idname = "avatar_toolkit.create_digitigrade_legs"
bl_label = t('Tools.create_digitigrade_legs.label')
bl_description = t('Tools.create_digitigrade_legs.desc')
+5 -6
View File
@@ -3,14 +3,13 @@ from bpy.types import Operator
from bpy_extras.io_utils import ImportHelper
from ..core.register import register_wrap
from ..core.importer import imports, import_types
from ..core.common import remove_default_objects
from ..core.common import remove_default_objects, open_web_after_delay_multi_threaded
from ..functions.translations import t
import pathlib
import os
from ..core import common
@register_wrap
class ImportAnyModel(Operator, ImportHelper):
class AvatarToolKit_OT_ImportAnyModel(Operator, ImportHelper):
bl_idname = 'avatar_toolkit.import_any_model'
bl_label = t('Tools.import_any_model.label')
bl_description = t('Tools.import_any_model.desc')
@@ -24,7 +23,7 @@ class ImportAnyModel(Operator, ImportHelper):
#since I wrote this myself, a bit more efficent than cats. mostly - @989onan
def execute(self, context: bpy.types.Context):
file_grouping_dict: dict[str, list[dict[str,str]]] = dict()#group our files so our importers can import them together. in the case of OBJ+MTL and others that need grouped files, this is extremely important.
common.remove_default_objects()
remove_default_objects()
#check if we are importing multiple files
is_multi = False
try:
@@ -69,9 +68,9 @@ class ImportAnyModel(Operator, ImportHelper):
else:
import_types[file_group_name]("",files,self.filepath) #give an empty directory, works just fine for 90%
except AttributeError as e:
print("Warning, you may not have the required importer!")
print("Warning, you may not have the required importer for extension type \"{extension}\"!".format(extension = file_group_name))
common.open_web_after_delay_multi_threaded(delay=12, url=t('Importing.importer_search_term').format(extension = file_group_name))
open_web_after_delay_multi_threaded(delay=12, url=t('Importing.importer_search_term').format(extension = file_group_name))
self.report({'ERROR'},t('Importing.need_importer').format(extension = file_group_name))
+2 -2
View File
@@ -6,7 +6,7 @@ from ..core.common import fix_uv_coordinates, get_selected_armature, is_valid_ar
from ..functions.translations import t
@register_wrap
class JoinAllMeshes(Operator):
class AvatarToolKit_OT_JoinAllMeshes(Operator):
bl_idname = "avatar_toolkit.join_all_meshes"
bl_label = t("Optimization.join_all_meshes.label")
bl_description = t("Optimization.join_all_meshes.desc")
@@ -72,7 +72,7 @@ class JoinAllMeshes(Operator):
finish_progress(context)
@register_wrap
class JoinSelectedMeshes(Operator):
class AvatarToolKit_OT_JoinSelectedMeshes(Operator):
bl_idname = "avatar_toolkit.join_selected_meshes"
bl_label = t("Optimization.join_selected_meshes.label")
bl_description = t("Optimization.join_selected_meshes.desc")
+2 -2
View File
@@ -12,7 +12,7 @@ class meshEntry(TypedDict):
cur_vertex_pass: int
@register_wrap
class RemoveDoublesSafelyAdvanced(Operator):
class AvatarToolKit_OT_RemoveDoublesSafelyAdvanced(Operator):
bl_idname = "avatar_toolkit.remove_doubles_safely_advanced"
bl_label = t("Optimization.remove_doubles_safely_advanced.label")
bl_description = t("Optimization.remove_doubles_safely_advanced.desc")
@@ -30,7 +30,7 @@ class RemoveDoublesSafelyAdvanced(Operator):
bpy.ops.avatar_toolkit.remove_doubles_safely('INVOKE_DEFAULT',advanced=True,merge_distance=self.merge_distance)
return {'FINISHED'}
@register_wrap
class RemoveDoublesSafely(Operator):
class AvatarToolKit_OT_RemoveDoublesSafely(Operator):
bl_idname = "avatar_toolkit.remove_doubles_safely"
bl_label = t("Optimization.remove_doubles_safely.label")
bl_description = t("Optimization.remove_doubles_safely.desc")
+1 -1
View File
@@ -8,7 +8,7 @@ from ..core.common import get_selected_armature, simplify_bonename, is_valid_arm
from ..functions.translations import t
@register_wrap
class ConvertToResonite(Operator):
class AvatarToolKit_OT_ConvertToResonite(Operator):
bl_idname = 'avatar_toolkit.convert_to_resonite'
bl_label = t('Tools.convert_to_resonite.label')
bl_description = t('Tools.convert_to_resonite.desc')
+2 -3
View File
@@ -1,11 +1,10 @@
import bpy
from bpy.types import Context, Operator
from ..core.register import register_wrap
from ..core.common import get_selected_armature, is_valid_armature, select_current_armature
from ..functions.translations import t
@register_wrap
class SeparateByMaterials(Operator):
class AvatarToolKit_OT_SeparateByMaterials(Operator):
bl_idname = "avatar_toolkit.separate_by_materials"
bl_label = t("Tools.separate_by_materials.label")
bl_description = t("Tools.separate_by_materials.desc")
@@ -25,7 +24,7 @@ class SeparateByMaterials(Operator):
return {'FINISHED'}
@register_wrap
class SeparateByLooseParts(Operator):
class AvatarToolKit_OT_SeparateByLooseParts(Operator):
bl_idname = "avatar_toolkit.separate_by_loose_parts"
bl_label = t("Tools.separate_by_loose_parts.label")
bl_description = t("Tools.separate_by_loose_parts.desc")
+1 -1
View File
@@ -6,7 +6,7 @@ from typing import List, Tuple
from ..core.common import get_selected_armature, is_valid_armature, get_all_meshes, init_progress, update_progress, finish_progress
@register_wrap
class AutoVisemeButton(bpy.types.Operator):
class AvatarToolKit_OT_AutoVisemeButton(bpy.types.Operator):
bl_idname = 'avatar_toolkit.create_visemes'
bl_label = t('AutoVisemeButton.label')
bl_description = t('AutoVisemeButton.desc')
+22
View File
@@ -79,6 +79,15 @@
"Quick_Access.label": "Quick Access",
"Quick_Access.options": "Quick Access:",
"Quick_Access.select_armature": "Select Armature:",
"Quick_Access.apply_armature_failed": "Applying armature as pose failed at the joining shapekeys back together stage!",
"Quick_Access.apply_pose_as_rest.desc": "Makes current pose the default rest pose.",
"Quick_Access.stop_pose_mode.desc": "Exits pose mode and clears all posing on all visible bones in pose mode.",
"Quick_Access.apply_pose_as_rest.label": "Apply Pose as Rest Pose",
"Quick_Access.apply_pose_as_shapekey.desc": "Makes the current pose a shapekey that can be activated later.\nThis is good for applying a jaw open position as a shapekey for facial movements.",
"Quick_Access.apply_pose_as_shapekey.label": "Apply Pose as Shapekey",
"Quick_Access.stop_pose_mode.label": "Exit Pose Mode",
"Quick_Access.start_pose_mode.desc": "Starts pose mode for the armature targeted by Avatar Toolkit.",
"Quick_Access.start_pose_mode.label": "Start Pose Mode",
"Quick_Access.select_export.label": "Select Export Method",
"Quick_Access.select_export_resonite.label": "Resonite",
"Settings.label": "Settings",
@@ -136,6 +145,19 @@
"Tools.apply_transforms.desc": "Apply position, rotation, and scale to the armature and its meshes",
"Tools.apply_transforms.invalid_armature": "Invalid armature selected",
"Tools.apply_transforms.success": "Transforms applied successfully to armature and meshes",
"Tools.remove_zero_weight_bones.success": "Zero weight bones removed successfully",
"Tools.remove_zero_weight_bones.label": "Remove Zero Weight Bones",
"Tools.remove_zero_weight_bones.desc": "Remove bones from the armature that have weights less than threshold.",
"Tools.merge_bones_to_active.delete_old.desc": "Remove old bones when merging.",
"Tools.merge_bones_to_active.delete_old.label": "Remove Old Bones",
"Tools.merge_bones_to_active.desc": "Merge selected bones to active bone (selected in bright blue or orange).",
"Tools.merge_bones_to_active.label": "Merge Bones to Active",
"Tools.merge_bones_to_parents.delete_old.desc": "Remove old bones when merging.",
"Tools.merge_bones_to_parents.delete_old.label": "Remove Old Bones",
"Tools.merge_bones_to_parents.desc": "Merges every bone in the selection to each of their parents.",
"Tools.merge_bones_to_parents.label": "Merge Bones to Individual Parents",
"Tools.remove_zero_weight_bones.threshold.label": "Weight Threshold",
"Tools.remove_zero_weight_bones.threshold.desc": "If a bone is not weighted to any part of any mesh under the armature with a threshold greater than this, it is removed",
"VisemePanel.create_visemes": "Create Visemes",
"VisemePanel.creating_viseme": "Creating viseme: {viseme_name}",
"VisemePanel.creating_viseme_detail": "Creating viseme: {viseme_name}",
+12 -12
View File
@@ -1,13 +1,13 @@
from bpy.types import UIList, Panel, UILayout, Object, Context,Material, Operator
import bpy
from ..core.register import register_wrap
from .panel import AvatarToolkitPanel
from ..core.common import SceneMatClass, material_list_bool, get_selected_armature
from ..functions.atlas_materials import Atlas_Materials
from .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 ..functions.translations import t
@register_wrap
class ExpandSection_Materials(Operator):
class AvatarToolKit_OT_ExpandSectionMaterials(Operator):
bl_idname = 'avatar_toolkit.expand_section_materials'
bl_label = ""
bl_description = ""
@@ -28,13 +28,13 @@ class ExpandSection_Materials(Operator):
newlist.append(mat_slot.material)
newitem: SceneMatClass = context.scene.materials.add()
newitem.mat = mat_slot.material
material_list_bool.old_list[context.scene.name] = newlist
MaterialListBool.old_list[context.scene.name] = newlist
else:
context.scene.texture_atlas_Has_Mat_List_Shown = False
return {'FINISHED'}
@register_wrap
class MaterialTextureAtlasProperties(UIList):
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'
@@ -59,13 +59,13 @@ class MaterialTextureAtlasProperties(UIList):
col.prop(item.mat, "texture_atlas_roughness")
@register_wrap
class TextureAtlasPanel(Panel):
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 = "Avatar Toolkit"
bl_parent_id = "OBJECT_PT_avatar_toolkit"
bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 4
def draw(self, context: Context):
@@ -77,12 +77,12 @@ class TextureAtlasPanel(Panel):
boxoutter = row.box()
direction_icon = 'RIGHTARROW' if not context.scene.texture_atlas_Has_Mat_List_Shown else 'DOWNARROW_HLT'
row = boxoutter.row()
row.operator(ExpandSection_Materials.bl_idname, text=(t("TextureAtlas.reload_list") if not context.scene.texture_atlas_Has_Mat_List_Shown else t("TextureAtlas.loaded_list")), icon=direction_icon)
row.operator(AvatarToolKit_OT_ExpandSectionMaterials.bl_idname, text=(t("TextureAtlas.reload_list") if not context.scene.texture_atlas_Has_Mat_List_Shown else t("TextureAtlas.loaded_list")), icon=direction_icon)
if context.scene.texture_atlas_Has_Mat_List_Shown:
row = boxoutter.row()
row.template_list(MaterialTextureAtlasProperties.bl_idname, 'material_list', context.scene, 'materials',
row.template_list(AvatarToolKit_UL_MaterialTextureAtlasProperties.bl_idname, 'material_list', context.scene, 'materials',
context.scene, 'texture_atlas_material_index', rows=12, type='DEFAULT')
row = layout.row()
row.operator(Atlas_Materials.bl_idname, text=t("TextureAtlas.atlas_materials"))
row.operator(AvatarToolKit_OT_AtlasMaterials.bl_idname, text=t("TextureAtlas.atlas_materials"))
else:
layout.label(text=t("Tools.select_armature"), icon='ERROR')
+12 -10
View File
@@ -1,18 +1,20 @@
import bpy
from ..core.register import register_wrap
from .panel import AvatarToolkitPanel
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..functions.translations import t
from ..functions.remove_doubles_safely import RemoveDoublesSafely, RemoveDoublesSafelyAdvanced
from ..functions.remove_doubles_safely import AvatarToolKit_OT_RemoveDoublesSafely, AvatarToolKit_OT_RemoveDoublesSafelyAdvanced
from ..core.common import get_selected_armature
from ..functions.join_meshes import AvatarToolKit_OT_JoinAllMeshes, AvatarToolKit_OT_JoinSelectedMeshes
from ..functions.combine_materials import AvatarToolKit_OT_CombineMaterials
@register_wrap
class AvatarToolkitOptimizationPanel(bpy.types.Panel):
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 = "Avatar Toolkit"
bl_parent_id = "OBJECT_PT_avatar_toolkit"
bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 2
def draw(self: bpy.types.Panel, context: bpy.types.Context):
@@ -24,18 +26,18 @@ class AvatarToolkitOptimizationPanel(bpy.types.Panel):
row = layout.row()
row.scale_y = 1.2
row.operator("avatar_toolkit.combine_materials", text=t("Optimization.combine_materials.label"), icon='MATERIAL')
row.operator(AvatarToolKit_OT_CombineMaterials.bl_idname, text=t("Optimization.combine_materials.label"), icon='MATERIAL')
row = layout.row(align=True)
row.scale_y = 1.2
row.operator(RemoveDoublesSafely.bl_idname, text=t("Optimization.remove_doubles_safely.label"), icon='SNAP_VERTEX')
row.operator(RemoveDoublesSafelyAdvanced.bl_idname, text=t("Optimization.remove_doubles_safely_advanced.label"), icon = "ACTION")
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=0.5)
layout.label(text=t("Optimization.joinmeshes.label"), icon='SETTINGS')
row = layout.row(align=True)
row.scale_y = 1.2
row.operator("avatar_toolkit.join_all_meshes", text=t("Optimization.join_all_meshes.label"), icon='OUTLINER_OB_MESH')
row.operator("avatar_toolkit.join_selected_meshes", text=t("Optimization.join_selected_meshes.label"), icon='STICKY_UVS_LOC')
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')
+3 -2
View File
@@ -8,14 +8,15 @@ def draw_title(self: bpy.types.Panel):
layout.label(text=t("AvatarToolkit.description"))
layout.label(text=t("AvatarToolkit.alpha_warning"))
CATEGORY_NAME = "Avatar Toolkit"
@register_wrap
class AvatarToolkitPanel(bpy.types.Panel):
class AvatarToolKit_PT_AvatarToolkitPanel(bpy.types.Panel):
bl_label = t("AvatarToolkit.label")
bl_idname = "OBJECT_PT_avatar_toolkit"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Avatar Toolkit"
bl_category = CATEGORY_NAME
def draw(self: bpy.types.Panel, context: bpy.types.Context):
draw_title(self)
+192 -16
View File
@@ -1,23 +1,23 @@
import bpy
from ..core.register import register_wrap
from .panel import AvatarToolkitPanel
from bpy.types import Context
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..core.export_resonite import AvatarToolKit_OT_ExportResonite
from bpy.types import Context, Mesh, Panel, Operator
from ..functions.translations import t
from ..core.import_pmx import import_pmx
from ..core.import_pmd import import_pmd
from ..functions.import_anything import ImportAnyModel
from ..core.common import get_selected_armature, set_selected_armature
from ..functions.import_anything import AvatarToolKit_OT_ImportAnyModel
from ..core.common import get_selected_armature, set_selected_armature, get_all_meshes
@register_wrap
@register_wrap
class AvatarToolkitQuickAccessPanel(bpy.types.Panel):
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 = "Avatar Toolkit"
bl_parent_id = "OBJECT_PT_avatar_toolkit"
bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 1
def draw(self, context: Context):
@@ -35,11 +35,23 @@ class AvatarToolkitQuickAccessPanel(bpy.types.Panel):
row = layout.row(align=True)
row.scale_y = 1.5
row.operator(ImportAnyModel.bl_idname, text=t("Quick_Access.import"), icon='IMPORT')
row.operator("avatar_toolkit.export_menu", text=t("Quick_Access.export"), icon='EXPORT')
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')
if get_selected_armature(context) != None:
if(context.mode == "POSE"):
row = layout.row(align=True)
row.operator(AvatarToolkit_OT_StopPoseMode.bl_idname, text=t("Quick_Access.stop_pose_mode.label"), icon='POSE_HLT')
row = layout.row(align=True)
row.operator(AvatarToolkit_OT_ApplyPoseAsRest.bl_idname, text=t("Quick_Access.apply_pose_as_rest.label"), icon='MOD_ARMATURE')
row = layout.row(align=True)
row.operator(AvatarToolkit_OT_ApplyPoseAsShapekey.bl_idname, text=t("Quick_Access.apply_pose_as_shapekey.label"), icon='MOD_ARMATURE')
else:
row = layout.row(align=True)
row.operator(AvatarToolkit_OT_StartPoseMode.bl_idname, text=t("Quick_Access.start_pose_mode.label"), icon='POSE_HLT')
@register_wrap
class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator):
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")
@@ -48,7 +60,7 @@ class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator):
def poll(cls, context):
return any(obj.type == 'MESH' for obj in context.scene.objects)
def execute(self, context: Context):
def execute(self, context: Context) -> set[str]:
return {'FINISHED'}
def invoke(self, context: Context, event):
@@ -58,16 +70,180 @@ class AVATAR_TOOLKIT_OT_export_menu(bpy.types.Operator):
def draw(self, context: Context):
layout = self.layout
layout.label(text=t("Quick_Access.select_export.label"), icon='EXPORT')
layout.operator("avatar_toolkit.export_resonite", text=t("Quick_Access.select_export_resonite.label"), icon='SCENE_DATA')
layout.operator("avatar_toolkit.export_fbx", text=t("Quick_Access.export_fbx.label"), icon='OBJECT_DATA')
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')
@register_wrap
class AVATAR_TOOLKIT_OT_export_fbx(bpy.types.Operator):
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):
def execute(self, context) -> set[str]:
bpy.ops.export_scene.fbx('INVOKE_DEFAULT')
return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_StartPoseMode(Operator):
bl_idname = 'avatar_toolkit.start_pose_mode'
bl_label = t("Quick_Access.start_pose_mode.label")
bl_description = t("Quick_Access.start_pose_mode.desc")
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return get_selected_armature(context) != None and context.mode != "POSE"
def execute(self, context: Context) -> set[str]:
context.view_layer.objects.active = get_selected_armature(context)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
context.view_layer.objects.active = get_selected_armature(context)
context.view_layer.objects.active.select_set(True)
bpy.ops.object.mode_set(mode='POSE')
return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_StopPoseMode(Operator):
bl_idname = 'avatar_toolkit.stop_pose_mode'
bl_label = t("Quick_Access.stop_pose_mode.label")
bl_description = t("Quick_Access.stop_pose_mode.desc")
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return get_selected_armature(context) != None and context.mode == "POSE"
def execute(self, context: Context) -> set[str]:
bpy.ops.pose.transforms_clear()
bpy.ops.pose.select_all(action="INVERT")
bpy.ops.pose.transforms_clear()
bpy.ops.pose.select_all(action="INVERT")
bpy.ops.object.mode_set(mode='OBJECT')
return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_ApplyPoseAsShapekey(Operator):
bl_idname = 'avatar_toolkit.apply_pose_as_shapekey'
bl_label = t("Quick_Access.apply_pose_as_shapekey.label")
bl_description = t("Quick_Access.apply_pose_as_shapekey.desc")
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return get_selected_armature(context) != None and context.mode == "POSE"
def execute(self, context: Context):
bpy.ops.object.mode_set(mode="OBJECT")
for obj in get_all_meshes(context):
modifier_armature_name: str = ""
context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.select_all(action="DESELECT")
context.view_layer.objects.active = obj
obj.select_set(True)
for modifier in obj.modifiers:
if modifier.type == "ARMATURE":
arm_modifier: bpy.types.ArmatureModifier = modifier
modifier_armature_name = arm_modifier.object.name
bpy.ops.object.modifier_apply_as_shapekey(modifier=modifier_armature_name,keep_modifier=True,report=True)
return {'FINISHED'}
@register_wrap
class AvatarToolkit_OT_ApplyPoseAsRest(Operator):
bl_idname = 'avatar_toolkit.apply_pose_as_rest'
bl_label = t("Quick_Access.apply_pose_as_rest.label")
bl_description = t("Quick_Access.apply_pose_as_rest.desc")
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return get_selected_armature(context) != None and context.mode == "POSE"
def execute(self, context: Context):
for obj in get_all_meshes(context):
mesh_data: Mesh = obj.data
if mesh_data.shape_keys:
shape_key_obj_list: list[bpy.types.Object] = []
modifier_armature_name: str = ""
for modifier in obj.modifiers:
if modifier.type == "ARMATURE":
arm_modifier: bpy.types.ArmatureModifier = modifier
modifier_armature_name = arm_modifier.object.name
for idx,shape in enumerate(mesh_data.shape_keys.key_blocks):
if idx == 0:
continue
context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.select_all(action="DESELECT")
context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.object.duplicate()
shape_obj = context.view_layer.objects.active
shape_obj.active_shape_key_index = idx
shape_obj.name = shape.name
bpy.ops.object.shape_key_move(type="TOP")
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.shape_key_remove(all=True)
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
shape_key_obj_list.append(shape_obj)
context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode="OBJECT")
context.view_layer.objects.active.select_set(True)
bpy.ops.object.shape_key_remove(all=True)
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
bpy.ops.object.select_all(action="DESELECT")
for shapekey_obj in shape_key_obj_list:
shapekey_obj.select_set(True)
context.view_layer.objects.active = obj
context.view_layer.objects.active.select_set(True)
try:
bpy.ops.object.join_shapes()
except:
self.report({'ERROR'}, t("Quick_Access.apply_armature_failed"))
context.view_layer.objects.active = shape_key_obj_list[0]
obj.select_set(False)
bpy.ops.object.delete(confirm=False)
return {'CANCELLED'}
context.view_layer.objects.active = shape_key_obj_list[0]
obj.select_set(False)
bpy.ops.object.delete(confirm=False)
else:
modifier_armature_name: str = ""
for modifier in obj.modifiers:
if modifier.type == "ARMATURE":
arm_modifier: bpy.types.ArmatureModifier = modifier
modifier_armature_name = arm_modifier.object.name
context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.select_all(action="DESELECT")
context.view_layer.objects.active.select_set(True)
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
armature_obj: bpy.types.Object = get_selected_armature(context)
context.view_layer.objects.active = armature_obj
armature_obj.select_set(True)
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="POSE")
bpy.ops.pose.armature_apply(selected=False)
return {'FINISHED'}
+3 -3
View File
@@ -1,6 +1,6 @@
import bpy
from ..core.register import register_wrap
from .panel import AvatarToolkitPanel
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..functions.translations import t
@register_wrap
@@ -9,8 +9,8 @@ class AvatarToolkitSettingsPanel(bpy.types.Panel):
bl_idname = "OBJECT_PT_avatar_toolkit_settings"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Avatar Toolkit"
bl_parent_id = "OBJECT_PT_avatar_toolkit"
bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 6
def draw(self, context):
+19 -12
View File
@@ -1,21 +1,23 @@
import bpy
from ..core.register import register_wrap
from .panel import AvatarToolkitPanel
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from bpy.types import Context
from ..functions.digitigrade_legs import CreateDigitigradeLegs
from ..functions.digitigrade_legs import AvatarToolKit_OT_CreateDigitigradeLegs
from ..functions.resonite_functions import AvatarToolKit_OT_ConvertToResonite
from ..functions.translations import t
from ..core.common import get_selected_armature
from ..functions.seperate_by import SeparateByMaterials, SeparateByLooseParts
from ..functions.additional_tools import ApplyTransforms
from ..functions.seperate_by import AvatarToolKit_OT_SeparateByMaterials, AvatarToolKit_OT_SeparateByLooseParts
from ..functions.additional_tools import AvatarToolKit_OT_ApplyTransforms
from ..functions.armature_modifying import AvatarToolkit_OT_RemoveZeroWeightBones, AvatarToolkit_OT_MergeBonesToActive, AvatarToolkit_OT_MergeBonesToParents
@register_wrap
class AvatarToolkitToolsPanel(bpy.types.Panel):
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 = "Avatar Toolkit"
bl_parent_id = "OBJECT_PT_avatar_toolkit"
bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 3
def draw(self, context: Context):
@@ -28,15 +30,20 @@ class AvatarToolkitToolsPanel(bpy.types.Panel):
row = layout.row(align=True)
row.scale_y = 1.5
row.operator("avatar_toolkit.convert_to_resonite", text=t("Tools.convert_to_resonite.label"), icon='SCENE_DATA')
row.operator(AvatarToolKit_OT_ConvertToResonite.bl_idname, text=t("Tools.convert_to_resonite.label"), icon='SCENE_DATA')
row = layout.row(align=True)
row.operator(CreateDigitigradeLegs.bl_idname, text=t("Tools.create_digitigrade_legs.label"), icon='BONE_DATA')
row.operator(AvatarToolKit_OT_CreateDigitigradeLegs.bl_idname, text=t("Tools.create_digitigrade_legs.label"), icon='BONE_DATA')
layout.separator()
row = layout.row(align=True)
layout.label(text=t("Tools.separate_by.label"), icon='MESH_DATA')
row.operator(SeparateByMaterials.bl_idname, text=t("Tools.separate_by_materials.label"), icon='MATERIAL')
row.operator(SeparateByLooseParts.bl_idname, text=t("Tools.separate_by_loose_parts.label"), icon='OUTLINER_OB_MESH')
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')
row = layout.row(align=True)
row.operator(ApplyTransforms.bl_idname, text=t("Tools.apply_transforms.label"), icon='OBJECT_ORIGIN')
row.operator(AvatarToolKit_OT_ApplyTransforms.bl_idname, text=t("Tools.apply_transforms.label"), icon='OBJECT_ORIGIN')
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')
else:
layout.label(text=t("Tools.select_armature"), icon='ERROR')
+5 -3
View File
@@ -1,5 +1,7 @@
import bpy
from ..core.register import register_wrap
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from ..functions.viseme import AvatarToolKit_OT_AutoVisemeButton
from ..functions.translations import t
from ..core.common import get_selected_armature
@@ -9,8 +11,8 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
bl_idname = "OBJECT_PT_avatar_toolkit_viseme"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Avatar Toolkit"
bl_parent_id = "OBJECT_PT_avatar_toolkit"
bl_category = CATEGORY_NAME
bl_parent_id = AvatarToolKit_PT_AvatarToolkitPanel.bl_idname
bl_order = 5
def draw(self, context: bpy.types.Context) -> None:
@@ -32,7 +34,7 @@ class AvatarToolkitVisemePanel(bpy.types.Panel):
row = layout.row()
row.scale_y = 1.2
row.operator("avatar_toolkit.create_visemes", text=t('VisemePanel.create_visemes'), icon='TRIA_RIGHT')
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: