# 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