Holy shit this was a pain

- Truly fixes PMX Import lol, i messed up completely
- Updated MMD Tools to use Cats One
This commit is contained in:
Yusarina
2025-11-19 06:35:06 +00:00
parent f0bda259d3
commit a929f68ad4
38 changed files with 4479 additions and 2709 deletions
+20 -30
View File
@@ -1,90 +1,82 @@
# -*- coding: utf-8 -*-
# Copyright 2014 MMD Tools authors
# This file was originally part of the MMD Tools add-on for Blender
# You can find MMD Tools here: https://github.com/MMD-Blender/blender_mmd_tools
# Neoneko has modified this file to work with Avatar Toolkit and may of made changes or improvements.
# MMD Tools is licensed under the terms of the GNU General Public License version 3 (GPLv3) same as Avatar Toolkit.
# This file is part of MMD Tools.
import bpy
from typing import Optional, Set, Dict, Any, List, Tuple, Union, Type
from .. import utils
from ..core import material
from ..core.material import FnMaterial
from ..core.model import FnModel
from . import patch_library_overridable
from ....core.logging_setup import logger
def _mmd_material_update_ambient_color(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_ambient_color(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_ambient_color()
def _mmd_material_update_diffuse_color(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_diffuse_color(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_diffuse_color()
def _mmd_material_update_alpha(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_alpha(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_alpha()
def _mmd_material_update_specular_color(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_specular_color(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_specular_color()
def _mmd_material_update_shininess(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_shininess(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_shininess()
def _mmd_material_update_is_double_sided(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_is_double_sided(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_is_double_sided()
def _mmd_material_update_sphere_texture_type(prop: "MMDMaterial", context: bpy.types.Context) -> None:
def _mmd_material_update_sphere_texture_type(prop: "MMDMaterial", context):
FnMaterial(prop.id_data).update_sphere_texture_type(context.active_object)
def _mmd_material_update_toon_texture(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_toon_texture(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_toon_texture()
def _mmd_material_update_enabled_drop_shadow(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_enabled_drop_shadow(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_drop_shadow()
def _mmd_material_update_enabled_self_shadow_map(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_enabled_self_shadow_map(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_self_shadow_map()
def _mmd_material_update_enabled_self_shadow(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_enabled_self_shadow(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_self_shadow()
def _mmd_material_update_enabled_toon_edge(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_enabled_toon_edge(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_enabled_toon_edge()
def _mmd_material_update_edge_color(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_edge_color(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_edge_color()
def _mmd_material_update_edge_weight(prop: "MMDMaterial", _context: bpy.types.Context) -> None:
def _mmd_material_update_edge_weight(prop: "MMDMaterial", _context):
FnMaterial(prop.id_data).update_edge_weight()
def _mmd_material_get_name_j(prop: "MMDMaterial") -> str:
def _mmd_material_get_name_j(prop: "MMDMaterial"):
return prop.get("name_j", "")
def _mmd_material_set_name_j(prop: "MMDMaterial", value: str) -> None:
def _mmd_material_set_name_j(prop: "MMDMaterial", value: str):
prop_value = value
if prop_value and prop_value != prop.get("name_j"):
root = FnModel.find_root_object(bpy.context.active_object)
if root is None:
logger.debug(f"No root object found, using unique name for material: {value}")
prop_value = utils.unique_name(value, {mat.mmd_material.name_j for mat in bpy.data.materials})
else:
logger.debug(f"Root object found, using unique name for material within model: {value}")
prop_value = utils.unique_name(value, {mat.mmd_material.name_j for mat in FnModel.iterate_materials(root)})
prop["name_j"] = prop_value
@@ -279,15 +271,13 @@ class MMDMaterial(bpy.types.PropertyGroup):
description="Comment",
)
def is_id_unique(self) -> bool:
def is_id_unique(self):
return self.material_id < 0 or not next((m for m in bpy.data.materials if m.mmd_material != self and m.mmd_material.material_id == self.material_id), None)
@staticmethod
def register() -> None:
logger.debug("Registering MMD material properties")
def register():
bpy.types.Material.mmd_material = patch_library_overridable(bpy.props.PointerProperty(type=MMDMaterial))
@staticmethod
def unregister() -> None:
logger.debug("Unregistering MMD material properties")
def unregister():
del bpy.types.Material.mmd_material
+49 -58
View File
@@ -1,38 +1,34 @@
# -*- coding: utf-8 -*-
# Copyright 2014 MMD Tools authors
# This file was originally part of the MMD Tools add-on for Blender
# You can find MMD Tools here: https://github.com/MMD-Blender/blender_mmd_tools
# Neoneko has modified this file to work with Avatar Toolkit and may of made changes or improvements.
# MMD Tools is licensed under the terms of the GNU General Public License version 3 (GPLv3) same as Avatar Toolkit.
# Copyright 2015 MMD Tools authors
# This file is part of MMD Tools.
import bpy
from typing import Optional, List, Dict, Any, Set, Tuple, Union, TypeVar, Type
from bpy.types import PropertyGroup, Object, ShapeKey
from .. import utils
from ..core.bone import FnBone
from ..core.material import FnMaterial
from ..core.model import FnModel, Model
from ..core.morph import FnMorph
from ....core.logging_setup import logger
def _morph_base_get_name(prop: "_MorphBase") -> str:
return prop.get("name", "")
def _morph_base_set_name(prop: "_MorphBase", value: str) -> None:
def _morph_base_set_name(prop: "_MorphBase", value: str):
mmd_root = prop.id_data.mmd_root
morph_type = "%s_morphs" % prop.bl_rna.identifier[:-5].lower()
# morph_type = mmd_root.active_morph_type
morph_type = f"{prop.bl_rna.identifier[:-5].lower()}_morphs"
# assert(prop.bl_rna.identifier.endswith('Morph'))
# logging.debug('_set_name: %s %s %s', prop, value, morph_type)
prop_name = prop.get("name", None)
if prop_name == value:
return
used_names: Set[str] = {x.name for x in getattr(mmd_root, morph_type) if x != prop}
used_names = {x.name for x in getattr(mmd_root, morph_type) if x != prop}
value = utils.unique_name(value, used_names)
if prop_name is not None:
if morph_type == "vertex_morphs":
kb_list: Dict[str, List[ShapeKey]] = {}
kb_list = {}
for mesh in FnModel.iterate_mesh_objects(prop.id_data):
for kb in getattr(mesh.data.shape_keys, "key_blocks", ()):
kb_list.setdefault(kb.name, []).append(kb)
@@ -43,7 +39,7 @@ def _morph_base_set_name(prop: "_MorphBase", value: str) -> None:
kb.name = value
elif morph_type == "uv_morphs":
vg_list: Dict[str, List[Any]] = {}
vg_list = {}
for mesh in FnModel.iterate_mesh_objects(prop.id_data):
for vg, n, x in FnMorph.get_uv_morph_vertex_groups(mesh):
vg_list.setdefault(n, []).append(vg)
@@ -72,7 +68,6 @@ def _morph_base_set_name(prop: "_MorphBase", value: str) -> None:
kb.name = value
prop["name"] = value
logger.debug(f"Renamed morph from '{prop_name}' to '{value}'")
class _MorphBase:
@@ -101,12 +96,16 @@ class _MorphBase:
)
def _bone_morph_data_update_bone_id(prop: "BoneMorphData", context: bpy.types.Context):
pass # Empty function is sufficient to trigger UI update
def _bone_morph_data_get_bone(prop: "BoneMorphData") -> str:
bone_id: int = prop.get("bone_id", -1)
bone_id = prop.get("bone_id", -1)
if bone_id < 0:
return ""
root_object: Object = prop.id_data
armature_object: Optional[Object] = FnModel.find_armature_object(root_object)
root_object = prop.id_data
armature_object = FnModel.find_armature_object(root_object)
if armature_object is None:
return ""
pose_bone = FnBone.find_pose_bone_by_bone_id(armature_object, bone_id)
@@ -115,9 +114,9 @@ def _bone_morph_data_get_bone(prop: "BoneMorphData") -> str:
return pose_bone.name
def _bone_morph_data_set_bone(prop: "BoneMorphData", value: str) -> None:
root: Object = prop.id_data
arm: Optional[Object] = FnModel.find_armature_object(root)
def _bone_morph_data_set_bone(prop: "BoneMorphData", value: str):
root = prop.id_data
arm = FnModel.find_armature_object(root)
# Load the library_override file. This function is triggered when loading, but the arm obj cannot be found.
# The arm obj is exist, but the relative relationship has not yet been established.
@@ -125,14 +124,13 @@ def _bone_morph_data_set_bone(prop: "BoneMorphData", value: str) -> None:
return
if value not in arm.pose.bones.keys():
prop["bone_id"] = -1
prop.bone_id = -1
return
pose_bone = arm.pose.bones[value]
prop["bone_id"] = FnBone.get_or_assign_bone_id(pose_bone)
logger.debug(f"Set bone morph data bone to '{value}' with ID {prop['bone_id']}")
prop.bone_id = FnBone.get_or_assign_bone_id(pose_bone)
def _bone_morph_data_update_location_or_rotation(prop: "BoneMorphData", _context: bpy.types.Context) -> None:
def _bone_morph_data_update_location_or_rotation(prop: "BoneMorphData", _context):
if not prop.name.startswith("mmd_bind"):
return
arm = FnModel(prop.id_data).morph_slider.dummy_armature
@@ -141,12 +139,9 @@ def _bone_morph_data_update_location_or_rotation(prop: "BoneMorphData", _context
if bone:
bone.location = prop.location
bone.rotation_quaternion = prop.rotation.__class__(*prop.rotation.to_axis_angle()) # Fix for consistency
logger.debug(f"Updated bone morph data location/rotation for '{prop.name}'")
class BoneMorphData(bpy.types.PropertyGroup):
""" """
bone: bpy.props.StringProperty(
name="Bone",
description="Target bone",
@@ -156,6 +151,7 @@ class BoneMorphData(bpy.types.PropertyGroup):
bone_id: bpy.props.IntProperty(
name="Bone ID",
update=_bone_morph_data_update_bone_id,
)
location: bpy.props.FloatVectorProperty(
@@ -191,61 +187,53 @@ class BoneMorph(_MorphBase, bpy.types.PropertyGroup):
)
def _material_morph_data_get_material(prop: "MaterialMorphData") -> str:
mat_p = prop.get("material_data", None)
if mat_p is not None:
return mat_p.name
def _material_morph_data_get_material(prop: "MaterialMorphData"):
mat_data = prop.get("material_data", None)
if mat_data is not None:
return mat_data.name
return ""
def _material_morph_data_set_material(prop: "MaterialMorphData", value: str) -> None:
def _material_morph_data_set_material(prop: "MaterialMorphData", value: str):
if value not in bpy.data.materials:
prop["material_data"] = None
prop["material_id"] = -1
logger.debug(f"Material '{value}' not found, setting material_data to None")
prop.material_data = None
prop.material_id = -1
else:
mat = bpy.data.materials[value]
fnMat = FnMaterial(mat)
prop["material_data"] = mat
prop["material_id"] = fnMat.material_id
logger.debug(f"Set material morph data material to '{value}' with ID {fnMat.material_id}")
prop.material_data = mat
prop.material_id = fnMat.material_id
def _material_morph_data_set_related_mesh(prop: "MaterialMorphData", value: str) -> None:
def _material_morph_data_set_related_mesh(prop: "MaterialMorphData", value: str):
mesh = FnModel.find_mesh_object_by_name(prop.id_data, value)
if mesh is not None:
prop["related_mesh_data"] = mesh.data
logger.debug(f"Set material morph data related mesh to '{value}'")
prop.related_mesh_data = mesh.data
else:
prop["related_mesh_data"] = None
logger.debug(f"Mesh '{value}' not found, setting related_mesh_data to None")
prop.related_mesh_data = None
def _material_morph_data_get_related_mesh(prop: "MaterialMorphData") -> str:
mesh_p = prop.get("related_mesh_data", None)
if mesh_p is not None:
return mesh_p.name
def _material_morph_data_get_related_mesh(prop):
mesh_data = prop.get("related_mesh_data", None)
if mesh_data is not None:
return mesh_data.name
return ""
def _material_morph_data_update_modifiable_values(prop: "MaterialMorphData", _context: bpy.types.Context) -> None:
def _material_morph_data_update_modifiable_values(prop: "MaterialMorphData", _context):
if not prop.name.startswith("mmd_bind"):
return
from ..core.shader import _MaterialMorph
mat = prop["material_data"]
if mat is not None:
_MaterialMorph.update_morph_inputs(mat, prop)
logger.debug(f"Updated material morph modifiable values for '{prop.name}'")
mat_data = prop.get("material_data", None)
if mat_data is not None:
_MaterialMorph.update_morph_inputs(mat_data, prop)
else:
for mat in FnModel(prop.id_data).materials():
_MaterialMorph.update_morph_inputs(mat, prop)
logger.debug(f"Updated material morph modifiable values for all materials")
for mat_data in FnModel(prop.id_data).materials():
_MaterialMorph.update_morph_inputs(mat_data, prop)
class MaterialMorphData(bpy.types.PropertyGroup):
""" """
related_mesh: bpy.props.StringProperty(
name="Related Mesh",
description="Stores a reference to the mesh where this morph data belongs to",
@@ -416,6 +404,9 @@ class UVMorphOffset(bpy.types.PropertyGroup):
name="UV Offset",
description="UV offset",
size=4,
# min=-1,
# max=1,
# precision=3,
step=0.1,
default=[0, 0, 0, 0],
)
+85 -29
View File
@@ -1,37 +1,31 @@
# -*- coding: utf-8 -*-
# Copyright 2014 MMD Tools authors
# This file was originally part of the MMD Tools add-on for Blender
# You can find MMD Tools here: https://github.com/MMD-Blender/blender_mmd_tools
# Neoneko has modified this file to work with Avatar Toolkit and may of made changes or improvements.
# MMD Tools is licensed under the terms of the GNU General Public License version 3 (GPLv3) same as Avatar Toolkit.
# This file is part of MMD Tools.
from typing import cast
from typing import cast, Optional, Any, Union
import bpy
from bpy.types import Context, PropertyGroup, PoseBone, Object, Armature
from ..core.bone import FnBone
from . import patch_library_overridable
from ....core.logging_setup import logger
def _mmd_bone_update_additional_transform(prop: "MMDBone", context: Context) -> None:
prop["is_additional_transform_dirty"] = True
def _mmd_bone_update_additional_transform(prop: "MMDBone", context: bpy.types.Context):
prop.is_additional_transform_dirty = True
# Apply additional transform (Assembly -> Bone button) (Very Slow)
p_bone = context.active_pose_bone
if p_bone and p_bone.mmd_bone.as_pointer() == prop.as_pointer():
logger.debug(f"Applying additional transformation for {p_bone.name}")
FnBone.apply_additional_transformation(prop.id_data)
def _mmd_bone_update_additional_transform_influence(prop: "MMDBone", context: Context) -> None:
def _mmd_bone_update_additional_transform_influence(prop: "MMDBone", context: bpy.types.Context):
pose_bone = context.active_pose_bone
if pose_bone and pose_bone.mmd_bone.as_pointer() == prop.as_pointer():
logger.debug(f"Updating additional transform influence for {pose_bone.name}")
FnBone.update_additional_transform_influence(pose_bone)
else:
prop["is_additional_transform_dirty"] = True
prop.is_additional_transform_dirty = True
def _mmd_bone_get_additional_transform_bone(prop: "MMDBone") -> str:
def _mmd_bone_get_additional_transform_bone(prop: "MMDBone"):
arm = prop.id_data
bone_id = prop.get("additional_transform_bone_id", -1)
if bone_id < 0:
@@ -42,17 +36,57 @@ def _mmd_bone_get_additional_transform_bone(prop: "MMDBone") -> str:
return pose_bone.name
def _mmd_bone_set_additional_transform_bone(prop: "MMDBone", value: str) -> None:
def _mmd_bone_set_additional_transform_bone(prop: "MMDBone", value: str):
arm = prop.id_data
prop["is_additional_transform_dirty"] = True
prop.is_additional_transform_dirty = True
if value not in arm.pose.bones.keys():
prop["additional_transform_bone_id"] = -1
prop.additional_transform_bone_id = -1
return
pose_bone = arm.pose.bones[value]
prop["additional_transform_bone_id"] = FnBone.get_or_assign_bone_id(pose_bone)
target_bone_id = FnBone.get_or_assign_bone_id(pose_bone)
if prop.bone_id == target_bone_id:
prop.additional_transform_bone_id = -1
return
prop.additional_transform_bone_id = target_bone_id
class MMDBone(PropertyGroup):
def _mmd_bone_update_display_connection(prop: "MMDBone", context: bpy.types.Context):
pass # Empty function is sufficient to trigger UI update
def _mmd_bone_get_display_connection_bone(prop: "MMDBone"):
arm = prop.id_data
bone_id = prop.get("display_connection_bone_id", -1)
if bone_id < 0:
return ""
pose_bone = FnBone.find_pose_bone_by_bone_id(arm, bone_id)
if pose_bone is None:
return ""
return pose_bone.name
def _mmd_bone_set_display_connection_bone(prop: "MMDBone", value: str):
arm = prop.id_data
if value not in arm.pose.bones.keys():
prop.display_connection_bone_id = -1
return
pose_bone = arm.pose.bones[value]
target_bone_id = FnBone.get_or_assign_bone_id(pose_bone)
if prop.bone_id == target_bone_id:
prop.display_connection_bone_id = -1
return
prop.display_connection_bone_id = target_bone_id
class MMDBone(bpy.types.PropertyGroup):
name_j: bpy.props.StringProperty(
name="Name",
description="Japanese Name",
@@ -188,12 +222,35 @@ class MMDBone(PropertyGroup):
is_additional_transform_dirty: bpy.props.BoolProperty(name="", default=True)
def is_id_unique(self) -> bool:
display_connection_bone: bpy.props.StringProperty(
name="Display Connection Bone",
description="Target bone for display connection",
set=_mmd_bone_set_display_connection_bone,
get=_mmd_bone_get_display_connection_bone,
)
display_connection_bone_id: bpy.props.IntProperty(
name="Display Connection Bone ID",
description="Bone ID for display connection (PMX displayConnection)",
default=-1,
update=_mmd_bone_update_display_connection,
)
display_connection_type: bpy.props.EnumProperty(
name="Display Connection Type",
description="Type of display connection",
items=[
("BONE", "Bone", "Connected to a bone"),
("OFFSET", "Offset", "Connected to an offset position"),
],
default="OFFSET",
)
def is_id_unique(self):
return self.bone_id < 0 or not next((b for b in self.id_data.pose.bones if b.mmd_bone != self and b.mmd_bone.bone_id == self.bone_id), None)
@staticmethod
def register() -> None:
logger.debug("Registering MMDBone properties")
def register():
bpy.types.PoseBone.mmd_bone = patch_library_overridable(bpy.props.PointerProperty(type=MMDBone))
bpy.types.PoseBone.is_mmd_shadow_bone = patch_library_overridable(bpy.props.BoolProperty(name="is_mmd_shadow_bone", default=False))
bpy.types.PoseBone.mmd_shadow_bone_type = patch_library_overridable(bpy.props.StringProperty(name="mmd_shadow_bone_type"))
@@ -203,25 +260,24 @@ class MMDBone(PropertyGroup):
description="MMD IK toggle is used to import/export animation of IK on-off",
update=_pose_bone_update_mmd_ik_toggle,
default=True,
)
),
)
@staticmethod
def unregister() -> None:
logger.debug("Unregistering MMDBone properties")
def unregister():
del bpy.types.PoseBone.mmd_ik_toggle
del bpy.types.PoseBone.mmd_shadow_bone_type
del bpy.types.PoseBone.is_mmd_shadow_bone
del bpy.types.PoseBone.mmd_bone
def _pose_bone_update_mmd_ik_toggle(prop: PoseBone, _context: Any) -> None:
def _pose_bone_update_mmd_ik_toggle(prop: bpy.types.PoseBone, _context):
v = prop.mmd_ik_toggle
armature_object = cast(Object, prop.id_data)
armature_object = cast("bpy.types.Object", prop.id_data)
for b in armature_object.pose.bones:
for c in b.constraints:
if c.type == "IK" and c.subtarget == prop.name:
logger.debug(f"Updating IK toggle for {b.name} {c.name}")
# logging.debug(' %s %s', b.name, c.name)
c.influence = v
b = b if c.use_tail else b.parent
for b in ([b] + b.parent_recursive)[: c.chain_count]:
+34 -43
View File
@@ -1,42 +1,35 @@
# -*- coding: utf-8 -*-
# Copyright 2014 MMD Tools authors
# This file was originally part of the MMD Tools add-on for Blender
# You can find MMD Tools here: https://github.com/MMD-Blender/blender_mmd_tools
# Neoneko has modified this file to work with Avatar Toolkit and may of made changes or improvements.
# MMD Tools is licensed under the terms of the GNU General Public License version 3 (GPLv3) same as Avatar Toolkit.
# This file is part of MMD Tools.
"""Properties for rigid bodies and joints"""
import bpy
from typing import Optional, Any, Set, List, Dict, Tuple, Union
from bpy.types import Context, Object, PropertyGroup, Material
from .. import bpyutils
from ..core import rigid_body
from ..core.rigid_body import RigidBodyMaterial, FnRigidBody
from ..core.model import FnModel
from ..core.rigid_body import FnRigidBody, RigidBodyMaterial
from . import patch_library_overridable
from ....core.logging_setup import logger
def _updateCollisionGroup(prop: PropertyGroup, _context: Context) -> None:
obj: Object = prop.id_data
materials: List[Material] = obj.data.materials
def _updateCollisionGroup(prop, _context):
obj = prop.id_data
materials = obj.data.materials
if len(materials) == 0:
materials.append(RigidBodyMaterial.getMaterial(prop.collision_group_number))
else:
obj.material_slots[0].material = RigidBodyMaterial.getMaterial(prop.collision_group_number)
def _updateType(prop: PropertyGroup, _context: Context) -> None:
obj: Object = prop.id_data
def _updateType(prop, _context):
obj = prop.id_data
rb = obj.rigid_body
if rb:
rb.kinematic = int(prop.type) == rigid_body.MODE_STATIC
def _updateShape(prop: PropertyGroup, _context: Context) -> None:
obj: Object = prop.id_data
def _updateShape(prop, _context):
obj = prop.id_data
if len(obj.data.vertices) > 0:
size = prop.size
@@ -47,8 +40,8 @@ def _updateShape(prop: PropertyGroup, _context: Context) -> None:
rb.collision_shape = prop.shape
def _get_bone(prop: PropertyGroup) -> str:
obj: Object = prop.id_data
def _get_bone(prop):
obj = prop.id_data
relation = obj.constraints.get("mmd_tools_rigid_parent", None)
if relation:
arm = relation.target
@@ -58,9 +51,9 @@ def _get_bone(prop: PropertyGroup) -> str:
return prop.get("bone", "")
def _set_bone(prop: PropertyGroup, value: str) -> None:
bone_name: str = value
obj: Object = prop.id_data
def _set_bone(prop, value):
bone_name = value
obj = prop.id_data
relation = obj.constraints.get("mmd_tools_rigid_parent", None)
if relation is None:
relation = obj.constraints.new("CHILD_OF")
@@ -81,21 +74,24 @@ def _set_bone(prop: PropertyGroup, value: str) -> None:
prop["bone"] = bone_name
def _get_size(prop: PropertyGroup) -> Tuple[float, float, float]:
def _get_size(prop):
if prop.id_data.mmd_type != "RIGID_BODY":
return (0, 0, 0)
return FnRigidBody.get_rigid_body_size(prop.id_data)
def _set_size(prop: PropertyGroup, value: Tuple[float, float, float]) -> None:
obj: Object = prop.id_data
def _set_size(prop, value):
obj = prop.id_data
assert obj.mode == "OBJECT" # not support other mode yet
shape: str = prop.shape
shape = prop.shape
mesh = obj.data
rb = obj.rigid_body
if len(mesh.vertices) == 0 or rb is None or rb.collision_shape != shape:
current_size = FnRigidBody.get_rigid_body_size(obj)
is_zero_size = all(abs(s) < 1e-6 for s in current_size)
if len(mesh.vertices) == 0 or rb is None or rb.collision_shape != shape or is_zero_size:
if shape == "SPHERE":
bpyutils.makeSphere(
radius=value[0],
@@ -149,15 +145,15 @@ def _set_size(prop: PropertyGroup, value: Tuple[float, float, float]) -> None:
mesh.update()
def _get_rigid_name(prop: PropertyGroup) -> str:
def _get_rigid_name(prop):
return prop.get("name", "")
def _set_rigid_name(prop: PropertyGroup, value: str) -> None:
def _set_rigid_name(prop, value):
prop["name"] = value
class MMDRigidBody(PropertyGroup):
class MMDRigidBody(bpy.types.PropertyGroup):
name_j: bpy.props.StringProperty(
name="Name",
description="Japanese Name",
@@ -230,18 +226,16 @@ class MMDRigidBody(PropertyGroup):
)
@staticmethod
def register() -> None:
logger.debug("Registering MMDRigidBody property")
def register():
bpy.types.Object.mmd_rigid = patch_library_overridable(bpy.props.PointerProperty(type=MMDRigidBody))
@staticmethod
def unregister() -> None:
logger.debug("Unregistering MMDRigidBody property")
def unregister():
del bpy.types.Object.mmd_rigid
def _updateSpringLinear(prop: PropertyGroup, context: Context) -> None:
obj: Object = prop.id_data
def _updateSpringLinear(prop, context):
obj = prop.id_data
rbc = obj.rigid_body_constraint
if rbc:
rbc.spring_stiffness_x = prop.spring_linear[0]
@@ -249,8 +243,8 @@ def _updateSpringLinear(prop: PropertyGroup, context: Context) -> None:
rbc.spring_stiffness_z = prop.spring_linear[2]
def _updateSpringAngular(prop: PropertyGroup, context: Context) -> None:
obj: Object = prop.id_data
def _updateSpringAngular(prop, context):
obj = prop.id_data
rbc = obj.rigid_body_constraint
if rbc and hasattr(rbc, "use_spring_ang_x"):
rbc.spring_stiffness_ang_x = prop.spring_angular[0]
@@ -258,7 +252,7 @@ def _updateSpringAngular(prop: PropertyGroup, context: Context) -> None:
rbc.spring_stiffness_ang_z = prop.spring_angular[2]
class MMDJoint(PropertyGroup):
class MMDJoint(bpy.types.PropertyGroup):
name_j: bpy.props.StringProperty(
name="Name",
description="Japanese Name",
@@ -292,12 +286,9 @@ class MMDJoint(PropertyGroup):
)
@staticmethod
def register() -> None:
logger.debug("Registering MMDJoint property")
def register():
bpy.types.Object.mmd_joint = patch_library_overridable(bpy.props.PointerProperty(type=MMDJoint))
@staticmethod
def unregister() -> None:
logger.debug("Unregistering MMDJoint property")
def unregister():
del bpy.types.Object.mmd_joint
+87 -56
View File
@@ -1,16 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright 2014 MMD Tools authors
# This file was originally part of the MMD Tools add-on for Blender
# You can find MMD Tools here: https://github.com/MMD-Blender/blender_mmd_tools
# Neoneko has modified this file to work with Avatar Toolkit and may of made changes or improvements.
# MMD Tools is licensed under the terms of the GNU General Public License version 3 (GPLv3) same as Avatar Toolkit.
# This file is part of MMD Tools.
"""Properties for MMD model root object"""
import bpy
from typing import Optional, List, Dict, Any, Set, Tuple, Union, Type, TypeVar, cast
from .. import utils
from ..bpyutils import FnContext
from ..core.material import FnMaterial
from ..core.model import FnModel
@@ -18,18 +12,19 @@ from ..core.sdef import FnSDEF
from . import patch_library_overridable
from .morph import BoneMorph, GroupMorph, MaterialMorph, UVMorph, VertexMorph
from .translations import MMDTranslation
from ....core.logging_setup import logger
IS_BLENDER_50_UP = bpy.app.version >= (5, 0)
def __driver_variables(constraint: bpy.types.Constraint, path: str, index: int = -1) -> Tuple[bpy.types.Driver, Any]:
def __driver_variables(constraint: bpy.types.Constraint, path: str, index=-1):
d = constraint.driver_add(path, index)
variables = d.driver.variables
for x in variables:
for x in reversed(variables):
variables.remove(x)
return d.driver, variables
def __add_single_prop(variables: Any, id_obj: bpy.types.Object, data_path: str, prefix: str) -> Any:
def __add_single_prop(variables, id_obj, data_path, prefix):
var = variables.new()
var.name = prefix + str(len(variables))
var.type = "SINGLE_PROP"
@@ -40,18 +35,17 @@ def __add_single_prop(variables: Any, id_obj: bpy.types.Object, data_path: str,
return var
def _toggleUsePropertyDriver(self: "MMDRoot", _context: bpy.types.Context) -> None:
def _toggleUsePropertyDriver(self: "MMDRoot", _context):
root_object: bpy.types.Object = self.id_data
armature_object = FnModel.find_armature_object(root_object)
if armature_object is None:
ik_map: Dict[Any, Tuple[Any, Any]] = {}
ik_map = {}
else:
bones = armature_object.pose.bones
ik_map = {bones[c.subtarget]: (b, c) for b in bones for c in b.constraints if c.type == "IK" and c.is_valid and c.subtarget in bones}
if self.use_property_driver:
logger.debug("Enabling property drivers for %s", root_object.name)
for ik, (b, c) in ik_map.items():
driver, variables = __driver_variables(c, "influence")
driver.expression = "%s" % __add_single_prop(variables, ik.id_data, ik.path_from_id("mmd_ik_toggle"), "use_ik").name
@@ -66,7 +60,6 @@ def _toggleUsePropertyDriver(self: "MMDRoot", _context: bpy.types.Context) -> No
driver, variables = __driver_variables(i, prop_hide)
driver.expression = "not %s" % __add_single_prop(variables, root_object, "mmd_root.show_meshes", "show").name
else:
logger.debug("Disabling property drivers for %s", root_object.name)
for ik, (b, c) in ik_map.items():
c.driver_remove("influence")
b = b if c.use_tail else b.parent
@@ -84,35 +77,31 @@ def _toggleUsePropertyDriver(self: "MMDRoot", _context: bpy.types.Context) -> No
# ===========================================
def _toggleUseToonTexture(self: "MMDRoot", _context: bpy.types.Context) -> None:
def _toggleUseToonTexture(self: "MMDRoot", _context):
use_toon = self.use_toon_texture
logger.debug("Toggling toon texture to %s for %s", use_toon, self.id_data.name)
for i in FnModel.iterate_mesh_objects(self.id_data):
for m in i.data.materials:
if m:
FnMaterial(m).use_toon_texture(use_toon)
def _toggleUseSphereTexture(self: "MMDRoot", _context: bpy.types.Context) -> None:
def _toggleUseSphereTexture(self: "MMDRoot", _context):
use_sphere = self.use_sphere_texture
logger.debug("Toggling sphere texture to %s for %s", use_sphere, self.id_data.name)
for i in FnModel.iterate_mesh_objects(self.id_data):
for m in i.data.materials:
if m:
FnMaterial(m).use_sphere_texture(use_sphere, i)
def _toggleUseSDEF(self: "MMDRoot", _context: bpy.types.Context) -> None:
def _toggleUseSDEF(self: "MMDRoot", _context):
mute_sdef = not self.use_sdef
logger.debug("Toggling SDEF to %s for %s", not mute_sdef, self.id_data.name)
for i in FnModel.iterate_mesh_objects(self.id_data):
FnSDEF.mute_sdef_set(i, mute_sdef)
def _toggleVisibilityOfMeshes(self: "MMDRoot", context: bpy.types.Context) -> None:
def _toggleVisibilityOfMeshes(self: "MMDRoot", context: bpy.types.Context):
root = self.id_data
hide = not self.show_meshes
logger.debug("Toggling mesh visibility to %s for %s", not hide, root.name)
for i in FnModel.iterate_mesh_objects(self.id_data):
i.hide_set(hide)
i.hide_render = hide
@@ -120,30 +109,27 @@ def _toggleVisibilityOfMeshes(self: "MMDRoot", context: bpy.types.Context) -> No
FnContext.set_active_object(context, root)
def _toggleVisibilityOfRigidBodies(self: "MMDRoot", context: bpy.types.Context) -> None:
def _toggleVisibilityOfRigidBodies(self: "MMDRoot", context: bpy.types.Context):
root = self.id_data
hide = not self.show_rigid_bodies
logger.debug("Toggling rigid body visibility to %s for %s", not hide, root.name)
for i in FnModel.iterate_rigid_body_objects(root):
i.hide_set(hide)
if hide and context.active_object is None:
FnContext.set_active_object(context, root)
def _toggleVisibilityOfJoints(self: "MMDRoot", context: bpy.types.Context) -> None:
def _toggleVisibilityOfJoints(self: "MMDRoot", context):
root_object = self.id_data
hide = not self.show_joints
logger.debug("Toggling joint visibility to %s for %s", not hide, root_object.name)
for i in FnModel.iterate_joint_objects(root_object):
i.hide_set(hide)
if hide and context.active_object is None:
FnContext.set_active_object(context, root_object)
def _toggleVisibilityOfTemporaryObjects(self: "MMDRoot", context: bpy.types.Context) -> None:
def _toggleVisibilityOfTemporaryObjects(self: "MMDRoot", context: bpy.types.Context):
root_object: bpy.types.Object = self.id_data
hide = not self.show_temporary_objects
logger.debug("Toggling temporary object visibility to %s for %s", not hide, root_object.name)
with FnContext.temp_override_active_layer_collection(context, root_object):
for i in FnModel.iterate_temporary_objects(root_object):
i.hide_set(hide)
@@ -151,48 +137,45 @@ def _toggleVisibilityOfTemporaryObjects(self: "MMDRoot", context: bpy.types.Cont
FnContext.set_active_object(context, root_object)
def _toggleShowNamesOfRigidBodies(self: "MMDRoot", _context: bpy.types.Context) -> None:
def _toggleShowNamesOfRigidBodies(self: "MMDRoot", _context):
root = self.id_data
show_names = root.mmd_root.show_names_of_rigid_bodies
logger.debug("Toggling rigid body names to %s for %s", show_names, root.name)
for i in FnModel.iterate_rigid_body_objects(root):
i.show_name = show_names
def _toggleShowNamesOfJoints(self: "MMDRoot", _context: bpy.types.Context) -> None:
def _toggleShowNamesOfJoints(self: "MMDRoot", _context):
root = self.id_data
show_names = root.mmd_root.show_names_of_joints
logger.debug("Toggling joint names to %s for %s", show_names, root.name)
for i in FnModel.iterate_joint_objects(root):
i.show_name = show_names
def _setVisibilityOfMMDRigArmature(prop: "MMDRoot", v: bool) -> None:
def _setVisibilityOfMMDRigArmature(prop: "MMDRoot", v: bool):
root = prop.id_data
arm = FnModel.find_armature_object(root)
if arm is None:
return
if not v and bpy.context.active_object == arm:
FnContext.set_active_object(bpy.context, root)
logger.debug("Setting armature visibility to %s for %s", v, root.name)
arm.hide_set(not v)
def _getVisibilityOfMMDRigArmature(prop: "MMDRoot") -> bool:
def _getVisibilityOfMMDRigArmature(prop: "MMDRoot"):
if prop.id_data.mmd_type != "ROOT":
return False
arm = FnModel.find_armature_object(prop.id_data)
return arm and not arm.hide_get()
return arm is not None and not arm.hide_get()
def _setActiveRigidbodyObject(prop: "MMDRoot", v: int) -> None:
def _setActiveRigidbodyObject(prop: "MMDRoot", v: int):
obj = FnContext.get_scene_objects(bpy.context)[v]
if FnModel.is_rigid_body_object(obj):
FnContext.set_active_and_select_single_object(bpy.context, obj)
prop["active_rigidbody_object_index"] = v
def _getActiveRigidbodyObject(prop: "MMDRoot") -> int:
def _getActiveRigidbodyObject(prop: "MMDRoot"):
context = bpy.context
active_obj = FnContext.get_active_object(context)
if FnModel.is_rigid_body_object(active_obj):
@@ -200,14 +183,14 @@ def _getActiveRigidbodyObject(prop: "MMDRoot") -> int:
return prop.get("active_rigidbody_object_index", 0)
def _setActiveJointObject(prop: "MMDRoot", v: int) -> None:
def _setActiveJointObject(prop: "MMDRoot", v: int):
obj = FnContext.get_scene_objects(bpy.context)[v]
if FnModel.is_joint_object(obj):
FnContext.set_active_and_select_single_object(bpy.context, obj)
prop["active_joint_object_index"] = v
def _getActiveJointObject(prop: "MMDRoot") -> int:
def _getActiveJointObject(prop: "MMDRoot"):
context = bpy.context
active_obj = FnContext.get_active_object(context)
if FnModel.is_joint_object(active_obj):
@@ -215,26 +198,26 @@ def _getActiveJointObject(prop: "MMDRoot") -> int:
return prop.get("active_joint_object_index", 0)
def _setActiveMorph(prop: "MMDRoot", v: bool) -> None:
def _setActiveMorph(prop: "MMDRoot", v: bool):
if "active_morph_indices" not in prop:
prop["active_morph_indices"] = [0] * 5
prop["active_morph_indices"][prop.get("active_morph_type", 3)] = v
def _getActiveMorph(prop: "MMDRoot") -> int:
def _getActiveMorph(prop: "MMDRoot"):
if "active_morph_indices" in prop:
return prop["active_morph_indices"][prop.get("active_morph_type", 3)]
return 0
def _setActiveMeshObject(prop: "MMDRoot", v: int) -> None:
def _setActiveMeshObject(prop: "MMDRoot", v: int):
obj = FnContext.get_scene_objects(bpy.context)[v]
if FnModel.is_mesh_object(obj):
FnContext.set_active_and_select_single_object(bpy.context, obj)
prop["active_mesh_index"] = v
def _getActiveMeshObject(prop: "MMDRoot") -> int:
def _getActiveMeshObject(prop: "MMDRoot"):
context = bpy.context
active_obj = FnContext.get_active_object(context)
if FnModel.is_mesh_object(active_obj):
@@ -393,6 +376,18 @@ class MMDRoot(bpy.types.PropertyGroup):
update=_toggleShowNamesOfJoints,
)
show_japanese_name: bpy.props.BoolProperty(
name="Japanese name",
description="Toggle Japanese name display",
default=True,
)
show_english_name: bpy.props.BoolProperty(
name="English name",
description="Toggle English name display",
default=True,
)
use_toon_texture: bpy.props.BoolProperty(
name="Use Toon Texture",
description="Use toon texture",
@@ -453,6 +448,15 @@ class MMDRoot(bpy.types.PropertyGroup):
default=0,
)
# *************************
# Bone
# *************************
active_bone_index: bpy.props.IntProperty(
name="Active Bone Index",
description="Index of the active bone in the armature",
default=0,
)
# *************************
# Morph
# *************************
@@ -513,29 +517,40 @@ class MMDRoot(bpy.types.PropertyGroup):
@staticmethod
def __get_select(prop: bpy.types.Object) -> bool:
utils.warn_deprecation("Object.select", "v4.0.0", "Use Object.select_get() method instead")
# TODO: Object.select is deprecated since v4.0.0, use Object.select_get() method instead
# utils.warn_deprecation("Object.select", "v4.0.0", "Use Object.select_get() method instead")
return prop.select_get()
@staticmethod
def __set_select(prop: bpy.types.Object, value: bool) -> None:
utils.warn_deprecation("Object.select", "v4.0.0", "Use Object.select_set() method instead")
# TODO: Object.select is deprecated since v4.0.0, use Object.select_set() method instead
# utils.warn_deprecation("Object.select", "v4.0.0", "Use Object.select_set() method instead")
prop.select_set(value)
@staticmethod
def __get_hide(prop: bpy.types.Object) -> bool:
utils.warn_deprecation("Object.hide", "v4.0.0", "Use Object.hide_get() method instead")
# TODO: Object.hide is deprecated since v4.0.0, use Object.hide_get() method instead
# utils.warn_deprecation("Object.hide", "v4.0.0", "Use Object.hide_get() method instead")
return prop.hide_get()
@staticmethod
def __set_hide(prop: bpy.types.Object, value: bool) -> None:
utils.warn_deprecation("Object.hide", "v4.0.0", "Use Object.hide_set() method instead")
# TODO: Object.hide is deprecated since v4.0.0, use Object.hide_set() method instead
# utils.warn_deprecation("Object.hide", "v4.0.0", "Use Object.hide_set() method instead")
prop.hide_set(value)
if prop.hide_viewport != value:
prop.hide_viewport = value
@staticmethod
def register() -> None:
logger.debug("Registering MMDRoot property group")
def __get_pose_bone_select(prop: bpy.types.PoseBone) -> bool:
return prop.bone.select
@staticmethod
def __set_pose_bone_select(prop: bpy.types.PoseBone, value: bool) -> None:
prop.bone.select = value
@staticmethod
def register():
bpy.types.Object.mmd_type = patch_library_overridable(
bpy.props.EnumProperty(
name="Type",
@@ -557,7 +572,7 @@ class MMDRoot(bpy.types.PropertyGroup):
("SPRING_CONSTRAINT", "Spring Constraint", "", 53),
("SPRING_GOAL", "Spring Goal", "", 54),
],
)
),
)
bpy.types.Object.mmd_root = patch_library_overridable(bpy.props.PointerProperty(type=MMDRoot))
@@ -570,7 +585,7 @@ class MMDRoot(bpy.types.PropertyGroup):
"ANIMATABLE",
"LIBRARY_EDITABLE",
},
)
),
)
bpy.types.Object.hide = patch_library_overridable(
bpy.props.BoolProperty(
@@ -581,13 +596,29 @@ class MMDRoot(bpy.types.PropertyGroup):
"ANIMATABLE",
"LIBRARY_EDITABLE",
},
)
),
)
if not IS_BLENDER_50_UP:
bpy.types.PoseBone.select = patch_library_overridable(
bpy.props.BoolProperty(
name="Select",
description="Pose bone selection state (compatibility layer for Blender 4.x, forwards to bone.select)",
get=MMDRoot.__get_pose_bone_select,
set=MMDRoot.__set_pose_bone_select,
options={
"SKIP_SAVE",
"ANIMATABLE",
"LIBRARY_EDITABLE",
},
),
)
@staticmethod
def unregister() -> None:
logger.debug("Unregistering MMDRoot property group")
def unregister():
del bpy.types.Object.hide
del bpy.types.Object.select
del bpy.types.Object.mmd_root
del bpy.types.Object.mmd_type
if not IS_BLENDER_50_UP:
del bpy.types.PoseBone.select
+2 -6
View File
@@ -1,9 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2014 MMD Tools authors
# This file was originally part of the MMD Tools add-on for Blender
# You can find MMD Tools here: https://github.com/MMD-Blender/blender_mmd_tools
# Neoneko has modified this file to work with Avatar Toolkit and may of made changes or improvements.
# MMD Tools is licensed under the terms of the GNU General Public License version 3 (GPLv3) same as Avatar Toolkit.
# Copyright 2021 MMD Tools authors
# This file is part of MMD Tools.
from typing import Dict, List, Optional, Tuple