- 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
94 lines
3.1 KiB
Python
94 lines
3.1 KiB
Python
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
|
from dataclasses import dataclass
|
|
|
|
from bpy.types import Armature, Context
|
|
from mathutils import Vector
|
|
|
|
from ...common import ops
|
|
from ...common.logger import get_logger
|
|
from ...common.scene_watcher import RunState, SceneWatcher
|
|
from ..extension import get_armature_extension
|
|
from .property_group import Vrm1LookAtPropertyGroup
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class LookAtPreviewUpdater(SceneWatcher):
|
|
armature_index: int = 0
|
|
|
|
def reset_run_progress(self) -> None:
|
|
self.armature_index = 0
|
|
|
|
def run(self, context: Context) -> RunState:
|
|
"""Detect updates to the target object of Look At and update the state."""
|
|
blend_data = context.blend_data
|
|
|
|
if not blend_data.armatures:
|
|
return RunState.FINISH
|
|
|
|
count = 20
|
|
|
|
armatures_len = len(blend_data.armatures)
|
|
if self.armature_index >= armatures_len:
|
|
self.armature_index = 0
|
|
|
|
start_armature_index = self.armature_index
|
|
end_armature_index = min(self.armature_index + count, armatures_len)
|
|
changed = False
|
|
for armature in blend_data.armatures[start_armature_index:end_armature_index]:
|
|
ext = get_armature_extension(armature)
|
|
if not ext.is_vrm1():
|
|
continue
|
|
look_at = ext.vrm1.look_at
|
|
if not look_at.enable_preview:
|
|
continue
|
|
preview_target_bpy_object = look_at.preview_target_bpy_object
|
|
if not preview_target_bpy_object:
|
|
continue
|
|
if (
|
|
Vector(look_at.previous_preview_target_bpy_object_location)
|
|
- preview_target_bpy_object.location
|
|
).length_squared > 0:
|
|
changed = True
|
|
break
|
|
|
|
if changed:
|
|
Vrm1LookAtPropertyGroup.update_all_previews(context)
|
|
return RunState.FINISH
|
|
|
|
self.armature_index += count
|
|
|
|
if end_armature_index < armatures_len:
|
|
return RunState.PREEMPT
|
|
|
|
return RunState.FINISH
|
|
|
|
def create_fast_path_performance_test_objects(self, context: Context) -> None:
|
|
blend_data = context.blend_data
|
|
|
|
for i in range(300):
|
|
if i % 3 != 0:
|
|
mesh = blend_data.meshes.new(f"Mesh#{i}")
|
|
obj = blend_data.objects.new(f"Object#{i}", mesh)
|
|
context.scene.collection.objects.link(obj)
|
|
continue
|
|
|
|
ops.icyp.make_basic_armature()
|
|
active_object = context.active_object
|
|
if not active_object:
|
|
message = f"Not an armature: {active_object}"
|
|
raise ValueError(message)
|
|
armature = active_object.data
|
|
if not isinstance(armature, Armature):
|
|
raise TypeError
|
|
|
|
ext = get_armature_extension(armature)
|
|
ext.spec_version = ext.SPEC_VERSION_VRM1
|
|
look_at = ext.vrm1.look_at
|
|
look_at.type = ext.vrm1.look_at.TYPE_BONE.identifier
|
|
look_at.preview_target_bpy_object = blend_data.objects[
|
|
i * 5 % len(blend_data.objects)
|
|
]
|
|
ext.vrm1.look_at.enable_preview = True
|