Fixes
This fixes is to get everything working on the new auto load and properties system. Also some other small fixes.
This commit is contained in:
+8
-9
@@ -161,15 +161,15 @@ def get_armatures(self, context: Context) -> List[Tuple[str, str, str]]:
|
|||||||
return armatures
|
return armatures
|
||||||
|
|
||||||
def get_armatures_that_are_not_selected(self, context: Context) -> List[Tuple[str, str, str]]:
|
def get_armatures_that_are_not_selected(self, context: Context) -> List[Tuple[str, str, str]]:
|
||||||
armatures = [(obj.name, obj.name, "") for obj in bpy.data.objects if ((obj.type == 'ARMATURE') and (obj.name != context.scene.selected_armature))]
|
armatures = [(obj.name, obj.name, "") for obj in bpy.data.objects if ((obj.type == 'ARMATURE') and (obj.name != context.scene.avatar_toolkit.selected_armature))]
|
||||||
if not armatures:
|
if not armatures:
|
||||||
return [('NONE', 'No Other Armature', '')]
|
return [('NONE', 'No Other Armature', '')]
|
||||||
return armatures
|
return armatures
|
||||||
|
|
||||||
def get_selected_armature(context: Context) -> Optional[Object]:
|
def get_selected_armature(context: Context) -> Optional[Object]:
|
||||||
try:
|
try:
|
||||||
if hasattr(context.scene, 'selected_armature'):
|
if hasattr(context.scene, 'avatar_toolkit'):
|
||||||
armature_name = context.scene.selected_armature
|
armature_name = context.scene.avatar_toolkit.selected_armature
|
||||||
if isinstance(armature_name, bytes):
|
if isinstance(armature_name, bytes):
|
||||||
try:
|
try:
|
||||||
armature_name = armature_name.decode('utf-8')
|
armature_name = armature_name.decode('utf-8')
|
||||||
@@ -209,9 +209,8 @@ def get_merge_armature_source(context: Context) -> Optional[Object]:
|
|||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def set_selected_armature(context: Context, armature: Optional[Object]) -> None:
|
def set_selected_armature(context: Context, armature: Optional[Object]) -> None:
|
||||||
context.scene.selected_armature = armature.name if armature else ""
|
context.scene.avatar_toolkit.selected_armature = armature.name if armature else ""
|
||||||
|
|
||||||
def is_valid_armature(armature: Object) -> bool:
|
def is_valid_armature(armature: Object) -> bool:
|
||||||
if not armature or armature.type != 'ARMATURE':
|
if not armature or armature.type != 'ARMATURE':
|
||||||
@@ -451,12 +450,12 @@ def remove_default_objects():
|
|||||||
|
|
||||||
def init_progress(context, steps):
|
def init_progress(context, steps):
|
||||||
context.window_manager.progress_begin(0, 100)
|
context.window_manager.progress_begin(0, 100)
|
||||||
context.scene.avatar_toolkit_progress_steps = steps
|
context.scene.avatar_toolkit.progress_steps = steps
|
||||||
context.scene.avatar_toolkit_progress_current = 0
|
context.scene.avatar_toolkit.progress_current = 0
|
||||||
|
|
||||||
def update_progress(self, context, message):
|
def update_progress(self, context, message):
|
||||||
context.scene.avatar_toolkit_progress_current += 1
|
context.scene.avatar_toolkit.progress_current += 1
|
||||||
progress = (context.scene.avatar_toolkit_progress_current / context.scene.avatar_toolkit_progress_steps) * 100
|
progress = (context.scene.avatar_toolkit.progress_current / context.scene.avatar_toolkit.progress_steps) * 100
|
||||||
context.window_manager.progress_update(progress)
|
context.window_manager.progress_update(progress)
|
||||||
context.area.header_text_set(message)
|
context.area.header_text_set(message)
|
||||||
self.report({'INFO'}, message)
|
self.report({'INFO'}, message)
|
||||||
|
|||||||
+11
-2
@@ -99,6 +99,13 @@ class AvatarToolkitSceneProperties(PropertyGroup):
|
|||||||
set=MaterialListBool.set_bool
|
set=MaterialListBool.set_bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
avatar_toolkit_updater_version_list: EnumProperty(
|
||||||
|
items=get_version_list,
|
||||||
|
name="Version List",
|
||||||
|
description="List of available versions"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AvatarToolkitMaterialProperties(PropertyGroup):
|
class AvatarToolkitMaterialProperties(PropertyGroup):
|
||||||
material_expanded: BoolProperty(
|
material_expanded: BoolProperty(
|
||||||
name="Expand Material",
|
name="Expand Material",
|
||||||
@@ -113,11 +120,13 @@ class AvatarToolkitMaterialProperties(PropertyGroup):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_texture_node_list(self, context):
|
def get_texture_node_list(self, context):
|
||||||
if self.use_nodes:
|
# Access the material through the property group's id_data
|
||||||
|
material = self.id_data
|
||||||
|
if material and material.use_nodes:
|
||||||
nodes = [(i.image.name if i.image else i.name+"_image",
|
nodes = [(i.image.name if i.image else i.name+"_image",
|
||||||
i.image.name if i.image else "node with no image...",
|
i.image.name if i.image else "node with no image...",
|
||||||
i.image.name if i.image else i.name, index+1)
|
i.image.name if i.image else i.name, index+1)
|
||||||
for index, i in enumerate(self.node_tree.nodes)
|
for index, i in enumerate(material.node_tree.nodes)
|
||||||
if i.bl_idname == "ShaderNodeTexImage"]
|
if i.bl_idname == "ShaderNodeTexImage"]
|
||||||
if not nodes:
|
if not nodes:
|
||||||
nodes = [("Error", "No images found", "Error", 0)]
|
nodes = [("Error", "No images found", "Error", 0)]
|
||||||
|
|||||||
@@ -84,11 +84,11 @@ def get_languages_list(self, context) -> List[Tuple[str, str, str]]:
|
|||||||
return [(str(i), get_language_display_name(lang), f"Use {lang} language") for i, lang in enumerate(languages)]
|
return [(str(i), get_language_display_name(lang), f"Use {lang} language") for i, lang in enumerate(languages)]
|
||||||
|
|
||||||
def update_language(self, context):
|
def update_language(self, context):
|
||||||
print(f"Updating language to: {self.avatar_toolkit_language}") # Debug print
|
print(f"Updating language to: {self.language}") # Debug print
|
||||||
save_preference("language", int(self.avatar_toolkit_language))
|
save_preference("language", int(self.language))
|
||||||
load_translations()
|
load_translations()
|
||||||
# Set a flag to indicate that a language change has occurred
|
# Set a flag to indicate that a language change has occurred
|
||||||
context.scene.avatar_toolkit_language_changed = True
|
context.scene.avatar_toolkit.language_changed = True
|
||||||
# Show popup after language change
|
# Show popup after language change
|
||||||
bpy.ops.avatar_toolkit.translation_restart_popup('INVOKE_DEFAULT')
|
bpy.ops.avatar_toolkit.translation_restart_popup('INVOKE_DEFAULT')
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -287,7 +287,7 @@ def draw_updater_panel(context: bpy.types.Context, layout: bpy.types.UILayout) -
|
|||||||
|
|
||||||
col.separator()
|
col.separator()
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.prop(context.scene, 'avatar_toolkit_updater_version_list', text='')
|
row.prop(context.scene.avatar_toolkit, 'avatar_toolkit_updater_version_list', text='')
|
||||||
row.operator(AvatarToolkit_OT_UpdateToLatest.bl_idname, text=t('Updater.UpdateToSelectedButton.label'))
|
row.operator(AvatarToolkit_OT_UpdateToLatest.bl_idname, text=t('Updater.UpdateToSelectedButton.label'))
|
||||||
|
|
||||||
col.separator()
|
col.separator()
|
||||||
|
|||||||
@@ -156,6 +156,15 @@ class AvatarToolkit_OT_RemoveZeroWeightBones(Operator):
|
|||||||
'matrix': bone.matrix.copy(),
|
'matrix': bone.matrix.copy(),
|
||||||
'parent': bone.parent.name if bone.parent else None
|
'parent': bone.parent.name if bone.parent else None
|
||||||
}
|
}
|
||||||
|
# Add end bones to transforms
|
||||||
|
if bone.name.endswith('_end'):
|
||||||
|
initial_transforms[bone.name] = {
|
||||||
|
'head': bone.head.copy(),
|
||||||
|
'tail': bone.tail.copy(),
|
||||||
|
'roll': bone.roll,
|
||||||
|
'matrix': bone.matrix.copy(),
|
||||||
|
'parent': bone.parent.name if bone.parent else None
|
||||||
|
}
|
||||||
|
|
||||||
# Get weighted bones
|
# Get weighted bones
|
||||||
armature.select_set(True)
|
armature.select_set(True)
|
||||||
|
|||||||
+114
-90
@@ -23,24 +23,24 @@ class MaterialImageList:
|
|||||||
self.h: int = 0
|
self.h: int = 0
|
||||||
self.fit = None
|
self.fit = None
|
||||||
|
|
||||||
def scale_images_to_largest(images: list[Image]) -> set:
|
def scale_images_to_largest(images: list[Image]) -> tuple[int, int]:
|
||||||
x: int = 0
|
try:
|
||||||
y: int = 0
|
valid_images = []
|
||||||
|
for img in images:
|
||||||
# Filter out None or invalid images
|
if img and hasattr(img, 'name'):
|
||||||
valid_images = [img for img in images if img and img.has_data]
|
image_data = bpy.data.images.get(img.name)
|
||||||
|
if image_data and image_data.has_data:
|
||||||
|
valid_images.append(image_data)
|
||||||
|
|
||||||
if not valid_images:
|
if not valid_images:
|
||||||
return 0, 0
|
return 1, 1
|
||||||
|
|
||||||
for image in valid_images:
|
max_width = max(img.size[0] for img in valid_images)
|
||||||
x = max(x, image.size[0])
|
max_height = max(img.size[1] for img in valid_images)
|
||||||
y = max(y, image.size[1])
|
|
||||||
|
|
||||||
for image in valid_images:
|
return max_width, max_height
|
||||||
image.scale(width=int(x), height=int(y))
|
except:
|
||||||
|
return 1, 1
|
||||||
return x, y
|
|
||||||
|
|
||||||
def MaterialImageList_to_Image_list(classitem: MaterialImageList) -> list[Image]:
|
def MaterialImageList_to_Image_list(classitem: MaterialImageList) -> list[Image]:
|
||||||
list_of_images: list[Image] = []
|
list_of_images: list[Image] = []
|
||||||
@@ -62,56 +62,75 @@ def get_material_images_from_scene(context: Context) -> list[MaterialImageList]:
|
|||||||
if obj.type == 'MESH':
|
if obj.type == 'MESH':
|
||||||
for mat_slot in obj.material_slots:
|
for mat_slot in obj.material_slots:
|
||||||
# Only process materials that are selected for atlas
|
# Only process materials that are selected for atlas
|
||||||
if mat_slot.material and mat_slot.material.include_in_atlas is True:
|
if mat_slot.material and mat_slot.material.avatar_toolkit.include_in_atlas:
|
||||||
new_mat_image_item = MaterialImageList()
|
new_mat_image_item = MaterialImageList()
|
||||||
try:
|
|
||||||
new_mat_image_item.albedo = bpy.data.images[mat_slot.material.texture_atlas_albedo]
|
def get_or_create_image(image_name, replacement_name, default_color):
|
||||||
except Exception:
|
if image_name and image_name in bpy.data.images:
|
||||||
name = mat_slot.material.name + "_albedo_replacement"
|
image = bpy.data.images[image_name]
|
||||||
if name in bpy.data.images:
|
else:
|
||||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
# Create a new image with the replacement name if it doesn't exist
|
||||||
new_mat_image_item.albedo = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
if replacement_name in bpy.data.images:
|
||||||
new_mat_image_item.albedo.pixels[:] = numpy.tile(numpy.array([0.0,0.0,0.0,1.0]), 32*32)
|
image = bpy.data.images[replacement_name]
|
||||||
try:
|
else:
|
||||||
new_mat_image_item.normal = bpy.data.images[mat_slot.material.texture_atlas_normal]
|
image = bpy.data.images.new(
|
||||||
except Exception:
|
name=replacement_name, width=32, height=32, alpha=True
|
||||||
name = mat_slot.material.name + "_normal_replacement"
|
)
|
||||||
if name in bpy.data.images:
|
# Set the pixel data to the default color
|
||||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
num_pixels = 32 * 32
|
||||||
new_mat_image_item.normal = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
pixel_data = numpy.tile(numpy.array(default_color), num_pixels)
|
||||||
new_mat_image_item.normal.pixels[:] = numpy.tile(numpy.array([0.5,0.5,1.0,1.0]), 32*32)
|
image.pixels[:] = pixel_data
|
||||||
try:
|
# Set use_fake_user to True to prevent Blender from removing the image
|
||||||
new_mat_image_item.emission = bpy.data.images[mat_slot.material.texture_atlas_emission]
|
image.use_fake_user = True
|
||||||
except Exception:
|
return image
|
||||||
name = mat_slot.material.name + "_emission_replacement"
|
|
||||||
if name in bpy.data.images:
|
# Albedo
|
||||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
albedo_name = getattr(mat_slot.material, 'texture_atlas_albedo', '')
|
||||||
new_mat_image_item.emission = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
new_mat_image_item.albedo = get_or_create_image(
|
||||||
new_mat_image_item.emission.pixels[:] = numpy.tile(numpy.array([0.0,0.0,0.0,1.0]), 32*32)
|
albedo_name,
|
||||||
try:
|
mat_slot.material.name + "_albedo_replacement",
|
||||||
new_mat_image_item.ambient_occlusion = bpy.data.images[mat_slot.material.texture_atlas_ambient_occlusion]
|
[0.0, 0.0, 0.0, 1.0]
|
||||||
except Exception:
|
)
|
||||||
name = mat_slot.material.name + "_ambient_occlusion_replacement"
|
|
||||||
if name in bpy.data.images:
|
# Normal
|
||||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
normal_name = getattr(mat_slot.material, 'texture_atlas_normal', '')
|
||||||
new_mat_image_item.ambient_occlusion = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
new_mat_image_item.normal = get_or_create_image(
|
||||||
new_mat_image_item.ambient_occlusion.pixels[:] = numpy.tile(numpy.array([1.0,1.0,1.0,1.0]), 32*32)
|
normal_name,
|
||||||
try:
|
mat_slot.material.name + "_normal_replacement",
|
||||||
new_mat_image_item.height = bpy.data.images[mat_slot.material.texture_atlas_height]
|
[0.5, 0.5, 1.0, 1.0]
|
||||||
except Exception:
|
)
|
||||||
name = mat_slot.material.name + "_height_replacement"
|
|
||||||
if name in bpy.data.images:
|
# Emission
|
||||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
emission_name = getattr(mat_slot.material, 'texture_atlas_emission', '')
|
||||||
new_mat_image_item.height = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
new_mat_image_item.emission = get_or_create_image(
|
||||||
new_mat_image_item.height.pixels[:] = numpy.tile(numpy.array([0.5,0.5,0.5,1.0]), 32*32)
|
emission_name,
|
||||||
try:
|
mat_slot.material.name + "_emission_replacement",
|
||||||
new_mat_image_item.roughness = bpy.data.images[mat_slot.material.texture_atlas_roughness]
|
[0.0, 0.0, 0.0, 1.0]
|
||||||
except Exception:
|
)
|
||||||
name = mat_slot.material.name + "_roughness_replacement"
|
|
||||||
if name in bpy.data.images:
|
# Ambient Occlusion
|
||||||
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
ao_name = getattr(mat_slot.material, 'texture_atlas_ambient_occlusion', '')
|
||||||
new_mat_image_item.roughness = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
new_mat_image_item.ambient_occlusion = get_or_create_image(
|
||||||
new_mat_image_item.roughness.pixels[:] = numpy.tile(numpy.array([1.0,1.0,1.0,0.0]), 32*32)
|
ao_name,
|
||||||
|
mat_slot.material.name + "_ambient_occlusion_replacement",
|
||||||
|
[1.0, 1.0, 1.0, 1.0]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Height
|
||||||
|
height_name = getattr(mat_slot.material, 'texture_atlas_height', '')
|
||||||
|
new_mat_image_item.height = get_or_create_image(
|
||||||
|
height_name,
|
||||||
|
mat_slot.material.name + "_height_replacement",
|
||||||
|
[0.5, 0.5, 0.5, 1.0]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Roughness
|
||||||
|
roughness_name = getattr(mat_slot.material, 'texture_atlas_roughness', '')
|
||||||
|
new_mat_image_item.roughness = get_or_create_image(
|
||||||
|
roughness_name,
|
||||||
|
mat_slot.material.name + "_roughness_replacement",
|
||||||
|
[1.0, 1.0, 1.0, 0.0]
|
||||||
|
)
|
||||||
|
|
||||||
new_mat_image_item.material = mat_slot.material
|
new_mat_image_item.material = mat_slot.material
|
||||||
new_mat_image_item.parent_mesh = obj
|
new_mat_image_item.parent_mesh = obj
|
||||||
@@ -120,6 +139,7 @@ def get_material_images_from_scene(context: Context) -> list[MaterialImageList]:
|
|||||||
return material_image_list
|
return material_image_list
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def prep_images_in_scene(context: Context) -> list[MaterialImageList]:
|
def prep_images_in_scene(context: Context) -> list[MaterialImageList]:
|
||||||
preped_images: list[MaterialImageList] = get_material_images_from_scene(context)
|
preped_images: list[MaterialImageList] = get_material_images_from_scene(context)
|
||||||
for MaterialImageClass in preped_images:
|
for MaterialImageClass in preped_images:
|
||||||
@@ -135,7 +155,6 @@ def prep_images_in_scene(context: Context) -> list[MaterialImageList]:
|
|||||||
|
|
||||||
|
|
||||||
class AvatarToolKit_OT_AtlasMaterials(Operator):
|
class AvatarToolKit_OT_AtlasMaterials(Operator):
|
||||||
|
|
||||||
bl_idname = "avatar_toolkit.atlas_materials"
|
bl_idname = "avatar_toolkit.atlas_materials"
|
||||||
bl_label = t("TextureAtlas.atlas_materials")
|
bl_label = t("TextureAtlas.atlas_materials")
|
||||||
bl_description = t("TextureAtlas.atlas_materials_desc")
|
bl_description = t("TextureAtlas.atlas_materials_desc")
|
||||||
@@ -143,12 +162,13 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context: Context) -> bool:
|
def poll(cls, context: Context) -> bool:
|
||||||
return context.scene.texture_atlas_Has_Mat_List_Shown
|
return context.scene.avatar_toolkit.texture_atlas_Has_Mat_List_Shown
|
||||||
|
|
||||||
def execute(self, context: Context) -> set:
|
def execute(self, context: Context) -> set:
|
||||||
try:
|
try:
|
||||||
# Get only materials that are explicitly marked for inclusion
|
# Get only materials that are explicitly marked for inclusion
|
||||||
selected_materials = [m for m in prep_images_in_scene(context) if m.material and m.material.include_in_atlas is True]
|
selected_materials = [m for m in prep_images_in_scene(context)
|
||||||
|
if m.material and m.material.avatar_toolkit.include_in_atlas is True]
|
||||||
|
|
||||||
if not selected_materials:
|
if not selected_materials:
|
||||||
self.report({'WARNING'}, t("TextureAtlas.no_materials_selected"))
|
self.report({'WARNING'}, t("TextureAtlas.no_materials_selected"))
|
||||||
@@ -157,13 +177,24 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
packer: BinPacker = BinPacker(selected_materials)
|
packer: BinPacker = BinPacker(selected_materials)
|
||||||
mat_images = packer.fit()
|
mat_images = packer.fit()
|
||||||
|
|
||||||
size: list[int] = [max([matimg.fit.w + matimg.albedo.size[0] for matimg in mat_images]),
|
size: list[int] = [
|
||||||
max([matimg.fit.h + matimg.albedo.size[1] for matimg in mat_images])]
|
max([
|
||||||
print([matimg.fit.w + matimg.albedo.size[1] for matimg in mat_images])
|
matimg.fit.w + matimg.albedo.size[0]
|
||||||
|
for matimg in mat_images
|
||||||
|
if matimg.albedo and matimg.albedo.has_data
|
||||||
|
] or [1]),
|
||||||
|
max([
|
||||||
|
matimg.fit.h + matimg.albedo.size[1]
|
||||||
|
for matimg in mat_images
|
||||||
|
if matimg.albedo and matimg.albedo.has_data
|
||||||
|
] or [1])
|
||||||
|
]
|
||||||
|
print([matimg.fit.w + matimg.albedo.size[0] for matimg in mat_images if matimg.albedo and matimg.albedo.has_data])
|
||||||
|
|
||||||
atlased_mat: MaterialImageList = MaterialImageList()
|
atlased_mat: MaterialImageList = MaterialImageList()
|
||||||
|
|
||||||
for mat in mat_images:
|
for mat in mat_images:
|
||||||
|
if mat.albedo and mat.albedo.has_data:
|
||||||
x: int = int(mat.fit.x)
|
x: int = int(mat.fit.x)
|
||||||
y: int = int(mat.fit.y)
|
y: int = int(mat.fit.y)
|
||||||
w: int = int(mat.albedo.size[0])
|
w: int = int(mat.albedo.size[0])
|
||||||
@@ -182,10 +213,10 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
uv_item.vector.x = (uv_item.vector.x * (w / size[0])) + (x / size[0])
|
uv_item.vector.x = (uv_item.vector.x * (w / size[0])) + (x / size[0])
|
||||||
uv_item.vector.y = (uv_item.vector.y * (h / size[1])) + (y / size[1])
|
uv_item.vector.y = (uv_item.vector.y * (h / size[1])) + (y / size[1])
|
||||||
|
|
||||||
for type in ["albedo","normal", "emission","ambient_occlusion","height", "roughness"]:
|
for texture_type in ["albedo", "normal", "emission", "ambient_occlusion", "height", "roughness"]:
|
||||||
new_image_name: str= "Atlas_"+type+"_"+context.scene.name+"_"+Path(bpy.data.filepath).stem
|
new_image_name: str = f"Atlas_{texture_type}_{context.scene.name}_{Path(bpy.data.filepath).stem}"
|
||||||
|
|
||||||
print("Processing "+type+" atlas image")
|
print(f"Processing {texture_type} atlas image")
|
||||||
|
|
||||||
if new_image_name in bpy.data.images:
|
if new_image_name in bpy.data.images:
|
||||||
bpy.data.images.remove(bpy.data.images[new_image_name])
|
bpy.data.images.remove(bpy.data.images[new_image_name])
|
||||||
@@ -194,34 +225,27 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
c_w = canvas.size[0]
|
c_w = canvas.size[0]
|
||||||
canvas_pixels: list[float] = list(canvas.pixels[:])
|
canvas_pixels: list[float] = list(canvas.pixels[:])
|
||||||
for mat in mat_images:
|
for mat in mat_images:
|
||||||
|
image_var: Image = getattr(mat, texture_type, None)
|
||||||
|
if image_var and image_var.has_data:
|
||||||
x: int = int(mat.fit.x)
|
x: int = int(mat.fit.x)
|
||||||
y: int = int(mat.fit.y)
|
y: int = int(mat.fit.y)
|
||||||
w: int = int(mat.albedo.size[0])
|
w: int = int(image_var.size[0])
|
||||||
h: int = int(mat.albedo.size[1])
|
h: int = int(image_var.size[1])
|
||||||
|
|
||||||
image_var: Image = eval("mat."+type)
|
|
||||||
|
|
||||||
image_pixels: list[float] = list(image_var.pixels[:])
|
image_pixels: list[float] = list(image_var.pixels[:])
|
||||||
|
|
||||||
print("writing image \""+image_var.name+"\" to canvas.")
|
print(f"Writing image \"{image_var.name}\" to canvas.")
|
||||||
print("x: \""+str(x)+"\" "+"y: \""+str(y)+"\" "+"w: \""+str(w)+"\" "+"h: \""+str(h)+"\" ")
|
print(f"x: \"{x}\" y: \"{y}\" w: \"{w}\" h: \"{h}\"")
|
||||||
for k in range(0, h):
|
for k in range(0, h):
|
||||||
for i in range(0, w):
|
for i in range(0, w):
|
||||||
for channel in range(0, 4):
|
for channel in range(0, 4):
|
||||||
canvas_pixels[
|
canvas_index = (((k + y) * c_w) + (i + x)) * 4 + channel
|
||||||
int((((k+y)*c_w)
|
image_index = ((k * w) + i) * 4 + channel
|
||||||
+
|
canvas_pixels[int(canvas_index)] = image_pixels[int(image_index)]
|
||||||
(i+x))*4)
|
|
||||||
+int(channel)
|
|
||||||
] = image_pixels[
|
|
||||||
int((
|
|
||||||
(k*w)
|
|
||||||
+i)*4)
|
|
||||||
+int(channel)]
|
|
||||||
|
|
||||||
canvas.pixels[:] = canvas_pixels[:]
|
canvas.pixels[:] = canvas_pixels[:]
|
||||||
canvas.save(filepath=os.path.join(os.path.dirname(bpy.data.filepath), new_image_name + ".png"))
|
canvas.save(filepath=os.path.join(os.path.dirname(bpy.data.filepath), new_image_name + ".png"))
|
||||||
exec("atlased_mat."+type+" = canvas")
|
setattr(atlased_mat, texture_type, canvas)
|
||||||
|
|
||||||
#I am sorry for the amount of nodes I'm instanciating here and their values.
|
#I am sorry for the amount of nodes I'm instanciating here and their values.
|
||||||
#This is so that the nodes look pretty in the UI, which I think looks kinda nice. - @989onan
|
#This is so that the nodes look pretty in the UI, which I think looks kinda nice. - @989onan
|
||||||
@@ -285,7 +309,7 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
if obj.type == 'MESH':
|
if obj.type == 'MESH':
|
||||||
mesh: Mesh = obj.data
|
mesh: Mesh = obj.data
|
||||||
for i, mat_slot in enumerate(obj.material_slots):
|
for i, mat_slot in enumerate(obj.material_slots):
|
||||||
if mat_slot.material and mat_slot.material.include_in_atlas is True:
|
if mat_slot.material and mat_slot.material.avatar_toolkit.include_in_atlas is True:
|
||||||
mesh.materials[i] = atlased_mat.material
|
mesh.materials[i] = atlased_mat.material
|
||||||
|
|
||||||
self.report({'INFO'}, t("TextureAtlas.atlas_completed"))
|
self.report({'INFO'}, t("TextureAtlas.atlas_completed"))
|
||||||
|
|||||||
@@ -51,10 +51,12 @@ class AvatarToolkit_OT_RemoveUnusedShapekeys(bpy.types.Operator):
|
|||||||
to_delete.append(kb.name)
|
to_delete.append(kb.name)
|
||||||
|
|
||||||
for kb_name in to_delete:
|
for kb_name in to_delete:
|
||||||
if ("-" in kb_name) or ("=" in kb_name) or ("~" in kb_name): #don't delete category names. - @989onan
|
if ("-" in kb_name) or ("=" in kb_name) or ("~" in kb_name):
|
||||||
continue
|
continue
|
||||||
ob.shape_key_remove(ob.data.shape_keys.key_blocks[kb_name])
|
ob.shape_key_remove(ob.data.shape_keys.key_blocks[kb_name])
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class AvatarToolkit_OT_ApplyShapeKey(bpy.types.Operator):
|
class AvatarToolkit_OT_ApplyShapeKey(bpy.types.Operator):
|
||||||
bl_idname = "avatar_toolkit.apply_shape_key"
|
bl_idname = "avatar_toolkit.apply_shape_key"
|
||||||
|
|||||||
@@ -302,7 +302,6 @@ def add_principled_shader(material: Material, bake_mmd=True):
|
|||||||
if material.blend_method != 'OPAQUE':
|
if material.blend_method != 'OPAQUE':
|
||||||
principled_shader.inputs["Alpha"].default_value = material.alpha_threshold
|
principled_shader.inputs["Alpha"].default_value = material.alpha_threshold
|
||||||
material.blend_method = 'CLIP'
|
material.blend_method = 'CLIP'
|
||||||
material.shadow_method = 'CLIP'
|
|
||||||
|
|
||||||
def fix_mmd_shader(material: Material):
|
def fix_mmd_shader(material: Material):
|
||||||
mmd_shader_node = material.node_tree.nodes.get("mmd_shader")
|
mmd_shader_node = material.node_tree.nodes.get("mmd_shader")
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
import os
|
|
||||||
import json
|
|
||||||
import bpy
|
|
||||||
from bpy.app.translations import locale
|
|
||||||
from typing import Dict, List, Tuple
|
|
||||||
from ..core.addon_preferences import save_preference, get_preference
|
|
||||||
|
|
||||||
# Use __file__ to get the current file's directory
|
|
||||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
main_dir = os.path.dirname(current_dir)
|
|
||||||
resources_dir = os.path.join(main_dir, "resources")
|
|
||||||
translations_dir = os.path.join(resources_dir, "translations")
|
|
||||||
|
|
||||||
dictionary: Dict[str, str] = dict()
|
|
||||||
languages: List[str] = []
|
|
||||||
verbose: bool = True
|
|
||||||
|
|
||||||
def load_translations() -> bool:
|
|
||||||
global dictionary, languages
|
|
||||||
|
|
||||||
old_dictionary = dictionary.copy()
|
|
||||||
|
|
||||||
dictionary = dict()
|
|
||||||
languages = ["auto"]
|
|
||||||
|
|
||||||
# Populate languages list
|
|
||||||
for i in os.listdir(translations_dir):
|
|
||||||
lang = i.split(".")[0]
|
|
||||||
if lang != "auto":
|
|
||||||
languages.append(lang)
|
|
||||||
|
|
||||||
language_index = get_preference("language", 0)
|
|
||||||
# print(f"Loading translations for language index: {language_index}") # Debug print
|
|
||||||
|
|
||||||
if language_index == 0: # "auto"
|
|
||||||
language = bpy.context.preferences.view.language
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
language = languages[language_index]
|
|
||||||
except IndexError:
|
|
||||||
language = bpy.context.preferences.view.language
|
|
||||||
|
|
||||||
# print(f"Selected language: {language}") # Debug print
|
|
||||||
|
|
||||||
translation_file: str = os.path.join(translations_dir, language + ".json")
|
|
||||||
if os.path.exists(translation_file):
|
|
||||||
with open(translation_file, 'r', encoding='utf-8') as file:
|
|
||||||
dictionary = json.load(file)["messages"]
|
|
||||||
# print(f"Loaded translations: {dictionary}") # Debug print
|
|
||||||
else:
|
|
||||||
custom_language: str = language.split("_")[0]
|
|
||||||
custom_translation_file: str = os.path.join(translations_dir, custom_language + ".json")
|
|
||||||
if os.path.exists(custom_translation_file):
|
|
||||||
with open(custom_translation_file, 'r', encoding='utf-8') as file:
|
|
||||||
dictionary = json.load(file)["messages"]
|
|
||||||
# print(f"Loaded custom translations: {dictionary}") # Debug print
|
|
||||||
else:
|
|
||||||
print(f"Translation file not found for language: {language}")
|
|
||||||
default_file: str = os.path.join(translations_dir, "en_US.json")
|
|
||||||
if os.path.exists(default_file):
|
|
||||||
with open(default_file, 'r', encoding='utf-8') as file:
|
|
||||||
dictionary = json.load(file)["messages"]
|
|
||||||
# print(f"Loaded default translations: {dictionary}") # Debug print
|
|
||||||
else:
|
|
||||||
print("Default translation file 'en_US.json' not found.")
|
|
||||||
|
|
||||||
return dictionary != old_dictionary
|
|
||||||
|
|
||||||
def t(phrase: str, default: str = None, **kwargs) -> str:
|
|
||||||
output: str = dictionary.get(phrase)
|
|
||||||
if output is None:
|
|
||||||
if verbose:
|
|
||||||
print(f'Warning: Unknown phrase: {phrase}')
|
|
||||||
return default if default is not None else phrase
|
|
||||||
# print(f"Translating '{phrase}' to '{output}'") # Debug print
|
|
||||||
return output.format(**kwargs) if kwargs else output
|
|
||||||
|
|
||||||
def get_language_display_name(lang: str) -> str:
|
|
||||||
if lang == "auto":
|
|
||||||
return t("Language.auto", "Automatic")
|
|
||||||
return t(f"Language.{lang}", lang)
|
|
||||||
|
|
||||||
def get_languages_list(self, context) -> List[Tuple[str, str, str]]:
|
|
||||||
return [(str(i), get_language_display_name(lang), f"Use {lang} language") for i, lang in enumerate(languages)]
|
|
||||||
|
|
||||||
def update_language(self, context):
|
|
||||||
print(f"Updating language to: {self.avatar_toolkit_language}") # Debug print
|
|
||||||
save_preference("language", int(self.avatar_toolkit_language))
|
|
||||||
load_translations()
|
|
||||||
# Set a flag to indicate that a language change has occurred
|
|
||||||
context.scene.avatar_toolkit_language_changed = True
|
|
||||||
# Show popup after language change
|
|
||||||
bpy.ops.avatar_toolkit.translation_restart_popup('INVOKE_DEFAULT')
|
|
||||||
|
|
||||||
# Initial load of translations
|
|
||||||
# print("Performing initial load of translations") # Debug print
|
|
||||||
load_translations()
|
|
||||||
+5
-5
@@ -28,16 +28,16 @@ class AvatarToolKit_OT_AutoVisemeButton(bpy.types.Operator):
|
|||||||
init_progress(context, 5) # 5 main steps
|
init_progress(context, 5) # 5 main steps
|
||||||
|
|
||||||
update_progress(self, context, t("VisemePanel.start_viseme_creation"))
|
update_progress(self, context, t("VisemePanel.start_viseme_creation"))
|
||||||
mesh = bpy.data.objects.get(context.scene.selected_mesh)
|
mesh = bpy.data.objects.get(context.scene.avatar_toolkit.selected_mesh)
|
||||||
if not mesh or not common.has_shapekeys(mesh):
|
if not mesh or not common.has_shapekeys(mesh):
|
||||||
raise ValueError(t('AutoVisemeButton.error.noShapekeys'))
|
raise ValueError(t('AutoVisemeButton.error.noShapekeys'))
|
||||||
|
|
||||||
update_progress(self, context, t("VisemePanel.removing_existing_visemes"))
|
update_progress(self, context, t("VisemePanel.removing_existing_visemes"))
|
||||||
self.remove_existing_vrc_shapekeys(mesh)
|
self.remove_existing_vrc_shapekeys(mesh)
|
||||||
|
|
||||||
shape_a = context.scene.avatar_toolkit_mouth_a
|
shape_a = context.scene.avatar_toolkit.mouth_a
|
||||||
shape_o = context.scene.avatar_toolkit_mouth_o
|
shape_o = context.scene.avatar_toolkit.mouth_o
|
||||||
shape_ch = context.scene.avatar_toolkit_mouth_ch
|
shape_ch = context.scene.avatar_toolkit.mouth_ch
|
||||||
|
|
||||||
if shape_a == "Basis" or shape_o == "Basis" or shape_ch == "Basis":
|
if shape_a == "Basis" or shape_o == "Basis" or shape_ch == "Basis":
|
||||||
raise ValueError(t('AutoVisemeButton.error.selectShapekeys'))
|
raise ValueError(t('AutoVisemeButton.error.selectShapekeys'))
|
||||||
@@ -62,7 +62,7 @@ class AvatarToolKit_OT_AutoVisemeButton(bpy.types.Operator):
|
|||||||
]
|
]
|
||||||
|
|
||||||
for viseme_name, shape_mix in visemes:
|
for viseme_name, shape_mix in visemes:
|
||||||
self.create_viseme(mesh, viseme_name, shape_mix, context.scene.avatar_toolkit_shape_intensity)
|
self.create_viseme(mesh, viseme_name, shape_mix, context.scene.avatar_toolkit.shape_intensity)
|
||||||
|
|
||||||
update_progress(self, context, t("VisemePanel.sorting_shapekeys"))
|
update_progress(self, context, t("VisemePanel.sorting_shapekeys"))
|
||||||
common.sort_shape_keys(mesh)
|
common.sort_shape_keys(mesh)
|
||||||
|
|||||||
Reference in New Issue
Block a user