# -*- 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. import bpy from .. import utils from ..core.bone import FnBone from ..core.material import FnMaterial from ..core.model import FnModel, Model from ..core.morph import FnMorph def _morph_base_get_name(prop: "_MorphBase") -> str: return prop.get("name", "") 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() # 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 = {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 = {} 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) if prop_name in kb_list: value = utils.unique_name(value, used_names | kb_list.keys()) for kb in kb_list[prop_name]: kb.name = value elif morph_type == "uv_morphs": 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) if prop_name in vg_list: 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) if 1: # morph_type != 'group_morphs': for m in mmd_root.group_morphs: for d in m.data: if d.name == prop_name and d.morph_type == morph_type: d.name = value frame_facial = mmd_root.display_item_frames.get("表情") for item in getattr(frame_facial, "data", []): if item.name == prop_name and item.morph_type == morph_type: item.name = value break obj = Model(prop.id_data).morph_slider.placeholder() if obj and value not in obj.data.shape_keys.key_blocks: kb = obj.data.shape_keys.key_blocks.get(prop_name, None) if kb: kb.name = value prop["name"] = value class _MorphBase: name: bpy.props.StringProperty( name="Name", description="Japanese Name", set=_morph_base_set_name, get=_morph_base_get_name, ) name_e: bpy.props.StringProperty( name="Name(Eng)", description="English Name", default="", ) category: bpy.props.EnumProperty( name="Category", description="Select category", items=[ ("SYSTEM", "Hidden", "", 0), ("EYEBROW", "Eye Brow", "", 1), ("EYE", "Eye", "", 2), ("MOUTH", "Mouth", "", 3), ("OTHER", "Other", "", 4), ], default="OTHER", ) def _bone_morph_data_get_bone(prop: "BoneMorphData") -> str: bone_id = prop.get("bone_id", -1) if bone_id < 0: return "" 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) if pose_bone is None: return "" return pose_bone.name 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. if arm is None: return if value not in arm.pose.bones.keys(): prop["bone_id"] = -1 return pose_bone = arm.pose.bones[value] prop["bone_id"] = FnBone.get_or_assign_bone_id(pose_bone) 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 if arm: bone = arm.pose.bones.get(prop.name, None) if bone: bone.location = prop.location bone.rotation_quaternion = prop.rotation.__class__(*prop.rotation.to_axis_angle()) # Fix for consistency class BoneMorphData(bpy.types.PropertyGroup): """ """ bone: bpy.props.StringProperty( name="Bone", description="Target bone", set=_bone_morph_data_set_bone, get=_bone_morph_data_get_bone, ) bone_id: bpy.props.IntProperty( name="Bone ID", ) location: bpy.props.FloatVectorProperty( name="Location", description="Location", subtype="TRANSLATION", size=3, default=[0, 0, 0], update=_bone_morph_data_update_location_or_rotation, ) rotation: bpy.props.FloatVectorProperty( name="Rotation", description="Rotation in quaternions", subtype="QUATERNION", size=4, default=[1, 0, 0, 0], update=_bone_morph_data_update_location_or_rotation, ) class BoneMorph(_MorphBase, bpy.types.PropertyGroup): """Bone Morph""" data: bpy.props.CollectionProperty( name="Morph Data", type=BoneMorphData, ) active_data: bpy.props.IntProperty( name="Active Bone Data", min=0, default=0, ) 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 _material_morph_data_set_material(prop: "MaterialMorphData", value: str): if value not in bpy.data.materials: 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 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 else: prop["related_mesh_data"] = None 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 _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) else: for mat in FnModel(prop.id_data).materials(): _MaterialMorph.update_morph_inputs(mat, 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", set=_material_morph_data_set_related_mesh, get=_material_morph_data_get_related_mesh, ) related_mesh_data: bpy.props.PointerProperty( name="Related Mesh Data", type=bpy.types.Mesh, ) offset_type: bpy.props.EnumProperty(name="Offset Type", description="Select offset type", items=[("MULT", "Multiply", "", 0), ("ADD", "Add", "", 1)], default="ADD") material: bpy.props.StringProperty( name="Material", description="Target material", get=_material_morph_data_get_material, set=_material_morph_data_set_material, ) material_id: bpy.props.IntProperty( name="Material ID", default=-1, ) material_data: bpy.props.PointerProperty( name="Material Data", type=bpy.types.Material, ) diffuse_color: bpy.props.FloatVectorProperty( name="Diffuse Color", description="Diffuse color", subtype="COLOR", size=4, soft_min=0, soft_max=1, precision=3, step=0.1, default=[0, 0, 0, 1], update=_material_morph_data_update_modifiable_values, ) specular_color: bpy.props.FloatVectorProperty( name="Specular Color", description="Specular color", subtype="COLOR", size=3, soft_min=0, soft_max=1, precision=3, step=0.1, default=[0, 0, 0], update=_material_morph_data_update_modifiable_values, ) shininess: bpy.props.FloatProperty( name="Reflect", description="Reflect", soft_min=0, soft_max=500, step=100.0, default=0.0, update=_material_morph_data_update_modifiable_values, ) ambient_color: bpy.props.FloatVectorProperty( name="Ambient Color", description="Ambient color", subtype="COLOR", size=3, soft_min=0, soft_max=1, precision=3, step=0.1, default=[0, 0, 0], update=_material_morph_data_update_modifiable_values, ) edge_color: bpy.props.FloatVectorProperty( name="Edge Color", description="Edge color", subtype="COLOR", size=4, soft_min=0, soft_max=1, precision=3, step=0.1, default=[0, 0, 0, 1], update=_material_morph_data_update_modifiable_values, ) edge_weight: bpy.props.FloatProperty( name="Edge Weight", description="Edge weight", soft_min=0, soft_max=2, step=0.1, default=0, update=_material_morph_data_update_modifiable_values, ) texture_factor: bpy.props.FloatVectorProperty( name="Texture factor", description="Texture factor", subtype="COLOR", size=4, soft_min=0, soft_max=1, precision=3, step=0.1, default=[0, 0, 0, 1], update=_material_morph_data_update_modifiable_values, ) sphere_texture_factor: bpy.props.FloatVectorProperty( name="Sphere Texture factor", description="Sphere texture factor", subtype="COLOR", size=4, soft_min=0, soft_max=1, precision=3, step=0.1, default=[0, 0, 0, 1], update=_material_morph_data_update_modifiable_values, ) toon_texture_factor: bpy.props.FloatVectorProperty( name="Toon Texture factor", description="Toon texture factor", subtype="COLOR", size=4, soft_min=0, soft_max=1, precision=3, step=0.1, default=[0, 0, 0, 1], update=_material_morph_data_update_modifiable_values, ) class MaterialMorph(_MorphBase, bpy.types.PropertyGroup): """Material Morph""" data: bpy.props.CollectionProperty( name="Morph Data", type=MaterialMorphData, ) active_data: bpy.props.IntProperty( name="Active Material Data", min=0, default=0, ) class UVMorphOffset(bpy.types.PropertyGroup): """UV Morph Offset""" index: bpy.props.IntProperty( name="Vertex Index", description="Vertex index", min=0, default=0, ) offset: bpy.props.FloatVectorProperty( name="UV Offset", description="UV offset", size=4, # min=-1, # max=1, # precision=3, step=0.1, default=[0, 0, 0, 0], ) class UVMorph(_MorphBase, bpy.types.PropertyGroup): """UV Morph""" uv_index: bpy.props.IntProperty( name="UV Index", description="UV index (UV, UV1 ~ UV4)", min=0, max=4, default=0, ) data_type: bpy.props.EnumProperty( name="Data Type", description="Select data type", items=[ ("DATA", "Data", "Store offset data in root object (deprecated)", 0), ("VERTEX_GROUP", "Vertex Group", "Store offset data in vertex groups", 1), ], default="DATA", ) data: bpy.props.CollectionProperty( name="Morph Data", type=UVMorphOffset, ) active_data: bpy.props.IntProperty( name="Active UV Data", min=0, default=0, ) vertex_group_scale: bpy.props.FloatProperty( name="Vertex Group Scale", description='The value scale of "Vertex Group" data type', precision=3, step=0.1, default=1, ) class GroupMorphOffset(bpy.types.PropertyGroup): """Group Morph Offset""" 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", ) factor: bpy.props.FloatProperty(name="Factor", description="Factor", soft_min=0, soft_max=1, precision=3, step=0.1, default=0) class GroupMorph(_MorphBase, bpy.types.PropertyGroup): """Group Morph""" data: bpy.props.CollectionProperty( name="Morph Data", type=GroupMorphOffset, ) active_data: bpy.props.IntProperty( name="Active Group Data", min=0, default=0, ) class VertexMorph(_MorphBase, bpy.types.PropertyGroup): """Vertex Morph"""