Files
blender-vrm/editor/vrm1/scene_watcher.py
小煜 091ad6a49a feat: Add VRM Blender addon with complete import/export functionality
- 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
2026-01-01 14:21:56 +08:00

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