- 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
1250 lines
44 KiB
Python
1250 lines
44 KiB
Python
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
from collections.abc import Set as AbstractSet
|
|
|
|
import bpy
|
|
from bpy.app.translations import pgettext
|
|
from bpy.types import (
|
|
Armature,
|
|
Context,
|
|
Mesh,
|
|
Object,
|
|
Panel,
|
|
UILayout,
|
|
)
|
|
|
|
from ...common.vrm0.human_bone import HumanBoneSpecifications
|
|
from .. import ops, search
|
|
from ..extension import (
|
|
get_armature_extension,
|
|
get_material_extension,
|
|
get_scene_extension,
|
|
)
|
|
from ..migration import defer_migrate
|
|
from ..ops import layout_operator
|
|
from ..panel import VRM_PT_vrm_armature_object_property, draw_template_list
|
|
from ..search import active_object_is_vrm0_armature
|
|
from . import ops as vrm0_ops
|
|
from .menu import VRM_MT_vrm0_blend_shape_master
|
|
from .ops import draw_bone_prop_search
|
|
from .property_group import (
|
|
Vrm0BlendShapeBindPropertyGroup,
|
|
Vrm0BlendShapeGroupPropertyGroup,
|
|
Vrm0BlendShapeMasterPropertyGroup,
|
|
Vrm0FirstPersonPropertyGroup,
|
|
Vrm0HumanoidPropertyGroup,
|
|
Vrm0MaterialValueBindPropertyGroup,
|
|
Vrm0MetaPropertyGroup,
|
|
Vrm0SecondaryAnimationColliderGroupPropertyGroup,
|
|
Vrm0SecondaryAnimationGroupPropertyGroup,
|
|
Vrm0SecondaryAnimationPropertyGroup,
|
|
)
|
|
from .ui_list import (
|
|
VRM_UL_vrm0_blend_shape_bind,
|
|
VRM_UL_vrm0_blend_shape_group,
|
|
VRM_UL_vrm0_first_person_mesh_annotation,
|
|
VRM_UL_vrm0_material_value_bind,
|
|
VRM_UL_vrm0_secondary_animation_collider_group,
|
|
VRM_UL_vrm0_secondary_animation_collider_group_collider,
|
|
VRM_UL_vrm0_secondary_animation_group,
|
|
VRM_UL_vrm0_secondary_animation_group_bone,
|
|
VRM_UL_vrm0_secondary_animation_group_collider_group,
|
|
)
|
|
|
|
|
|
def draw_vrm0_humanoid_operators_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
) -> None:
|
|
bone_operator_column = layout.column()
|
|
layout_operator(
|
|
bone_operator_column,
|
|
vrm0_ops.VRM_OT_assign_vrm0_humanoid_human_bones_automatically,
|
|
icon="ARMATURE_DATA",
|
|
).armature_object_name = armature.name
|
|
save_load_row = bone_operator_column.split(factor=0.5, align=True)
|
|
save_load_row.operator(
|
|
ops.VRM_OT_save_human_bone_mappings.bl_idname,
|
|
icon="EXPORT",
|
|
text="Save",
|
|
)
|
|
save_load_row.operator(
|
|
ops.VRM_OT_load_human_bone_mappings.bl_idname, icon="IMPORT", text="Load"
|
|
)
|
|
|
|
|
|
def draw_vrm0_humanoid_required_bones_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
split_factor: float = 0.2,
|
|
) -> None:
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
|
|
layout.label(text="Required VRM Human Bones", icon="ARMATURE_DATA")
|
|
row = layout.row(align=True).split(factor=split_factor, align=True)
|
|
label_column = row.column(align=True)
|
|
label_column.label(text=HumanBoneSpecifications.HEAD.label)
|
|
label_column.label(text=HumanBoneSpecifications.NECK.label)
|
|
label_column.label(text=HumanBoneSpecifications.CHEST.label)
|
|
label_column.label(text=HumanBoneSpecifications.SPINE.label)
|
|
label_column.label(text=HumanBoneSpecifications.HIPS.label)
|
|
|
|
search_column = row.column(align=True)
|
|
draw_bone_prop_search(search_column, HumanBoneSpecifications.HEAD, armature)
|
|
draw_bone_prop_search(search_column, HumanBoneSpecifications.NECK, armature)
|
|
draw_bone_prop_search(search_column, HumanBoneSpecifications.CHEST, armature)
|
|
draw_bone_prop_search(search_column, HumanBoneSpecifications.SPINE, armature)
|
|
draw_bone_prop_search(search_column, HumanBoneSpecifications.HIPS, armature)
|
|
|
|
row = layout.row(align=True).split(factor=split_factor, align=True)
|
|
label_column = row.column(align=True)
|
|
label_column.label(text="", translate=False)
|
|
label_column.label(text=HumanBoneSpecifications.LEFT_UPPER_ARM.label_no_left_right)
|
|
label_column.label(text=HumanBoneSpecifications.LEFT_LOWER_ARM.label_no_left_right)
|
|
label_column.label(text=HumanBoneSpecifications.LEFT_HAND.label_no_left_right)
|
|
label_column.separator()
|
|
label_column.label(text=HumanBoneSpecifications.LEFT_UPPER_LEG.label_no_left_right)
|
|
label_column.label(text=HumanBoneSpecifications.LEFT_LOWER_LEG.label_no_left_right)
|
|
label_column.label(text=HumanBoneSpecifications.LEFT_FOOT.label_no_left_right)
|
|
|
|
search_column = row.column(align=True)
|
|
right_left_row = search_column.row(align=True)
|
|
right_left_row.label(text="Right")
|
|
right_left_row.label(text="Left")
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.RIGHT_UPPER_ARM, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.LEFT_UPPER_ARM, armature
|
|
)
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.RIGHT_LOWER_ARM, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.LEFT_LOWER_ARM, armature
|
|
)
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(right_left_row, HumanBoneSpecifications.RIGHT_HAND, armature)
|
|
draw_bone_prop_search(right_left_row, HumanBoneSpecifications.LEFT_HAND, armature)
|
|
|
|
search_column.separator()
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.RIGHT_UPPER_LEG, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.LEFT_UPPER_LEG, armature
|
|
)
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.RIGHT_LOWER_LEG, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.LEFT_LOWER_LEG, armature
|
|
)
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(right_left_row, HumanBoneSpecifications.RIGHT_FOOT, armature)
|
|
draw_bone_prop_search(right_left_row, HumanBoneSpecifications.LEFT_FOOT, armature)
|
|
|
|
|
|
def draw_vrm0_humanoid_optional_bones_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
split_factor: float = 0.2,
|
|
) -> None:
|
|
split_factor = 0.2
|
|
|
|
layout.label(text="Optional VRM Human Bones", icon="BONE_DATA")
|
|
|
|
row = layout.row(align=True).split(factor=split_factor, align=True)
|
|
label_column = row.column(align=True)
|
|
label_column.label(text="", translate=False)
|
|
label_column.label(text=HumanBoneSpecifications.LEFT_EYE.label_no_left_right)
|
|
label_column.label(text=HumanBoneSpecifications.JAW.label)
|
|
label_column.label(text=HumanBoneSpecifications.RIGHT_SHOULDER.label_no_left_right)
|
|
label_column.label(text=HumanBoneSpecifications.UPPER_CHEST.label)
|
|
label_column.label(text=HumanBoneSpecifications.RIGHT_TOES.label_no_left_right)
|
|
|
|
search_column = row.column(align=True)
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
right_left_row.label(text="Right")
|
|
right_left_row.label(text="Left")
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(right_left_row, HumanBoneSpecifications.RIGHT_EYE, armature)
|
|
draw_bone_prop_search(right_left_row, HumanBoneSpecifications.LEFT_EYE, armature)
|
|
|
|
draw_bone_prop_search(search_column, HumanBoneSpecifications.JAW, armature)
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.RIGHT_SHOULDER, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
right_left_row, HumanBoneSpecifications.LEFT_SHOULDER, armature
|
|
)
|
|
|
|
draw_bone_prop_search(search_column, HumanBoneSpecifications.UPPER_CHEST, armature)
|
|
|
|
right_left_row = search_column.row(align=True)
|
|
draw_bone_prop_search(right_left_row, HumanBoneSpecifications.RIGHT_TOES, armature)
|
|
draw_bone_prop_search(right_left_row, HumanBoneSpecifications.LEFT_TOES, armature)
|
|
|
|
row = layout.row(align=True).split(factor=split_factor, align=True)
|
|
label_column = row.column(align=True)
|
|
label_column.label(text="", translate=False)
|
|
label_column.label(text="Left Thumb:")
|
|
label_column.label(text="Left Index:")
|
|
label_column.label(text="Left Middle:")
|
|
label_column.label(text="Left Ring:")
|
|
label_column.label(text="Left Little:")
|
|
label_column.separator()
|
|
label_column.label(text="Right Thumb:")
|
|
label_column.label(text="Right Index:")
|
|
label_column.label(text="Right Middle:")
|
|
label_column.label(text="Right Ring:")
|
|
label_column.label(text="Right Little:")
|
|
|
|
search_column = row.column(align=True)
|
|
finger_row = search_column.row(align=True)
|
|
finger_row.label(text="Root")
|
|
finger_row.label(text="", translate=False)
|
|
finger_row.label(text="Tip")
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_THUMB_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_THUMB_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_THUMB_DISTAL, armature
|
|
)
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_INDEX_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_INDEX_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_INDEX_DISTAL, armature
|
|
)
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_MIDDLE_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_MIDDLE_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_MIDDLE_DISTAL, armature
|
|
)
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_RING_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_RING_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_RING_DISTAL, armature
|
|
)
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_LITTLE_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_LITTLE_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.LEFT_LITTLE_DISTAL, armature
|
|
)
|
|
|
|
search_column.separator()
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_THUMB_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_THUMB_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_THUMB_DISTAL, armature
|
|
)
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_INDEX_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_INDEX_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_INDEX_DISTAL, armature
|
|
)
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_MIDDLE_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_MIDDLE_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_MIDDLE_DISTAL, armature
|
|
)
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_RING_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_RING_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_RING_DISTAL, armature
|
|
)
|
|
|
|
finger_row = search_column.row(align=True)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_LITTLE_PROXIMAL, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_LITTLE_INTERMEDIATE, armature
|
|
)
|
|
draw_bone_prop_search(
|
|
finger_row, HumanBoneSpecifications.RIGHT_LITTLE_DISTAL, armature
|
|
)
|
|
|
|
|
|
def draw_vrm0_humanoid_layout(
|
|
context: Context,
|
|
armature: Object,
|
|
layout: UILayout,
|
|
humanoid: Vrm0HumanoidPropertyGroup,
|
|
) -> None:
|
|
if defer_migrate(armature.name):
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
Vrm0HumanoidPropertyGroup.defer_update_all_node_candidates(armature_data.name)
|
|
|
|
data = armature.data
|
|
if not isinstance(data, Armature):
|
|
return
|
|
|
|
armature_box = layout
|
|
|
|
t_pose_box = armature_box.box()
|
|
column = t_pose_box.row().column()
|
|
column.label(text="VRM T-Pose", icon="OUTLINER_OB_ARMATURE")
|
|
column.prop(humanoid, "pose")
|
|
if humanoid.pose == humanoid.POSE_CUSTOM_POSE.identifier:
|
|
label = "Pose Library" if bpy.app.version < (3, 0) else "Pose Asset"
|
|
column.prop_search(
|
|
humanoid,
|
|
"pose_library",
|
|
context.blend_data,
|
|
"actions",
|
|
text=label,
|
|
translate=False,
|
|
)
|
|
pose_library = humanoid.pose_library
|
|
if pose_library and pose_library.pose_markers:
|
|
column.prop_search(
|
|
humanoid,
|
|
"pose_marker_name",
|
|
pose_library,
|
|
"pose_markers",
|
|
text="Pose",
|
|
translate=False,
|
|
)
|
|
|
|
draw_vrm0_humanoid_operators_layout(armature, armature_box)
|
|
|
|
if ops.VRM_OT_simplify_vroid_bones.vroid_bones_exist(data):
|
|
simplify_vroid_bones_op = layout_operator(
|
|
armature_box,
|
|
ops.VRM_OT_simplify_vroid_bones,
|
|
text=pgettext(ops.VRM_OT_simplify_vroid_bones.bl_label),
|
|
icon="GREASEPENCIL",
|
|
)
|
|
simplify_vroid_bones_op.armature_object_name = armature.name
|
|
|
|
split_factor = 0.2
|
|
draw_vrm0_humanoid_required_bones_layout(armature, armature_box.box(), split_factor)
|
|
draw_vrm0_humanoid_optional_bones_layout(armature, armature_box.box(), split_factor)
|
|
|
|
layout.label(text="Arm", icon="VIEW_PAN", translate=False) # TODO: Translation
|
|
layout.prop(
|
|
humanoid,
|
|
"arm_stretch",
|
|
)
|
|
layout.prop(humanoid, "upper_arm_twist")
|
|
layout.prop(humanoid, "lower_arm_twist")
|
|
layout.separator()
|
|
layout.label(text="Leg", icon="MOD_DYNAMICPAINT")
|
|
layout.prop(humanoid, "leg_stretch")
|
|
layout.prop(humanoid, "upper_leg_twist")
|
|
layout.prop(humanoid, "lower_leg_twist")
|
|
layout.prop(humanoid, "feet_spacing")
|
|
layout.separator()
|
|
layout.prop(humanoid, "has_translation_dof")
|
|
|
|
|
|
class VRM_PT_vrm0_humanoid_armature_object_property(Panel):
|
|
bl_idname = "VRM_PT_vrm0_humanoid_armature_object_property"
|
|
bl_label = "VRM 0.x Humanoid"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "object"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
bl_parent_id = VRM_PT_vrm_armature_object_property.bl_idname
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return active_object_is_vrm0_armature(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="ARMATURE_DATA")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
active_object = context.active_object
|
|
if not active_object:
|
|
return
|
|
armature_data = active_object.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
draw_vrm0_humanoid_layout(
|
|
context,
|
|
active_object,
|
|
self.layout,
|
|
get_armature_extension(armature_data).vrm0.humanoid,
|
|
)
|
|
|
|
|
|
class VRM_PT_vrm0_humanoid_ui(Panel):
|
|
bl_idname = "VRM_PT_vrm0_humanoid_ui"
|
|
bl_label = "VRM 0.x Humanoid"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = "VRM"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return search.current_armature_is_vrm0(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="USER")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
armature = search.current_armature(context)
|
|
if not armature:
|
|
return
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
draw_vrm0_humanoid_layout(
|
|
context,
|
|
armature,
|
|
self.layout,
|
|
get_armature_extension(armature_data).vrm0.humanoid,
|
|
)
|
|
|
|
|
|
def draw_vrm0_first_person_layout(
|
|
armature: Object,
|
|
_context: Context,
|
|
layout: UILayout,
|
|
first_person: Vrm0FirstPersonPropertyGroup,
|
|
) -> None:
|
|
defer_migrate(armature.name)
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
layout.prop_search(
|
|
first_person.first_person_bone, "bone_name", armature_data, "bones"
|
|
)
|
|
layout.prop(first_person, "first_person_bone_offset", icon="BONE_DATA")
|
|
layout.prop(first_person, "look_at_type_name")
|
|
layout.label(text="Mesh Annotations", icon="RESTRICT_RENDER_OFF")
|
|
|
|
(
|
|
mesh_annotation_collection_ops,
|
|
mesh_annotation_collection_item_ops,
|
|
mesh_annotation_index,
|
|
_,
|
|
_,
|
|
) = draw_template_list(
|
|
layout,
|
|
VRM_UL_vrm0_first_person_mesh_annotation,
|
|
first_person,
|
|
"mesh_annotations",
|
|
"active_mesh_annotation_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_first_person_mesh_annotation,
|
|
vrm0_ops.VRM_OT_remove_vrm0_first_person_mesh_annotation,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_first_person_mesh_annotation,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_first_person_mesh_annotation,
|
|
)
|
|
|
|
for mesh_annotation_collection_op in mesh_annotation_collection_ops:
|
|
mesh_annotation_collection_op.armature_object_name = armature.name
|
|
|
|
for mesh_annotation_collection_item_op in mesh_annotation_collection_item_ops:
|
|
mesh_annotation_collection_item_op.mesh_annotation_index = mesh_annotation_index
|
|
|
|
layout.separator()
|
|
box = layout.box()
|
|
box.label(text="Look At Horizontal Inner", icon="FULLSCREEN_EXIT")
|
|
box.prop(first_person.look_at_horizontal_inner, "curve")
|
|
box.prop(first_person.look_at_horizontal_inner, "x_range")
|
|
box.prop(first_person.look_at_horizontal_inner, "y_range")
|
|
box = layout.box()
|
|
box.label(text="Look At Horizontal Outer", icon="FULLSCREEN_ENTER")
|
|
box.prop(first_person.look_at_horizontal_outer, "curve")
|
|
box.prop(first_person.look_at_horizontal_outer, "x_range")
|
|
box.prop(first_person.look_at_horizontal_outer, "y_range")
|
|
box = layout.box()
|
|
box.label(text="Look At Vertical Up", icon="TRIA_UP")
|
|
box.prop(first_person.look_at_vertical_up, "curve")
|
|
box.prop(first_person.look_at_vertical_up, "x_range")
|
|
box.prop(first_person.look_at_vertical_up, "y_range")
|
|
box = layout.box()
|
|
box.label(text="Look At Vertical Down", icon="TRIA_DOWN")
|
|
box.prop(first_person.look_at_vertical_down, "curve")
|
|
box.prop(first_person.look_at_vertical_down, "x_range")
|
|
box.prop(first_person.look_at_vertical_down, "y_range")
|
|
|
|
|
|
class VRM_PT_vrm0_first_person_armature_object_property(Panel):
|
|
bl_idname = "VRM_PT_vrm0_first_person_armature_object_property"
|
|
bl_label = "VRM 0.x First Person"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "object"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
bl_parent_id = VRM_PT_vrm_armature_object_property.bl_idname
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return active_object_is_vrm0_armature(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="HIDE_OFF")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
active_object = context.active_object
|
|
if not active_object:
|
|
return
|
|
armature_data = active_object.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
ext = get_armature_extension(armature_data)
|
|
draw_vrm0_first_person_layout(
|
|
active_object,
|
|
context,
|
|
self.layout,
|
|
ext.vrm0.first_person,
|
|
)
|
|
|
|
|
|
class VRM_PT_vrm0_first_person_ui(Panel):
|
|
bl_idname = "VRM_PT_vrm0_first_person_ui"
|
|
bl_label = "VRM 0.x First Person"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = "VRM"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return search.current_armature_is_vrm0(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="HIDE_OFF")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
armature = search.current_armature(context)
|
|
if not armature:
|
|
return
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
draw_vrm0_first_person_layout(
|
|
armature,
|
|
context,
|
|
self.layout,
|
|
get_armature_extension(armature_data).vrm0.first_person,
|
|
)
|
|
|
|
|
|
def draw_vrm0_blend_shape_master_layout(
|
|
armature: Object,
|
|
context: Context,
|
|
layout: UILayout,
|
|
blend_shape_master: Vrm0BlendShapeMasterPropertyGroup,
|
|
) -> None:
|
|
defer_migrate(armature.name)
|
|
blend_data = context.blend_data
|
|
|
|
(
|
|
blend_shape_group_collection_ops,
|
|
blend_shape_group_collection_item_ops,
|
|
blend_shape_group_index,
|
|
blend_shape_group,
|
|
(add_blend_shape_group_op, _, _, _),
|
|
) = draw_template_list(
|
|
layout,
|
|
VRM_UL_vrm0_blend_shape_group,
|
|
blend_shape_master,
|
|
"blend_shape_groups",
|
|
"active_blend_shape_group_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_blend_shape_group,
|
|
vrm0_ops.VRM_OT_remove_vrm0_blend_shape_group,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_blend_shape_group,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_blend_shape_group,
|
|
menu=VRM_MT_vrm0_blend_shape_master,
|
|
)
|
|
|
|
for blend_shape_group_collection_op in blend_shape_group_collection_ops:
|
|
blend_shape_group_collection_op.armature_object_name = armature.name
|
|
|
|
for blend_shape_group_collection_item_op in blend_shape_group_collection_item_ops:
|
|
blend_shape_group_collection_item_op.blend_shape_group_index = (
|
|
blend_shape_group_index
|
|
)
|
|
|
|
add_blend_shape_group_op.name = "New"
|
|
|
|
if isinstance(blend_shape_group, Vrm0BlendShapeGroupPropertyGroup):
|
|
column = layout.column()
|
|
column.prop(blend_shape_group, "name")
|
|
column.prop(blend_shape_group, "preset_name")
|
|
column.prop(blend_shape_group, "is_binary", icon="IPO_CONSTANT")
|
|
column.prop(blend_shape_group, "preview", icon="PLAY", text="Preview")
|
|
column.separator(factor=0.5)
|
|
|
|
binds_box = column.box()
|
|
binds_box.label(text="Binds", icon="MESH_DATA")
|
|
|
|
(
|
|
bind_collection_ops,
|
|
bind_collection_item_ops,
|
|
bind_index,
|
|
bind,
|
|
_,
|
|
) = draw_template_list(
|
|
binds_box,
|
|
VRM_UL_vrm0_blend_shape_bind,
|
|
blend_shape_group,
|
|
"binds",
|
|
"active_bind_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_blend_shape_bind,
|
|
vrm0_ops.VRM_OT_remove_vrm0_blend_shape_bind,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_blend_shape_bind,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_blend_shape_bind,
|
|
)
|
|
|
|
for bind_collection_op in bind_collection_ops:
|
|
bind_collection_op.armature_object_name = armature.name
|
|
bind_collection_op.blend_shape_group_index = blend_shape_group_index
|
|
|
|
for bind_collection_item_op in bind_collection_item_ops:
|
|
bind_collection_item_op.bind_index = bind_index
|
|
|
|
if isinstance(bind, Vrm0BlendShapeBindPropertyGroup):
|
|
bind_column = binds_box.column()
|
|
bind_column.prop(
|
|
bind.mesh,
|
|
"bpy_object",
|
|
text="Mesh",
|
|
icon="OUTLINER_OB_MESH",
|
|
)
|
|
|
|
mesh_object = blend_data.objects.get(bind.mesh.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:
|
|
bind_column.prop_search(
|
|
bind,
|
|
"index",
|
|
shape_keys,
|
|
"key_blocks",
|
|
text="Shape key",
|
|
)
|
|
bind_column.prop(bind, "weight", slider=True)
|
|
|
|
column.separator(factor=0.2)
|
|
material_value_binds_box = column.box()
|
|
material_value_binds_box.label(text="Material Values", icon="MATERIAL")
|
|
|
|
(
|
|
material_value_collection_ops,
|
|
material_value_collection_item_ops,
|
|
material_value_index,
|
|
material_value,
|
|
_,
|
|
) = draw_template_list(
|
|
material_value_binds_box,
|
|
VRM_UL_vrm0_material_value_bind,
|
|
blend_shape_group,
|
|
"material_values",
|
|
"active_material_value_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_material_value_bind,
|
|
vrm0_ops.VRM_OT_remove_vrm0_material_value_bind,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_material_value_bind,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_material_value_bind,
|
|
)
|
|
|
|
for material_value_bind_collection_op in material_value_collection_ops:
|
|
material_value_bind_collection_op.armature_object_name = armature.name
|
|
material_value_bind_collection_op.blend_shape_group_index = (
|
|
blend_shape_group_index
|
|
)
|
|
|
|
for (
|
|
material_value_bind_collection_item_op
|
|
) in material_value_collection_item_ops:
|
|
material_value_bind_collection_item_op.material_value_index = (
|
|
material_value_index
|
|
)
|
|
|
|
if isinstance(material_value, Vrm0MaterialValueBindPropertyGroup):
|
|
material_value_column = material_value_binds_box.column()
|
|
material_value_column.prop_search(
|
|
material_value, "material", blend_data, "materials"
|
|
)
|
|
|
|
material = material_value.material
|
|
if not material:
|
|
material_value_column.prop(
|
|
material_value, "property_name", icon="PROPERTIES"
|
|
)
|
|
else:
|
|
scene_extension = get_scene_extension(context.scene)
|
|
scene_extension.defer_update_vrm0_material_property_names()
|
|
ext = get_material_extension(material)
|
|
node, legacy_shader_name = search.legacy_shader_node(material)
|
|
if ext.mtoon1.enabled or (
|
|
node and legacy_shader_name == "MToon_unversioned"
|
|
):
|
|
if bpy.app.version >= (3, 2):
|
|
material_value_column.prop_search(
|
|
material_value,
|
|
"property_name",
|
|
get_scene_extension(context.scene),
|
|
"vrm0_material_mtoon0_property_names",
|
|
icon="PROPERTIES",
|
|
results_are_suggestions=True,
|
|
)
|
|
else:
|
|
material_value_column.prop_search(
|
|
material_value,
|
|
"property_name",
|
|
get_scene_extension(context.scene),
|
|
"vrm0_material_mtoon0_property_names",
|
|
icon="PROPERTIES",
|
|
)
|
|
elif bpy.app.version >= (3, 2):
|
|
material_value_column.prop_search(
|
|
material_value,
|
|
"property_name",
|
|
get_scene_extension(context.scene),
|
|
"vrm0_material_gltf_property_names",
|
|
icon="PROPERTIES",
|
|
results_are_suggestions=True,
|
|
)
|
|
else:
|
|
material_value_column.prop_search(
|
|
material_value,
|
|
"property_name",
|
|
get_scene_extension(context.scene),
|
|
"vrm0_material_gltf_property_names",
|
|
icon="PROPERTIES",
|
|
)
|
|
|
|
for (
|
|
target_value_index,
|
|
target_value,
|
|
) in enumerate(material_value.target_value):
|
|
target_value_row = material_value_column.row()
|
|
target_value_row.prop(
|
|
target_value, "value", text=f"Value {target_value_index}"
|
|
)
|
|
remove_target_value_op = layout_operator(
|
|
target_value_row,
|
|
vrm0_ops.VRM_OT_remove_vrm0_material_value_bind_target_value,
|
|
text="",
|
|
translate=False,
|
|
icon="REMOVE",
|
|
)
|
|
remove_target_value_op.armature_object_name = armature.name
|
|
remove_target_value_op.blend_shape_group_index = blend_shape_group_index
|
|
remove_target_value_op.material_value_index = material_value_index
|
|
remove_target_value_op.target_value_index = target_value_index
|
|
add_target_value_op = layout_operator(
|
|
material_value_column,
|
|
vrm0_ops.VRM_OT_add_vrm0_material_value_bind_target_value,
|
|
icon="ADD",
|
|
)
|
|
add_target_value_op.armature_object_name = armature.name
|
|
add_target_value_op.blend_shape_group_index = blend_shape_group_index
|
|
add_target_value_op.material_value_index = material_value_index
|
|
|
|
|
|
class VRM_PT_vrm0_blend_shape_master_armature_object_property(Panel):
|
|
bl_idname = "VRM_PT_vrm0_blend_shape_master_armature_object_property"
|
|
bl_label = "VRM 0.x Blend Shape Proxy"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "object"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
bl_parent_id = VRM_PT_vrm_armature_object_property.bl_idname
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return active_object_is_vrm0_armature(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="SHAPEKEY_DATA")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
active_object = context.active_object
|
|
if not active_object:
|
|
return
|
|
armature_data = active_object.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
ext = get_armature_extension(armature_data)
|
|
draw_vrm0_blend_shape_master_layout(
|
|
active_object, context, self.layout, ext.vrm0.blend_shape_master
|
|
)
|
|
|
|
|
|
class VRM_PT_vrm0_blend_shape_master_ui(Panel):
|
|
bl_idname = "VRM_PT_vrm0_blend_shape_master_ui"
|
|
bl_label = "VRM 0.x Blend Shape Proxy"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = "VRM"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return search.current_armature_is_vrm0(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="SHAPEKEY_DATA")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
armature = search.current_armature(context)
|
|
if not armature:
|
|
return
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
draw_vrm0_blend_shape_master_layout(
|
|
armature,
|
|
context,
|
|
self.layout,
|
|
get_armature_extension(armature_data).vrm0.blend_shape_master,
|
|
)
|
|
|
|
|
|
def draw_vrm0_secondary_animation_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
secondary_animation: Vrm0SecondaryAnimationPropertyGroup,
|
|
) -> None:
|
|
defer_migrate(armature.name)
|
|
draw_vrm0_secondary_animation_bone_groups_layout(
|
|
armature, layout, secondary_animation
|
|
)
|
|
draw_vrm0_secondary_animation_collider_groups_layout(
|
|
armature, layout, secondary_animation
|
|
)
|
|
|
|
|
|
def draw_vrm0_secondary_animation_bone_groups_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
secondary_animation: Vrm0SecondaryAnimationPropertyGroup,
|
|
) -> None:
|
|
data = armature.data
|
|
if not isinstance(data, Armature):
|
|
return
|
|
|
|
header_row = layout.row()
|
|
header_row.alignment = "LEFT"
|
|
header_row.prop(
|
|
secondary_animation,
|
|
"show_expanded_bone_groups",
|
|
icon="TRIA_DOWN"
|
|
if secondary_animation.show_expanded_bone_groups
|
|
else "TRIA_RIGHT",
|
|
emboss=False,
|
|
)
|
|
if not secondary_animation.show_expanded_bone_groups:
|
|
return
|
|
|
|
box = layout.box()
|
|
|
|
(
|
|
bone_group_collection_ops,
|
|
bone_group_collection_item_ops,
|
|
bone_group_index,
|
|
bone_group,
|
|
_,
|
|
) = draw_template_list(
|
|
box,
|
|
VRM_UL_vrm0_secondary_animation_group,
|
|
secondary_animation,
|
|
"bone_groups",
|
|
"active_bone_group_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_secondary_animation_group,
|
|
vrm0_ops.VRM_OT_remove_vrm0_secondary_animation_group,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_secondary_animation_group,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_secondary_animation_group,
|
|
)
|
|
|
|
for bone_group_collection_op in bone_group_collection_ops:
|
|
bone_group_collection_op.armature_object_name = armature.name
|
|
|
|
for bone_group_collection_item_op in bone_group_collection_item_ops:
|
|
bone_group_collection_item_op.bone_group_index = bone_group_index
|
|
|
|
if not isinstance(bone_group, Vrm0SecondaryAnimationGroupPropertyGroup):
|
|
return
|
|
|
|
column = box.column()
|
|
column.prop(bone_group, "comment", icon="BOOKMARKS")
|
|
column.prop(bone_group, "stiffiness", icon="RIGID_BODY_CONSTRAINT")
|
|
column.prop(bone_group, "drag_force", icon="FORCE_DRAG")
|
|
column.separator()
|
|
column.prop(bone_group, "gravity_power", icon="OUTLINER_OB_FORCE_FIELD")
|
|
column.prop(bone_group, "gravity_dir", icon="OUTLINER_OB_FORCE_FIELD")
|
|
column.separator()
|
|
column.prop_search(
|
|
bone_group.center,
|
|
"bone_name",
|
|
data,
|
|
"bones",
|
|
icon="PIVOT_MEDIAN",
|
|
text="Center Bone",
|
|
)
|
|
column.prop(
|
|
bone_group,
|
|
"hit_radius",
|
|
icon="MOD_PHYSICS",
|
|
)
|
|
column.separator()
|
|
|
|
column.label(text="Bones:")
|
|
|
|
(
|
|
bone_collection_ops,
|
|
bone_collection_item_ops,
|
|
bone_index,
|
|
_bone,
|
|
_,
|
|
) = draw_template_list(
|
|
column,
|
|
VRM_UL_vrm0_secondary_animation_group_bone,
|
|
bone_group,
|
|
"bones",
|
|
"active_bone_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_secondary_animation_group_bone,
|
|
vrm0_ops.VRM_OT_remove_vrm0_secondary_animation_group_bone,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_secondary_animation_group_bone,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_secondary_animation_group_bone,
|
|
)
|
|
|
|
for bone_collection_op in bone_collection_ops:
|
|
bone_collection_op.armature_object_name = armature.name
|
|
bone_collection_op.bone_group_index = bone_group_index
|
|
|
|
for bone_collection_item_op in bone_collection_item_ops:
|
|
bone_collection_item_op.bone_index = bone_index
|
|
|
|
column.separator()
|
|
column.label(text="Collider Groups:")
|
|
|
|
(
|
|
collider_group_collection_ops,
|
|
collider_group_collection_item_ops,
|
|
collider_group_index,
|
|
_collider_group,
|
|
_,
|
|
) = draw_template_list(
|
|
column,
|
|
VRM_UL_vrm0_secondary_animation_group_collider_group,
|
|
bone_group,
|
|
"collider_groups",
|
|
"active_collider_group_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_secondary_animation_group_collider_group,
|
|
vrm0_ops.VRM_OT_remove_vrm0_secondary_animation_group_collider_group,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_secondary_animation_group_collider_group,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_secondary_animation_group_collider_group,
|
|
)
|
|
|
|
for collider_group_collection_op in collider_group_collection_ops:
|
|
collider_group_collection_op.armature_object_name = armature.name
|
|
collider_group_collection_op.bone_group_index = bone_group_index
|
|
|
|
for collider_group_collection_item_op in collider_group_collection_item_ops:
|
|
collider_group_collection_item_op.collider_group_index = collider_group_index
|
|
|
|
|
|
def draw_vrm0_secondary_animation_collider_groups_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
secondary_animation: Vrm0SecondaryAnimationPropertyGroup,
|
|
) -> None:
|
|
data = armature.data
|
|
if not isinstance(data, Armature):
|
|
return
|
|
|
|
header_row = layout.row()
|
|
header_row.alignment = "LEFT"
|
|
header_row.prop(
|
|
secondary_animation,
|
|
"show_expanded_collider_groups",
|
|
icon="TRIA_DOWN"
|
|
if secondary_animation.show_expanded_collider_groups
|
|
else "TRIA_RIGHT",
|
|
emboss=False,
|
|
)
|
|
if not secondary_animation.show_expanded_collider_groups:
|
|
return
|
|
|
|
box = layout.box()
|
|
|
|
(
|
|
collider_group_collection_ops,
|
|
collider_group_collection_item_ops,
|
|
collider_group_index,
|
|
collider_group,
|
|
_,
|
|
) = draw_template_list(
|
|
box,
|
|
VRM_UL_vrm0_secondary_animation_collider_group,
|
|
secondary_animation,
|
|
"collider_groups",
|
|
"active_collider_group_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_secondary_animation_collider_group,
|
|
vrm0_ops.VRM_OT_remove_vrm0_secondary_animation_collider_group,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_secondary_animation_collider_group,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_secondary_animation_collider_group,
|
|
)
|
|
|
|
for collider_group_collection_op in collider_group_collection_ops:
|
|
collider_group_collection_op.armature_object_name = armature.name
|
|
|
|
for collider_group_collection_item_op in collider_group_collection_item_ops:
|
|
collider_group_collection_item_op.collider_group_index = collider_group_index
|
|
|
|
if not isinstance(collider_group, Vrm0SecondaryAnimationColliderGroupPropertyGroup):
|
|
return
|
|
|
|
column = box.column()
|
|
column.label(text=collider_group.name)
|
|
column.prop_search(collider_group.node, "bone_name", data, "bones")
|
|
column.label(text="Colliders:")
|
|
|
|
(
|
|
collider_collection_ops,
|
|
collider_collection_item_ops,
|
|
collider_index,
|
|
_,
|
|
_,
|
|
) = draw_template_list(
|
|
column,
|
|
VRM_UL_vrm0_secondary_animation_collider_group_collider,
|
|
collider_group,
|
|
"colliders",
|
|
"active_collider_index",
|
|
vrm0_ops.VRM_OT_add_vrm0_secondary_animation_collider_group_collider,
|
|
vrm0_ops.VRM_OT_remove_vrm0_secondary_animation_collider_group_collider,
|
|
vrm0_ops.VRM_OT_move_up_vrm0_secondary_animation_collider_group_collider,
|
|
vrm0_ops.VRM_OT_move_down_vrm0_secondary_animation_collider_group_collider,
|
|
)
|
|
|
|
for collider_collection_op in collider_collection_ops:
|
|
collider_collection_op.armature_object_name = armature.name
|
|
collider_collection_op.collider_group_index = collider_group_index
|
|
|
|
for collider_collection_item_op in collider_collection_item_ops:
|
|
collider_collection_item_op.armature_object_name = armature.name
|
|
collider_collection_item_op.collider_group_index = collider_group_index
|
|
collider_collection_item_op.collider_index = collider_index
|
|
|
|
|
|
class VRM_PT_vrm0_secondary_animation_armature_object_property(Panel):
|
|
bl_idname = "VRM_PT_vrm0_secondary_animation_armature_object_property"
|
|
bl_label = "VRM 0.x Spring Bone"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "object"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
bl_parent_id = VRM_PT_vrm_armature_object_property.bl_idname
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return active_object_is_vrm0_armature(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="PHYSICS")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
active_object = context.active_object
|
|
if not active_object:
|
|
return
|
|
armature_data = active_object.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
ext = get_armature_extension(armature_data)
|
|
draw_vrm0_secondary_animation_layout(
|
|
active_object, self.layout, ext.vrm0.secondary_animation
|
|
)
|
|
|
|
|
|
class VRM_PT_vrm0_secondary_animation_ui(Panel):
|
|
bl_idname = "VRM_PT_vrm0_secondary_animation_ui"
|
|
bl_label = "VRM 0.x Spring Bone"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = "VRM"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return search.current_armature_is_vrm0(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="PHYSICS")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
armature = search.current_armature(context)
|
|
if not armature:
|
|
return
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
draw_vrm0_secondary_animation_layout(
|
|
armature,
|
|
self.layout,
|
|
get_armature_extension(armature_data).vrm0.secondary_animation,
|
|
)
|
|
|
|
|
|
def draw_vrm0_meta_layout(
|
|
armature: Object,
|
|
_context: Context,
|
|
layout: UILayout,
|
|
meta: Vrm0MetaPropertyGroup,
|
|
) -> None:
|
|
defer_migrate(armature.name)
|
|
|
|
thumbnail_column = layout.column()
|
|
thumbnail_column.label(text="Thumbnail:")
|
|
thumbnail_column.template_ID_preview(meta, "texture")
|
|
|
|
layout.prop(meta, "title", icon="FILE_BLEND")
|
|
layout.prop(meta, "version", icon="LINENUMBERS_ON")
|
|
layout.prop(meta, "author", icon="USER")
|
|
layout.prop(meta, "contact_information", icon="URL")
|
|
layout.prop(meta, "reference", icon="URL")
|
|
layout.prop(meta, "allowed_user_name", icon="MATCLOTH")
|
|
layout.prop(
|
|
meta,
|
|
"violent_ussage_name",
|
|
icon="ORPHAN_DATA",
|
|
)
|
|
layout.prop(meta, "sexual_ussage_name", icon="HEART")
|
|
layout.prop(
|
|
meta,
|
|
"commercial_ussage_name",
|
|
icon="SOLO_OFF",
|
|
)
|
|
layout.prop(meta, "other_permission_url", icon="URL")
|
|
layout.prop(meta, "license_name", icon="COMMUNITY")
|
|
if meta.license_name == Vrm0MetaPropertyGroup.LICENSE_NAME_OTHER.identifier:
|
|
layout.prop(meta, "other_license_url", icon="URL")
|
|
|
|
|
|
class VRM_PT_vrm0_meta_armature_object_property(Panel):
|
|
bl_idname = "VRM_PT_vrm0_meta_armature_object_property"
|
|
bl_label = "VRM 0.x Meta"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "object"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
bl_parent_id = VRM_PT_vrm_armature_object_property.bl_idname
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return active_object_is_vrm0_armature(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="FILE_BLEND")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
active_object = context.active_object
|
|
if not active_object:
|
|
return
|
|
armature_data = active_object.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
ext = get_armature_extension(armature_data)
|
|
draw_vrm0_meta_layout(active_object, context, self.layout, ext.vrm0.meta)
|
|
|
|
|
|
class VRM_PT_vrm0_meta_ui(Panel):
|
|
bl_idname = "VRM_PT_vrm0_meta_ui"
|
|
bl_label = "VRM 0.x Meta"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = "VRM"
|
|
bl_options: AbstractSet[str] = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return search.current_armature_is_vrm0(context)
|
|
|
|
def draw_header(self, _context: Context) -> None:
|
|
self.layout.label(icon="FILE_BLEND")
|
|
|
|
def draw(self, context: Context) -> None:
|
|
armature = search.current_armature(context)
|
|
if not armature:
|
|
return
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
draw_vrm0_meta_layout(
|
|
armature,
|
|
context,
|
|
self.layout,
|
|
get_armature_extension(armature_data).vrm0.meta,
|
|
)
|