c31d25dd01
You can choose between errors, warning, info or full debug, errors will always log to ensure we don't have silent failures with debug on or off.
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"""
|