From 704f905421534d6f813529bc7a2a22150ead8098 Mon Sep 17 00:00:00 2001 From: UuuNyaa Date: Sun, 17 Dec 2023 20:27:51 -0800 Subject: [PATCH] Refactor property callbacks --- mmd_tools/core/material.py | 4 +- mmd_tools/core/model.py | 26 +++++++++- mmd_tools/core/pmx/importer.py | 2 +- mmd_tools/properties/material.py | 82 +++++++++++++++---------------- mmd_tools/properties/morph.py | 67 +++++++++++++------------ mmd_tools/properties/pose_bone.py | 41 ++++++---------- mmd_tools/utils.py | 21 ++++++-- 7 files changed, 134 insertions(+), 109 deletions(-) diff --git a/mmd_tools/core/material.py b/mmd_tools/core/material.py index 6a8d520..5c29e32 100644 --- a/mmd_tools/core/material.py +++ b/mmd_tools/core/material.py @@ -4,7 +4,7 @@ import logging import os -from typing import Optional, Tuple +from typing import Optional, Tuple, cast import bpy @@ -80,7 +80,7 @@ def swap_materials(mesh_object: bpy.types.Object, mat1_ref: str | int, mat2_ref: Raises: MaterialNotFoundError: If one of the materials is not found """ - mesh: bpy.types.Mesh = mesh_object.data + mesh = cast(bpy.types.Mesh, mesh_object.data) try: # Try to find the materials mat1 = mesh.materials[mat1_ref] diff --git a/mmd_tools/core/model.py b/mmd_tools/core/model.py index 29d25e8..705faf4 100644 --- a/mmd_tools/core/model.py +++ b/mmd_tools/core/model.py @@ -5,7 +5,7 @@ import itertools import logging import time -from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional, Set, TypeGuard, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional, Set, TypeGuard, Union, cast import bpy import idprop @@ -77,6 +77,17 @@ def find_bone_order_mesh_object(root_object: bpy.types.Object) -> Optional[bpy.t # TODO consistency issue return next(filter(lambda o: o.type == "MESH" and "mmd_bone_order_override" in o.modifiers, armature_object.children), None) + @staticmethod + def find_mesh_by_name(root_object: bpy.types.Object, name: str) -> Optional[bpy.types.Object]: + armature_object = FnModel.find_armature(root_object) + if armature_object is None: + return None + for o in FnModel.child_meshes(armature_object): + if o.name != name: + continue + return o + return None + @staticmethod def all_children(obj: bpy.types.Object) -> Iterator[bpy.types.Object]: child: bpy.types.Object @@ -124,6 +135,19 @@ def iterate_temporary_objects(root_object: bpy.types.Object, rigid_track_only: b return rigid_body_objects return itertools.chain(rigid_body_objects, FnModel.filtered_children(FnModel.is_temporary_object, temporary_group_object)) + @staticmethod + def iterate_materials(root_object: bpy.types.Object) -> Iterable[bpy.types.Material]: + armature_object = FnModel.find_armature(root_object) + if armature_object is None: + return [] + return (material for mesh_object in FnModel.child_meshes(armature_object) for material in cast(bpy.types.Mesh, mesh_object.data).materials if material is not None) + + @staticmethod + def iterate_unique_materials(root_object: bpy.types.Object) -> Iterable[bpy.types.Material]: + materials: Dict[bpy.types.Material, None] = {} # use dict because set does not guarantee the order + materials.update((material, None) for material in FnModel.iterate_materials(root_object)) + return materials.keys() + @staticmethod def is_root_object(obj: Optional[bpy.types.Object]) -> TypeGuard[bpy.types.Object]: return obj is not None and obj.mmd_type == "ROOT" diff --git a/mmd_tools/core/pmx/importer.py b/mmd_tools/core/pmx/importer.py index 3480970..86217fa 100644 --- a/mmd_tools/core/pmx/importer.py +++ b/mmd_tools/core/pmx/importer.py @@ -812,7 +812,7 @@ def __translateBoneNames(self): def __fixRepeatedMorphName(self): used_names = set() for m in self.__model.morphs: - m.name = utils.uniqueName(m.name or "Morph", used_names) + m.name = utils.unique_name(m.name or "Morph", used_names) used_names.add(m.name) def execute(self, **args): diff --git a/mmd_tools/properties/material.py b/mmd_tools/properties/material.py index 231966b..6f0354e 100644 --- a/mmd_tools/properties/material.py +++ b/mmd_tools/properties/material.py @@ -7,80 +7,78 @@ from mmd_tools import utils from mmd_tools.core import material from mmd_tools.core.material import FnMaterial -from mmd_tools.core.model import Model +from mmd_tools.core.model import FnModel from mmd_tools.properties import patch_library_overridable -def _updateAmbientColor(prop, context): +def _mmd_material_update_ambient_color(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_ambient_color() -def _updateDiffuseColor(prop, context): +def _mmd_material_update_diffuse_color(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_diffuse_color() -def _updateAlpha(prop, context): +def _mmd_material_update_alpha(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_alpha() -def _updateSpecularColor(prop, context): +def _mmd_material_update_specular_color(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_specular_color() -def _updateShininess(prop, context): +def _mmd_material_update_shininess(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_shininess() -def _updateIsDoubleSided(prop, context): +def _mmd_material_update_is_double_sided(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_is_double_sided() -def _updateSphereMapType(prop, context): +def _mmd_material_update_sphere_texture_type(prop: "MMDMaterial", context): FnMaterial(prop.id_data).update_sphere_texture_type(context.active_object) -def _updateToonTexture(prop, context): +def _mmd_material_update_toon_texture(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_toon_texture() -def _updateDropShadow(prop, context): +def _mmd_material_update_enabled_drop_shadow(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_drop_shadow() -def _updateSelfShadowMap(prop, context): +def _mmd_material_update_enabled_self_shadow_map(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_self_shadow_map() -def _updateSelfShadow(prop, context): +def _mmd_material_update_enabled_self_shadow(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_self_shadow() -def _updateEnabledToonEdge(prop, context): +def _mmd_material_update_enabled_toon_edge(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_enabled_toon_edge() -def _updateEdgeColor(prop, context): +def _mmd_material_update_edge_color(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_edge_color() -def _updateEdgeWeight(prop, context): +def _mmd_material_update_edge_weight(prop: "MMDMaterial", _context): FnMaterial(prop.id_data).update_edge_weight() -def _getNameJ(prop): +def _mmd_material_get_name_j(prop: "MMDMaterial"): return prop.get("name_j", "") -def _setNameJ(prop, value): - old_value = prop.get("name_j") +def _mmd_material_set_name_j(prop: "MMDMaterial", value: str): prop_value = value - if prop_value and prop_value != old_value: - root = Model.findRoot(bpy.context.active_object) - if root: - rig = Model(root) - prop_value = utils.uniqueName(value, [mat.mmd_material.name_j for mat in rig.materials() if mat]) + if prop_value and prop_value != prop.get("name_j"): + root = FnModel.find_root(bpy.context.active_object) + if root is None: + prop_value = utils.unique_name(value, {mat.mmd_material.name_j for mat in bpy.data.materials}) else: - prop_value = utils.uniqueName(value, [mat.mmd_material.name_j for mat in bpy.data.materials]) + prop_value = utils.unique_name(value, {mat.mmd_material.name_j for mat in FnModel.iterate_materials(root)}) prop["name_j"] = prop_value @@ -97,8 +95,8 @@ class MMDMaterial(bpy.types.PropertyGroup): name="Name", description="Japanese Name", default="", - set=_setNameJ, - get=_getNameJ, + set=_mmd_material_set_name_j, + get=_mmd_material_get_name_j, ) name_e: bpy.props.StringProperty( @@ -124,7 +122,7 @@ class MMDMaterial(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0.4, 0.4, 0.4], - update=_updateAmbientColor, + update=_mmd_material_update_ambient_color, ) diffuse_color: bpy.props.FloatVectorProperty( @@ -137,7 +135,7 @@ class MMDMaterial(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0.8, 0.8, 0.8], - update=_updateDiffuseColor, + update=_mmd_material_update_diffuse_color, ) alpha: bpy.props.FloatProperty( @@ -148,7 +146,7 @@ class MMDMaterial(bpy.types.PropertyGroup): precision=3, step=0.1, default=1.0, - update=_updateAlpha, + update=_mmd_material_update_alpha, ) specular_color: bpy.props.FloatVectorProperty( @@ -161,7 +159,7 @@ class MMDMaterial(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0.625, 0.625, 0.625], - update=_updateSpecularColor, + update=_mmd_material_update_specular_color, ) shininess: bpy.props.FloatProperty( @@ -171,42 +169,42 @@ class MMDMaterial(bpy.types.PropertyGroup): soft_max=512, step=100.0, default=50.0, - update=_updateShininess, + update=_mmd_material_update_shininess, ) is_double_sided: bpy.props.BoolProperty( name="Double Sided", description="Both sides of mesh should be rendered", default=False, - update=_updateIsDoubleSided, + update=_mmd_material_update_is_double_sided, ) enabled_drop_shadow: bpy.props.BoolProperty( name="Ground Shadow", description="Display ground shadow", default=True, - update=_updateDropShadow, + update=_mmd_material_update_enabled_drop_shadow, ) enabled_self_shadow_map: bpy.props.BoolProperty( name="Self Shadow Map", description="Object can become shadowed by other objects", default=True, - update=_updateSelfShadowMap, + update=_mmd_material_update_enabled_self_shadow_map, ) enabled_self_shadow: bpy.props.BoolProperty( name="Self Shadow", description="Object can cast shadows", default=True, - update=_updateSelfShadow, + update=_mmd_material_update_enabled_self_shadow, ) enabled_toon_edge: bpy.props.BoolProperty( name="Toon Edge", description="Use toon edge", default=False, - update=_updateEnabledToonEdge, + update=_mmd_material_update_enabled_toon_edge, ) edge_color: bpy.props.FloatVectorProperty( @@ -219,7 +217,7 @@ class MMDMaterial(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0, 0, 0, 1], - update=_updateEdgeColor, + update=_mmd_material_update_edge_color, ) edge_weight: bpy.props.FloatProperty( @@ -230,7 +228,7 @@ class MMDMaterial(bpy.types.PropertyGroup): soft_max=2, step=1.0, default=1.0, - update=_updateEdgeWeight, + update=_mmd_material_update_edge_weight, ) sphere_texture_type: bpy.props.EnumProperty( @@ -242,14 +240,14 @@ class MMDMaterial(bpy.types.PropertyGroup): (str(material.SPHERE_MODE_ADD), "Add", "", 3), (str(material.SPHERE_MODE_SUBTEX), "SubTexture", "", 4), ], - update=_updateSphereMapType, + update=_mmd_material_update_sphere_texture_type, ) is_shared_toon_texture: bpy.props.BoolProperty( name="Use Shared Toon Texture", description="Use shared toon texture or custom toon texture", default=False, - update=_updateToonTexture, + update=_mmd_material_update_toon_texture, ) toon_texture: bpy.props.StringProperty( @@ -257,7 +255,7 @@ class MMDMaterial(bpy.types.PropertyGroup): subtype="FILE_PATH", description="The file path of custom toon texture", default="", - update=_updateToonTexture, + update=_mmd_material_update_toon_texture, ) shared_toon_texture: bpy.props.IntProperty( @@ -266,7 +264,7 @@ class MMDMaterial(bpy.types.PropertyGroup): default=0, min=0, max=9, - update=_updateToonTexture, + update=_mmd_material_update_toon_texture, ) comment: bpy.props.StringProperty( diff --git a/mmd_tools/properties/morph.py b/mmd_tools/properties/morph.py index 0382e58..81ead6d 100644 --- a/mmd_tools/properties/morph.py +++ b/mmd_tools/properties/morph.py @@ -11,11 +11,11 @@ from mmd_tools.core.morph import FnMorph -def _get_name(prop): +def _morph_base_get_name(prop: "_MorphBase") -> str: return prop.get("name", "") -def _set_name(prop, value): +def _morph_base_set_name(prop: "_MorphBase", value: str): mmd_root = prop.id_data.mmd_root # morph_type = mmd_root.active_morph_type morph_type = "%s_morphs" % prop.bl_rna.identifier[:-5].lower() @@ -26,7 +26,7 @@ def _set_name(prop, value): return used_names = {x.name for x in getattr(mmd_root, morph_type) if x != prop} - value = utils.uniqueName(value, used_names) + value = utils.unique_name(value, used_names) if prop_name is not None: if morph_type == "vertex_morphs": kb_list = {} @@ -37,7 +37,7 @@ def _set_name(prop, value): kb_list.setdefault(kb.name, []).append(kb) if prop_name in kb_list: - value = utils.uniqueName(value, used_names | kb_list.keys()) + value = utils.unique_name(value, used_names | kb_list.keys()) for kb in kb_list[prop_name]: kb.name = value @@ -50,7 +50,7 @@ def _set_name(prop, value): vg_list.setdefault(n, []).append(vg) if prop_name in vg_list: - value = utils.uniqueName(value, used_names | vg_list.keys()) + value = utils.unique_name(value, used_names | vg_list.keys()) for vg in vg_list[prop_name]: vg.name = vg.name.replace(prop_name, value) @@ -79,8 +79,8 @@ class _MorphBase: name: bpy.props.StringProperty( name="Name", description="Japanese Name", - set=_set_name, - get=_get_name, + set=_morph_base_set_name, + get=_morph_base_get_name, ) name_e: bpy.props.StringProperty( name="Name(Eng)", @@ -101,7 +101,7 @@ class _MorphBase: ) -def _get_bone(prop): +def _bone_morph_data_get_bone(prop: "BoneMorphData") -> str: bone_id = prop.get("bone_id", -1) if bone_id < 0: return "" @@ -115,7 +115,7 @@ def _get_bone(prop): return pose_bone.name -def _set_bone(prop, value): +def _bone_morph_data_set_bone(prop: "BoneMorphData", value: str): root = prop.id_data arm = FnModel.find_armature(root) @@ -131,7 +131,7 @@ def _set_bone(prop, value): prop["bone_id"] = FnBone.get_or_assign_bone_id(pose_bone) -def _update_bone_morph_data(prop, context): +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 @@ -148,8 +148,8 @@ class BoneMorphData(bpy.types.PropertyGroup): bone: bpy.props.StringProperty( name="Bone", description="Target bone", - set=_set_bone, - get=_get_bone, + set=_bone_morph_data_set_bone, + get=_bone_morph_data_get_bone, ) bone_id: bpy.props.IntProperty( @@ -162,7 +162,7 @@ class BoneMorphData(bpy.types.PropertyGroup): subtype="TRANSLATION", size=3, default=[0, 0, 0], - update=_update_bone_morph_data, + update=_bone_morph_data_update_location_or_rotation, ) rotation: bpy.props.FloatVectorProperty( @@ -171,7 +171,7 @@ class BoneMorphData(bpy.types.PropertyGroup): subtype="QUATERNION", size=4, default=[1, 0, 0, 0], - update=_update_bone_morph_data, + update=_bone_morph_data_update_location_or_rotation, ) @@ -189,14 +189,14 @@ class BoneMorph(_MorphBase, bpy.types.PropertyGroup): ) -def _get_material(prop): +def _material_morph_data_get_material(prop: "MaterialMorphData"): mat_p = prop.get("material_data", None) if mat_p is not None: return mat_p.name return "" -def _set_material(prop, value): +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 @@ -207,23 +207,22 @@ def _set_material(prop, value): prop["material_id"] = fnMat.material_id -def _set_related_mesh(prop, value): - rig = FnModel(prop.id_data) - mesh = rig.findMesh(value) +def _material_morph_data_set_related_mesh(prop: "MaterialMorphData", value: str): + mesh = FnModel.find_mesh_by_name(prop.id_data, value) if mesh is not None: prop["related_mesh_data"] = mesh.data else: prop["related_mesh_data"] = None -def _get_related_mesh(prop): +def _material_morph_data_get_related_mesh(prop): mesh_p = prop.get("related_mesh_data", None) if mesh_p is not None: return mesh_p.name return "" -def _update_material_morph_data(prop, context): +def _material_morph_data_update_modifiable_values(prop: "MaterialMorphData", _context): if not prop.name.startswith("mmd_bind"): return from mmd_tools.core.shader import _MaterialMorph @@ -242,8 +241,8 @@ 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", - set=_set_related_mesh, - get=_get_related_mesh, + set=_material_morph_data_set_related_mesh, + get=_material_morph_data_get_related_mesh, ) related_mesh_data: bpy.props.PointerProperty( @@ -256,8 +255,8 @@ class MaterialMorphData(bpy.types.PropertyGroup): material: bpy.props.StringProperty( name="Material", description="Target material", - get=_get_material, - set=_set_material, + get=_material_morph_data_get_material, + set=_material_morph_data_set_material, ) material_id: bpy.props.IntProperty( @@ -280,7 +279,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0, 0, 0, 1], - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) specular_color: bpy.props.FloatVectorProperty( @@ -293,7 +292,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0, 0, 0], - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) shininess: bpy.props.FloatProperty( @@ -303,7 +302,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): soft_max=500, step=100.0, default=0.0, - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) ambient_color: bpy.props.FloatVectorProperty( @@ -316,7 +315,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0, 0, 0], - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) edge_color: bpy.props.FloatVectorProperty( @@ -329,7 +328,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0, 0, 0, 1], - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) edge_weight: bpy.props.FloatProperty( @@ -339,7 +338,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): soft_max=2, step=0.1, default=0, - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) texture_factor: bpy.props.FloatVectorProperty( @@ -352,7 +351,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0, 0, 0, 1], - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) sphere_texture_factor: bpy.props.FloatVectorProperty( @@ -365,7 +364,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0, 0, 0, 1], - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) toon_texture_factor: bpy.props.FloatVectorProperty( @@ -378,7 +377,7 @@ class MaterialMorphData(bpy.types.PropertyGroup): precision=3, step=0.1, default=[0, 0, 0, 1], - update=_update_material_morph_data, + update=_material_morph_data_update_modifiable_values, ) diff --git a/mmd_tools/properties/pose_bone.py b/mmd_tools/properties/pose_bone.py index c61067f..0203e4b 100644 --- a/mmd_tools/properties/pose_bone.py +++ b/mmd_tools/properties/pose_bone.py @@ -2,20 +2,21 @@ # Copyright 2014 MMD Tools authors # This file is part of MMD Tools. +from typing import cast import bpy from mmd_tools.core.bone import FnBone from mmd_tools.properties import patch_library_overridable -def _updateMMDBoneAdditionalTransform(prop, context: bpy.types.Context): +def _mmd_bone_update_additional_transform(prop: "MMDBone", context: bpy.types.Context): prop["is_additional_transform_dirty"] = True p_bone = context.active_pose_bone if p_bone and p_bone.mmd_bone.as_pointer() == prop.as_pointer(): FnBone.apply_additional_transformation(prop.id_data) -def _updateAdditionalTransformInfluence(prop, context: bpy.types.Context): +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(): FnBone.update_additional_transform_influence(pose_bone) @@ -23,7 +24,7 @@ def _updateAdditionalTransformInfluence(prop, context: bpy.types.Context): prop["is_additional_transform_dirty"] = True -def _getAdditionalTransformBone(prop): +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: @@ -34,7 +35,7 @@ def _getAdditionalTransformBone(prop): return pose_bone.name -def _setAdditionalTransformBone(prop, value): +def _mmd_bone_set_additional_transform_bone(prop: "MMDBone", value: str): arm = prop.id_data prop["is_additional_transform_dirty"] = True if value not in arm.pose.bones.keys(): @@ -145,28 +146,28 @@ class MMDBone(bpy.types.PropertyGroup): name="Additional Rotation", description="Additional rotation", default=False, - update=_updateMMDBoneAdditionalTransform, + update=_mmd_bone_update_additional_transform, ) has_additional_location: bpy.props.BoolProperty( name="Additional Location", description="Additional location", default=False, - update=_updateMMDBoneAdditionalTransform, + update=_mmd_bone_update_additional_transform, ) additional_transform_bone: bpy.props.StringProperty( name="Additional Transform Bone", description="Additional transform bone", - set=_setAdditionalTransformBone, - get=_getAdditionalTransformBone, - update=_updateMMDBoneAdditionalTransform, + set=_mmd_bone_set_additional_transform_bone, + get=_mmd_bone_get_additional_transform_bone, + update=_mmd_bone_update_additional_transform, ) additional_transform_bone_id: bpy.props.IntProperty( name="Additional Transform Bone ID", default=-1, - update=_updateMMDBoneAdditionalTransform, + update=_mmd_bone_update_additional_transform, ) additional_transform_influence: bpy.props.FloatProperty( @@ -175,7 +176,7 @@ class MMDBone(bpy.types.PropertyGroup): default=1, soft_min=-1, soft_max=1, - update=_updateAdditionalTransformInfluence, + update=_mmd_bone_update_additional_transform_influence, ) is_additional_transform_dirty: bpy.props.BoolProperty(name="", default=True) @@ -192,7 +193,7 @@ def register(): bpy.props.BoolProperty( name="MMD IK Toggle", description="MMD IK toggle is used to import/export animation of IK on-off", - update=_mmd_ik_toggle_update, + update=_pose_bone_update_mmd_ik_toggle, default=True, ) ) @@ -205,20 +206,10 @@ def unregister(): del bpy.types.PoseBone.mmd_bone -def _mmd_ik_toggle_get(prop): - return prop.get("mmd_ik_toggle", True) - - -def _mmd_ik_toggle_set(prop, v): - if v != prop.get("mmd_ik_toggle", None): - prop["mmd_ik_toggle"] = v - _mmd_ik_toggle_update(prop, None) - - -def _mmd_ik_toggle_update(prop, context): +def _pose_bone_update_mmd_ik_toggle(prop: bpy.types.PoseBone, _context): v = prop.mmd_ik_toggle - # logging.debug('_mmd_ik_toggle_update %s %s', v, prop.name) - for b in prop.id_data.pose.bones: + 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: # logging.debug(' %s %s', b.name, c.name) diff --git a/mmd_tools/utils.py b/mmd_tools/utils.py index b024cd3..7eaaa06 100644 --- a/mmd_tools/utils.py +++ b/mmd_tools/utils.py @@ -5,7 +5,7 @@ import logging import os import re -from typing import Callable, Optional +from typing import Callable, Optional, Set import bpy @@ -181,13 +181,26 @@ def makePmxBoneMap(armObj): return {(i.mmd_bone.name_j or i.get("mmd_bone_name_j", i.get("name_j", i.name))): i for i in armObj.pose.bones} -def uniqueName(name, used_names): +__REMOVE_PREFIX_DIGITS_REGEXP = re.compile(r"\.\d{1,}$") + + +def unique_name(name: str, used_names: Set[str]) -> str: + """Helper function for storing unique names. + This function is a limited and simplified version of bpy_extras.io_utils.unique_name. + + Args: + name (str): The name to make unique. + used_names (Set[str]): A set of names that are already used. + + Returns: + str: The unique name, formatted as "{name}.{number:03d}". + """ if name not in used_names: return name count = 1 - new_name = orig_name = re.sub(r"\.\d{1,}$", "", name) + new_name = orig_name = __REMOVE_PREFIX_DIGITS_REGEXP.sub("", name) while new_name in used_names: - new_name = "%s.%03d" % (orig_name, count) + new_name = f"{orig_name}.{count:03d}" count += 1 return new_name