- Add core VRM addon infrastructure with manifest and registration - Add common utilities module with file system, logging, and conversion helpers - Add human bone mapper with support for multiple rigging standards (Mixamo, MMD, Unreal, Rigify, etc.) - Add VRM 0.x and 1.x format support with property groups and handlers - Add editor UI panels for VRM metadata, spring bones, and MToon materials - Add exporter with glTF2 extension support for VRM format serialization - Add importer with scene reconstruction and armature generation - Add MToon shader support with auto-setup and material migration - Add spring bone physics simulation with constraint handling - Add node constraint editor for advanced rigging control - Add comprehensive validation and error handling with user dialogs - Add scene watcher for real-time property synchronization - Add workspace management and preference system - Include Python cache files and Blender manifest configuration - This is the initial commit establishing the complete VRM addon ecosystem for Blender
303 lines
8.9 KiB
Python
303 lines
8.9 KiB
Python
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
from bpy.types import Context, Mesh, UILayout, UIList
|
|
|
|
from ...common.logger import get_logger
|
|
from ..property_group import StringPropertyGroup
|
|
from .property_group import (
|
|
Vrm1ExpressionsPropertyGroup,
|
|
Vrm1FirstPersonPropertyGroup,
|
|
Vrm1MaterialColorBindPropertyGroup,
|
|
Vrm1MeshAnnotationPropertyGroup,
|
|
Vrm1MetaPropertyGroup,
|
|
Vrm1MorphTargetBindPropertyGroup,
|
|
Vrm1TextureTransformBindPropertyGroup,
|
|
)
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class VRM_UL_vrm1_meta_author(UIList):
|
|
bl_idname = "VRM_UL_vrm1_meta_author"
|
|
|
|
def draw_item(
|
|
self,
|
|
_context: Context,
|
|
layout: UILayout,
|
|
meta: object,
|
|
author: object,
|
|
_icon: int,
|
|
_active_data: object,
|
|
_active_prop_name: str,
|
|
index: int,
|
|
_flt_flag: int,
|
|
) -> None:
|
|
if not isinstance(meta, Vrm1MetaPropertyGroup):
|
|
return
|
|
if not isinstance(author, StringPropertyGroup):
|
|
return
|
|
|
|
icon = "USER"
|
|
|
|
if self.layout_type == "GRID":
|
|
layout.alignment = "CENTER"
|
|
layout.label(text="", translate=False, icon=icon)
|
|
return
|
|
|
|
if self.layout_type not in {"DEFAULT", "COMPACT"}:
|
|
return
|
|
|
|
if index == meta.active_author_index:
|
|
layout.prop(author, "value", icon=icon, text="", translate=False)
|
|
else:
|
|
layout.label(text=author.value, icon=icon, translate=False)
|
|
|
|
|
|
class VRM_UL_vrm1_meta_reference(UIList):
|
|
bl_idname = "VRM_UL_vrm1_meta_reference"
|
|
|
|
def draw_item(
|
|
self,
|
|
_context: Context,
|
|
layout: UILayout,
|
|
meta: object,
|
|
reference: object,
|
|
_icon: int,
|
|
_active_data: object,
|
|
_active_prop_name: str,
|
|
index: int,
|
|
_flt_flag: int,
|
|
) -> None:
|
|
if not isinstance(meta, Vrm1MetaPropertyGroup):
|
|
return
|
|
if not isinstance(reference, StringPropertyGroup):
|
|
return
|
|
|
|
icon = "URL"
|
|
|
|
if self.layout_type == "GRID":
|
|
layout.alignment = "CENTER"
|
|
layout.label(text="", translate=False, icon=icon)
|
|
return
|
|
|
|
if self.layout_type not in {"DEFAULT", "COMPACT"}:
|
|
return
|
|
|
|
if index == meta.active_reference_index:
|
|
layout.prop(reference, "value", icon=icon, text="", translate=False)
|
|
else:
|
|
layout.label(text=reference.value, icon=icon, translate=False)
|
|
|
|
|
|
class VRM_UL_vrm1_first_person_mesh_annotation(UIList):
|
|
bl_idname = "VRM_UL_vrm1_first_person_mesh_annotation"
|
|
|
|
def draw_item(
|
|
self,
|
|
_context: Context,
|
|
layout: UILayout,
|
|
first_person: object,
|
|
mesh_annotation: object,
|
|
_icon: int,
|
|
_active_data: object,
|
|
_active_prop_name: str,
|
|
index: int,
|
|
_flt_flag: int,
|
|
) -> None:
|
|
if not isinstance(first_person, Vrm1FirstPersonPropertyGroup):
|
|
return
|
|
if not isinstance(mesh_annotation, Vrm1MeshAnnotationPropertyGroup):
|
|
return
|
|
|
|
icon = "OUTLINER_OB_MESH"
|
|
|
|
if self.layout_type == "GRID":
|
|
layout.alignment = "CENTER"
|
|
layout.label(text="", translate=False, icon=icon)
|
|
return
|
|
|
|
if self.layout_type not in {"DEFAULT", "COMPACT"}:
|
|
return
|
|
|
|
row = layout.split(factor=0.6, align=True)
|
|
if index == first_person.active_mesh_annotation_index:
|
|
row.prop(
|
|
mesh_annotation.node,
|
|
"bpy_object",
|
|
text="",
|
|
translate=False,
|
|
icon=icon,
|
|
)
|
|
else:
|
|
row.label(
|
|
text=mesh_annotation.node.mesh_object_name,
|
|
translate=False,
|
|
icon=icon,
|
|
)
|
|
row.prop(mesh_annotation, "type", text="", translate=False)
|
|
|
|
|
|
class VRM_UL_vrm1_expression(UIList):
|
|
bl_idname = "VRM_UL_vrm1_expression"
|
|
|
|
def draw_item(
|
|
self,
|
|
_context: Context,
|
|
layout: UILayout,
|
|
expressions: object,
|
|
_item: object,
|
|
_icon: int,
|
|
_active_data: object,
|
|
_active_prop_name: str,
|
|
index: int,
|
|
_flt_flag: int,
|
|
) -> None:
|
|
if not isinstance(expressions, Vrm1ExpressionsPropertyGroup):
|
|
return
|
|
preset_expression_items = list(
|
|
expressions.preset.name_to_expression_dict().items()
|
|
)
|
|
if index < len(preset_expression_items):
|
|
name, expression = preset_expression_items[index]
|
|
icon = expressions.preset.get_icon(name)
|
|
if not icon:
|
|
logger.error("Unknown preset expression: %s", name)
|
|
icon = "SHAPEKEY_DATA"
|
|
else:
|
|
custom_expressions = expressions.custom
|
|
custom_index = index - len(preset_expression_items)
|
|
if custom_index >= len(custom_expressions):
|
|
return
|
|
expression = custom_expressions[custom_index]
|
|
name = expression.custom_name
|
|
icon = "SHAPEKEY_DATA"
|
|
|
|
if self.layout_type == "GRID":
|
|
layout.alignment = "CENTER"
|
|
layout.label(text="", translate=False, icon=icon)
|
|
return
|
|
|
|
if self.layout_type not in {"DEFAULT", "COMPACT"}:
|
|
return
|
|
|
|
split = layout.split(align=True, factor=0.55)
|
|
split.label(text=name, translate=False, icon=icon)
|
|
split.prop(expression, "preview", text="Preview")
|
|
|
|
|
|
class VRM_UL_vrm1_morph_target_bind(UIList):
|
|
bl_idname = "VRM_UL_vrm1_morph_target_bind"
|
|
|
|
def draw_item(
|
|
self,
|
|
context: Context,
|
|
layout: UILayout,
|
|
_data: object,
|
|
morph_target_bind: object,
|
|
icon: int,
|
|
_active_data: object,
|
|
_active_prop_name: str,
|
|
_index: int,
|
|
_flt_flag: int,
|
|
) -> None:
|
|
blend_data = context.blend_data
|
|
if not isinstance(morph_target_bind, Vrm1MorphTargetBindPropertyGroup):
|
|
return
|
|
|
|
if self.layout_type == "GRID":
|
|
layout.alignment = "CENTER"
|
|
layout.label(text="", icon_value=icon)
|
|
return
|
|
|
|
if self.layout_type not in {"DEFAULT", "COMPACT"}:
|
|
return
|
|
|
|
name = morph_target_bind.node.mesh_object_name
|
|
mesh_object = blend_data.objects.get(morph_target_bind.node.mesh_object_name)
|
|
if mesh_object:
|
|
mesh_data = mesh_object.data
|
|
if isinstance(mesh_data, Mesh):
|
|
shape_keys = mesh_data.shape_keys
|
|
if shape_keys:
|
|
keys = shape_keys.key_blocks.keys()
|
|
if morph_target_bind.index in keys:
|
|
name += " / " + morph_target_bind.index
|
|
layout.label(text=name, translate=False, icon="MESH_DATA")
|
|
|
|
|
|
class VRM_UL_vrm1_material_color_bind(UIList):
|
|
bl_idname = "VRM_UL_vrm0_material_color_bind"
|
|
|
|
def draw_item(
|
|
self,
|
|
_context: Context,
|
|
layout: UILayout,
|
|
_data: object,
|
|
material_color_bind: object,
|
|
icon: int,
|
|
_active_data: object,
|
|
_active_prop_name: str,
|
|
_index: int,
|
|
_flt_flag: int,
|
|
) -> None:
|
|
if not isinstance(material_color_bind, Vrm1MaterialColorBindPropertyGroup):
|
|
return
|
|
|
|
if self.layout_type == "GRID":
|
|
layout.alignment = "CENTER"
|
|
layout.label(text="", translate=False, icon_value=icon)
|
|
return
|
|
|
|
if self.layout_type not in {"DEFAULT", "COMPACT"}:
|
|
return
|
|
|
|
name = ""
|
|
material = material_color_bind.material
|
|
if material:
|
|
name = material.name
|
|
type_name = next(
|
|
(
|
|
enum.name
|
|
for enum in Vrm1MaterialColorBindPropertyGroup.type_enum
|
|
if enum.identifier == material_color_bind.type
|
|
),
|
|
None,
|
|
)
|
|
if type_name:
|
|
name += " / " + type_name
|
|
layout.label(text=name, translate=False, icon="MATERIAL")
|
|
|
|
|
|
class VRM_UL_vrm1_texture_transform_bind(UIList):
|
|
bl_idname = "VRM_UL_vrm1_texture_transform_bind"
|
|
|
|
def draw_item(
|
|
self,
|
|
_context: Context,
|
|
layout: UILayout,
|
|
_data: object,
|
|
texture_transform_bind: object,
|
|
icon: int,
|
|
_active_data: object,
|
|
_active_prop_name: str,
|
|
_index: int,
|
|
_flt_flag: int,
|
|
) -> None:
|
|
if not isinstance(
|
|
texture_transform_bind, Vrm1TextureTransformBindPropertyGroup
|
|
):
|
|
return
|
|
|
|
if self.layout_type == "GRID":
|
|
layout.alignment = "CENTER"
|
|
layout.label(text="", translate=False, icon_value=icon)
|
|
return
|
|
|
|
if self.layout_type not in {"DEFAULT", "COMPACT"}:
|
|
return
|
|
|
|
name = ""
|
|
material = texture_transform_bind.material
|
|
if material:
|
|
name = material.name
|
|
layout.label(text=name, translate=False, icon="MATERIAL")
|