use crate::core::animation::Node;
use kurbo::Affine;
#[cfg(feature = "runtime")]
use vello::Scene;
pub trait Scene2D {
#[cfg(feature = "runtime")]
fn render(&self, scene: &mut Scene);
fn update(&mut self, dt: std::time::Duration);
fn state_hash(&self) -> u64;
fn is_dirty(&self) -> bool {
true
}
fn set_dirty(&mut self, _dirty: bool) {}
}
pub struct BaseScene {
pub nodes: Vec<Box<dyn Node>>,
pub video_timeline: crate::core::Timeline,
#[cfg(feature = "audio")]
pub audio_timeline: crate::core::Timeline,
pub previous_frame_hash: u64,
pub is_dirty: bool,
}
impl BaseScene {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
video_timeline: crate::core::Timeline::new(),
#[cfg(feature = "audio")]
audio_timeline: crate::core::Timeline::new(),
previous_frame_hash: 0,
is_dirty: true,
}
}
pub fn add(&mut self, node: Box<dyn Node>) {
self.nodes.push(node);
}
pub fn reset(&mut self) {
self.video_timeline.reset();
#[cfg(feature = "audio")]
self.audio_timeline.reset();
for node in &mut self.nodes {
node.reset();
}
}
#[cfg(feature = "audio")]
pub fn collect_audio_events(
&mut self,
current_time: std::time::Duration,
events: &mut Vec<crate::core::animation::base::AudioEvent>,
) {
self.video_timeline
.collect_audio_events(current_time, events);
self.audio_timeline
.collect_audio_events(current_time, events);
}
}
impl Scene2D for BaseScene {
#[cfg(feature = "runtime")]
fn render(&self, scene: &mut Scene) {
for node in &self.nodes {
node.render(scene, Affine::IDENTITY, 1.0);
}
}
fn update(&mut self, dt: std::time::Duration) {
self.video_timeline.update(dt);
#[cfg(feature = "audio")]
self.audio_timeline.update(dt);
for node in &mut self.nodes {
node.update(dt);
}
let current_hash = self.state_hash();
if current_hash != self.previous_frame_hash {
self.is_dirty = true;
self.previous_frame_hash = current_hash;
} else {
self.is_dirty = false;
}
}
fn state_hash(&self) -> u64 {
use rayon::prelude::*;
self.nodes
.par_iter()
.enumerate()
.map(|(i, node)| crate::assets::hash::combine_hashes(node.state_hash(), i as u64))
.reduce(|| 0u64, |a, b| a.wrapping_add(b))
}
fn is_dirty(&self) -> bool {
self.is_dirty
}
fn set_dirty(&mut self, dirty: bool) {
self.is_dirty = dirty;
}
}