Mercurial > code > home > repos > light9
diff blender/time_sync/time_from_graph.py @ 2453:b23afde50bc2
blender addons get thier own pdm setup for now. fix time_from_graph startup race
author | drewp@bigasterisk.com |
---|---|
date | Sun, 18 May 2025 20:08:35 -0700 |
parents | src/light9/blender/time_sync/time_from_graph.py@e683b449506b |
children | 405abed9a45c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/blender/time_sync/time_from_graph.py Sun May 18 20:08:35 2025 -0700 @@ -0,0 +1,96 @@ +import threading +import time + +import bpy # type: ignore +from bpy.app.handlers import persistent # type: ignore +from light9_sync.asyncio_thread import startLoopInThread +from rdfdb.syncedgraph.syncedgraph import SyncedGraph + +from light9 import networking +from light9.namespaces import L9 +from light9.run_local import log +from light9.typedgraph import typedValue + + +def clamp(lo, hi, x): + return max(lo, min(hi, x)) + + +UPDATE_PERIOD = 1 / 20 + + +class Sync: + lock = threading.Lock() + duration: float = 1 + wallStartTime: float | None = None + pausedSongTime: float | None = None + playing = False + latestTime: float + + def __init__(self): + # main thread + self.lastSetFrame = -1 + self.lastGraphFrame = -1 + startLoopInThread(self.task()) + # need persistent because `blender --addons ...` seems to start addon + # before loading scene. + bpy.app.timers.register(self.update, persistent=True) + bpy.app.handlers.frame_change_post.append(self.on_frame_change_post) + + ## updates from graph -> self + + async def task(self): + # bg thread with asyncio loop + self.graph = SyncedGraph(networking.rdfdb.url, "time_sync") + self.graph.addHandler(self.syncFromGraph) + + def syncFromGraph(self): + # bg thread + with self.lock: + asco = L9['ascoltami'] + self.wallStartTime = typedValue(float | None, self.graph, asco, L9['wallStartTime']) + self.pausedSongTime = typedValue(float | None, self.graph, asco, L9['pausedSongTime']) + self.duration = typedValue(float | None, self.graph, asco, L9['duration']) or 1.0 + self.playing = typedValue(bool | None, self.graph, asco, L9['playing']) or False + + def currentTime(self) -> float | None: + if self.wallStartTime is not None: + return time.time() - self.wallStartTime + if self.pausedSongTime is not None: + return self.pausedSongTime + log.warn('no time data') + return None + + ## graph time -> blender time + + # @persistent + def update(self): + # main thread? wherever blender runs timers + if self.playing: + with self.lock: + t = self.currentTime() + if t is not None: + self.setBlenderTime(t, self.duration) + return UPDATE_PERIOD + + def setBlenderTime(self, t: float, duration: float): + scene = bpy.context.scene + fps = scene.render.fps + scene.frame_start = 0 + scene.frame_end = int(duration * fps) + fr = int(clamp(t, 0, duration) * fps) + self.lastSetFrame = fr + scene.frame_set(fr) + + ## blender time -> graph time + + @persistent + def on_frame_change_post(self, scene, deps): + if scene.frame_current != self.lastSetFrame: + # self.setGraphTime(scene.frame_current / scene.render.fps, self.duration) + self.lastSetFrame = scene.frame_current + t = scene.frame_current / scene.render.fps + self.setInGraph(t) + + def setInGraph(self, t: float): + log.warning(f'todo: set graph to {t:.2f}')