+85
-95
@@ -113,10 +113,16 @@ def get_armature(context: Context, armature_name: Optional[str] = None) -> Optio
|
|||||||
return next((obj for obj in context.view_layer.objects if obj.type == 'ARMATURE'), None)
|
return next((obj for obj in context.view_layer.objects if obj.type == 'ARMATURE'), None)
|
||||||
|
|
||||||
def get_armatures(self, context: Context) -> List[Tuple[str, str, str]]:
|
def get_armatures(self, context: Context) -> List[Tuple[str, str, str]]:
|
||||||
return [(obj.name, obj.name, "") for obj in bpy.data.objects if obj.type == 'ARMATURE']
|
armatures = [(obj.name, obj.name, "") for obj in bpy.data.objects if obj.type == 'ARMATURE']
|
||||||
|
if not armatures:
|
||||||
|
return [('NONE', 'No Armature', '')]
|
||||||
|
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]]:
|
||||||
return [(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.selected_armature))]
|
||||||
|
if not armatures:
|
||||||
|
return [('NONE', 'No Other Armature', '')]
|
||||||
|
return armatures
|
||||||
|
|
||||||
def get_selected_armature(context: Context) -> Optional[Object]:
|
def get_selected_armature(context: Context) -> Optional[Object]:
|
||||||
try:
|
try:
|
||||||
@@ -211,106 +217,90 @@ def apply_shapekey_to_basis(context: bpy.types.Context, obj: bpy.types.Object, s
|
|||||||
mesh.shape_keys.key_blocks[shape_key_name].name = shape_key_name + "_reversed"
|
mesh.shape_keys.key_blocks[shape_key_name].name = shape_key_name + "_reversed"
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def apply_pose_as_rest(context: Context, armature_obj: bpy.types.Object, meshes: list[bpy.types.Object]) -> bool:
|
def apply_pose_as_rest(context: Context, armature_obj: Object, meshes: list[Object]) -> bool:
|
||||||
for obj in meshes:
|
for mesh_obj in meshes:
|
||||||
mesh_data: Mesh = obj.data
|
if not 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
|
|
||||||
if not (arm_modifier.object == armature_obj):
|
|
||||||
continue
|
continue
|
||||||
modifier_armature_name = arm_modifier.object.name
|
|
||||||
|
|
||||||
if modifier_armature_name == "":
|
if mesh_obj.data.shape_keys and mesh_obj.data.shape_keys.key_blocks:
|
||||||
continue
|
if len(mesh_obj.data.shape_keys.key_blocks) == 1:
|
||||||
for idx,shape in enumerate(mesh_data.shape_keys.key_blocks):
|
basis = mesh_obj.data.shape_keys.key_blocks[0]
|
||||||
if idx == 0:
|
basis_name = basis.name
|
||||||
continue
|
mesh_obj.shape_key_remove(basis)
|
||||||
context.view_layer.objects.active = obj
|
apply_armature_to_mesh(armature_obj, mesh_obj)
|
||||||
|
mesh_obj.shape_key_add(name=basis_name)
|
||||||
bpy.ops.object.mode_set(mode="OBJECT")
|
|
||||||
bpy.ops.object.select_all(action="DESELECT")
|
|
||||||
context.view_layer.objects.active = obj
|
|
||||||
obj.select_set(True)
|
|
||||||
|
|
||||||
#create duplicate of object
|
|
||||||
bpy.ops.object.duplicate()
|
|
||||||
|
|
||||||
shape_obj = context.view_layer.objects.active
|
|
||||||
|
|
||||||
#make current shapekey a separate object
|
|
||||||
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)
|
|
||||||
|
|
||||||
#for modifier_name in [i.name for i in shape_obj.modifiers]:
|
|
||||||
# bpy.ops.object.modifier_remove(modifier=modifier_name)
|
|
||||||
|
|
||||||
shape_key_obj_list.append(shape_obj) #add to a list of shape key objects
|
|
||||||
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:
|
|
||||||
|
|
||||||
#delete shapekey objects to not leave ourselves in a bad exit state - @989onan
|
|
||||||
context.view_layer.objects.active = shape_key_obj_list[0]
|
|
||||||
obj.select_set(False)
|
|
||||||
bpy.ops.object.delete(confirm=False)
|
|
||||||
return False
|
|
||||||
context.view_layer.objects.active = shape_key_obj_list[0]
|
|
||||||
obj.select_set(False)
|
|
||||||
bpy.ops.object.delete(confirm=False)
|
|
||||||
else:
|
else:
|
||||||
modifier_armature_name: str = ""
|
apply_armature_to_mesh_with_shapekeys(armature_obj, mesh_obj, context)
|
||||||
|
else:
|
||||||
for modifier in obj.modifiers:
|
apply_armature_to_mesh(armature_obj, mesh_obj)
|
||||||
if modifier.type == "ARMATURE":
|
|
||||||
arm_modifier: bpy.types.ArmatureModifier = modifier
|
|
||||||
if not (arm_modifier.object == armature_obj):
|
|
||||||
continue
|
|
||||||
modifier_armature_name = arm_modifier.object.name
|
|
||||||
|
|
||||||
if modifier_armature_name == "":
|
|
||||||
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.select_set(True)
|
|
||||||
bpy.ops.object.modifier_apply(modifier=modifier_armature_name)
|
|
||||||
|
|
||||||
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.object.mode_set(mode='POSE')
|
||||||
bpy.ops.pose.armature_apply(selected=False)
|
bpy.ops.pose.armature_apply(selected=False)
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def apply_armature_to_mesh(armature_obj: Object, mesh_obj: Object) -> None:
|
||||||
|
armature_mod = mesh_obj.modifiers.new('PoseToRest', 'ARMATURE')
|
||||||
|
armature_mod.object = armature_obj
|
||||||
|
|
||||||
|
if bpy.app.version >= (3, 5):
|
||||||
|
mesh_obj.modifiers.move(mesh_obj.modifiers.find(armature_mod.name), 0)
|
||||||
|
else:
|
||||||
|
for _ in range(len(mesh_obj.modifiers) - 1):
|
||||||
|
bpy.ops.object.modifier_move_up(modifier=armature_mod.name)
|
||||||
|
|
||||||
|
with bpy.context.temp_override(object=mesh_obj):
|
||||||
|
bpy.ops.object.modifier_apply(modifier=armature_mod.name)
|
||||||
|
|
||||||
|
def apply_armature_to_mesh_with_shapekeys(armature_obj: Object, mesh_obj: Object, context: Context) -> None:
|
||||||
|
old_active_index = mesh_obj.active_shape_key_index
|
||||||
|
old_show_only = mesh_obj.show_only_shape_key
|
||||||
|
mesh_obj.show_only_shape_key = True
|
||||||
|
|
||||||
|
shape_keys = mesh_obj.data.shape_keys.key_blocks
|
||||||
|
vertex_groups = []
|
||||||
|
mutes = []
|
||||||
|
for sk in shape_keys:
|
||||||
|
vertex_groups.append(sk.vertex_group)
|
||||||
|
sk.vertex_group = ''
|
||||||
|
mutes.append(sk.mute)
|
||||||
|
sk.mute = False
|
||||||
|
|
||||||
|
disabled_mods = []
|
||||||
|
for mod in mesh_obj.modifiers:
|
||||||
|
if mod.show_viewport:
|
||||||
|
mod.show_viewport = False
|
||||||
|
disabled_mods.append(mod)
|
||||||
|
|
||||||
|
arm_mod = mesh_obj.modifiers.new('PoseToRest', 'ARMATURE')
|
||||||
|
arm_mod.object = armature_obj
|
||||||
|
|
||||||
|
co_length = len(mesh_obj.data.vertices) * 3
|
||||||
|
eval_cos = np.empty(co_length, dtype=np.single)
|
||||||
|
|
||||||
|
for i, shape_key in enumerate(shape_keys):
|
||||||
|
mesh_obj.active_shape_key_index = i
|
||||||
|
|
||||||
|
depsgraph = context.evaluated_depsgraph_get()
|
||||||
|
eval_mesh = mesh_obj.evaluated_get(depsgraph)
|
||||||
|
eval_mesh.data.vertices.foreach_get('co', eval_cos)
|
||||||
|
|
||||||
|
shape_key.data.foreach_set('co', eval_cos)
|
||||||
|
if i == 0:
|
||||||
|
mesh_obj.data.vertices.foreach_set('co', eval_cos)
|
||||||
|
|
||||||
|
for mod in disabled_mods:
|
||||||
|
mod.show_viewport = True
|
||||||
|
mesh_obj.modifiers.remove(arm_mod)
|
||||||
|
|
||||||
|
for sk, vg, mute in zip(shape_keys, vertex_groups, mutes):
|
||||||
|
sk.vertex_group = vg
|
||||||
|
sk.mute = mute
|
||||||
|
|
||||||
|
mesh_obj.active_shape_key_index = old_active_index
|
||||||
|
mesh_obj.show_only_shape_key = old_show_only
|
||||||
|
|
||||||
def get_all_meshes(context: Context) -> List[Object]:
|
def get_all_meshes(context: Context) -> List[Object]:
|
||||||
armature = get_selected_armature(context)
|
armature = get_selected_armature(context)
|
||||||
if armature and is_valid_armature(armature):
|
if armature and is_valid_armature(armature):
|
||||||
|
|||||||
+31
-5
@@ -23,10 +23,28 @@ def register() -> None:
|
|||||||
description=t("VisemePanel.selected_mesh.desc")
|
description=t("VisemePanel.selected_mesh.desc")
|
||||||
)))
|
)))
|
||||||
|
|
||||||
register_property((bpy.types.Scene, "merge_armature_source", bpy.props.EnumProperty(
|
register_property((bpy.types.Object, "material_group_expanded", bpy.props.BoolProperty(
|
||||||
items=get_armatures_that_are_not_selected,
|
name="Expand Material Group",
|
||||||
name=t("MergeArmatures.selected_armature.label"),
|
description="Show/hide materials for this mesh",
|
||||||
description=t("MergeArmatures.selected_armature.label")
|
default=False
|
||||||
|
)))
|
||||||
|
|
||||||
|
register_property((bpy.types.Material, "material_expanded", bpy.props.BoolProperty(
|
||||||
|
name="Expand Material",
|
||||||
|
description="Show/hide material properties",
|
||||||
|
default=False
|
||||||
|
)))
|
||||||
|
|
||||||
|
register_property((bpy.types.Scene, "material_search_filter", bpy.props.StringProperty(
|
||||||
|
name="Search Materials",
|
||||||
|
description="Filter materials by name",
|
||||||
|
default=""
|
||||||
|
)))
|
||||||
|
|
||||||
|
register_property((bpy.types.Material, "include_in_atlas", bpy.props.BoolProperty(
|
||||||
|
name=t("TextureAtlas.include_in_atlas"),
|
||||||
|
description=t("TextureAtlas.include_in_atlas_desc"),
|
||||||
|
default=True
|
||||||
)))
|
)))
|
||||||
|
|
||||||
register_property((bpy.types.Scene, "merge_armature_apply_transforms", bpy.props.BoolProperty(
|
register_property((bpy.types.Scene, "merge_armature_apply_transforms", bpy.props.BoolProperty(
|
||||||
@@ -73,7 +91,15 @@ def register() -> None:
|
|||||||
register_property((bpy.types.Scene, "selected_armature", bpy.props.EnumProperty(
|
register_property((bpy.types.Scene, "selected_armature", bpy.props.EnumProperty(
|
||||||
items=get_armatures,
|
items=get_armatures,
|
||||||
name=t("Quick_Access.selected_armature.label"),
|
name=t("Quick_Access.selected_armature.label"),
|
||||||
description=t("Quick_Access.selected_armature.desc")
|
description=t("Quick_Access.selected_armature.desc"),
|
||||||
|
default=0
|
||||||
|
)))
|
||||||
|
|
||||||
|
register_property((bpy.types.Scene, "merge_armature_source", bpy.props.EnumProperty(
|
||||||
|
items=get_armatures_that_are_not_selected,
|
||||||
|
name=t("MergeArmatures.selected_armature.label"),
|
||||||
|
description=t("MergeArmatures.selected_armature.label"),
|
||||||
|
default=0
|
||||||
)))
|
)))
|
||||||
|
|
||||||
register_property((bpy.types.Scene, "avatar_toolkit_updater_version_list", bpy.props.EnumProperty(
|
register_property((bpy.types.Scene, "avatar_toolkit_updater_version_list", bpy.props.EnumProperty(
|
||||||
|
|||||||
@@ -27,9 +27,18 @@ def register_properties():
|
|||||||
else:
|
else:
|
||||||
prop()
|
prop()
|
||||||
|
|
||||||
|
def clear_registration():
|
||||||
|
__bl_classes.clear()
|
||||||
|
__bl_ordered_classes.clear()
|
||||||
|
__bl_props.clear()
|
||||||
|
|
||||||
def unregister_properties():
|
def unregister_properties():
|
||||||
for prop in reversed(__bl_props):
|
for prop in reversed(__bl_props):
|
||||||
|
try:
|
||||||
delattr(prop[0], prop[1])
|
delattr(prop[0], prop[1])
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
clear_registration()
|
||||||
|
|
||||||
#- @989onan had to add this from Cats. This is extremely important else you will be screamed at by register order issues!
|
#- @989onan had to add this from Cats. This is extremely important else you will be screamed at by register order issues!
|
||||||
# Find order to register to solve dependencies
|
# Find order to register to solve dependencies
|
||||||
|
|||||||
+8
-3
@@ -7,6 +7,7 @@ import shutil
|
|||||||
import pathlib
|
import pathlib
|
||||||
import zipfile
|
import zipfile
|
||||||
import time
|
import time
|
||||||
|
from urllib import request, error
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from bpy.app.handlers import persistent
|
from bpy.app.handlers import persistent
|
||||||
from ..functions.translations import t
|
from ..functions.translations import t
|
||||||
@@ -140,9 +141,9 @@ def get_github_releases() -> bool:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
ssl._create_default_https_context = ssl._create_unverified_context
|
ssl._create_default_https_context = ssl._create_unverified_context
|
||||||
with urllib.request.urlopen(f'https://api.github.com/repos/{GITHUB_REPO}/releases') as url:
|
with request.urlopen(f'https://api.github.com/repos/{GITHUB_REPO}/releases') as url:
|
||||||
data = json.loads(url.read().decode())
|
data = json.loads(url.read().decode())
|
||||||
except urllib.error.URLError:
|
except error.URLError:
|
||||||
print('URL ERROR')
|
print('URL ERROR')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -191,6 +192,10 @@ def finish_update_checking(error: str = '') -> None:
|
|||||||
return None # Important for bpy.app.timers
|
return None # Important for bpy.app.timers
|
||||||
|
|
||||||
def update_now(latest: bool = False) -> None:
|
def update_now(latest: bool = False) -> None:
|
||||||
|
if not version_list:
|
||||||
|
print("No version list available. Please check for updates first.")
|
||||||
|
return
|
||||||
|
|
||||||
if latest:
|
if latest:
|
||||||
update_link = version_list[latest_version_str][0]
|
update_link = version_list[latest_version_str][0]
|
||||||
else:
|
else:
|
||||||
@@ -210,7 +215,7 @@ def download_file(update_url: str) -> None:
|
|||||||
try:
|
try:
|
||||||
ssl._create_default_https_context = ssl._create_unverified_context
|
ssl._create_default_https_context = ssl._create_unverified_context
|
||||||
urllib.request.urlretrieve(update_url, update_zip_file)
|
urllib.request.urlretrieve(update_url, update_zip_file)
|
||||||
except urllib.error.URLError:
|
except error.URLError:
|
||||||
finish_update(error=t('download_file.cantConnect'))
|
finish_update(error=t('download_file.cantConnect'))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -64,25 +64,38 @@ class AvatarToolkit_OT_ApplyPoseAsShapekey(Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return get_selected_armature(context) != None and context.mode == "POSE"
|
armature = common.get_selected_armature(context)
|
||||||
|
return armature and context.mode == 'POSE'
|
||||||
|
|
||||||
def execute(self, context: Context):
|
def execute(self, context):
|
||||||
bpy.ops.object.mode_set(mode="OBJECT")
|
armature_obj = common.get_selected_armature(context)
|
||||||
for obj in get_all_meshes(context):
|
mesh_objects = common.get_all_meshes(context)
|
||||||
|
|
||||||
modifier_armature_name: str = ""
|
for mesh_obj in mesh_objects:
|
||||||
context.view_layer.objects.active = obj
|
if not mesh_obj.data:
|
||||||
|
continue
|
||||||
|
|
||||||
bpy.ops.object.mode_set(mode="OBJECT")
|
# Ensure basis exists
|
||||||
bpy.ops.object.select_all(action="DESELECT")
|
if not mesh_obj.data.shape_keys:
|
||||||
context.view_layer.objects.active = obj
|
mesh_obj.shape_key_add(name='Basis')
|
||||||
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)
|
|
||||||
|
|
||||||
|
# Store current pose as new shapekey
|
||||||
|
new_shape = mesh_obj.shape_key_add(name='Pose_Shapekey', from_mix=False)
|
||||||
|
|
||||||
|
# Evaluate mesh in current pose
|
||||||
|
depsgraph = context.evaluated_depsgraph_get()
|
||||||
|
eval_mesh = mesh_obj.evaluated_get(depsgraph)
|
||||||
|
|
||||||
|
# Apply evaluated vertices to new shapekey
|
||||||
|
for i, v in enumerate(eval_mesh.data.vertices):
|
||||||
|
new_shape.data[i].co = v.co.copy()
|
||||||
|
|
||||||
|
# Reset pose
|
||||||
|
bpy.ops.pose.select_all(action='SELECT')
|
||||||
|
bpy.ops.pose.transforms_clear()
|
||||||
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
|
||||||
|
self.report({'INFO'}, t('Tools.apply_pose_as_rest.success'))
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@register_wrap
|
@register_wrap
|
||||||
@@ -97,11 +110,13 @@ class AvatarToolkit_OT_ApplyPoseAsRest(Operator):
|
|||||||
return get_selected_armature(context) != None and context.mode == "POSE"
|
return get_selected_armature(context) != None and context.mode == "POSE"
|
||||||
|
|
||||||
def execute(self, context: Context):
|
def execute(self, context: Context):
|
||||||
|
if not common.apply_pose_as_rest(armature_obj=get_selected_armature(context),
|
||||||
if common.apply_pose_as_rest(armature_obj=get_selected_armature(context),meshes=get_all_meshes(context), context=context):
|
meshes=get_all_meshes(context),
|
||||||
|
context=context):
|
||||||
self.report({'ERROR'}, t("Quick_Access.apply_armature_failed"))
|
self.report({'ERROR'}, t("Quick_Access.apply_armature_failed"))
|
||||||
|
return {'CANCELLED'}
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
@register_wrap
|
@register_wrap
|
||||||
class AvatarToolkit_OT_RemoveZeroWeightBones(Operator):
|
class AvatarToolkit_OT_RemoveZeroWeightBones(Operator):
|
||||||
|
|||||||
@@ -10,19 +10,38 @@ from ..core.common import SceneMatClass, MaterialListBool
|
|||||||
from ..core.packer.rectangle_packer import MaterialImageList, BinPacker
|
from ..core.packer.rectangle_packer import MaterialImageList, BinPacker
|
||||||
from ..functions.translations import t
|
from ..functions.translations import t
|
||||||
|
|
||||||
def scale_images_to_largest(images:list[Image]) -> set:
|
class MaterialImageList:
|
||||||
print([image.name for image in images])
|
def __init__(self):
|
||||||
x: int=0
|
self.albedo: Image = None
|
||||||
y: int=0
|
self.normal: Image = None
|
||||||
for image in images:
|
self.emission: Image = None
|
||||||
x = max(x,image.size[0])
|
self.ambient_occlusion: Image = None
|
||||||
y = max(y,image.size[1])
|
self.height: Image = None
|
||||||
print(x,y)
|
self.roughness: Image = None
|
||||||
|
self.material: Material = None
|
||||||
|
self.parent_mesh: Object = None
|
||||||
|
self.w: int = 0
|
||||||
|
self.h: int = 0
|
||||||
|
self.fit = None
|
||||||
|
|
||||||
for image in images:
|
def scale_images_to_largest(images: list[Image]) -> set:
|
||||||
|
x: int = 0
|
||||||
|
y: int = 0
|
||||||
|
|
||||||
|
# Filter out None or invalid images
|
||||||
|
valid_images = [img for img in images if img and img.has_data]
|
||||||
|
|
||||||
|
if not valid_images:
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
for image in valid_images:
|
||||||
|
x = max(x, image.size[0])
|
||||||
|
y = max(y, image.size[1])
|
||||||
|
|
||||||
|
for image in valid_images:
|
||||||
image.scale(width=int(x), height=int(y))
|
image.scale(width=int(x), height=int(y))
|
||||||
|
|
||||||
return x,y
|
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] = []
|
||||||
@@ -38,62 +57,67 @@ def MaterialImageList_to_Image_list(classitem: MaterialImageList) -> list[Image]
|
|||||||
|
|
||||||
|
|
||||||
def get_material_images_from_scene(context: Context) -> list[MaterialImageList]:
|
def get_material_images_from_scene(context: Context) -> list[MaterialImageList]:
|
||||||
mat: SceneMatClass = None
|
|
||||||
material_image_list: list[MaterialImageList] = []
|
material_image_list: list[MaterialImageList] = []
|
||||||
for mat in context.scene.materials:
|
|
||||||
new_mat_image_item: MaterialImageList = MaterialImageList()
|
for obj in context.scene.objects:
|
||||||
|
if obj.type == 'MESH':
|
||||||
|
for mat_slot in obj.material_slots:
|
||||||
|
# Only process materials that are selected for atlas
|
||||||
|
if mat_slot.material and mat_slot.material.include_in_atlas is True:
|
||||||
|
new_mat_image_item = MaterialImageList()
|
||||||
try:
|
try:
|
||||||
new_mat_image_item.albedo = bpy.data.images[mat.mat.texture_atlas_albedo]
|
new_mat_image_item.albedo = bpy.data.images[mat_slot.material.texture_atlas_albedo]
|
||||||
except Exception as e:
|
except Exception:
|
||||||
name: str = mat.mat.name+"_albedo_replacement"
|
name = mat_slot.material.name + "_albedo_replacement"
|
||||||
if name in bpy.data.images:
|
if name in bpy.data.images:
|
||||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||||
new_mat_image_item.albedo = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
new_mat_image_item.albedo = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||||
new_mat_image_item.albedo.pixels[:] = numpy.tile(numpy.array([0.0,0.0,0.0,1.0]), 32*32)
|
new_mat_image_item.albedo.pixels[:] = numpy.tile(numpy.array([0.0,0.0,0.0,1.0]), 32*32)
|
||||||
try:
|
try:
|
||||||
new_mat_image_item.normal = bpy.data.images[mat.mat.texture_atlas_normal]
|
new_mat_image_item.normal = bpy.data.images[mat_slot.material.texture_atlas_normal]
|
||||||
except Exception:
|
except Exception:
|
||||||
name: str = mat.mat.name+"_normal_replacement"
|
name = mat_slot.material.name + "_normal_replacement"
|
||||||
if name in bpy.data.images:
|
if name in bpy.data.images:
|
||||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||||
new_mat_image_item.normal = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
new_mat_image_item.normal = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||||
new_mat_image_item.normal.pixels[:] = numpy.tile(numpy.array([0.5,0.5,1.0,1.0]), 32*32)
|
new_mat_image_item.normal.pixels[:] = numpy.tile(numpy.array([0.5,0.5,1.0,1.0]), 32*32)
|
||||||
try:
|
try:
|
||||||
new_mat_image_item.emission = bpy.data.images[mat.mat.texture_atlas_emission]
|
new_mat_image_item.emission = bpy.data.images[mat_slot.material.texture_atlas_emission]
|
||||||
except Exception:
|
except Exception:
|
||||||
name: str = mat.mat.name+"_emission_replacement"
|
name = mat_slot.material.name + "_emission_replacement"
|
||||||
if name in bpy.data.images:
|
if name in bpy.data.images:
|
||||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||||
new_mat_image_item.emission = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
new_mat_image_item.emission = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||||
new_mat_image_item.emission.pixels[:] = numpy.tile(numpy.array([0.0,0.0,0.0,1.0]), 32*32)
|
new_mat_image_item.emission.pixels[:] = numpy.tile(numpy.array([0.0,0.0,0.0,1.0]), 32*32)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_mat_image_item.ambient_occlusion = bpy.data.images[mat.mat.texture_atlas_ambient_occlusion]
|
new_mat_image_item.ambient_occlusion = bpy.data.images[mat_slot.material.texture_atlas_ambient_occlusion]
|
||||||
except Exception:
|
except Exception:
|
||||||
name: str = mat.mat.name+"_ambient_occlusion_replacement"
|
name = mat_slot.material.name + "_ambient_occlusion_replacement"
|
||||||
if name in bpy.data.images:
|
if name in bpy.data.images:
|
||||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||||
new_mat_image_item.ambient_occlusion = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
new_mat_image_item.ambient_occlusion = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||||
new_mat_image_item.ambient_occlusion.pixels[:] = numpy.tile(numpy.array([1.0,1.0,1.0,1.0]), 32*32)
|
new_mat_image_item.ambient_occlusion.pixels[:] = numpy.tile(numpy.array([1.0,1.0,1.0,1.0]), 32*32)
|
||||||
try:
|
try:
|
||||||
new_mat_image_item.height = bpy.data.images[mat.mat.texture_atlas_height]
|
new_mat_image_item.height = bpy.data.images[mat_slot.material.texture_atlas_height]
|
||||||
except Exception:
|
except Exception:
|
||||||
name: str = mat.mat.name+"_height_replacement"
|
name = mat_slot.material.name + "_height_replacement"
|
||||||
if name in bpy.data.images:
|
if name in bpy.data.images:
|
||||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||||
new_mat_image_item.height = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
new_mat_image_item.height = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||||
new_mat_image_item.height.pixels[:] = numpy.tile(numpy.array([0.5,0.5,0.5,1.0]), 32*32)
|
new_mat_image_item.height.pixels[:] = numpy.tile(numpy.array([0.5,0.5,0.5,1.0]), 32*32)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_mat_image_item.roughness = bpy.data.images[mat.mat.texture_atlas_roughness]
|
new_mat_image_item.roughness = bpy.data.images[mat_slot.material.texture_atlas_roughness]
|
||||||
except Exception:
|
except Exception:
|
||||||
name: str = mat.mat.name+"_roughness_replacement"
|
name = mat_slot.material.name + "_roughness_replacement"
|
||||||
if name in bpy.data.images:
|
if name in bpy.data.images:
|
||||||
bpy.data.images.remove(image=bpy.data.images[name],do_unlink=True)
|
bpy.data.images.remove(image=bpy.data.images[name], do_unlink=True)
|
||||||
new_mat_image_item.roughness = bpy.data.images.new(name=name,width=32,height=32, alpha=True)
|
new_mat_image_item.roughness = bpy.data.images.new(name=name, width=32, height=32, alpha=True)
|
||||||
new_mat_image_item.roughness.pixels[:] = numpy.tile(numpy.array([1.0,1.0,1.0,0.0]), 32*32)
|
new_mat_image_item.roughness.pixels[:] = numpy.tile(numpy.array([1.0,1.0,1.0,0.0]), 32*32)
|
||||||
new_mat_image_item.material = mat.mat
|
|
||||||
|
new_mat_image_item.material = mat_slot.material
|
||||||
|
new_mat_image_item.parent_mesh = obj
|
||||||
material_image_list.append(new_mat_image_item)
|
material_image_list.append(new_mat_image_item)
|
||||||
|
|
||||||
return material_image_list
|
return material_image_list
|
||||||
|
|
||||||
|
|
||||||
@@ -124,13 +148,16 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
|
|
||||||
def execute(self, context: Context) -> set:
|
def execute(self, context: Context) -> set:
|
||||||
try:
|
try:
|
||||||
mat_images: list[MaterialImageList] = prep_images_in_scene(context)
|
# 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]
|
||||||
|
|
||||||
packer: BinPacker = BinPacker(mat_images)
|
if not selected_materials:
|
||||||
|
self.report({'WARNING'}, t("TextureAtlas.no_materials_selected"))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
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.w + matimg.albedo.size[0] for matimg in mat_images]),
|
||||||
max([matimg.fit.h + matimg.albedo.size[1] for matimg in mat_images])]
|
max([matimg.fit.h + matimg.albedo.size[1] for matimg in mat_images])]
|
||||||
print([matimg.fit.w + matimg.albedo.size[1] for matimg in mat_images])
|
print([matimg.fit.w + matimg.albedo.size[1] for matimg in mat_images])
|
||||||
@@ -144,9 +171,8 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
h: int = int(mat.albedo.size[1])
|
h: int = int(mat.albedo.size[1])
|
||||||
|
|
||||||
for obj in bpy.data.objects:
|
for obj in bpy.data.objects:
|
||||||
|
if obj.type == 'MESH':
|
||||||
mesh: Mesh = obj.data
|
mesh: Mesh = obj.data
|
||||||
|
|
||||||
|
|
||||||
for layer in mesh.polygons:
|
for layer in mesh.polygons:
|
||||||
if obj.material_slots[layer.material_index].material:
|
if obj.material_slots[layer.material_index].material:
|
||||||
if obj.material_slots[layer.material_index].material == mat.material:
|
if obj.material_slots[layer.material_index].material == mat.material:
|
||||||
@@ -167,7 +193,6 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
|
|
||||||
canvas: Image = bpy.data.images.new(name=new_image_name, width=int(size[0]),height=int(size[1]), alpha=True)
|
canvas: Image = bpy.data.images.new(name=new_image_name, width=int(size[0]),height=int(size[1]), alpha=True)
|
||||||
c_w = canvas.size[0]
|
c_w = canvas.size[0]
|
||||||
#c_h = canvas.size[1]
|
|
||||||
canvas_pixels: list[float] = list(canvas.pixels[:])
|
canvas_pixels: list[float] = list(canvas.pixels[:])
|
||||||
for mat in mat_images:
|
for mat in mat_images:
|
||||||
x: int = int(mat.fit.x)
|
x: int = int(mat.fit.x)
|
||||||
@@ -199,14 +224,12 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
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")
|
exec("atlased_mat."+type+" = canvas")
|
||||||
|
|
||||||
|
#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
|
||||||
atlased_mat.material = bpy.data.materials.new(name="Atlas_Final_"+bpy.context.scene.name+"_"+Path(bpy.data.filepath).stem)
|
atlased_mat.material = bpy.data.materials.new(name="Atlas_Final_"+bpy.context.scene.name+"_"+Path(bpy.data.filepath).stem)
|
||||||
atlased_mat.material.use_nodes = True
|
atlased_mat.material.use_nodes = True
|
||||||
atlased_mat.material.node_tree.nodes.clear()
|
atlased_mat.material.node_tree.nodes.clear()
|
||||||
|
|
||||||
|
|
||||||
#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
|
|
||||||
principled_node: ShaderNodeBsdfPrincipled = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeBsdfPrincipled")
|
principled_node: ShaderNodeBsdfPrincipled = atlased_mat.material.node_tree.nodes.new(type="ShaderNodeBsdfPrincipled")
|
||||||
principled_node.location.x = 7.29706335067749
|
principled_node.location.x = 7.29706335067749
|
||||||
principled_node.location.y = 298.918212890625
|
principled_node.location.y = 298.918212890625
|
||||||
@@ -258,12 +281,13 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
atlased_mat.material.node_tree.links.new(output_node.inputs["Surface"], principled_node.outputs["BSDF"])
|
atlased_mat.material.node_tree.links.new(output_node.inputs["Surface"], principled_node.outputs["BSDF"])
|
||||||
atlased_mat.material.node_tree.links.new(normal_map_node.inputs["Color"], normal_node.outputs["Color"])
|
atlased_mat.material.node_tree.links.new(normal_map_node.inputs["Color"], normal_node.outputs["Color"])
|
||||||
|
|
||||||
|
# Only update selected materials for meshes
|
||||||
for obj in context.scene.objects:
|
for obj in context.scene.objects:
|
||||||
|
if obj.type == 'MESH':
|
||||||
mesh: Mesh = obj.data
|
mesh: Mesh = obj.data
|
||||||
mesh.materials.clear()
|
for i, mat_slot in enumerate(obj.material_slots):
|
||||||
|
if mat_slot.material and mat_slot.material.include_in_atlas is True:
|
||||||
mesh.materials.append(atlased_mat.material)
|
mesh.materials[i] = atlased_mat.material
|
||||||
|
|
||||||
self.report({'INFO'}, t("TextureAtlas.atlas_completed"))
|
self.report({'INFO'}, t("TextureAtlas.atlas_completed"))
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@@ -272,4 +296,3 @@ class AvatarToolKit_OT_AtlasMaterials(Operator):
|
|||||||
raise e
|
raise e
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -169,6 +169,10 @@
|
|||||||
"Tools.remove_zero_weight_bones.threshold.label": "Weight Threshold",
|
"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",
|
"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",
|
||||||
"Tools.connect_bones.label": "Connect Bones",
|
"Tools.connect_bones.label": "Connect Bones",
|
||||||
|
"Tools.bone_tools.label": "Bone Tools",
|
||||||
|
"Tools.additional_tools.label": "Additional Tools",
|
||||||
|
"Tools.merge_twist_bones.label": "Merge Twist Bones",
|
||||||
|
"Tools.merge_twist_bones.desc": "Merge twist bones into their parent bones",
|
||||||
"Tools.connect_bones.desc": "Connect bones with their respective children",
|
"Tools.connect_bones.desc": "Connect bones with their respective children",
|
||||||
"Tools.connect_bones.invalid_armature": "Invalid armature selected",
|
"Tools.connect_bones.invalid_armature": "Invalid armature selected",
|
||||||
"Tools.connect_bones.min_distance.label": "Minimum Distance",
|
"Tools.connect_bones.min_distance.label": "Minimum Distance",
|
||||||
@@ -223,6 +227,9 @@
|
|||||||
"VisemePanel.start_viseme_creation": "Starting viseme creation...",
|
"VisemePanel.start_viseme_creation": "Starting viseme creation...",
|
||||||
"VisemePanel.viseme_created_successfully": "Viseme {viseme_name} created successfully",
|
"VisemePanel.viseme_created_successfully": "Viseme {viseme_name} created successfully",
|
||||||
"VisemePanel.viseme_creation_completed": "Viseme creation completed.",
|
"VisemePanel.viseme_creation_completed": "Viseme creation completed.",
|
||||||
|
"MMDOptions.title": "MMD Options",
|
||||||
|
"MMDOptions.no_armature_selected": "No armature selected",
|
||||||
|
"MMDOptions.label": "MMD Options",
|
||||||
"MMDOptions.cleanup_mesh.label": "Cleanup Mesh",
|
"MMDOptions.cleanup_mesh.label": "Cleanup Mesh",
|
||||||
"MMDOptions.cleanup_mesh.desc": "Clean up the mesh by removing empty objects, unused vertex groups, unused vertices, and empty shape keys",
|
"MMDOptions.cleanup_mesh.desc": "Clean up the mesh by removing empty objects, unused vertex groups, unused vertices, and empty shape keys",
|
||||||
"MMDOptions.removing_empty_objects": "Removing empty objects",
|
"MMDOptions.removing_empty_objects": "Removing empty objects",
|
||||||
@@ -249,7 +256,8 @@
|
|||||||
"MMDOptions.renaming_bones": "Renaming bones",
|
"MMDOptions.renaming_bones": "Renaming bones",
|
||||||
"MMDOptions.armature_optimization_complete": "Armature optimization complete",
|
"MMDOptions.armature_optimization_complete": "Armature optimization complete",
|
||||||
"MMDOptions.convert_materials.label": "Convert Materials",
|
"MMDOptions.convert_materials.label": "Convert Materials",
|
||||||
"MMDOptions.convert_materials.desc": "Convert materials to use Principled BSDF shader and fix MMD and VRM shaders", "MMDOptions.converting_materials": "Converting materials for {name}",
|
"MMDOptions.convert_materials.desc": "Convert materials to use Principled BSDF shader and fix MMD and VRM shaders",
|
||||||
|
"MMDOptions.converting_materials": "Converting materials for {name}",
|
||||||
"Updater.label": "Updater",
|
"Updater.label": "Updater",
|
||||||
"Updater.CheckForUpdateButton.label": "Check for Updates",
|
"Updater.CheckForUpdateButton.label": "Check for Updates",
|
||||||
"Updater.CheckForUpdateButton.label_alt": "No Updates Available",
|
"Updater.CheckForUpdateButton.label_alt": "No Updates Available",
|
||||||
@@ -282,7 +290,11 @@
|
|||||||
"CreditsSupport.help_text1": "Check out our wiki first, we HIGHLY encourage",
|
"CreditsSupport.help_text1": "Check out our wiki first, we HIGHLY encourage",
|
||||||
"CreditsSupport.help_text2": "that you read it before seeking further support.",
|
"CreditsSupport.help_text2": "that you read it before seeking further support.",
|
||||||
"CreditsSupport.wiki_button": "Wiki",
|
"CreditsSupport.wiki_button": "Wiki",
|
||||||
"CreditsSupport.discord_button": "Join Discord"
|
"CreditsSupport.discord_button": "Join Discord",
|
||||||
|
"TextureAtlas.include_in_atlas": "Include in Atlas",
|
||||||
|
"TextureAtlas.include_in_atlas_desc": "Include this material in the texture atlas",
|
||||||
|
"Scene.avatar_toolkit_updater_version_list.name": "Version List",
|
||||||
|
"Scene.avatar_toolkit_updater_version_list.description": "List of available versions to update to",
|
||||||
|
"TextureAtlas.no_materials_selected": "No materials selected for atlas"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,150 +1,298 @@
|
|||||||
{
|
{
|
||||||
"authors": ["Avatar Toolkit Team"],
|
"authors": ["Avatar Toolkit Team"],
|
||||||
"messages": {
|
"messages": {
|
||||||
"AutoVisemeButton.desc": "シェイプキーに基づいて自動的にビセームを作成する",
|
"AutoVisemeButton.desc": "シェイプキーに基づいて自動的にビセムを作成",
|
||||||
"AutoVisemeButton.error.noShapekeys": "シェイプキーが見つかりません",
|
"AutoVisemeButton.error.noShapekeys": "シェイプキーが見つかりません",
|
||||||
"AutoVisemeButton.error.selectShapekeys": "シェイプキーを選択してください",
|
"AutoVisemeButton.error.selectShapekeys": "シェイプキーを選択してください",
|
||||||
"AutoVisemeButton.label": "ビセームを作成",
|
"AutoVisemeButton.label": "ビセムを作成",
|
||||||
"AutoVisemeButton.success": "ビセームが正常に作成されました",
|
"AutoVisemeButton.success": "ビセムの作成に成功しました",
|
||||||
"AvatarToolkit.alpha_warning": "これは早期アルファ版であり、バグや問題が発生する可能性があります。",
|
"AvatarToolkit.label": "Avatar Toolkit (アルファ版)",
|
||||||
"AvatarToolkit.description": "Blenderでアバターを作成および編集するための",
|
"AvatarToolkit.desc1": "Avatar Toolkitは早期アクセス段階です",
|
||||||
"AvatarToolkit.label": "Avatar Toolkit",
|
"AvatarToolkit.desc2": "問題が発生する可能性があります。",
|
||||||
"AvatarToolkit.welcome": "Avatar Toolkitへようこそ、",
|
"AvatarToolkit.desc3": "問題を見つけた場合はGithubで報告してください。",
|
||||||
"Export.resonite.desc": "すべてのアニメーションとマテリアルを含むGLBをエクスポートします。アニメーションデータについては以下を参照してください:",
|
"Export.resonite.desc": "アニメーションとマテリアルを含むGLBをエクスポート。アニメーションデータについては:",
|
||||||
"Export.resonite.label": "Resoniteにエクスポート",
|
"Export.resonite.label": "Resoniteにエクスポート",
|
||||||
"Importer.export_resonite.desc": "ResoniteにGLTFとしてエクスポートします。モデルがBlenderで正しいスケールであることを確認し、Resoniteでメートル単位でインポートしてください。",
|
"Importer.export_resonite.desc": "GLTFとしてResoniteにエクスポート。Blenderでモデルのスケールを確認し、Resoniteではメートル単位でインポートしてください。",
|
||||||
"Importer.export_resonite.label": "Resoniteにエクスポート",
|
"Importer.export_resonite.label": "Resoniteにエクスポート",
|
||||||
"Importer.export_vrchat.desc": "VRChatにエクスポートします。ChilloutVRでも動作する可能性があります。Catsのエクスポートに似ています。",
|
"Importer.export_vrchat.desc": "VRChatにエクスポート(ChilloutVRでも動作する可能性あり)。Catsのエクスポートに似ています。",
|
||||||
"Importer.export_vrchat.label": "VRChatにエクスポート",
|
"Importer.export_vrchat.label": "VRChatにエクスポート",
|
||||||
"Importer.mmd_anim_importer.desc": "MMDアニメーション(.vmd)をインポート",
|
"Importer.mmd_anim_importer.desc": "MMDアニメーション(.vmd)をインポート",
|
||||||
"Importer.mmd_anim_importer.label": "MMDアニメーション",
|
"Importer.mmd_anim_importer.label": "MMDアニメーション",
|
||||||
"Importing.importer_search_term": "https://search.brave.com/search?q=Blender+{extension}+インポーターアドオン&source=web",
|
"Importing.importer_search_term": "https://search.brave.com/search?q=blender+{extension}+importer+addon&source=web",
|
||||||
"Importing.need_importer": "{extension}タイプに必要なインポーターがありません!インポーター検索用のウェブブラウザを開きます...",
|
"Importing.need_importer": "{extension}タイプに必要なインポーターがありません!インポーター検索用にウェブブラウザを開きます...",
|
||||||
"Language.auto": "自動",
|
"Language.auto": "自動",
|
||||||
"Language.en_US": "English",
|
"Language.en_US": "English",
|
||||||
"Language.ja_JP": "日本語",
|
"Language.ja_JP": "日本語",
|
||||||
"Optimization.applying_transforms": "トランスフォームを適用中...",
|
"Optimization.applying_transforms": "トランスフォームを適用中...",
|
||||||
"Optimization.cleaning_material_names": "マテリアル名をクリーニング中...",
|
"Optimization.cleaning_material_names": "マテリアル名を整理中...",
|
||||||
"Optimization.cleaning_material_slots": "マテリアルスロットをクリーニング中...",
|
"Optimization.cleaning_material_slots": "マテリアルスロットを整理中...",
|
||||||
"Optimization.clearing_unused_data": "未使用データをクリア中...",
|
"Optimization.clearing_unused_data": "未使用データを削除中...",
|
||||||
"Optimization.combine_materials.desc": "ドローコールを減らしパフォーマンスを向上させるために類似したマテリアルを結合する",
|
"Optimization.materials_optimization_report": "マテリアル最適化完了:{num_combined}個のマテリアルを結合、{num_cleaned_slots}個のマテリアルスロットを整理、{num_cleaned_names}個のマテリアル名を整理、{num_removed_data_blocks}個の未使用データブロックを削除しました",
|
||||||
|
"Optimization.combine_materials.desc": "描画コールを減らしパフォーマンスを向上させるため、類似したマテリアルを結合",
|
||||||
"Optimization.combine_materials.label": "マテリアルを結合",
|
"Optimization.combine_materials.label": "マテリアルを結合",
|
||||||
"Optimization.consolidating_materials": "マテリアルを統合中...",
|
"Optimization.consolidating_materials": "マテリアルを統合中...",
|
||||||
"Optimization.finalizing": "最終処理中...",
|
"Optimization.finalizing": "最終処理中...",
|
||||||
"Optimization.fixing_uv_coordinates": "UV座標を修正中...",
|
"Optimization.fixing_uv_coordinates": "UV座標を修正中...",
|
||||||
"Optimization.join_all_meshes.desc": "ドローコールを減らすためにすべてのメッシュを1つのオブジェクトにマージする",
|
"Optimization.join_all_meshes.desc": "描画コールを減らすため、すべてのメッシュを1つのオブジェクトに結合",
|
||||||
"Optimization.join_all_meshes.label": "すべてのメッシュを結合",
|
"Optimization.join_all_meshes.label": "すべてのメッシュを結合",
|
||||||
"Optimization.join_error": "メッシュ結合中にエラーが発生しました",
|
"Optimization.join_error": "メッシュ結合中にエラーが発生",
|
||||||
"Optimization.join_operation_failed": "結合操作に失敗しました",
|
"Optimization.join_operation_failed": "結合操作に失敗しました",
|
||||||
"Optimization.join_selected_meshes.desc": "選択したメッシュのみを1つのオブジェクトにマージする",
|
"Optimization.join_selected_meshes.desc": "選択したメッシュのみを1つのオブジェクトに結合",
|
||||||
"Optimization.join_selected_meshes.label": "選択したメッシュを結合",
|
"Optimization.join_selected_meshes.label": "選択したメッシュを結合",
|
||||||
"Optimization.joinmeshes.label": "メッシュを結合:",
|
"Optimization.joinmeshes.label": "メッシュの結合:",
|
||||||
"Optimization.joining_meshes": "メッシュを結合中...",
|
"Optimization.joining_meshes": "メッシュを結合中...",
|
||||||
"Optimization.label": "最適化",
|
"Optimization.label": "最適化",
|
||||||
"Optimization.material_attribute_mismatch": "マテリアル{material_name}の属性が一致しません。スキップします",
|
"Optimization.material_attribute_mismatch": "マテリアル{material_name}の属性が一致しません。スキップします",
|
||||||
"Optimization.materials_combined": "{num_combined}個のマテリアルを結合しました",
|
"Optimization.materials_combined": "{num_combined}個のマテリアルを結合しました",
|
||||||
"Optimization.meshes_joined": "メッシュが正常に結合されました",
|
"Optimization.meshes_joined": "メッシュの結合に成功しました",
|
||||||
"Optimization.no_armature_selected": "アーマチュアが選択されていません",
|
"Optimization.no_armature_selected": "アーマチュアが選択されていません",
|
||||||
"Optimization.no_mesh_selected": "メッシュオブジェクトが選択されていません",
|
"Optimization.no_mesh_selected": "メッシュオブジェクトが選択されていません",
|
||||||
"Optimization.no_meshes_found": "選択されたアーマチュアに対応するメッシュが見つかりません",
|
"Optimization.no_meshes_found": "選択したアーマチュアにメッシュが見つかりません",
|
||||||
"Optimization.options.label": "最適化:",
|
"Optimization.options.label": "最適化:",
|
||||||
"Optimization.preparing_meshes": "メッシュを準備中...",
|
"Optimization.preparing_meshes": "メッシュを準備中...",
|
||||||
"Optimization.processing_mesh_no_shapekeys": "シェイプキーのないメッシュ「{mesh_name}」を処理中",
|
"Optimization.processing_mesh_no_shapekeys": "シェイプキーのないメッシュ「{mesh_name}」を処理中",
|
||||||
"Optimization.processing_shapekey": "メッシュ「{mesh_name}」のシェイプキー「{shapekeyname}」を処理中",
|
"Optimization.processing_shapekey": "メッシュ「{mesh_name}」のシェイプキー「{shapekeyname}」を処理中",
|
||||||
"Optimization.remove_doubles_completed": "重複頂点の削除が完了しました",
|
"Optimization.remove_doubles_completed": "重複頂点の削除が完了しました",
|
||||||
"Optimization.remove_doubles_safely.desc": "口の形状などの重要な特徴を保持しながら重複頂点を削除する",
|
"Optimization.remove_doubles_safely.desc": "口の形状などの重要な特徴を保持しながら重複頂点を削除します。\n素早い解決策ですが、動く頂点は結合しません。",
|
||||||
"Optimization.remove_doubles_safely.label": "安全に重複頂点を削除",
|
"Optimization.remove_doubles_safely.label": "安全に重複頂点を削除",
|
||||||
"Optimization.select_armature": "アーマチュアを選択してください",
|
"Optimization.remove_doubles_safely_advanced.label": "高度な安全重複頂点削除",
|
||||||
"Optimization.select_at_least_two_meshes": "少なくとも2つのメッシュオブジェクトを選択してください",
|
"Optimization.remove_doubles_safely_advanced.desc": "口の形状などの重要な特徴を保持しながら重複頂点を削除します。\n基本版と異なり、動く頂点も結合しますがシェイプキーは保持します。\n例:唇を閉じることはありませんが、唇を構成する分割されたポリゴンは修正します。",
|
||||||
"Optimization.selected_meshes_joined": "選択したメッシュが正常に結合されました",
|
"UVTools.align_uv_to_target.warning.too_much": "エラー!選択が多すぎます。2つのエッジを選択していますか?",
|
||||||
"Optimization.selecting_meshes": "メッシュを選択中...",
|
"UVTools.align_uv_to_target.warning.need_a_line": "各選択オブジェクトにUVポイントの1行が必要です。オブジェクト「{obj}」がこの要件を満たしていません!",
|
||||||
"Optimization.transform_apply_failed": "トランスフォームの適用に失敗しました",
|
"avatar_toolkit.align_uv_edges_to_target.label": "UVエッジをターゲットに合わせる",
|
||||||
"Optimization.vertex_excluded": "シェイプキーのインデックス「{index}」に移動した頂点があります。重複マージから除外します!",
|
"avatar_toolkit.align_uv_edges_to_target.desc": "選択された各メッシュのUVポイントの線をアクティブメッシュの選択されたUVポイントの線に合わせます。\nあるモデルのテクスチャを別のモデルに適用する際に便利です。\n2Dカーソルからの距離を使用して各メッシュのUVポイントの線の開始点を識別します。",
|
||||||
|
"Quick_Access.selected_armature.label": "選択されたアーマチュア",
|
||||||
|
"Quick_Access.selected_armature.desc": "Avatar Toolkitの操作対象となる現在の「ターゲット」アーマチュア",
|
||||||
"Quick_Access.export": "エクスポート",
|
"Quick_Access.export": "エクスポート",
|
||||||
"Quick_Access.export_fbx.desc": "モデルをFBXとしてエクスポート",
|
"Quick_Access.export_fbx.desc": "モデルをFBXとしてエクスポート",
|
||||||
"Quick_Access.export_fbx.label": "FBXをエクスポート",
|
"Quick_Access.export_fbx.label": "FBXエクスポート",
|
||||||
"Quick_Access.export_menu.desc": "サポートされているフォーマットにエクスポート",
|
"Quick_Access.export_menu.desc": "サポートされている形式にエクスポート",
|
||||||
"Quick_Access.export_menu.label": "エクスポートメニュー",
|
"Quick_Access.export_menu.label": "エクスポートメニュー",
|
||||||
"Quick_Access.import": "インポート",
|
"Quick_Access.import": "インポート",
|
||||||
"Quick_Access.import_export.label": "インポート/エクスポート:",
|
"Quick_Access.import_export.label": "インポート/エクスポート:",
|
||||||
"Quick_Access.import_menu.desc": "モデルをインポート",
|
"Quick_Access.import_menu.desc": "モデルをインポート",
|
||||||
"Quick_Access.import_menu.label": "インポートメニュー",
|
"Quick_Access.import_menu.label": "インポートメニュー",
|
||||||
"Quick_Access.import_pmd": "PMDをインポート",
|
"Quick_Access.import_pmd": "PMDインポート",
|
||||||
"Quick_Access.import_pmd.desc": "MMD PMDモデルをインポート",
|
"Quick_Access.import_pmd.desc": "MMD PMDモデルをインポート",
|
||||||
"Quick_Access.import_pmx": "PMXをインポート",
|
"Quick_Access.import_pmx": "PMXインポート",
|
||||||
"Quick_Access.import_pmx.desc": "MMD PMXモデルをインポート",
|
"Quick_Access.import_pmx.desc": "MMD PMXモデルをインポート",
|
||||||
"Quick_Access.import_success": "モデルが正常にインポートされました",
|
"Quick_Access.import_success": "モデルのインポートに成功しました",
|
||||||
"Quick_Access.label": "クイックアクセス",
|
"Quick_Access.label": "クイックアクセス",
|
||||||
"Quick_Access.options": "クイックアクセス:",
|
"Quick_Access.options": "クイックアクセス:",
|
||||||
"Quick_Access.select_armature": "アーマチュアを選択:",
|
"Quick_Access.select_armature": "アーマチュアを選択:",
|
||||||
|
"Quick_Access.apply_armature_failed": "シェイプキーの結合段階でポーズをアーマチュアに適用できませんでした!",
|
||||||
|
"Quick_Access.apply_pose_as_rest.desc": "現在のポーズをデフォルトの休止ポーズにします。",
|
||||||
|
"Quick_Access.stop_pose_mode.desc": "ポーズモードを終了し、ポーズモードの全ての表示ボーンのポーズをクリアします。",
|
||||||
|
"Quick_Access.apply_pose_as_rest.label": "ポーズを休止ポーズとして適用",
|
||||||
|
"Quick_Access.apply_pose_as_shapekey.desc": "現在のポーズを後で有効化できるシェイプキーとして作成します。\n顎の開閉位置を顔の動きのシェイプキーとして適用する際に便利です。",
|
||||||
|
"Quick_Access.apply_pose_as_shapekey.label": "ポーズをシェイプキーとして適用",
|
||||||
|
"Quick_Access.stop_pose_mode.label": "ポーズモードを終了",
|
||||||
|
"Quick_Access.start_pose_mode.desc": "Avatar Toolkitのターゲットアーマチュアのポーズモードを開始します。",
|
||||||
|
"Quick_Access.start_pose_mode.label": "ポーズモードを開始",
|
||||||
"Quick_Access.select_export.label": "エクスポート方法を選択",
|
"Quick_Access.select_export.label": "エクスポート方法を選択",
|
||||||
"Quick_Access.select_export_resonite.label": "Resonite",
|
"Quick_Access.select_export_resonite.label": "Resonite",
|
||||||
"Settings.label": "設定",
|
"Settings.label": "設定",
|
||||||
"Settings.language.desc": "アドオンのUI言語を選択",
|
"Settings.language.desc": "アドオンのUI言語を選択",
|
||||||
"Settings.language.label": "言語:",
|
"Settings.language.label": "言語:",
|
||||||
"Settings.translation_restart_popup.description": "翻訳の更新に関する情報",
|
"Settings.translation_restart_popup.description": "翻訳の更新について",
|
||||||
"Settings.translation_restart_popup.label": "翻訳の更新",
|
"Settings.translation_restart_popup.label": "翻訳の更新",
|
||||||
"Settings.translation_restart_popup.message1": "一部の翻訳は適用されない場合があります",
|
"Settings.translation_restart_popup.message1": "一部の翻訳はBlenderを再起動するまで",
|
||||||
"Settings.translation_restart_popup.message2": "Blenderを再起動するまで。",
|
"Settings.translation_restart_popup.message2": "適用されない場合があります。",
|
||||||
"TextureAtlas.atlas_completed": "テクスチャアトラスの作成が完了しました",
|
"TextureAtlas.atlas_completed": "テクスチャアトラスの作成が完了しました",
|
||||||
"TextureAtlas.atlas_error": "テクスチャアトラスの作成中にエラーが発生しました",
|
"TextureAtlas.atlas_error": "テクスチャアトラスの作成中にエラーが発生しました",
|
||||||
"TextureAtlas.atlas_materials": "マテリアルをアトラス化",
|
"TextureAtlas.atlas_materials": "マテリアルをアトラス化",
|
||||||
"TextureAtlas.atlas_materials_desc": "モデルを最適化するためにマテリアルをアトラス化する",
|
"TextureAtlas.atlas_materials_desc": "モデルを最適化するためにマテリアルをアトラス化",
|
||||||
"TextureAtlas.label": "テクスチャアトラス化",
|
"TextureAtlas.label": "テクスチャアトラス",
|
||||||
"TextureAtlas.loaded_list": "テクスチャアトラスマテリアルリストを読み込みました",
|
"TextureAtlas.loaded_list": "テクスチャアトラスマテリアルリストを読み込みました",
|
||||||
"TextureAtlas.material_list_label": "テクスチャアトラスマテリアルリストのマテリアル",
|
"TextureAtlas.material_list_label": "テクスチャアトラスマテリアルリストのマテリアル",
|
||||||
"TextureAtlas.reload_list": "テクスチャアトラスマテリアルリストを再読み込み",
|
"TextureAtlas.reload_list": "テクスチャアトラスマテリアルリストを再読み込み",
|
||||||
"Tools.bones_translated_success": "すべての骨をヒューマノイド名に正常に変換しました",
|
"Tools.bones_translated_success": "すべてのボーンを正常にヒューマノイド名に変換しました",
|
||||||
"Tools.bones_translated_with_fails": "{translate_bone_fails}個の骨をヒューマノイド名に変換できませんでした。それらの名前に「<noik>」を追加します。",
|
"Tools.bones_translated_with_fails": "{translate_bone_fails}個のボーンをヒューマノイド名に変換できませんでした。名前に「<noik>」を追加します。",
|
||||||
"Tools.convert_to_resonite.desc": "モデルの骨の名前をResoniteと互換性のある名前に変換します",
|
"Tools.convert_to_resonite.desc": "モデルのボーン名をResonite互換の名前に変換",
|
||||||
"Tools.convert_to_resonite.label": "Resoniteに変換",
|
"Tools.convert_to_resonite.label": "Resoniteに変換",
|
||||||
"Tools.create_digitigrade_legs.desc": "選択した骨のチェーンからデジティグレード脚を作成する",
|
"Tools.create_digitigrade_legs.desc": "選択したボーンチェーンから獣脚を作成",
|
||||||
"Tools.create_digitigrade_legs.label": "デジティグレード脚を作成",
|
"Tools.create_digitigrade_legs.label": "獣脚を作成",
|
||||||
"Tools.digitigrade_legs.error.bone_format": "骨のフォーマットが正しくありません!4つの連続した骨のチェーンを選択してください!",
|
"Tools.digitigrade_legs.error.bone_format": "ボーンの形式が正しくありません!4つの連続したボーンのチェーンを選択してください!",
|
||||||
"Tools.digitigrade_legs.success": "デジティグレード脚が正常に作成されました",
|
"Tools.digitigrade_legs.success": "獣脚の作成に成功しました",
|
||||||
"Tools.import_any_model.desc": "サポートされているモデル(FBX、SMD、DMX、GLTF、PMD、PMXなど)をインポートします。",
|
"Tools.import_any_model.desc": "FBX、SMD、DMX、GLTF、PMD、PMXなど、サポートされているモデルをインポート",
|
||||||
"Tools.import_any_model.label": "モデルをインポート",
|
"Tools.import_any_model.label": "モデルをインポート",
|
||||||
"Tools.label": "ツール",
|
"Tools.label": "ツール",
|
||||||
"Tools.no_armature_selected": "アーマチュアが選択されていません",
|
"Tools.no_armature_selected": "アーマチュアが選択されていません",
|
||||||
"Tools.select_armature": "アーマチュアを選択してください",
|
"Tools.select_armature": "アーマチュアを選択してください",
|
||||||
"Tools.tools_title.label": "ツール:",
|
"Tools.tools_title.label": "ツール:",
|
||||||
"Tools.separate_by.label": "別:",
|
"Tools.separate_by.label": "分離方法:",
|
||||||
"Tools.separate_by_materials.label": "マテリアルで分離",
|
"Tools.separate_by_materials.label": "マテリアルで分離",
|
||||||
"Tools.separate_by_materials.desc": "選択されたメッシュをマテリアルごとに分離します",
|
"Tools.separate_by_materials.desc": "選択したメッシュをマテリアルで分離",
|
||||||
"Tools.separate_by_materials.success": "メッシュがマテリアルごとに正常に分離されました",
|
"Tools.separate_by_materials.success": "メッシュをマテリアルで分離しました",
|
||||||
"Tools.separate_by_loose_parts.label": "バラバラの部分で分離",
|
"Tools.separate_by_loose_parts.label": "分離パーツで分離",
|
||||||
"Tools.separate_by_loose_parts.desc": "選択されたメッシュをバラバラの部分ごとに分離します",
|
"Tools.separate_by_loose_parts.desc": "選択したメッシュを分離パーツで分離",
|
||||||
"Tools.separate_by_loose_parts.success": "メッシュがバラバラの部分ごとに正常に分離されました",
|
"Tools.separate_by_loose_parts.success": "メッシュを分離パーツで分離しました",
|
||||||
"Tools.apply_transforms.label": "トランスフォームを適用",
|
"Tools.apply_transforms.label": "トランスフォームを適用",
|
||||||
"Tools.apply_transforms.desc": "アーマチュアとそのメッシュに位置、回転、スケールを適用します",
|
"Tools.apply_transforms.desc": "アーマチュアとそのメッシュに位置、回転、スケールを適用",
|
||||||
"Tools.apply_transforms.invalid_armature": "無効なアーマチュアが選択されています",
|
"Tools.apply_transforms.invalid_armature": "無効なアーマチュアが選択されています",
|
||||||
"Tools.apply_transforms.success": "アーマチュアとメッシュにトランスフォームが正常に適用されました",
|
"Tools.apply_transforms.success": "アーマチュアとメッシュにトランスフォームを適用しました",
|
||||||
"VisemePanel.create_visemes": "ビセームを作成",
|
"Tools.remove_unused_shapekeys.label": "未使用のシェイプキーを削除",
|
||||||
"VisemePanel.creating_viseme": "ビセームを作成中:{viseme_name}",
|
"Tools.remove_unused_shapekeys.tolerance.desc": "シェイプキーを保持する最小の頂点移動量\n(任意の座標での位置)",
|
||||||
"VisemePanel.creating_viseme_detail": "ビセームを作成中:{viseme_name}",
|
"Tools.remove_unused_shapekeys.desc": "何も動かさないシェイプキーを削除します。\nカテゴリーシェイプキーは削除しません。\n(例:名前に「~」「-」「=」を含むもの)",
|
||||||
"VisemePanel.creating_visemes": "ビセームを作成中...",
|
"Tools.remove_unused_shapekeys.tolerance.label": "位置の許容値",
|
||||||
|
"Tools.apply_shape_key.label": "シェイプキーをベースに適用",
|
||||||
|
"Tools.apply_shape_key.desc": "選択したシェイプキーをベースに適用し、デフォルトでオンにします。",
|
||||||
|
"Tools.apply_shape_key.error": "シェイプキーが何らかの理由でマージされませんでした!",
|
||||||
|
"Tools.remove_zero_weight_bones.success": "ウェイトのないボーンを削除しました",
|
||||||
|
"Tools.remove_zero_weight_bones.label": "ウェイトのないボーンを削除",
|
||||||
|
"Tools.remove_zero_weight_bones.desc": "閾値以下のウェイトを持つボーンをアーマチュアから削除します。",
|
||||||
|
"Tools.merge_bones_to_active.delete_old.desc": "マージ時に古いボーンを削除します。",
|
||||||
|
"Tools.merge_bones_to_active.delete_old.label": "古いボーンを削除",
|
||||||
|
"Tools.merge_bones_to_active.desc": "選択したボーンをアクティブなボーン(青または橙色で選択)にマージします。",
|
||||||
|
"Tools.merge_bones_to_active.label": "ボーンをアクティブなものにマージ",
|
||||||
|
"Tools.merge_bones_to_parents.delete_old.desc": "マージ時に古いボーンを削除します。",
|
||||||
|
"Tools.merge_bones_to_parents.delete_old.label": "古いボーンを削除",
|
||||||
|
"Tools.merge_bones_to_parents.desc": "選択した各ボーンをそれぞれの親ボーンにマージします。",
|
||||||
|
"Tools.merge_bones_to_parents.label": "ボーンを個別の親にマージ",
|
||||||
|
"Tools.remove_zero_weight_bones.threshold.label": "ウェイトの閾値",
|
||||||
|
"Tools.remove_zero_weight_bones.threshold.desc": "アーマチュア下のメッシュのどの部分にもこの閾値以上のウェイトがないボーンは削除されます",
|
||||||
|
"Tools.connect_bones.label": "ボーンを接続",
|
||||||
|
"Tools.bone_tools.label": "ボーンツール",
|
||||||
|
"Tools.additional_tools.label": "追加ツール",
|
||||||
|
"Tools.merge_twist_bones.label": "ツイストボーンをマージ",
|
||||||
|
"Tools.merge_twist_bones.desc": "ツイストボーンを親ボーンにマージ",
|
||||||
|
"Tools.connect_bones.desc": "ボーンをそれぞれの子ボーンと接続",
|
||||||
|
"Tools.connect_bones.invalid_armature": "無効なアーマチュアが選択されています",
|
||||||
|
"Tools.connect_bones.min_distance.label": "最小距離",
|
||||||
|
"Tools.connect_bones.min_distance.desc": "ボーンを接続する最小距離",
|
||||||
|
"Tools.connect_bones.success": "{bones_connected}個のボーンを接続しました",
|
||||||
|
"Tools.delete_bone_constraints.label": "ボーンの制約を削除",
|
||||||
|
"Tools.delete_bone_constraints.desc": "アーマチュアのボーンから全ての制約を削除",
|
||||||
|
"Tools.delete_bone_constraints.invalid_armature": "無効なアーマチュアが選択されています",
|
||||||
|
"Tools.delete_bone_constraints.success": "ボーンから{constraints_removed}個の制約を削除しました",
|
||||||
|
"Tools.convert_rigify_to_unity.label": "RigifyをUnityに変換",
|
||||||
|
"Tools.convert_rigify_to_unity.desc": "RigifyアーマチュアをUnityで使用できるように準備",
|
||||||
|
"Tools.convert_rigify_to_unity.success": "RigifyアーマチュアをUnity用に変換しました",
|
||||||
|
"VisemePanel.create_visemes": "ビセムを作成",
|
||||||
|
"VisemePanel.creating_viseme": "ビセムを作成中:{viseme_name}",
|
||||||
|
"VisemePanel.creating_viseme_detail": "ビセムを作成中:{viseme_name}",
|
||||||
|
"VisemePanel.creating_visemes": "ビセムを作成中...",
|
||||||
"VisemePanel.error.noArmature": "アーマチュアが選択されていません",
|
"VisemePanel.error.noArmature": "アーマチュアが選択されていません",
|
||||||
"VisemePanel.error.noMesh": "メッシュが選択されていません",
|
"VisemePanel.error.noMesh": "メッシュが選択されていません",
|
||||||
"VisemePanel.error.noShapekeys": "選択されたメッシュにシェイプキーがありません",
|
"VisemePanel.error.noShapekeys": "選択したメッシュにシェイプキーがありません",
|
||||||
"VisemePanel.error.selectMesh": "ビセームを作成するメッシュを選択してください",
|
"VisemePanel.error.selectMesh": "ビセムを作成するメッシュを選択してください",
|
||||||
"VisemePanel.info.selectMesh": "ビセームを作成するメッシュを選択してください",
|
"VisemePanel.info.selectMesh": "ビセムを作成するメッシュを選択してください",
|
||||||
"VisemePanel.label": "ビセーム",
|
"VisemePanel.label": "ビセム",
|
||||||
"VisemePanel.mixing_shape": "シェイプをミックス中:{shape_name}、値:{value}",
|
"VisemePanel.mixing_shape": "シェイプを混合中:{shape_name} 値:{value}",
|
||||||
"VisemePanel.mouth_a.desc": "'A'の口の形状のシェイプキー",
|
"VisemePanel.mouth_a.desc": "'A'の口の形のシェイプキー",
|
||||||
"VisemePanel.mouth_a.label": "口 A",
|
"VisemePanel.mouth_a.label": "口 A",
|
||||||
"VisemePanel.mouth_ch.desc": "'CH'の口の形状のシェイプキー",
|
"VisemePanel.mouth_ch.desc": "'CH'の口の形のシェイプキー",
|
||||||
"VisemePanel.mouth_ch.label": "口 CH",
|
"VisemePanel.mouth_ch.label": "口 CH",
|
||||||
"VisemePanel.mouth_o.desc": "'O'の口の形状のシェイプキー",
|
"VisemePanel.mouth_o.desc": "'O'の口の形のシェイプキー",
|
||||||
"VisemePanel.mouth_o.label": "口 O",
|
"VisemePanel.mouth_o.label": "口 O",
|
||||||
"VisemePanel.removing_existing_viseme": "既存のビセームを削除中:{viseme_name}",
|
"VisemePanel.removing_existing_viseme": "既存のビセムを削除中:{viseme_name}",
|
||||||
"VisemePanel.removing_existing_visemes": "既存のビセームを削除中...",
|
"VisemePanel.removing_existing_visemes": "既存のビセムを削除中...",
|
||||||
"VisemePanel.select_mesh": "メッシュを選択",
|
"VisemePanel.select_mesh": "メッシュを選択",
|
||||||
"VisemePanel.selected_shapes": "選択されたシェイプ:A={shape_a}、O={shape_o}、CH={shape_ch}",
|
"VisemePanel.selected_mesh.label": "選択されたメッシュ",
|
||||||
|
"VisemePanel.selected_mesh.desc": "ビセム操作用に現在選択されているメッシュ",
|
||||||
|
"VisemePanel.selected_shapes": "選択されたシェイプ:A={shape_a}, O={shape_o}, CH={shape_ch}",
|
||||||
"VisemePanel.shape_intensity": "シェイプの強度",
|
"VisemePanel.shape_intensity": "シェイプの強度",
|
||||||
"VisemePanel.shape_intensity_desc": "ビセームシェイプキーの強度",
|
"VisemePanel.shape_intensity_desc": "ビセムシェイプキーの強度",
|
||||||
"VisemePanel.sorting_shapekeys": "シェイプキーをソート中...",
|
"VisemePanel.sorting_shapekeys": "シェイプキーを並べ替え中...",
|
||||||
"VisemePanel.start_viseme_creation": "ビセーム作成を開始...",
|
"VisemePanel.start_viseme_creation": "ビセム作成を開始...",
|
||||||
"VisemePanel.viseme_created_successfully": "ビセーム{viseme_name}が正常に作成されました",
|
"VisemePanel.viseme_created_successfully": "ビセム{viseme_name}の作成に成功しました",
|
||||||
"VisemePanel.viseme_creation_completed": "ビセーム作成が完了しました。"
|
"VisemePanel.viseme_creation_completed": "ビセム作成が完了しました。",
|
||||||
|
"MergeArmatures.select_armature": "アーマチュアを選択してください",
|
||||||
|
"MergeArmatures.title.label": "アーマチュアのマージ:",
|
||||||
|
"MergeArmatures.label": "アーマチュアをマージ",
|
||||||
|
"MergeArmatures.selected_armature.label": "マージ元のアーマチュア",
|
||||||
|
"MergeArmatures.selected_armature.desc": "Avatar Toolkitのターゲットアーマチュアにマージされるアーマチュア",
|
||||||
|
"MergeArmatures.target_armature.label": "マージ先のアーマチュア",
|
||||||
|
"MergeArmatures.target_armature.desc": "アーマチュアのマージ先となるターゲットアーマチュア",
|
||||||
|
"MergeArmature.merge_armatures.label": "アーマチュアをマージ",
|
||||||
|
"MergeArmature.merge_armatures.desc": "{selected_armature_label}をAvatar Toolkitのターゲットアーマチュアにマージ",
|
||||||
|
"MergeArmature.merge_armatures.align_bones.label": "ボーンを整列",
|
||||||
|
"MergeArmature.merge_armatures.align_bones.desc": "マージ前にソースアーマチュアのボーンをターゲットアーマチュアに合わせて\nボーンを伸縮させます。",
|
||||||
|
"MergeArmature.merge_armatures.apply_transforms.label": "トランスフォームを適用",
|
||||||
|
"MergeArmature.merge_armatures.apply_transforms.desc": "マージ前にアーマチュアとそのメッシュにトランスフォームを適用します。",
|
||||||
|
"MMDOptions.optimize_armature.label": "アーマチュアを最適化",
|
||||||
|
"MMDOptions.optimize_armature.desc": "ボーンのロールの修正、ボーンの整列、ボーンの接続などでアーマチュアを最適化",
|
||||||
|
"MMDOptions.fixing_bone_rolls": "ボーンのロールを修正中",
|
||||||
|
"MMDOptions.aligning_bones": "ボーンを整列中",
|
||||||
|
"MMDOptions.connecting_bones": "ボーンを接続中",
|
||||||
|
"MMDOptions.deleting_bone_constraints": "ボーンの制約を削除中",
|
||||||
|
"MMDOptions.merging_bones_to_parents": "ボーンを親にマージ中",
|
||||||
|
"MMDOptions.reordering_bones": "ボーンを並べ替え中",
|
||||||
|
"MMDOptions.fixing_armature_names": "アーマチュア名を修正中",
|
||||||
|
"MMDOptions.renaming_bones": "ボーン名を変更中",
|
||||||
|
"MMDOptions.armature_optimization_complete": "アーマチュアの最適化が完了しました",
|
||||||
|
"MMDOptions.convert_materials.label": "マテリアルを変換",
|
||||||
|
"MMDOptions.convert_materials.desc": "マテリアルをPrincipled BSDFシェーダーを使用するように変換し、MMDとVRMシェーダーを修正",
|
||||||
|
"MMDOptions.converting_materials": "{name}のマテリアルを変換中",
|
||||||
|
"MMDOptions.title": "MMDオプション",
|
||||||
|
"MMDOptions.no_armature_selected": "アーマチュアが選択されていません",
|
||||||
|
"MMDOptions.label": "MMDオプション",
|
||||||
|
"MMDOptions.cleanup_mesh.label": "メッシュのクリーンアップ",
|
||||||
|
"MMDOptions.cleanup_mesh.desc": "空のオブジェクト、未使用の頂点グループ、未使用の頂点、空のシェイプキーを削除してメッシュをクリーンアップ",
|
||||||
|
"MMDOptions.removing_empty_objects": "空のオブジェクトを削除中",
|
||||||
|
"MMDOptions.removing_unused_vertex_groups": "未使用の頂点グループを削除中",
|
||||||
|
"MMDOptions.removing_unused_vertices": "未使用の頂点を削除中",
|
||||||
|
"MMDOptions.removing_empty_shape_keys": "空のシェイプキーを削除中",
|
||||||
|
"MMDOptions.optimize_weights.label": "ウェイトを最適化",
|
||||||
|
"MMDOptions.optimize_weights.desc": "頂点あたりのウェイト数を制限してウェイトを最適化",
|
||||||
|
"MMDOptions.max_weights.label": "最大ウェイト数",
|
||||||
|
"MMDOptions.max_weights.desc": "頂点あたりの最大ウェイト数",
|
||||||
|
"MMDOptions.merging_weights": "ウェイトを結合中",
|
||||||
|
"MMDOptions.removing_zero_weight_bones": "ウェイトのないボーンを削除中",
|
||||||
|
"MMDOptions.limiting_vertex_weights": "頂点ウェイトを制限中",
|
||||||
|
"MMDOptions.weight_optimization_complete": "ウェイトの最適化が完了しました",
|
||||||
|
"Updater.label": "アップデーター",
|
||||||
|
"Updater.CheckForUpdateButton.label": "アップデートを確認",
|
||||||
|
"Updater.CheckForUpdateButton.label_alt": "利用可能なアップデートはありません",
|
||||||
|
"Updater.UpdateToLatestButton.label": "{name}にアップデート",
|
||||||
|
"Updater.UpdateToSelectedButton.label": "アップデート",
|
||||||
|
"Updater.currentVersion": "現在のバージョン:{name}",
|
||||||
|
"Updater.CheckForUpdateButton.desc": "利用可能なアップデートを確認",
|
||||||
|
"UpdateToLatestButton.desc": "最新バージョンにアップデート",
|
||||||
|
"UpdateNotificationPopup.label": "アップデート通知",
|
||||||
|
"UpdateNotificationPopup.desc": "利用可能なアップデートについての通知",
|
||||||
|
"UpdateNotificationPopup.newUpdate": "新しいアップデートが利用可能:{version}",
|
||||||
|
"RestartBlenderPopup.label": "Blenderを再起動",
|
||||||
|
"RestartBlenderPopup.desc": "アップデートを完了するためにBlenderを再起動",
|
||||||
|
"RestartBlenderPopup.message": "アップデートが成功しました!Blenderを再起動してください。",
|
||||||
|
"check_for_update.cantCheck": "アップデートを確認できません",
|
||||||
|
"download_file.cantConnect": "アップデートサーバーに接続できません",
|
||||||
|
"download_file.cantFindZip": "アップデートファイルが見つかりません",
|
||||||
|
"download_file.cantFindAvatarToolkit": "アップデートパッケージ内にAvatar Toolkitファイルが見つかりません",
|
||||||
|
"CreditsSupport.label": "クレジット&サポート",
|
||||||
|
"CreditsSupport.credits_title": "クレジット",
|
||||||
|
"CreditsSupport.credits_text1": "Avatar Toolkitは以下のNeonekoチームによって作成されました:",
|
||||||
|
"CreditsSupport.credits_text2": "YusarinaとOnan989",
|
||||||
|
"CreditsSupport.credits_text3": "一部のコードはCats Blender Pluginを参考にしています。",
|
||||||
|
"CreditsSupport.credits_text4": "元のプラグインの貢献者に感謝します。",
|
||||||
|
"CreditsSupport.support_text1": "私たちの活動を支援したい場合は、",
|
||||||
|
"CreditsSupport.support_text2": "pally.ggページで寄付/投げ銭ができます。",
|
||||||
|
"CreditsSupport.support_title": "サポートする",
|
||||||
|
"CreditsSupport.support_button": "サポートする",
|
||||||
|
"CreditsSupport.help_title": "ヘルプが必要ですか?",
|
||||||
|
"CreditsSupport.help_text1": "まずはWikiをご確認ください。さらなるサポートを",
|
||||||
|
"CreditsSupport.help_text2": "求める前にWikiを読むことを強くお勧めします。",
|
||||||
|
"CreditsSupport.wiki_button": "Wiki",
|
||||||
|
"CreditsSupport.discord_button": "Discordに参加",
|
||||||
|
"TextureAtlas.include_in_atlas": "アトラスに含める",
|
||||||
|
"TextureAtlas.include_in_atlas_desc": "このマテリアルをテクスチャアトラスに含める",
|
||||||
|
"Scene.avatar_toolkit_updater_version_list.name": "バージョンリスト",
|
||||||
|
"Scene.avatar_toolkit_updater_version_list.description": "アップデート可能なバージョンのリスト",
|
||||||
|
"TextureAtlas.albedo": "アルベド",
|
||||||
|
"TextureAtlas.normal": "法線",
|
||||||
|
"TextureAtlas.emission": "発光",
|
||||||
|
"TextureAtlas.ambient_occlusion": "アンビエントオクルージョン",
|
||||||
|
"TextureAtlas.height": "ハイト",
|
||||||
|
"TextureAtlas.roughness": "ラフネス",
|
||||||
|
"TextureAtlas.error.label": "エラー",
|
||||||
|
"TextureAtlas.none.label": "なし",
|
||||||
|
"TextureAtlas.no_nodes_error.desc": "このマテリアルはノードを使用していません!",
|
||||||
|
"TextureAtlas.no_images_error.desc": "このマテリアルには画像がありません!",
|
||||||
|
"TextureAtlas.texture_use_atlas.desc": "{name}マップアトラスに使用するテクスチャ",
|
||||||
|
"TextureAtlas.no_materials_selected": "アトラス用のマテリアルが選択されていません",
|
||||||
|
"Optimization.select_armature": "アーマチュアを選択してください",
|
||||||
|
"CheckForUpdateButton.label": "アップデートを確認",
|
||||||
|
"CheckForUpdateButton.desc": "利用可能なアップデートを確認",
|
||||||
|
"UpdateToLatestButton.label": "最新バージョンにアップデート"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+118
-22
@@ -1,11 +1,56 @@
|
|||||||
from bpy.types import UIList, Panel, UILayout, Object, Context,Material, Operator
|
from bpy.types import UIList, Panel, UILayout, Object, Context,Material, Operator
|
||||||
import bpy
|
import bpy
|
||||||
|
from math import sqrt
|
||||||
from ..core.register import register_wrap
|
from ..core.register import register_wrap
|
||||||
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
from .panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
||||||
from ..core.common import SceneMatClass, MaterialListBool, get_selected_armature
|
from ..core.common import SceneMatClass, MaterialListBool, get_selected_armature
|
||||||
from ..functions.atlas_materials import AvatarToolKit_OT_AtlasMaterials
|
from ..functions.atlas_materials import AvatarToolKit_OT_AtlasMaterials
|
||||||
from ..functions.translations import t
|
from ..functions.translations import t
|
||||||
|
|
||||||
|
@register_wrap
|
||||||
|
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.materials:
|
||||||
|
item.mat.include_in_atlas = True
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@register_wrap
|
||||||
|
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.materials:
|
||||||
|
item.mat.include_in_atlas = False
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@register_wrap
|
||||||
|
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.materials:
|
||||||
|
item.mat.material_expanded = True
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@register_wrap
|
||||||
|
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.materials:
|
||||||
|
item.mat.material_expanded = False
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
@register_wrap
|
@register_wrap
|
||||||
class AvatarToolKit_OT_ExpandSectionMaterials(Operator):
|
class AvatarToolKit_OT_ExpandSectionMaterials(Operator):
|
||||||
bl_idname = 'avatar_toolkit.expand_section_materials'
|
bl_idname = 'avatar_toolkit.expand_section_materials'
|
||||||
@@ -40,23 +85,70 @@ class AvatarToolKit_UL_MaterialTextureAtlasProperties(UIList):
|
|||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
bl_region_type = 'UI'
|
bl_region_type = 'UI'
|
||||||
|
|
||||||
def draw_item(self , context: Context, layout: UILayout, data: bpy.types.Object, item:SceneMatClass, icon, active_data, active_propname, index):
|
def draw_header(self, context):
|
||||||
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
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, "material_search_filter", text="", icon='VIEWZOOM')
|
||||||
|
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.label(text=item.mat.name, icon = "MATERIAL")
|
row.label(text=f"Estimated Atlas Size: {self.calculate_atlas_size(context)}px")
|
||||||
col = box.row()
|
|
||||||
col.prop(item.mat, "texture_atlas_albedo")
|
def draw_item(self, context: Context, layout: UILayout, data: Object, item: SceneMatClass, icon, active_data, active_propname, index):
|
||||||
col = box.row()
|
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
||||||
col.prop(item.mat, "texture_atlas_normal")
|
if context.scene.material_search_filter and context.scene.material_search_filter.lower() not in item.mat.name.lower():
|
||||||
col = box.row()
|
return
|
||||||
col.prop(item.mat, "texture_atlas_emission")
|
|
||||||
col = box.row()
|
row = layout.row()
|
||||||
col.prop(item.mat, "texture_atlas_ambient_occlusion")
|
|
||||||
col = box.row()
|
# Add a clear checkbox for material selection
|
||||||
col.prop(item.mat, "texture_atlas_height")
|
row.prop(item.mat, "include_in_atlas", text="", icon='CHECKBOX_HLT' if item.mat.include_in_atlas else 'CHECKBOX_DEHLT')
|
||||||
col = box.row()
|
|
||||||
col.prop(item.mat, "texture_atlas_roughness")
|
# Material name and expansion toggle
|
||||||
|
row.prop(item.mat, "material_expanded",
|
||||||
|
text=item.mat.name,
|
||||||
|
icon='DOWNARROW_HLT' if item.mat.material_expanded else 'RIGHTARROW',
|
||||||
|
emboss=False)
|
||||||
|
|
||||||
|
# Show texture settings if expanded
|
||||||
|
if item.mat.material_expanded and item.mat.include_in_atlas:
|
||||||
|
box = layout.box()
|
||||||
|
col = box.column(align=True)
|
||||||
|
self.draw_texture_row(col, item.mat, "texture_atlas_albedo", "IMAGE_RGB")
|
||||||
|
self.draw_texture_row(col, item.mat, "texture_atlas_normal", "NORMALS_FACE")
|
||||||
|
self.draw_texture_row(col, item.mat, "texture_atlas_emission", "LIGHT")
|
||||||
|
self.draw_texture_row(col, item.mat, "texture_atlas_ambient_occlusion", "SHADING_SOLID")
|
||||||
|
self.draw_texture_row(col, item.mat, "texture_atlas_height", "IMAGE_ZDEPTH")
|
||||||
|
self.draw_texture_row(col, item.mat, "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 is_material_ready(self, material):
|
||||||
|
return bool(material.texture_atlas_albedo or
|
||||||
|
material.texture_atlas_normal or
|
||||||
|
material.texture_atlas_emission)
|
||||||
|
|
||||||
|
def calculate_atlas_size(self, context):
|
||||||
|
total_size = 0
|
||||||
|
for mat in context.scene.materials:
|
||||||
|
if mat.mat.include_in_atlas:
|
||||||
|
if mat.mat.texture_atlas_albedo:
|
||||||
|
img = bpy.data.images[mat.mat.texture_atlas_albedo]
|
||||||
|
total_size += img.size[0] * img.size[1]
|
||||||
|
return f"{int(sqrt(total_size))}x{int(sqrt(total_size))}"
|
||||||
|
|
||||||
@register_wrap
|
@register_wrap
|
||||||
class AvatarToolKit_PT_TextureAtlasPanel(Panel):
|
class AvatarToolKit_PT_TextureAtlasPanel(Panel):
|
||||||
@@ -74,7 +166,6 @@ class AvatarToolKit_PT_TextureAtlasPanel(Panel):
|
|||||||
|
|
||||||
if armature:
|
if armature:
|
||||||
layout.label(text=t("TextureAtlas.label"), icon='TEXTURE')
|
layout.label(text=t("TextureAtlas.label"), icon='TEXTURE')
|
||||||
|
|
||||||
layout.separator(factor=0.5)
|
layout.separator(factor=0.5)
|
||||||
|
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
@@ -86,16 +177,21 @@ class AvatarToolKit_PT_TextureAtlasPanel(Panel):
|
|||||||
|
|
||||||
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
if context.scene.texture_atlas_Has_Mat_List_Shown:
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.template_list(AvatarToolKit_UL_MaterialTextureAtlasProperties.bl_idname, 'material_list',
|
row.template_list(AvatarToolKit_UL_MaterialTextureAtlasProperties.bl_idname,
|
||||||
context.scene, 'materials', context.scene, 'texture_atlas_material_index',
|
'material_list',
|
||||||
rows=12, type='DEFAULT')
|
context.scene,
|
||||||
|
'materials',
|
||||||
|
context.scene,
|
||||||
|
'texture_atlas_material_index',
|
||||||
|
rows=12,
|
||||||
|
type='DEFAULT')
|
||||||
|
|
||||||
layout.separator(factor=1.0)
|
layout.separator(factor=1.0)
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.scale_y = 1.5
|
row.scale_y = 1.5
|
||||||
row.operator(AvatarToolKit_OT_AtlasMaterials.bl_idname, text=t("TextureAtlas.atlas_materials"), icon='NODE_TEXTURE')
|
row.operator(AvatarToolKit_OT_AtlasMaterials.bl_idname,
|
||||||
|
text=t("TextureAtlas.atlas_materials"),
|
||||||
|
icon='NODE_TEXTURE')
|
||||||
else:
|
else:
|
||||||
layout.label(text=t("Tools.select_armature"), icon='ERROR')
|
layout.label(text=t("Tools.select_armature"), icon='ERROR')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user