Mercurial > code > home > repos > light9
view blender/time_sync/blender_time.py @ 2455:2d454737a916
split blender code to new file
author | drewp@bigasterisk.com |
---|---|
date | Tue, 20 May 2025 09:24:35 -0700 |
parents | blender/time_sync/time_from_graph.py@405abed9a45c |
children | d94480bfb179 |
line wrap: on
line source
from dataclasses import dataclass from typing import Callable import bpy from bpy.app.handlers import persistent from light9.run_local import log UPDATE_PERIOD = 1 / 20 def clamp(lo, hi, x): return max(lo, min(hi, x)) class _TimeEvent(): pass class SceneLoaded(_TimeEvent): pass @dataclass class PausedGotoTime(_TimeEvent): t: float @dataclass class PlayingGotoTime(_TimeEvent): t: float class BlenderTime: """all methods to run in main thread""" def __init__(self, onEvent: Callable[[_TimeEvent], None]): self._onEvent = onEvent self.lastSetFrame = -1 def start(self): bpy.app.handlers.load_post.append(self._on_load_post) bpy.app.handlers.frame_change_post.append(self._on_frame_change_post) # need persistent because `blender --addons ...` seems to start addon # before loading scene. bpy.app.timers.register(self.update, persistent=True) self.emitEvent(SceneLoaded()) def setSceneDuration(self, duration: float): self.duration = duration scene = bpy.context.scene fps = scene.render.fps scene.frame_start = 0 scene.frame_end = int(duration * fps) def frameDuration(self): return # todo: need to be in screen context or something context_override = bpy.context.copy() # context_override["selected_objects"] = list(context.scene.objects) with bpy.context.temp_override(**context_override): bpy.ops.action.view_all() def setCurrentTime(self, t: float): scene = bpy.context.scene fps = scene.render.fps fr = int(clamp(t, 0, self.duration) * fps) self.lastSetFrame = fr scene.frame_set(fr) def setBlenderTime(self, t: float, duration: float): log.info(f'set blender time to {t:.2f}') self.setSceneDuration(duration) self.frameDuration() self.setCurrentTime(t) def emitEvent(self, event: _TimeEvent): log.info(f'🌹 emitEvent {event}') self._onEvent(event) @persistent def update(self): if 0: if self.playing: with self.lock: t = self.currentTime() if t is not None: self.blenderTime.setBlenderTime(t, self.duration) return UPDATE_PERIOD @persistent def _on_load_post(self, scene, deps): self.emitEvent(SceneLoaded()) @persistent def _on_frame_change_post(self, scene, deps): # if scene.frame_current == self.lastSetFrame: # return # blender requested this frame change, either playing or paused (scrubbing timeline) self.lastSetFrame = scene.frame_current t = round(scene.frame_current / scene.render.fps, 3) if bpy.context.screen.is_animation_playing and not bpy.context.screen.is_scrubbing: self.emitEvent(PlayingGotoTime(t=t)) else: self.emitEvent(PausedGotoTime(t=t))