489 lines
14 KiB
Python
489 lines
14 KiB
Python
# -*- 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"""
|