- 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
1118 lines
41 KiB
Python
1118 lines
41 KiB
Python
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
import functools
|
|
from collections.abc import Sequence
|
|
from sys import float_info
|
|
from typing import TYPE_CHECKING, ClassVar, Optional
|
|
|
|
import bpy
|
|
from bpy.props import (
|
|
BoolProperty,
|
|
CollectionProperty,
|
|
EnumProperty,
|
|
FloatProperty,
|
|
FloatVectorProperty,
|
|
IntProperty,
|
|
PointerProperty,
|
|
StringProperty,
|
|
)
|
|
from bpy.types import (
|
|
Action,
|
|
Armature,
|
|
Context,
|
|
Image,
|
|
Material,
|
|
Mesh,
|
|
Object,
|
|
PropertyGroup,
|
|
)
|
|
from mathutils import Vector
|
|
|
|
from ...common import convert
|
|
from ...common.logger import get_logger
|
|
from ...common.vrm0.human_bone import (
|
|
HumanBoneName,
|
|
HumanBoneSpecification,
|
|
HumanBoneSpecifications,
|
|
)
|
|
from ..property_group import (
|
|
BonePropertyGroup,
|
|
FloatPropertyGroup,
|
|
MeshObjectPropertyGroup,
|
|
StringPropertyGroup,
|
|
property_group_enum,
|
|
)
|
|
from ..vrm1.property_group import Vrm1HumanoidPropertyGroup
|
|
|
|
if TYPE_CHECKING:
|
|
from ..property_group import CollectionPropertyProtocol
|
|
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_Humanoid.cs#L70-L164
|
|
class Vrm0HumanoidBonePropertyGroup(PropertyGroup):
|
|
bone: StringProperty( # type: ignore[valid-type]
|
|
name="VRM Humanoid Bone Name"
|
|
)
|
|
node: PointerProperty( # type: ignore[valid-type]
|
|
name="Bone Name",
|
|
type=BonePropertyGroup,
|
|
)
|
|
use_default_values: BoolProperty( # type: ignore[valid-type]
|
|
name="Unity's HumanLimit.useDefaultValues",
|
|
default=True,
|
|
)
|
|
min: FloatVectorProperty( # type: ignore[valid-type]
|
|
size=3,
|
|
name="Unity's HumanLimit.min",
|
|
)
|
|
max: FloatVectorProperty( # type: ignore[valid-type]
|
|
size=3,
|
|
name="Unity's HumanLimit.max",
|
|
)
|
|
center: FloatVectorProperty( # type: ignore[valid-type]
|
|
size=3,
|
|
name="Unity's HumanLimit.center",
|
|
)
|
|
axis_length: FloatProperty( # type: ignore[valid-type]
|
|
name="Unity's HumanLimit.axisLength"
|
|
)
|
|
|
|
# for UI
|
|
node_candidates: CollectionProperty( # type: ignore[valid-type]
|
|
type=StringPropertyGroup
|
|
)
|
|
|
|
def update_node_candidates(
|
|
self,
|
|
armature_data: Armature,
|
|
bpy_bone_name_to_human_bone_specification: dict[str, HumanBoneSpecification],
|
|
error_bpy_bone_names: Sequence[str],
|
|
) -> bool:
|
|
human_bone_name = HumanBoneName.from_str(self.bone)
|
|
if human_bone_name is None:
|
|
logger.warning("Bone name '%s' is invalid", self.bone)
|
|
return False
|
|
target = HumanBoneSpecifications.get(human_bone_name)
|
|
new_candidates = BonePropertyGroup.find_bone_candidates(
|
|
armature_data,
|
|
target,
|
|
bpy_bone_name_to_human_bone_specification,
|
|
error_bpy_bone_names,
|
|
)
|
|
if {n.value for n in self.node_candidates} == new_candidates:
|
|
return False
|
|
|
|
self.node_candidates.clear()
|
|
# Preserving list order
|
|
for bone_name in armature_data.bones.keys():
|
|
if bone_name not in new_candidates:
|
|
continue
|
|
candidate = self.node_candidates.add()
|
|
candidate.value = bone_name
|
|
|
|
return True
|
|
|
|
def specification(self) -> HumanBoneSpecification:
|
|
name = HumanBoneName.from_str(self.bone)
|
|
if name is None:
|
|
message = f'HumanBone "{self.bone}" is invalid'
|
|
raise ValueError(message)
|
|
return HumanBoneSpecifications.get(name)
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
bone: str # type: ignore[no-redef]
|
|
node: BonePropertyGroup # type: ignore[no-redef]
|
|
use_default_values: bool # type: ignore[no-redef]
|
|
min: Sequence[float] # type: ignore[no-redef]
|
|
max: Sequence[float] # type: ignore[no-redef]
|
|
center: Sequence[float] # type: ignore[no-redef]
|
|
axis_length: float # type: ignore[no-redef]
|
|
node_candidates: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
StringPropertyGroup
|
|
]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_Humanoid.cs#L166-L195
|
|
class Vrm0HumanoidPropertyGroup(PropertyGroup):
|
|
human_bones: CollectionProperty( # type: ignore[valid-type]
|
|
name="Human Bones",
|
|
type=Vrm0HumanoidBonePropertyGroup,
|
|
)
|
|
arm_stretch: FloatProperty( # type: ignore[valid-type]
|
|
name="Arm Stretch",
|
|
default=0.05,
|
|
)
|
|
leg_stretch: FloatProperty( # type: ignore[valid-type]
|
|
name="Leg Stretch",
|
|
default=0.05,
|
|
)
|
|
upper_arm_twist: FloatProperty( # type: ignore[valid-type]
|
|
name="Upper Arm Twist",
|
|
default=0.5,
|
|
)
|
|
lower_arm_twist: FloatProperty( # type: ignore[valid-type]
|
|
name="Lower Arm Twist",
|
|
default=0.5,
|
|
)
|
|
upper_leg_twist: FloatProperty( # type: ignore[valid-type]
|
|
name="Upper Leg Twist",
|
|
default=0.5,
|
|
)
|
|
lower_leg_twist: FloatProperty( # type: ignore[valid-type]
|
|
name="Lower Leg Twist",
|
|
default=0.5,
|
|
)
|
|
feet_spacing: FloatProperty( # type: ignore[valid-type]
|
|
name="Feet Spacing",
|
|
default=0,
|
|
)
|
|
has_translation_dof: BoolProperty( # type: ignore[valid-type]
|
|
name="Has Translation DoF",
|
|
default=False,
|
|
)
|
|
|
|
POSE_AUTO_POSE = Vrm1HumanoidPropertyGroup.POSE_AUTO_POSE
|
|
POSE_REST_POSITION_POSE = Vrm1HumanoidPropertyGroup.POSE_REST_POSITION_POSE
|
|
POSE_CURRENT_POSE = Vrm1HumanoidPropertyGroup.POSE_CURRENT_POSE
|
|
POSE_CUSTOM_POSE = Vrm1HumanoidPropertyGroup.POSE_CUSTOM_POSE
|
|
|
|
pose: bpy.props.EnumProperty( # type: ignore[valid-type]
|
|
items=Vrm1HumanoidPropertyGroup.pose_enum.items(),
|
|
name="T-Pose",
|
|
description="T-Pose",
|
|
default=POSE_AUTO_POSE.identifier,
|
|
)
|
|
|
|
# for T-Pose
|
|
def update_pose_library(self, _context: Context) -> None:
|
|
self.pose_marker_name = ""
|
|
|
|
pose_library: PointerProperty( # type: ignore[valid-type]
|
|
type=Action,
|
|
name="Pose Library",
|
|
description="Pose library for T Pose",
|
|
update=update_pose_library,
|
|
)
|
|
pose_marker_name: StringProperty() # type: ignore[valid-type]
|
|
|
|
# for UI
|
|
last_bone_names_str: StringProperty() # type: ignore[valid-type]
|
|
initial_automatic_bone_assignment: BoolProperty( # type: ignore[valid-type]
|
|
default=True
|
|
)
|
|
|
|
def all_required_bones_are_assigned(self) -> bool:
|
|
for name in HumanBoneSpecifications.required_names:
|
|
for human_bone in self.human_bones:
|
|
if human_bone.bone != name:
|
|
continue
|
|
if human_bone.node.bone_name not in human_bone.node_candidates:
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def defer_update_all_node_candidates(
|
|
armature_data_name: str,
|
|
*,
|
|
force: bool = False,
|
|
) -> None:
|
|
bpy.app.timers.register(
|
|
functools.partial(
|
|
Vrm0HumanoidPropertyGroup.update_all_node_candidates_timer_callback,
|
|
armature_data_name,
|
|
force=force,
|
|
)
|
|
)
|
|
|
|
@staticmethod
|
|
def update_all_node_candidates_timer_callback(
|
|
armature_object_name: str, *, force: bool = False
|
|
) -> None:
|
|
"""Wrap update_all_node_candidates() to match bpy.app.timers.register."""
|
|
context = bpy.context # Context cannot cross frames so we need to get a new one
|
|
Vrm0HumanoidPropertyGroup.update_all_node_candidates(
|
|
context, armature_object_name, force=force
|
|
)
|
|
|
|
@staticmethod
|
|
def update_all_node_candidates(
|
|
context: Optional[Context],
|
|
armature_data_name: str,
|
|
*,
|
|
force: bool = False,
|
|
) -> None:
|
|
from ..extension import get_armature_extension
|
|
|
|
if context is None:
|
|
context = bpy.context
|
|
|
|
armature_data = context.blend_data.armatures.get(armature_data_name)
|
|
if not armature_data:
|
|
return
|
|
|
|
humanoid = get_armature_extension(armature_data).vrm0.humanoid
|
|
|
|
bone_names_str = "\n".join(
|
|
bone.name + "\n" + (parent.name if (parent := bone.parent) else "")
|
|
for bone in sorted(armature_data.bones.values(), key=lambda b: b.name)
|
|
)
|
|
|
|
if not force and humanoid.last_bone_names_str == bone_names_str:
|
|
return
|
|
humanoid.last_bone_names_str = bone_names_str
|
|
|
|
BonePropertyGroup.update_all_vrm0_node_candidates(armature_data)
|
|
|
|
@staticmethod
|
|
def fixup_human_bones(obj: Object) -> None:
|
|
from ..extension import get_armature_extension
|
|
|
|
armature_data = obj.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
|
|
humanoid = get_armature_extension(armature_data).vrm0.humanoid
|
|
|
|
# Add bone maps that don't exist
|
|
refresh = False
|
|
for human_bone_name in HumanBoneSpecifications.all_names:
|
|
if any(
|
|
human_bone.bone == human_bone_name
|
|
for human_bone in humanoid.human_bones
|
|
):
|
|
continue
|
|
human_bone = humanoid.human_bones.add()
|
|
human_bone.bone = human_bone_name
|
|
refresh = True
|
|
|
|
# Remove duplicate bone maps
|
|
fixup = True
|
|
while fixup:
|
|
fixup = False
|
|
found_bones: list[str] = []
|
|
for i, human_bone in enumerate(list(humanoid.human_bones)):
|
|
if (
|
|
human_bone.bone in HumanBoneSpecifications.all_names
|
|
and human_bone.bone not in found_bones
|
|
):
|
|
found_bones.append(human_bone.bone)
|
|
continue
|
|
humanoid.human_bones.remove(i)
|
|
refresh = True
|
|
fixup = True
|
|
break
|
|
|
|
# If multiple bone maps have the same Blender bone assigned, remove one of them
|
|
fixup = True
|
|
while fixup:
|
|
fixup = False
|
|
found_node_bone_names: list[str] = []
|
|
for human_bone in humanoid.human_bones:
|
|
if not human_bone.node.bone_name:
|
|
continue
|
|
if human_bone.node.bone_name not in found_node_bone_names:
|
|
found_node_bone_names.append(human_bone.node.bone_name)
|
|
continue
|
|
human_bone.node.bone_name = ""
|
|
refresh = True
|
|
fixup = True
|
|
break
|
|
|
|
if not refresh:
|
|
return
|
|
|
|
secondary_animation = get_armature_extension(
|
|
armature_data
|
|
).vrm0.secondary_animation
|
|
for collider_group in secondary_animation.collider_groups:
|
|
collider_group.refresh(obj)
|
|
for bone_group in secondary_animation.bone_groups:
|
|
bone_group.refresh(obj)
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
human_bones: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
Vrm0HumanoidBonePropertyGroup
|
|
]
|
|
arm_stretch: float # type: ignore[no-redef]
|
|
leg_stretch: float # type: ignore[no-redef]
|
|
upper_arm_twist: float # type: ignore[no-redef]
|
|
lower_arm_twist: float # type: ignore[no-redef]
|
|
upper_leg_twist: float # type: ignore[no-redef]
|
|
lower_leg_twist: float # type: ignore[no-redef]
|
|
feet_spacing: float # type: ignore[no-redef]
|
|
has_translation_dof: bool # type: ignore[no-redef]
|
|
pose: str # type: ignore[no-redef]
|
|
pose_library: Optional[Action] # type: ignore[no-redef]
|
|
pose_marker_name: str # type: ignore[no-redef]
|
|
last_bone_names_str: str # type: ignore[no-redef]
|
|
initial_automatic_bone_assignment: bool # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_FirstPerson.cs#L10-L22
|
|
class Vrm0DegreeMapPropertyGroup(PropertyGroup):
|
|
curve: FloatVectorProperty( # type: ignore[valid-type]
|
|
size=8,
|
|
name="Curve",
|
|
default=(0, 0, 0, 1, 1, 1, 1, 0),
|
|
)
|
|
x_range: FloatProperty( # type: ignore[valid-type]
|
|
name="X Range",
|
|
default=90,
|
|
)
|
|
y_range: FloatProperty( # type: ignore[valid-type]
|
|
name="Y Range",
|
|
default=10,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
curve: Sequence[float] # type: ignore[no-redef]
|
|
x_range: float # type: ignore[no-redef]
|
|
y_range: float # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_FirstPerson.cs#L32-L41
|
|
class Vrm0MeshAnnotationPropertyGroup(PropertyGroup):
|
|
mesh: PointerProperty( # type: ignore[valid-type]
|
|
name="Mesh",
|
|
type=MeshObjectPropertyGroup,
|
|
description="Mesh on restrict render in the first person camera",
|
|
)
|
|
|
|
first_person_flag_enum, *__first_person_flags = property_group_enum(
|
|
("Auto", "Auto", "Auto restrict render", "NONE", 0),
|
|
(
|
|
"FirstPersonOnly",
|
|
"First-Person Only",
|
|
"(Maybe needless) Restrict render in the third person camera",
|
|
"NONE",
|
|
1,
|
|
),
|
|
(
|
|
"ThirdPersonOnly",
|
|
"Third-Person Only",
|
|
"Restrict render in the first person camera for face, hairs or hat",
|
|
"NONE",
|
|
2,
|
|
),
|
|
("Both", "Both", "No restrict render for body, arms or legs", "NONE", 3),
|
|
)
|
|
|
|
first_person_flag: EnumProperty( # type: ignore[valid-type]
|
|
items=first_person_flag_enum.items(),
|
|
name="First Person Flag",
|
|
description="Restrict render in the first person camera",
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
mesh: MeshObjectPropertyGroup # type: ignore[no-redef]
|
|
first_person_flag: str # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_FirstPerson.cs#L50-L91
|
|
class Vrm0FirstPersonPropertyGroup(PropertyGroup):
|
|
first_person_bone: PointerProperty( # type: ignore[valid-type]
|
|
name="First Person Bone",
|
|
type=BonePropertyGroup,
|
|
description="Bone to follow the first person camera",
|
|
)
|
|
first_person_bone_offset: FloatVectorProperty( # type: ignore[valid-type]
|
|
size=3,
|
|
name="First Person Bone Offset",
|
|
description=(
|
|
"Offset from the first person bone to follow the first person camera"
|
|
),
|
|
subtype="TRANSLATION",
|
|
unit="LENGTH",
|
|
default=(0, 0, 0),
|
|
)
|
|
mesh_annotations: CollectionProperty( # type: ignore[valid-type]
|
|
name="Mesh Annotations",
|
|
type=Vrm0MeshAnnotationPropertyGroup,
|
|
)
|
|
look_at_type_name_enum, *__look_at_types = property_group_enum(
|
|
("Bone", "Bone", "Use bones to eye movement", "BONE_DATA", 0),
|
|
(
|
|
"BlendShape",
|
|
"Blend Shape",
|
|
"Use blend Shapes of VRM Blend Shape Proxy to eye movement.",
|
|
"SHAPEKEY_DATA",
|
|
1,
|
|
),
|
|
)
|
|
look_at_type_name: EnumProperty( # type: ignore[valid-type]
|
|
items=look_at_type_name_enum.items(),
|
|
name="Look At Type Name",
|
|
description="How to eye movement",
|
|
)
|
|
look_at_horizontal_inner: PointerProperty( # type: ignore[valid-type]
|
|
type=Vrm0DegreeMapPropertyGroup,
|
|
name="Look At Horizontal Inner",
|
|
)
|
|
look_at_horizontal_outer: PointerProperty( # type: ignore[valid-type]
|
|
type=Vrm0DegreeMapPropertyGroup,
|
|
name="Look At Horizontal Outer",
|
|
)
|
|
look_at_vertical_down: PointerProperty( # type: ignore[valid-type]
|
|
type=Vrm0DegreeMapPropertyGroup,
|
|
name="Look At Vertical Down",
|
|
)
|
|
look_at_vertical_up: PointerProperty( # type: ignore[valid-type]
|
|
type=Vrm0DegreeMapPropertyGroup,
|
|
name="lookAt Vertical Up",
|
|
)
|
|
|
|
# for UI
|
|
active_mesh_annotation_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
first_person_bone: BonePropertyGroup # type: ignore[no-redef]
|
|
first_person_bone_offset: Sequence[float] # type: ignore[no-redef]
|
|
mesh_annotations: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
Vrm0MeshAnnotationPropertyGroup
|
|
]
|
|
look_at_type_name: str # type: ignore[no-redef]
|
|
look_at_horizontal_inner: Vrm0DegreeMapPropertyGroup # type: ignore[no-redef]
|
|
look_at_horizontal_outer: Vrm0DegreeMapPropertyGroup # type: ignore[no-redef]
|
|
look_at_vertical_down: Vrm0DegreeMapPropertyGroup # type: ignore[no-redef]
|
|
look_at_vertical_up: Vrm0DegreeMapPropertyGroup # type: ignore[no-redef]
|
|
active_mesh_annotation_index: int # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_BlendShape.cs#L18-L30
|
|
class Vrm0BlendShapeBindPropertyGroup(PropertyGroup):
|
|
mesh: PointerProperty( # type: ignore[valid-type]
|
|
name="Mesh",
|
|
type=MeshObjectPropertyGroup,
|
|
)
|
|
index: StringProperty( # type: ignore[valid-type]
|
|
name="Index"
|
|
)
|
|
weight: FloatProperty( # type: ignore[valid-type]
|
|
name="Weight",
|
|
min=0,
|
|
default=1,
|
|
max=1,
|
|
subtype="FACTOR",
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
mesh: MeshObjectPropertyGroup # type: ignore[no-redef]
|
|
index: str # type: ignore[no-redef]
|
|
weight: float # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_BlendShape.cs#L9-L16
|
|
class Vrm0MaterialValueBindPropertyGroup(PropertyGroup):
|
|
material: PointerProperty( # type: ignore[valid-type]
|
|
name="Material",
|
|
type=Material,
|
|
)
|
|
|
|
# Use StringProperty instead of EnumProperty for property_name.
|
|
# This is necessary to allow arbitrary values to be entered.
|
|
property_name: StringProperty( # type: ignore[valid-type]
|
|
name="Property Name"
|
|
)
|
|
|
|
target_value: CollectionProperty( # type: ignore[valid-type]
|
|
name="Target Value",
|
|
type=FloatPropertyGroup,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
material: Optional[Material] # type: ignore[no-redef]
|
|
property_name: str # type: ignore[no-redef]
|
|
target_value: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
FloatPropertyGroup
|
|
]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_BlendShape.cs#L62-L99
|
|
class Vrm0BlendShapeGroupPropertyGroup(PropertyGroup):
|
|
name: StringProperty( # type: ignore[valid-type]
|
|
name="Name",
|
|
description="Name of the blendshape group",
|
|
)
|
|
|
|
preset_name_enum, (PRESET_NAME_UNKNOWN, *__preset_names) = property_group_enum(
|
|
("unknown", "Unknown", "", "SHAPEKEY_DATA", 0),
|
|
("neutral", "Neutral", "", "VIEW_ORTHO", 1),
|
|
("a", "A", "", "EVENT_A", 2),
|
|
("i", "I", "", "EVENT_I", 3),
|
|
("u", "U", "", "EVENT_U", 4),
|
|
("e", "E", "", "EVENT_E", 5),
|
|
("o", "O", "", "EVENT_O", 6),
|
|
("blink", "Blink", "", "HIDE_ON", 7),
|
|
("joy", "Joy", "", "HEART", 8),
|
|
("angry", "Angry", "", "ORPHAN_DATA", 9),
|
|
("sorrow", "Sorrow", "", "MOD_FLUIDSIM", 10),
|
|
("fun", "Fun", "", "LIGHT_SUN", 11),
|
|
("lookup", "Look Up", "", "ANCHOR_TOP", 12),
|
|
("lookdown", "Look Down", "", "ANCHOR_BOTTOM", 13),
|
|
("lookleft", "Look Left", "", "ANCHOR_RIGHT", 14),
|
|
("lookright", "Look Right", "", "ANCHOR_LEFT", 15),
|
|
("blink_l", "Blink_L", "", "HIDE_ON", 16),
|
|
("blink_r", "Blink_R", "", "HIDE_ON", 17),
|
|
)
|
|
|
|
preset_name: EnumProperty( # type: ignore[valid-type]
|
|
items=preset_name_enum.items(),
|
|
name="Preset",
|
|
description="Preset name in VRM avatar",
|
|
)
|
|
binds: CollectionProperty( # type: ignore[valid-type]
|
|
type=Vrm0BlendShapeBindPropertyGroup,
|
|
name="Binds",
|
|
)
|
|
material_values: CollectionProperty( # type: ignore[valid-type]
|
|
type=Vrm0MaterialValueBindPropertyGroup,
|
|
name="Material Values",
|
|
)
|
|
is_binary: BoolProperty( # type: ignore[valid-type]
|
|
name="Is Binary",
|
|
description="Use binary change in the blendshape group",
|
|
)
|
|
|
|
# for UI
|
|
active_bind_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
active_material_value_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
|
|
# During animation playback, shape key values can only be changed
|
|
# in frame_change_pre/frame_change_post, so we store the changed values here
|
|
frame_change_post_shape_key_updates: ClassVar[dict[tuple[str, str], float]] = {}
|
|
|
|
def get_preview(self) -> float:
|
|
value = self.get("preview")
|
|
if isinstance(value, (float, int)):
|
|
return float(value)
|
|
return 0.0
|
|
|
|
def set_preview(self, value_obj: object) -> None:
|
|
context = bpy.context
|
|
|
|
value = convert.float_or_none(value_obj)
|
|
if value is None:
|
|
return
|
|
|
|
current_value = convert.float_or_none(self.get("preview"))
|
|
if (
|
|
current_value is not None
|
|
and abs(current_value - value) < float_info.epsilon
|
|
):
|
|
return
|
|
|
|
self["preview"] = value
|
|
|
|
for bind in self.binds:
|
|
mesh_object = context.blend_data.objects.get(bind.mesh.mesh_object_name)
|
|
if not mesh_object or mesh_object.type != "MESH":
|
|
continue
|
|
mesh = mesh_object.data
|
|
if not isinstance(mesh, Mesh):
|
|
continue
|
|
mesh_shape_keys = mesh.shape_keys
|
|
if not mesh_shape_keys:
|
|
continue
|
|
shape_key = context.blend_data.shape_keys.get(mesh_shape_keys.name)
|
|
if not shape_key:
|
|
continue
|
|
key_blocks = shape_key.key_blocks
|
|
if not key_blocks:
|
|
continue
|
|
if bind.index not in key_blocks:
|
|
continue
|
|
if self.is_binary:
|
|
# https://github.com/vrm-c/UniVRM/blob/38ccb92300c9ab41c72eb3d5b8dc8ce664a659d5/Assets/VRM/Runtime/BlendShape/BlendShapeMerger.cs#L89-L92
|
|
preview = 0.0 if self.preview < 0.5 else 1.0
|
|
else:
|
|
preview = self.preview
|
|
key_block_value = bind.weight * preview # Lerp 0.0 * (1 - a) + weight * a
|
|
key_blocks[bind.index].value = key_block_value
|
|
Vrm0BlendShapeGroupPropertyGroup.frame_change_post_shape_key_updates[
|
|
(shape_key.name, bind.index)
|
|
] = key_block_value
|
|
|
|
preview: FloatProperty( # type: ignore[valid-type]
|
|
name="Blend Shape Proxy",
|
|
min=0,
|
|
max=1,
|
|
subtype="FACTOR",
|
|
get=get_preview,
|
|
set=set_preview,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
name: str # type: ignore[no-redef]
|
|
preset_name: str # type: ignore[no-redef]
|
|
binds: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
Vrm0BlendShapeBindPropertyGroup
|
|
]
|
|
material_values: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
Vrm0MaterialValueBindPropertyGroup
|
|
]
|
|
is_binary: bool # type: ignore[no-redef]
|
|
active_bind_index: int # type: ignore[no-redef]
|
|
active_material_value_index: int # type: ignore[no-redef]
|
|
preview: float # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_SecondaryAnimation.cs#L10-L18
|
|
class Vrm0SecondaryAnimationColliderPropertyGroup(PropertyGroup):
|
|
bpy_object: PointerProperty( # type: ignore[valid-type]
|
|
type=Object
|
|
)
|
|
|
|
def refresh(self, armature: Object, bone_name: str) -> None:
|
|
if not self.bpy_object or not self.bpy_object.name:
|
|
return
|
|
|
|
if self.bpy_object.parent != armature:
|
|
self.bpy_object.parent = armature
|
|
if self.bpy_object.empty_display_type != "SPHERE":
|
|
self.bpy_object.empty_display_type = "SPHERE"
|
|
|
|
if bone_name:
|
|
if self.bpy_object.parent_type != "BONE":
|
|
self.bpy_object.parent_type = "BONE"
|
|
if self.bpy_object.parent_bone != bone_name:
|
|
self.bpy_object.parent_bone = bone_name
|
|
elif self.bpy_object.parent_type != "OBJECT":
|
|
self.bpy_object.parent_type = "OBJECT"
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
bpy_object: Optional[Object] # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/vrm-specification/blob/f2d8f158297fc883aef9c3071ca68fbe46b03f45/specification/0.0/schema/vrm.secondaryanimation.collidergroup.schema.json
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_SecondaryAnimation.cs#L21-L29
|
|
class Vrm0SecondaryAnimationColliderGroupPropertyGroup(PropertyGroup):
|
|
node: PointerProperty( # type: ignore[valid-type]
|
|
name="Node",
|
|
type=BonePropertyGroup,
|
|
)
|
|
# Use the collider's own data for offset and radius
|
|
colliders: CollectionProperty( # type: ignore[valid-type]
|
|
name="Colliders",
|
|
type=Vrm0SecondaryAnimationColliderPropertyGroup,
|
|
)
|
|
|
|
def refresh(self, armature: Object) -> None:
|
|
from ..extension import get_armature_extension
|
|
|
|
name = (
|
|
str(self.node.bone_name) if self.node and self.node.bone_name else ""
|
|
) + f"#{self.uuid}"
|
|
|
|
if self.name != name:
|
|
self.name = name
|
|
|
|
for index, collider in reversed(
|
|
tuple((index, collider) for index, collider in enumerate(self.colliders))
|
|
):
|
|
if not collider.bpy_object or not collider.bpy_object.name:
|
|
self.colliders.remove(index)
|
|
else:
|
|
collider.refresh(armature, self.node.bone_name)
|
|
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
|
|
for bone_group in get_armature_extension(
|
|
armature_data
|
|
).vrm0.secondary_animation.bone_groups:
|
|
bone_group.refresh(armature)
|
|
|
|
# for UI
|
|
show_expanded: BoolProperty() # type: ignore[valid-type]
|
|
|
|
active_collider_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
|
|
# for reference from Vrm0SecondaryAnimationGroupPropertyGroup
|
|
name: StringProperty() # type: ignore[valid-type]
|
|
uuid: StringProperty() # type: ignore[valid-type]
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
node: BonePropertyGroup # type: ignore[no-redef]
|
|
colliders: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
Vrm0SecondaryAnimationColliderPropertyGroup
|
|
]
|
|
show_expanded: bool # type: ignore[no-redef]
|
|
active_collider_index: int # type: ignore[no-redef]
|
|
name: str # type: ignore[no-redef]
|
|
uuid: str # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_SecondaryAnimation.cs#L32-L67
|
|
class Vrm0SecondaryAnimationGroupPropertyGroup(PropertyGroup):
|
|
comment: StringProperty( # type: ignore[valid-type]
|
|
name="Comment",
|
|
description="Comment about the purpose of springs",
|
|
)
|
|
|
|
# typo in VRM 0.0 specification
|
|
# https://github.com/vrm-c/vrm-specification/blob/1723a45abfb4f12ac5d3635a3f66dc45e2f93c83/specification/0.0/schema/vrm.secondaryanimation.spring.schema.json#L9-L12
|
|
stiffiness: FloatProperty( # type: ignore[valid-type]
|
|
name="Stiffness",
|
|
min=0.0,
|
|
soft_max=4.0,
|
|
subtype="FACTOR",
|
|
description="Stiffness of springs",
|
|
)
|
|
|
|
gravity_power: FloatProperty( # type: ignore[valid-type]
|
|
name="Gravity Power",
|
|
min=0.0,
|
|
soft_max=2.0,
|
|
subtype="FACTOR",
|
|
description="Gravity power of springs",
|
|
)
|
|
|
|
def update_gravity_dir(self, _context: Context) -> None:
|
|
gravity_dir = Vector(self.gravity_dir)
|
|
normalized_gravity_dir = gravity_dir.normalized()
|
|
if (gravity_dir - normalized_gravity_dir).length > 0.0001:
|
|
self.gravity_dir = normalized_gravity_dir
|
|
|
|
gravity_dir: FloatVectorProperty( # type: ignore[valid-type]
|
|
size=3,
|
|
min=-1,
|
|
max=1,
|
|
subtype="XYZ",
|
|
name="Gravity Direction",
|
|
description="Gravity direction of springs",
|
|
update=update_gravity_dir,
|
|
)
|
|
drag_force: FloatProperty( # type: ignore[valid-type]
|
|
name="Drag Force",
|
|
min=0.0,
|
|
max=1.0,
|
|
subtype="FACTOR",
|
|
description="Drag Force of springs",
|
|
)
|
|
center: PointerProperty( # type: ignore[valid-type]
|
|
name="Center",
|
|
type=BonePropertyGroup,
|
|
description="Origin of Physics simulation to stop springs on moving",
|
|
)
|
|
hit_radius: FloatProperty( # type: ignore[valid-type]
|
|
name="Hit Radius",
|
|
min=0.0,
|
|
soft_max=0.5,
|
|
subtype="DISTANCE",
|
|
description="Hit Radius of springs",
|
|
)
|
|
bones: CollectionProperty( # type: ignore[valid-type]
|
|
name="Bones",
|
|
type=BonePropertyGroup,
|
|
description="Bones of the spring roots",
|
|
)
|
|
collider_groups: CollectionProperty( # type: ignore[valid-type]
|
|
name="Collider Group",
|
|
type=StringPropertyGroup,
|
|
description="Enabled collider Groups of springs",
|
|
)
|
|
|
|
# for UI
|
|
show_expanded: BoolProperty() # type: ignore[valid-type]
|
|
show_expanded_bones: BoolProperty( # type: ignore[valid-type]
|
|
name="Bones"
|
|
)
|
|
show_expanded_collider_groups: BoolProperty( # type: ignore[valid-type]
|
|
name="Collider Groups"
|
|
)
|
|
|
|
active_bone_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
active_collider_group_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
|
|
def refresh(self, armature: Object) -> None:
|
|
from ..extension import get_armature_extension
|
|
|
|
armature_data = armature.data
|
|
if not isinstance(armature_data, Armature):
|
|
return
|
|
ext = get_armature_extension(armature_data)
|
|
collider_group_uuid_to_name = {
|
|
collider_group.uuid: collider_group.name
|
|
for collider_group in ext.vrm0.secondary_animation.collider_groups
|
|
}
|
|
for index, collider_group in reversed(list(enumerate(self.collider_groups))):
|
|
uuid_str = collider_group.value.split("#")[-1]
|
|
if not uuid_str:
|
|
self.collider_groups.remove(index)
|
|
continue
|
|
|
|
name = collider_group_uuid_to_name.get(uuid_str)
|
|
if not name:
|
|
self.collider_groups.remove(index)
|
|
continue
|
|
|
|
if collider_group.value != name:
|
|
collider_group.value = name
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
comment: str # type: ignore[no-redef]
|
|
stiffiness: float # type: ignore[no-redef]
|
|
gravity_power: float # type: ignore[no-redef]
|
|
gravity_dir: Sequence[float] # type: ignore[no-redef]
|
|
drag_force: float # type: ignore[no-redef]
|
|
center: BonePropertyGroup # type: ignore[no-redef]
|
|
hit_radius: float # type: ignore[no-redef]
|
|
bones: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
BonePropertyGroup
|
|
]
|
|
collider_groups: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
StringPropertyGroup
|
|
]
|
|
show_expanded: bool # type: ignore[no-redef]
|
|
show_expanded_bones: bool # type: ignore[no-redef]
|
|
show_expanded_collider_groups: bool # type: ignore[no-redef]
|
|
active_bone_index: int # type: ignore[no-redef]
|
|
active_collider_group_index: int # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_Meta.cs#L33-L149
|
|
class Vrm0MetaPropertyGroup(PropertyGroup):
|
|
allowed_user_name_enum, __allowed_user_names = property_group_enum(
|
|
("OnlyAuthor", "Only Author", "", "NONE", 0),
|
|
("ExplicitlyLicensedPerson", "Explicitly Licensed Person", "", "NONE", 1),
|
|
("Everyone", "Everyone", "", "NONE", 2),
|
|
)
|
|
|
|
violent_ussage_name_enum, __violent_ussage_names = property_group_enum(
|
|
("Disallow", "Disallow", "", "NONE", 0),
|
|
("Allow", "Allow", "", "NONE", 1),
|
|
)
|
|
|
|
sexual_ussage_name_enum, __sexual_ussage_names = property_group_enum(
|
|
("Disallow", "Disallow", "", "NONE", 0),
|
|
("Allow", "Allow", "", "NONE", 1),
|
|
)
|
|
|
|
commercial_ussage_name_enum, __commercial_ussage_names = property_group_enum(
|
|
("Disallow", "Disallow", "", "NONE", 0),
|
|
("Allow", "Allow", "", "NONE", 1),
|
|
)
|
|
|
|
(
|
|
license_name_enum,
|
|
(
|
|
LICENSE_NAME_REDISTRIBUTION_PROHIBITED,
|
|
LICENSE_NAME_CC0,
|
|
LICENSE_NAME_CC_BY,
|
|
LICENSE_NAME_CC_BY_NC,
|
|
LICENSE_NAME_CC_BY_SA,
|
|
LICENSE_NAME_CC_BY_NC_SA,
|
|
LICENSE_NAME_CC_BY_ND,
|
|
LICENSE_NAME_CC_BY_NC_ND,
|
|
LICENSE_NAME_OTHER,
|
|
),
|
|
) = property_group_enum(
|
|
("Redistribution_Prohibited", "Redistribution Prohibited", "", "NONE", 0),
|
|
("CC0", "CC0", "", "NONE", 1),
|
|
("CC_BY", "CC BY", "", "NONE", 2),
|
|
("CC_BY_NC", "CC BY NC", "", "NONE", 3),
|
|
("CC_BY_SA", "CC BY SA", "", "NONE", 4),
|
|
("CC_BY_NC_SA", "CC BY NC SA", "", "NONE", 5),
|
|
("CC_BY_ND", "CC BY ND", "", "NONE", 6), # codespell:ignore nd
|
|
("CC_BY_NC_ND", "CC BY NC ND", "", "NONE", 7), # codespell:ignore nd
|
|
("Other", "Other", "", "NONE", 8),
|
|
)
|
|
|
|
title: StringProperty( # type: ignore[valid-type]
|
|
name="Title",
|
|
description="Title of the avatar",
|
|
)
|
|
version: StringProperty( # type: ignore[valid-type]
|
|
name="Version",
|
|
description="Version of the avatar",
|
|
)
|
|
author: StringProperty( # type: ignore[valid-type]
|
|
name="Author",
|
|
description="Author of the avatar",
|
|
)
|
|
contact_information: StringProperty( # type: ignore[valid-type]
|
|
name="Contact Information",
|
|
description="Contact Information about the avatar",
|
|
)
|
|
reference: StringProperty( # type: ignore[valid-type]
|
|
name="Reference",
|
|
description="Referenced works about the avatar",
|
|
)
|
|
allowed_user_name: EnumProperty( # type: ignore[valid-type]
|
|
items=allowed_user_name_enum.items(),
|
|
name="Allowed User",
|
|
description="Allowed user of the avatar",
|
|
)
|
|
violent_ussage_name: EnumProperty( # type: ignore[valid-type]
|
|
items=violent_ussage_name_enum.items(),
|
|
name="Violent Usage",
|
|
description="Violent usage of the avatar",
|
|
)
|
|
sexual_ussage_name: EnumProperty( # type: ignore[valid-type]
|
|
items=sexual_ussage_name_enum.items(),
|
|
name="Sexual Usage",
|
|
description="Sexual Usage of the avatar",
|
|
)
|
|
commercial_ussage_name: EnumProperty( # type: ignore[valid-type]
|
|
items=commercial_ussage_name_enum.items(),
|
|
name="Commercial Usage",
|
|
description="Commercial Usage of the avatar",
|
|
)
|
|
other_permission_url: StringProperty( # type: ignore[valid-type]
|
|
name="Other Permission URL",
|
|
description="URL about other permissions of the avatar",
|
|
)
|
|
license_name: EnumProperty( # type: ignore[valid-type]
|
|
items=license_name_enum.items(),
|
|
name="License",
|
|
description="License of the avatar",
|
|
)
|
|
other_license_url: StringProperty( # type: ignore[valid-type]
|
|
name="Other License URL",
|
|
description="URL about other License of the avatar",
|
|
)
|
|
texture: PointerProperty( # type: ignore[valid-type]
|
|
name="Thumbnail",
|
|
type=Image,
|
|
description="Thumbnail of the avatar",
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
title: str # type: ignore[no-redef]
|
|
version: str # type: ignore[no-redef]
|
|
author: str # type: ignore[no-redef]
|
|
contact_information: str # type: ignore[no-redef]
|
|
reference: str # type: ignore[no-redef]
|
|
allowed_user_name: str # type: ignore[no-redef]
|
|
violent_ussage_name: str # type: ignore[no-redef]
|
|
sexual_ussage_name: str # type: ignore[no-redef]
|
|
commercial_ussage_name: str # type: ignore[no-redef]
|
|
other_permission_url: str # type: ignore[no-redef]
|
|
license_name: str # type: ignore[no-redef]
|
|
other_license_url: str # type: ignore[no-redef]
|
|
texture: Optional[Image] # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_BlendShape.cs#L101-L106
|
|
class Vrm0BlendShapeMasterPropertyGroup(PropertyGroup):
|
|
blend_shape_groups: CollectionProperty( # type: ignore[valid-type]
|
|
name="Blend Shape Group",
|
|
type=Vrm0BlendShapeGroupPropertyGroup,
|
|
)
|
|
|
|
def restore_blend_shape_group_bind_object_assignments(
|
|
self, context: Context
|
|
) -> None:
|
|
for blend_shape_group in self.blend_shape_groups:
|
|
for bind in blend_shape_group.binds:
|
|
bind.mesh.restore_object_assignment(context)
|
|
|
|
# for UI
|
|
active_blend_shape_group_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
blend_shape_groups: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
Vrm0BlendShapeGroupPropertyGroup
|
|
]
|
|
active_blend_shape_group_index: int # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_SecondaryAnimation.cs#L69-L78
|
|
class Vrm0SecondaryAnimationPropertyGroup(PropertyGroup):
|
|
bone_groups: CollectionProperty( # type: ignore[valid-type]
|
|
name="Secondary Animation Groups",
|
|
type=Vrm0SecondaryAnimationGroupPropertyGroup,
|
|
)
|
|
collider_groups: CollectionProperty( # type: ignore[valid-type]
|
|
name="Collider Groups",
|
|
type=Vrm0SecondaryAnimationColliderGroupPropertyGroup,
|
|
)
|
|
|
|
# for UI
|
|
show_expanded_bone_groups: BoolProperty( # type: ignore[valid-type]
|
|
name="Spring Bone Groups",
|
|
)
|
|
show_expanded_collider_groups: BoolProperty( # type: ignore[valid-type]
|
|
name="Collider Groups",
|
|
)
|
|
|
|
active_bone_group_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
active_collider_group_index: IntProperty(min=0) # type: ignore[valid-type]
|
|
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
bone_groups: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
Vrm0SecondaryAnimationGroupPropertyGroup
|
|
]
|
|
collider_groups: CollectionPropertyProtocol[ # type: ignore[no-redef]
|
|
Vrm0SecondaryAnimationColliderGroupPropertyGroup
|
|
]
|
|
show_expanded_bone_groups: bool # type: ignore[no-redef]
|
|
show_expanded_collider_groups: bool # type: ignore[no-redef]
|
|
active_bone_group_index: int # type: ignore[no-redef]
|
|
active_collider_group_index: int # type: ignore[no-redef]
|
|
|
|
|
|
# https://github.com/vrm-c/UniVRM/blob/v0.91.1/Assets/VRM/Runtime/Format/glTF_VRM_extensions.cs#L8-L48
|
|
class Vrm0PropertyGroup(PropertyGroup):
|
|
meta: PointerProperty( # type: ignore[valid-type]
|
|
name="VRM Meta",
|
|
type=Vrm0MetaPropertyGroup,
|
|
)
|
|
humanoid: PointerProperty( # type: ignore[valid-type]
|
|
name="VRM Humanoid",
|
|
type=Vrm0HumanoidPropertyGroup,
|
|
)
|
|
first_person: PointerProperty( # type: ignore[valid-type]
|
|
name="VRM First Person",
|
|
type=Vrm0FirstPersonPropertyGroup,
|
|
)
|
|
blend_shape_master: PointerProperty( # type: ignore[valid-type]
|
|
name="VRM Blend Shape Master",
|
|
type=Vrm0BlendShapeMasterPropertyGroup,
|
|
)
|
|
secondary_animation: PointerProperty( # type: ignore[valid-type]
|
|
name="VRM Secondary Animation",
|
|
type=Vrm0SecondaryAnimationPropertyGroup,
|
|
)
|
|
if TYPE_CHECKING:
|
|
# This code is auto generated.
|
|
# To regenerate, run the `uv run tools/property_typing.py` command.
|
|
meta: Vrm0MetaPropertyGroup # type: ignore[no-redef]
|
|
humanoid: Vrm0HumanoidPropertyGroup # type: ignore[no-redef]
|
|
first_person: Vrm0FirstPersonPropertyGroup # type: ignore[no-redef]
|
|
blend_shape_master: Vrm0BlendShapeMasterPropertyGroup # type: ignore[no-redef]
|
|
secondary_animation: ( # type: ignore[no-redef]
|
|
Vrm0SecondaryAnimationPropertyGroup
|
|
)
|