# -*- 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. """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 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 def __driver_variables(constraint: bpy.types.Constraint, path: str, index: int = -1) -> Tuple[bpy.types.Driver, Any]: d = constraint.driver_add(path, index) variables = d.driver.variables for x in 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: var = variables.new() var.name = prefix + str(len(variables)) var.type = "SINGLE_PROP" target = var.targets[0] target.id_type = "OBJECT" target.id = id_obj target.data_path = data_path return var def _toggleUsePropertyDriver(self: "MMDRoot", _context: bpy.types.Context) -> None: 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]] = {} 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 b = b if c.use_tail else b.parent for b in ([b] + b.parent_recursive)[: c.chain_count]: c = next((c for c in b.constraints if c.type == "LIMIT_ROTATION" and not c.mute), None) if c: 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 for i in FnModel.iterate_mesh_objects(root_object): for prop_hide in ("hide_viewport", "hide_render"): 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 for b in ([b] + b.parent_recursive)[: c.chain_count]: c = next((c for c in b.constraints if c.type == "LIMIT_ROTATION" and not c.mute), None) if c: c.driver_remove("influence") for i in FnModel.iterate_mesh_objects(root_object): for prop_hide in ("hide_viewport", "hide_render"): i.driver_remove(prop_hide) # =========================================== # Callback functions # =========================================== def _toggleUseToonTexture(self: "MMDRoot", _context: bpy.types.Context) -> None: 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: 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: 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: 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 if hide and context.active_object is None: FnContext.set_active_object(context, root) def _toggleVisibilityOfRigidBodies(self: "MMDRoot", context: bpy.types.Context) -> None: 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: 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: 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) if hide and context.active_object is None: FnContext.set_active_object(context, root_object) def _toggleShowNamesOfRigidBodies(self: "MMDRoot", _context: bpy.types.Context) -> None: 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: 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: 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: if prop.id_data.mmd_type != "ROOT": return False arm = FnModel.find_armature_object(prop.id_data) return arm and not arm.hide_get() def _setActiveRigidbodyObject(prop: "MMDRoot", v: int) -> None: 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: context = bpy.context active_obj = FnContext.get_active_object(context) if FnModel.is_rigid_body_object(active_obj): prop["active_rigidbody_object_index"] = FnContext.get_scene_objects(context).find(active_obj.name) return prop.get("active_rigidbody_object_index", 0) def _setActiveJointObject(prop: "MMDRoot", v: int) -> None: 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: context = bpy.context active_obj = FnContext.get_active_object(context) if FnModel.is_joint_object(active_obj): prop["active_joint_object_index"] = FnContext.get_scene_objects(context).find(active_obj.name) return prop.get("active_joint_object_index", 0) def _setActiveMorph(prop: "MMDRoot", v: bool) -> None: 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: 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: 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: context = bpy.context active_obj = FnContext.get_active_object(context) if FnModel.is_mesh_object(active_obj): prop["active_mesh_index"] = FnContext.get_scene_objects(context).find(active_obj.name) return prop.get("active_mesh_index", -1) # =========================================== # Property classes # =========================================== class MMDDisplayItem(bpy.types.PropertyGroup): """PMX 表示項目(表示枠内の1項目)""" type: bpy.props.EnumProperty( name="Type", description="Select item type", items=[ ("BONE", "Bone", "", 1), ("MORPH", "Morph", "", 2), ], ) morph_type: bpy.props.EnumProperty( name="Morph Type", description="Select morph type", items=[ ("material_morphs", "Material", "Material Morphs", 0), ("uv_morphs", "UV", "UV Morphs", 1), ("bone_morphs", "Bone", "Bone Morphs", 2), ("vertex_morphs", "Vertex", "Vertex Morphs", 3), ("group_morphs", "Group", "Group Morphs", 4), ], default="vertex_morphs", ) class MMDDisplayItemFrame(bpy.types.PropertyGroup): """PMX 表示枠 PMXファイル内では表示枠がリストで格納されています。 """ name_e: bpy.props.StringProperty( name="Name(Eng)", description="English Name", default="", ) # 特殊枠フラグ # 特殊枠はファイル仕様上の固定枠(削除、リネーム不可) is_special: bpy.props.BoolProperty( name="Special", description="Is special", default=False, ) # 表示項目のリスト data: bpy.props.CollectionProperty( name="Display Items", type=MMDDisplayItem, ) # 現在アクティブな項目のインデックス active_item: bpy.props.IntProperty( name="Active Display Item", min=0, default=0, ) class MMDRoot(bpy.types.PropertyGroup): """MMDモデルデータ モデルルート用に作成されたEmtpyオブジェクトで使用します """ name: bpy.props.StringProperty( name="Name", description="The name of the MMD model", default="", ) name_e: bpy.props.StringProperty( name="Name (English)", description="The english name of the MMD model", default="", ) comment_text: bpy.props.StringProperty( name="Comment", description="The text datablock of the comment", default="", ) comment_e_text: bpy.props.StringProperty( name="Comment (English)", description="The text datablock of the english comment", default="", ) ik_loop_factor: bpy.props.IntProperty( name="MMD IK Loop Factor", description="Scaling factor of MMD IK loop", min=1, soft_max=10, max=100, default=1, ) # TODO: Replace to driver for NLA show_meshes: bpy.props.BoolProperty( name="Show Meshes", description="Show all meshes of the MMD model", # get=_show_meshes_get, # set=_show_meshes_set, update=_toggleVisibilityOfMeshes, default=True, ) show_rigid_bodies: bpy.props.BoolProperty( name="Show Rigid Bodies", description="Show all rigid bodies of the MMD model", update=_toggleVisibilityOfRigidBodies, ) show_joints: bpy.props.BoolProperty( name="Show Joints", description="Show all joints of the MMD model", update=_toggleVisibilityOfJoints, ) show_temporary_objects: bpy.props.BoolProperty( name="Show Temps", description="Show all temporary objects of the MMD model", update=_toggleVisibilityOfTemporaryObjects, ) show_armature: bpy.props.BoolProperty( name="Show Armature", description="Show the armature object of the MMD model", get=_getVisibilityOfMMDRigArmature, set=_setVisibilityOfMMDRigArmature, ) show_names_of_rigid_bodies: bpy.props.BoolProperty( name="Show Rigid Body Names", description="Show rigid body names", update=_toggleShowNamesOfRigidBodies, ) show_names_of_joints: bpy.props.BoolProperty( name="Show Joint Names", description="Show joint names", update=_toggleShowNamesOfJoints, ) use_toon_texture: bpy.props.BoolProperty( name="Use Toon Texture", description="Use toon texture", update=_toggleUseToonTexture, default=True, ) use_sphere_texture: bpy.props.BoolProperty( name="Use Sphere Texture", description="Use sphere texture", update=_toggleUseSphereTexture, default=True, ) use_sdef: bpy.props.BoolProperty( name="Use SDEF", description="Use SDEF", update=_toggleUseSDEF, default=True, ) use_property_driver: bpy.props.BoolProperty( name="Use Property Driver", description="Setup drivers for MMD property animation (Visibility and IK toggles)", update=_toggleUsePropertyDriver, default=False, ) is_built: bpy.props.BoolProperty( name="Is Built", ) active_rigidbody_index: bpy.props.IntProperty( name="Active Rigidbody Index", min=0, get=_getActiveRigidbodyObject, set=_setActiveRigidbodyObject, ) active_joint_index: bpy.props.IntProperty( name="Active Joint Index", min=0, get=_getActiveJointObject, set=_setActiveJointObject, ) # ************************* # Display Items # ************************* display_item_frames: bpy.props.CollectionProperty( name="Display Frames", type=MMDDisplayItemFrame, ) active_display_item_frame: bpy.props.IntProperty( name="Active Display Item Frame", min=0, default=0, ) # ************************* # Morph # ************************* material_morphs: bpy.props.CollectionProperty( name="Material Morphs", type=MaterialMorph, ) uv_morphs: bpy.props.CollectionProperty( name="UV Morphs", type=UVMorph, ) bone_morphs: bpy.props.CollectionProperty( name="Bone Morphs", type=BoneMorph, ) vertex_morphs: bpy.props.CollectionProperty(name="Vertex Morphs", type=VertexMorph) group_morphs: bpy.props.CollectionProperty( name="Group Morphs", type=GroupMorph, ) active_morph_type: bpy.props.EnumProperty( name="Active Morph Type", description="Select current morph type", items=[ ("material_morphs", "Material", "Material Morphs", 0), ("uv_morphs", "UV", "UV Morphs", 1), ("bone_morphs", "Bone", "Bone Morphs", 2), ("vertex_morphs", "Vertex", "Vertex Morphs", 3), ("group_morphs", "Group", "Group Morphs", 4), ], default="vertex_morphs", ) active_morph: bpy.props.IntProperty( name="Active Morph", min=0, set=_setActiveMorph, get=_getActiveMorph, ) morph_panel_show_settings: bpy.props.BoolProperty( name="Morph Panel Show Settings", description="Show Morph Settings", default=True, ) active_mesh_index: bpy.props.IntProperty( name="Active Mesh", min=0, set=_setActiveMeshObject, get=_getActiveMeshObject, ) # ************************* # Translation # ************************* translation: bpy.props.PointerProperty( name="Translation", type=MMDTranslation, ) @staticmethod def __get_select(prop: bpy.types.Object) -> bool: 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") 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") 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") prop.hide_set(value) if prop.hide_viewport != value: prop.hide_viewport = value @staticmethod def register() -> None: logger.debug("Registering MMDRoot property group") bpy.types.Object.mmd_type = patch_library_overridable( bpy.props.EnumProperty( name="Type", description="Internal MMD type of this object (DO NOT CHANGE IT DIRECTLY)", default="NONE", items=[ ("NONE", "None", "", 1), ("ROOT", "Root", "", 2), ("RIGID_GRP_OBJ", "Rigid Body Grp Empty", "", 3), ("JOINT_GRP_OBJ", "Joint Grp Empty", "", 4), ("TEMPORARY_GRP_OBJ", "Temporary Grp Empty", "", 5), ("PLACEHOLDER", "Place Holder", "", 6), ("CAMERA", "Camera", "", 21), ("JOINT", "Joint", "", 22), ("RIGID_BODY", "Rigid body", "", 23), ("LIGHT", "Light", "", 24), ("TRACK_TARGET", "Track Target", "", 51), ("NON_COLLISION_CONSTRAINT", "Non Collision Constraint", "", 52), ("SPRING_CONSTRAINT", "Spring Constraint", "", 53), ("SPRING_GOAL", "Spring Goal", "", 54), ], ) ) bpy.types.Object.mmd_root = patch_library_overridable(bpy.props.PointerProperty(type=MMDRoot)) bpy.types.Object.select = patch_library_overridable( bpy.props.BoolProperty( get=MMDRoot.__get_select, set=MMDRoot.__set_select, options={ "SKIP_SAVE", "ANIMATABLE", "LIBRARY_EDITABLE", }, ) ) bpy.types.Object.hide = patch_library_overridable( bpy.props.BoolProperty( get=MMDRoot.__get_hide, set=MMDRoot.__set_hide, options={ "SKIP_SAVE", "ANIMATABLE", "LIBRARY_EDITABLE", }, ) ) @staticmethod def unregister() -> None: logger.debug("Unregistering MMDRoot property group") del bpy.types.Object.hide del bpy.types.Object.select del bpy.types.Object.mmd_root del bpy.types.Object.mmd_type