- 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
600 lines
20 KiB
Python
600 lines
20 KiB
Python
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
from collections.abc import Set as AbstractSet
|
|
from typing import Optional
|
|
|
|
from bpy.types import Armature, Context, Object, Panel, UILayout
|
|
|
|
from .. import search
|
|
from ..extension import get_armature_extension
|
|
from ..migration import defer_migrate
|
|
from ..panel import VRM_PT_vrm_armature_object_property, draw_template_list
|
|
from ..search import active_object_is_vrm1_armature
|
|
from . import ops
|
|
from .property_group import (
|
|
SpringBone1ColliderGroupPropertyGroup,
|
|
SpringBone1ColliderPropertyGroup,
|
|
SpringBone1JointPropertyGroup,
|
|
SpringBone1SpringBonePropertyGroup,
|
|
SpringBone1SpringPropertyGroup,
|
|
)
|
|
from .ui_list import (
|
|
VRM_UL_spring_bone1_collider,
|
|
VRM_UL_spring_bone1_collider_group,
|
|
VRM_UL_spring_bone1_collider_group_collider,
|
|
VRM_UL_spring_bone1_joint,
|
|
VRM_UL_spring_bone1_spring,
|
|
VRM_UL_spring_bone1_spring_collider_group,
|
|
)
|
|
|
|
|
|
def draw_spring_bone1_collider_sphere_layout(
|
|
layout: UILayout,
|
|
armature_data: Armature,
|
|
collider: SpringBone1ColliderPropertyGroup,
|
|
) -> None:
|
|
layout.prop_search(collider.node, "bone_name", armature_data, "bones")
|
|
layout.prop(collider, "ui_collider_type")
|
|
bpy_object = collider.bpy_object
|
|
if bpy_object:
|
|
layout.prop(bpy_object, "name", icon="MESH_UVSPHERE", text="Name")
|
|
layout.prop(collider.shape.sphere, "offset", text="Offset")
|
|
layout.separator(factor=0.5)
|
|
layout.prop(collider.shape.sphere, "radius", slider=True)
|
|
layout.prop(collider.extensions.vrmc_spring_bone_extended_collider, "enabled")
|
|
|
|
|
|
def draw_spring_bone1_collider_capsule_layout(
|
|
layout: UILayout,
|
|
armature_data: Armature,
|
|
collider: SpringBone1ColliderPropertyGroup,
|
|
) -> None:
|
|
layout.prop_search(collider.node, "bone_name", armature_data, "bones")
|
|
layout.prop(collider, "ui_collider_type")
|
|
bpy_object = collider.bpy_object
|
|
if bpy_object:
|
|
layout.prop(bpy_object, "name", icon="MESH_UVSPHERE", text="Head")
|
|
layout.prop(collider.shape.capsule, "offset", text="")
|
|
layout.separator(factor=0.5)
|
|
layout.prop(collider.shape.capsule, "radius", slider=True)
|
|
layout.separator(factor=0.5)
|
|
if bpy_object and bpy_object.children:
|
|
layout.prop(
|
|
bpy_object.children[0],
|
|
"name",
|
|
icon="MESH_UVSPHERE",
|
|
text="Tail",
|
|
)
|
|
layout.prop(collider.shape.capsule, "tail", text="")
|
|
layout.prop(collider.extensions.vrmc_spring_bone_extended_collider, "enabled")
|
|
|
|
|
|
def draw_spring_bone1_collider_extended_fallback_layout(
|
|
layout: UILayout,
|
|
_armature_data: Armature,
|
|
collider: SpringBone1ColliderPropertyGroup,
|
|
) -> None:
|
|
extended = collider.extensions.vrmc_spring_bone_extended_collider
|
|
layout.prop(extended, "automatic_fallback_generation")
|
|
if extended.automatic_fallback_generation:
|
|
return
|
|
layout.prop(collider, "shape_type", text="Fallback")
|
|
if collider.shape_type == collider.SHAPE_TYPE_SPHERE.identifier:
|
|
layout.prop(collider.shape.sphere, "fallback_offset")
|
|
layout.separator(factor=0.5)
|
|
layout.prop(collider.shape.sphere, "fallback_radius", slider=True)
|
|
elif collider.shape_type == collider.SHAPE_TYPE_CAPSULE.identifier:
|
|
layout.prop(collider.shape.capsule, "fallback_offset")
|
|
layout.separator(factor=0.5)
|
|
layout.prop(collider.shape.capsule, "fallback_radius", slider=True)
|
|
layout.separator(factor=0.5)
|
|
layout.prop(collider.shape.capsule, "fallback_tail")
|
|
|
|
|
|
def draw_spring_bone1_collider_extended_sphere_layout(
|
|
layout: UILayout,
|
|
armature_data: Armature,
|
|
collider: SpringBone1ColliderPropertyGroup,
|
|
) -> None:
|
|
extended = collider.extensions.vrmc_spring_bone_extended_collider
|
|
layout.prop_search(collider.node, "bone_name", armature_data, "bones")
|
|
layout.prop(collider, "ui_collider_type")
|
|
bpy_object = collider.bpy_object
|
|
if bpy_object:
|
|
layout.prop(bpy_object, "name", icon="MESH_UVSPHERE", text="Offset")
|
|
layout.prop(extended.shape.sphere, "offset", text="")
|
|
layout.separator(factor=0.5)
|
|
layout.prop(extended.shape.sphere, "radius", slider=True)
|
|
if not (extended.enabled and extended.shape.sphere.inside):
|
|
layout.prop(extended, "enabled")
|
|
draw_spring_bone1_collider_extended_fallback_layout(
|
|
layout,
|
|
armature_data,
|
|
collider,
|
|
)
|
|
|
|
|
|
def draw_spring_bone1_collider_extended_capsule_layout(
|
|
layout: UILayout,
|
|
armature_data: Armature,
|
|
collider: SpringBone1ColliderPropertyGroup,
|
|
) -> None:
|
|
extended = collider.extensions.vrmc_spring_bone_extended_collider
|
|
layout.prop_search(collider.node, "bone_name", armature_data, "bones")
|
|
layout.prop(collider, "ui_collider_type")
|
|
bpy_object = collider.bpy_object
|
|
if bpy_object:
|
|
layout.prop(bpy_object, "name", icon="MESH_UVSPHERE", text="Head")
|
|
layout.prop(extended.shape.capsule, "offset", text="")
|
|
layout.separator(factor=0.5)
|
|
layout.prop(extended.shape.capsule, "radius", slider=True)
|
|
layout.separator(factor=0.5)
|
|
if bpy_object and bpy_object.children:
|
|
layout.prop(
|
|
bpy_object.children[0],
|
|
"name",
|
|
icon="MESH_UVSPHERE",
|
|
text="Tail",
|
|
)
|
|
layout.prop(extended.shape.capsule, "tail", text="")
|
|
if not (extended.enabled and extended.shape.capsule.inside):
|
|
layout.prop(extended, "enabled")
|
|
draw_spring_bone1_collider_extended_fallback_layout(
|
|
layout,
|
|
armature_data,
|
|
collider,
|
|
)
|
|
|
|
|
|
def draw_spring_bone1_collider_extended_plane_layout(
|
|
layout: UILayout,
|
|
armature_data: Armature,
|
|
collider: SpringBone1ColliderPropertyGroup,
|
|
) -> None:
|
|
extended = collider.extensions.vrmc_spring_bone_extended_collider
|
|
layout.prop_search(collider.node, "bone_name", armature_data, "bones")
|
|
layout.prop(collider, "ui_collider_type")
|
|
bpy_object = collider.bpy_object
|
|
if bpy_object:
|
|
layout.prop(bpy_object, "name", icon="MESH_UVSPHERE", text="Offset")
|
|
layout.prop(extended.shape.plane, "offset", text="")
|
|
layout.separator(factor=0.5)
|
|
layout.prop(extended.shape.plane, "normal")
|
|
|
|
draw_spring_bone1_collider_extended_fallback_layout(
|
|
layout,
|
|
armature_data,
|
|
collider,
|
|
)
|
|
|
|
|
|
def draw_spring_bone1_collider_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
collider: SpringBone1ColliderPropertyGroup,
|
|
) -> None:
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
extended = collider.extensions.vrmc_spring_bone_extended_collider
|
|
if extended.enabled:
|
|
if extended.shape_type == extended.SHAPE_TYPE_EXTENDED_PLANE.identifier:
|
|
draw_spring_bone1_collider_extended_plane_layout(
|
|
layout, armature_data, collider
|
|
)
|
|
elif extended.shape_type == extended.SHAPE_TYPE_EXTENDED_SPHERE.identifier:
|
|
draw_spring_bone1_collider_extended_sphere_layout(
|
|
layout, armature_data, collider
|
|
)
|
|
elif extended.shape_type == extended.SHAPE_TYPE_EXTENDED_CAPSULE.identifier:
|
|
draw_spring_bone1_collider_extended_capsule_layout(
|
|
layout, armature_data, collider
|
|
)
|
|
elif collider.shape_type == collider.SHAPE_TYPE_SPHERE.identifier:
|
|
draw_spring_bone1_collider_sphere_layout(layout, armature_data, collider)
|
|
elif collider.shape_type == collider.SHAPE_TYPE_CAPSULE.identifier:
|
|
draw_spring_bone1_collider_capsule_layout(layout, armature_data, collider)
|
|
layout.separator(factor=0.5)
|
|
|
|
|
|
def draw_spring_bone1_spring_bone_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
spring_bone: SpringBone1SpringBonePropertyGroup,
|
|
) -> None:
|
|
defer_migrate(armature.name)
|
|
|
|
layout.prop(spring_bone, "enable_animation")
|
|
# layout.operator(ops.VRM_OT_reset_spring_bone1_animation_state.bl_idname)
|
|
|
|
draw_spring_bone1_colliders_layout(armature, layout, spring_bone)
|
|
draw_spring_bone1_collider_groups_layout(armature, layout, spring_bone)
|
|
draw_spring_bone1_springs_layout(armature, layout, spring_bone)
|
|
|
|
|
|
def draw_spring_bone1_colliders_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
spring_bone: SpringBone1SpringBonePropertyGroup,
|
|
) -> None:
|
|
colliders_box = layout.box()
|
|
colliders_header_row = colliders_box.row()
|
|
colliders_header_row.alignment = "LEFT"
|
|
colliders_header_row.prop(
|
|
spring_bone,
|
|
"show_expanded_colliders",
|
|
icon="TRIA_DOWN" if spring_bone.show_expanded_colliders else "TRIA_RIGHT",
|
|
emboss=False,
|
|
)
|
|
|
|
if not spring_bone.show_expanded_colliders:
|
|
return
|
|
|
|
(
|
|
collider_collection_ops,
|
|
collider_collection_item_ops,
|
|
collider_index,
|
|
collider,
|
|
_,
|
|
) = draw_template_list(
|
|
colliders_box,
|
|
VRM_UL_spring_bone1_collider,
|
|
spring_bone,
|
|
"colliders",
|
|
"active_collider_index",
|
|
ops.VRM_OT_add_spring_bone1_collider,
|
|
ops.VRM_OT_remove_spring_bone1_collider,
|
|
ops.VRM_OT_move_up_spring_bone1_collider,
|
|
ops.VRM_OT_move_down_spring_bone1_collider,
|
|
)
|
|
|
|
for collider_collection_op in collider_collection_ops:
|
|
collider_collection_op.armature_object_name = armature.name
|
|
|
|
for collider_collection_item_op in collider_collection_item_ops:
|
|
collider_collection_item_op.collider_index = collider_index
|
|
|
|
if not isinstance(collider, SpringBone1ColliderPropertyGroup):
|
|
return
|
|
|
|
draw_spring_bone1_collider_layout(armature, colliders_box.column(), collider)
|
|
|
|
|
|
def draw_spring_bone1_collider_groups_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
spring_bone: SpringBone1SpringBonePropertyGroup,
|
|
) -> None:
|
|
collider_groups_box = layout.box()
|
|
collider_groups_header_row = collider_groups_box.row()
|
|
collider_groups_header_row.alignment = "LEFT"
|
|
collider_groups_header_row.prop(
|
|
spring_bone,
|
|
"show_expanded_collider_groups",
|
|
icon="TRIA_DOWN" if spring_bone.show_expanded_collider_groups else "TRIA_RIGHT",
|
|
emboss=False,
|
|
)
|
|
|
|
if not spring_bone.show_expanded_collider_groups:
|
|
return
|
|
|
|
(
|
|
collider_group_collection_ops,
|
|
collider_group_collection_item_ops,
|
|
collider_group_index,
|
|
collider_group,
|
|
_,
|
|
) = draw_template_list(
|
|
collider_groups_box,
|
|
VRM_UL_spring_bone1_collider_group,
|
|
spring_bone,
|
|
"collider_groups",
|
|
"active_collider_group_index",
|
|
ops.VRM_OT_add_spring_bone1_collider_group,
|
|
ops.VRM_OT_remove_spring_bone1_collider_group,
|
|
ops.VRM_OT_move_up_spring_bone1_collider_group,
|
|
ops.VRM_OT_move_down_spring_bone1_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, SpringBone1ColliderGroupPropertyGroup):
|
|
return
|
|
|
|
collider_group_column = collider_groups_box.column()
|
|
collider_group_column.prop(
|
|
collider_group,
|
|
"vrm_name",
|
|
)
|
|
|
|
colliders_box = collider_group_column.box()
|
|
colliders_column = colliders_box.column()
|
|
colliders_column.label(text="Colliders:")
|
|
|
|
(
|
|
collider_collection_ops,
|
|
collider_collection_item_ops,
|
|
collider_index,
|
|
_collider,
|
|
_,
|
|
) = draw_template_list(
|
|
colliders_column,
|
|
VRM_UL_spring_bone1_collider_group_collider,
|
|
collider_group,
|
|
"colliders",
|
|
"active_collider_index",
|
|
ops.VRM_OT_add_spring_bone1_collider_group_collider,
|
|
ops.VRM_OT_remove_spring_bone1_collider_group_collider,
|
|
ops.VRM_OT_move_up_spring_bone1_collider_group_collider,
|
|
ops.VRM_OT_move_down_spring_bone1_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.collider_index = collider_index
|
|
|
|
|
|
def draw_spring_bone1_springs_layout(
|
|
armature: Object,
|
|
layout: UILayout,
|
|
spring_bone: SpringBone1SpringBonePropertyGroup,
|
|
) -> None:
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
|
|
springs_box = layout.box()
|
|
springs_header_row = springs_box.row()
|
|
springs_header_row.alignment = "LEFT"
|
|
springs_header_row.prop(
|
|
spring_bone,
|
|
"show_expanded_springs",
|
|
icon="TRIA_DOWN" if spring_bone.show_expanded_springs else "TRIA_RIGHT",
|
|
emboss=False,
|
|
)
|
|
|
|
if not spring_bone.show_expanded_springs:
|
|
return
|
|
|
|
(
|
|
spring_collection_ops,
|
|
spring_collection_item_ops,
|
|
spring_index,
|
|
spring,
|
|
_,
|
|
) = draw_template_list(
|
|
springs_box,
|
|
VRM_UL_spring_bone1_spring,
|
|
spring_bone,
|
|
"springs",
|
|
"active_spring_index",
|
|
ops.VRM_OT_add_spring_bone1_spring,
|
|
ops.VRM_OT_remove_spring_bone1_spring,
|
|
ops.VRM_OT_move_up_spring_bone1_spring,
|
|
ops.VRM_OT_move_down_spring_bone1_spring,
|
|
)
|
|
|
|
for spring_collection_op in spring_collection_ops:
|
|
spring_collection_op.armature_object_name = armature.name
|
|
|
|
for spring_collection_item_op in spring_collection_item_ops:
|
|
spring_collection_item_op.spring_index = spring_index
|
|
|
|
if not isinstance(spring, SpringBone1SpringPropertyGroup):
|
|
return
|
|
|
|
spring_column = springs_box.column()
|
|
spring_column.prop(
|
|
spring,
|
|
"vrm_name",
|
|
)
|
|
spring_column.prop_search(
|
|
spring.center,
|
|
"bone_name",
|
|
armature_data,
|
|
"bones",
|
|
text="Center",
|
|
)
|
|
|
|
joints_box = spring_column.box()
|
|
joints_column = joints_box.column()
|
|
joints_column.label(text="Joints:")
|
|
|
|
(
|
|
joint_collection_ops,
|
|
joint_collection_item_ops,
|
|
joint_index,
|
|
joint,
|
|
(add_joint_op, _, _, _),
|
|
) = draw_template_list(
|
|
joints_column,
|
|
VRM_UL_spring_bone1_joint,
|
|
spring,
|
|
"joints",
|
|
"active_joint_index",
|
|
ops.VRM_OT_add_spring_bone1_joint,
|
|
ops.VRM_OT_remove_spring_bone1_joint,
|
|
ops.VRM_OT_move_up_spring_bone1_joint,
|
|
ops.VRM_OT_move_down_spring_bone1_joint,
|
|
)
|
|
|
|
for joint_collection_op in joint_collection_ops:
|
|
joint_collection_op.armature_object_name = armature.name
|
|
joint_collection_op.spring_index = spring_index
|
|
|
|
for joint_collection_item_op in joint_collection_item_ops:
|
|
joint_collection_item_op.joint_index = joint_index
|
|
|
|
add_joint_op.guess_properties = True
|
|
|
|
if isinstance(joint, SpringBone1JointPropertyGroup):
|
|
joints_column.prop_search(joint.node, "bone_name", armature_data, "bones")
|
|
joints_column.prop(joint, "stiffness", slider=True)
|
|
joints_column.prop(joint, "gravity_power", slider=True)
|
|
joints_column.prop(joint, "gravity_dir")
|
|
joints_column.prop(joint, "drag_force", slider=True)
|
|
joints_column.prop(joint, "hit_radius", slider=True)
|
|
|
|
collider_groups_box = spring_column.box()
|
|
collider_groups_column = collider_groups_box.column()
|
|
collider_groups_column.label(text="Collider Groups:")
|
|
|
|
(
|
|
collider_group_collection_ops,
|
|
collider_group_collection_item_ops,
|
|
collider_group_index,
|
|
_collider_group,
|
|
_,
|
|
) = draw_template_list(
|
|
collider_groups_column,
|
|
VRM_UL_spring_bone1_spring_collider_group,
|
|
spring,
|
|
"collider_groups",
|
|
"active_collider_group_index",
|
|
ops.VRM_OT_add_spring_bone1_spring_collider_group,
|
|
ops.VRM_OT_remove_spring_bone1_spring_collider_group,
|
|
ops.VRM_OT_move_up_spring_bone1_spring_collider_group,
|
|
ops.VRM_OT_move_down_spring_bone1_spring_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.spring_index = spring_index
|
|
|
|
for collider_group_collection_item_op in collider_group_collection_item_ops:
|
|
collider_group_collection_item_op.collider_group_index = collider_group_index
|
|
|
|
|
|
class VRM_PT_spring_bone1_armature_object_property(Panel):
|
|
bl_idname = "VRM_PT_vrm1_spring_bone_armature_object_property"
|
|
bl_label = "Spring Bone"
|
|
bl_translation_context = "VRM"
|
|
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_vrm1_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
|
|
draw_spring_bone1_spring_bone_layout(
|
|
active_object,
|
|
self.layout,
|
|
get_armature_extension(armature_data).spring_bone1,
|
|
)
|
|
|
|
|
|
class VRM_PT_spring_bone1_ui(Panel):
|
|
bl_idname = "VRM_PT_vrm1_spring_bone_ui"
|
|
bl_label = "Spring Bone"
|
|
bl_translation_context = "VRM"
|
|
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_vrm1(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_spring_bone1_spring_bone_layout(
|
|
armature,
|
|
self.layout,
|
|
get_armature_extension(armature_data).spring_bone1,
|
|
)
|
|
|
|
|
|
class VRM_PT_spring_bone1_collider_property(Panel):
|
|
bl_idname = "VRM_PT_spring_bone1_collider_property"
|
|
bl_label = "VRM Spring Bone Collider"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "data"
|
|
|
|
@classmethod
|
|
def active_armature_and_collider(
|
|
cls, context: Context
|
|
) -> Optional[tuple[Object, SpringBone1ColliderPropertyGroup]]:
|
|
active_object = context.active_object
|
|
if not active_object:
|
|
return None
|
|
if active_object.type != "EMPTY":
|
|
return None
|
|
parent = active_object.parent
|
|
if not parent:
|
|
return None
|
|
|
|
if active_object.parent_type == "BONE":
|
|
collider_object = context.active_object
|
|
elif active_object.parent_type == "OBJECT":
|
|
if parent.type == "ARMATURE":
|
|
collider_object = active_object
|
|
elif parent.parent_type == "BONE" or (
|
|
parent.parent_type == "OBJECT"
|
|
and parent.parent
|
|
and parent.parent.type == "ARMATURE"
|
|
):
|
|
collider_object = parent
|
|
else:
|
|
return None
|
|
else:
|
|
return None
|
|
for obj in context.blend_data.objects:
|
|
if obj.type != "ARMATURE":
|
|
continue
|
|
armature_data = obj.data
|
|
if not isinstance(armature_data, Armature):
|
|
continue
|
|
for collider in get_armature_extension(
|
|
armature_data
|
|
).spring_bone1.colliders:
|
|
if collider.bpy_object == collider_object:
|
|
return (obj, collider)
|
|
return None
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return cls.active_armature_and_collider(context) is not None
|
|
|
|
def draw(self, context: Context) -> None:
|
|
armature_and_collider = self.active_armature_and_collider(context)
|
|
if armature_and_collider is None:
|
|
return
|
|
armature, collider = armature_and_collider
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
if get_armature_extension(armature_data).is_vrm1():
|
|
draw_spring_bone1_collider_layout(armature, self.layout.column(), collider)
|
|
return
|
|
|
|
self.layout.label(text="This is a VRM 1.0 Spring Bone Collider", icon="INFO")
|