diff --git a/studio/src/anim/camera.rs b/studio/src/anim/camera.rs index 9642876..9fff519 100644 --- a/studio/src/anim/camera.rs +++ b/studio/src/anim/camera.rs @@ -61,7 +61,7 @@ fn draw_camera_keyframes( gizmos.line(origin, origin + z_axis, Color::BLUE); } - let seek_time = **r_seek_time; + let seek_time = r_seek_time.time; let keyframes_sorted = q_cam_keyframes .iter() .sorted_by(|(a, _), (b, _)| a.partial_cmp(&b.0).unwrap()) diff --git a/studio/src/anim/mod.rs b/studio/src/anim/mod.rs index b523e33..f246573 100644 --- a/studio/src/anim/mod.rs +++ b/studio/src/anim/mod.rs @@ -36,9 +36,6 @@ impl Plugin for AnimPlugin { camera::CameraAnimPlugin, timeline_animation::TimelinePlugin, )); - - #[cfg(feature = "wasm")] - app.add_systems(Update, wasm::update_global_seek_time); } } diff --git a/studio/src/anim/timeline_animation.rs b/studio/src/anim/timeline_animation.rs index 17c1e00..9ac646a 100644 --- a/studio/src/anim/timeline_animation.rs +++ b/studio/src/anim/timeline_animation.rs @@ -5,6 +5,7 @@ use bevy::{ }, prelude::*, }; +use serde::{Deserialize, Serialize}; use space_editor::space_prefab::editor_registry::EditorRegistryExt; pub struct TimelinePlugin; @@ -14,15 +15,38 @@ impl Plugin for TimelinePlugin { app.register_type::() .editor_registry::() .register_type::() - .editor_registry::(); + .editor_registry::() + .add_event::(); - app.insert_resource(GlobalSeekTime(0.)); + app.insert_resource(GlobalSeekTime { + time: 0., + playing: false, + }); + + #[cfg(feature = "wasm")] + app.add_systems( + Update, + ( + wasm::update_global_seek_time, + wasm::notify_seek_time_changes.map(bevy::utils::error), + ), + ); } } -#[derive(Resource, Clone, Copy, Debug, Default, Deref, DerefMut, Reflect)] +#[derive(Event, Clone, Copy, Debug)] +pub enum TimelineEvent { + Play, + Pause, + Seek(f32), +} + +#[derive(Resource, Clone, Copy, Debug, Default, Reflect, Serialize, Deserialize)] #[reflect(Resource)] -pub struct GlobalSeekTime(pub f32); +pub struct GlobalSeekTime { + pub time: f32, + pub playing: bool, +} #[derive(Component, Clone, Debug, Reflect, PartialEq)] #[reflect(Component, MapEntities)] @@ -83,23 +107,62 @@ pub struct AnimationTarget; #[cfg(feature = "wasm")] mod wasm { - use bevy::prelude::*; + use bevy::{prelude::*, window::PrimaryWindow}; use wasm_bindgen::prelude::*; use crate::wasm::{StaticBuffer, WasmMessageBuffer}; - use super::GlobalSeekTime; + use super::{GlobalSeekTime, TimelineEvent}; - static SEEK_TIME: WasmMessageBuffer = WasmMessageBuffer::new(); + static SEEK_TIME: WasmMessageBuffer = WasmMessageBuffer::new(); #[wasm_bindgen(js_name = seekTime)] pub fn seek_time(timestamp: f32) { - SEEK_TIME.write_message(timestamp); + SEEK_TIME.write_message(TimelineEvent::Seek(timestamp)); } - pub(super) fn update_global_seek_time(mut r_seek_time: ResMut) { + #[wasm_bindgen(js_name = play)] + pub fn play() { + SEEK_TIME.write_message(TimelineEvent::Play); + } + + #[wasm_bindgen(js_name = pause)] + pub fn pause() { + SEEK_TIME.write_message(TimelineEvent::Pause); + } + + pub(super) fn update_global_seek_time(mut e_timeline_event: EventWriter) { if let Some(timestamp) = SEEK_TIME.take_message() { - **r_seek_time = timestamp; + e_timeline_event.send(timestamp); } } + + #[wasm_bindgen(typescript_custom_section)] + const SEEK_TIME_CHANGED_EVENT: &str = r#" + export interface SeekTimeChanged extends CustomEvent { + type: "seek-time-changed"; + detail: number; + } + + declare global { + export interface GlobalEventHandlersEventMap { + "seek-time-changed": CameraKeyframesEvent; + } + } + "#; + + pub(super) fn notify_seek_time_changes( + r_global_seek_time: Res, + q_window: Query<&Window, With>, + ) -> Result<(), String> { + let Ok(window) = q_window.get_single() else { + return Ok(()); + }; + + crate::wasm::dispatch_to_js( + window, + "seek-time-changed", + &serde_json::to_value(r_global_seek_time.as_ref()).map_err(|e| format!("{e}"))?, + ) + } }