diff --git a/Cargo.lock b/Cargo.lock index a5df9ebd..b718fc96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,6 @@ dependencies = [ "surreal-editor", "surreal-graphics", "surreal-input", - "surreal-scripting", ] [[package]] @@ -480,13 +479,6 @@ dependencies = [ "syn 1.0.107", ] -[[package]] -name = "surreal-scripting" -version = "0.0.0" -dependencies = [ - "surreal-common", -] - [[package]] name = "syn" version = "1.0.107" diff --git a/Cargo.toml b/Cargo.toml index 50a5df92..a7a36026 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,19 +34,14 @@ bitflags = "2.1.0" common = { package = "surreal-common", path = "./common" } editor = { package = "surreal-editor", path = "./editor", optional = true } -# backends +# framework sdl = { package = "surreal-backend-sdl", path = "./backends/sdl", optional = true } # modules audio = { package = "surreal-audio", path = "./modules/audio", optional = true } graphics = { package = "surreal-graphics", path = "./modules/graphics", optional = true } input = { package = "surreal-input", path = "./modules/input", optional = true } -scripting = { package = "surreal-scripting", path = "./modules/scripting", optional = true } [[example]] name = "hello-world" required-features = ["sdl", "graphics"] - -[[example]] -name = "sprites" -required-features = ["sdl", "graphics"] diff --git a/common/src/collections.rs b/common/src/collections.rs index dd23ff2b..a8cb6099 100644 --- a/common/src/collections.rs +++ b/common/src/collections.rs @@ -39,4 +39,4 @@ pub type FastMultiMap = MultiMap>; /// A faster spatial hash grid that is not resilient to DoS attacks. -pub type FastSpatialHashGrid = SpatialHashGrid>; +pub type FastSpatialHashMap = SpatialHashMap>; diff --git a/common/src/maths/lerp.rs b/common/src/maths/lerp.rs index 079008cc..62132197 100644 --- a/common/src/maths/lerp.rs +++ b/common/src/maths/lerp.rs @@ -1,4 +1,4 @@ -use super::Scalar; +use super::{Quat, Scalar, Vec2, Vec3}; /// Allows linear interpolation of arbitrary values. pub trait Lerp { @@ -15,6 +15,34 @@ impl Lerp for T { } } +impl Lerp for Vec2 { + #[inline(always)] + fn lerp(a: Self, b: Self, t: f32) -> Self { + let x = f32::lerp(a.x, b.x, t); + let y = f32::lerp(a.y, b.y, t); + + Self::new(x, y) + } +} + +impl Lerp for Vec3 { + #[inline(always)] + fn lerp(a: Self, b: Self, t: f32) -> Self { + let x = f32::lerp(a.x, b.x, t); + let y = f32::lerp(a.y, b.y, t); + let z = f32::lerp(a.z, b.z, t); + + Self::new(x, y, z) + } +} + +impl Lerp for Quat { + #[inline(always)] + fn lerp(a: Self, b: Self, t: f32) -> Self { + a.slerp(b, t) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/common/src/maths/linear.rs b/common/src/maths/linear.rs index e39184e8..186dc391 100644 --- a/common/src/maths/linear.rs +++ b/common/src/maths/linear.rs @@ -18,3 +18,11 @@ mod planes; mod rays; mod scalars; mod vectors; + +/// Represents a numerical space with identity constants +pub trait Identity { + const ZERO: Self; + const ONE: Self; + const MIN: Self; + const MAX: Self; +} diff --git a/common/src/maths/linear/scalars.rs b/common/src/maths/linear/scalars.rs index a5c618d2..2d1ed8c2 100644 --- a/common/src/maths/linear/scalars.rs +++ b/common/src/maths/linear/scalars.rs @@ -1,9 +1,12 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; +use crate::Identity; + /// Represents a scalar type that allows standard equality and arithmetic. pub trait Scalar: Copy + Default + + Identity + PartialOrd + PartialEq + Add @@ -16,11 +19,6 @@ pub trait Scalar: + DivAssign + Sized { - const ZERO: Self; - const ONE: Self; - const MIN: Self; - const MAX: Self; - /// Converts a value from a 32-bit floating point number. fn from_f32(value: f32) -> Self; @@ -33,13 +31,15 @@ pub trait Scalar: /// Implements the numeric traits for standard purpose a numeric type. macro_rules! impl_scalar { - ($type:ty) => { - impl Scalar for $type { + ($name:ty) => { + impl Identity for $name { const ZERO: Self = 0 as Self; const ONE: Self = 1 as Self; - const MIN: Self = <$type>::MIN; - const MAX: Self = <$type>::MAX; + const MIN: Self = <$name>::MIN; + const MAX: Self = <$name>::MAX; + } + impl Scalar for $name { #[inline(always)] fn from_f32(value: f32) -> Self { value as Self diff --git a/common/src/maths/linear/vectors.rs b/common/src/maths/linear/vectors.rs index 28c7fb72..df055609 100644 --- a/common/src/maths/linear/vectors.rs +++ b/common/src/maths/linear/vectors.rs @@ -32,6 +32,7 @@ pub trait Vector: Copy + Clone + Default + + Identity + Add + AddAssign + Add @@ -46,11 +47,6 @@ pub trait Vector: + DivAssign + Sized { - const ZERO: Self; - const ONE: Self; - const MIN: Self; - const MAX: Self; - /// The type of the space that this vector is in. type Space: Space; @@ -60,12 +56,14 @@ pub trait Vector: macro_rules! impl_vector { ($name:ident, $space:ident, $scalar:ident) => { - impl Vector for $name { + impl Identity for $name { const ZERO: Self = Self::splat($scalar::ZERO); const ONE: Self = Self::splat($scalar::ONE); const MIN: Self = Self::splat($scalar::MIN); const MAX: Self = Self::splat($scalar::MAX); + } + impl Vector for $name { type Space = $space; type Scalar = $scalar; } diff --git a/common/src/maths/time/clocks.rs b/common/src/maths/time/clocks.rs index 3a83503a..cc20ff42 100644 --- a/common/src/maths/time/clocks.rs +++ b/common/src/maths/time/clocks.rs @@ -32,7 +32,7 @@ impl DeltaClock { let delta_time = current_time - self.last_time; self.last_time = current_time; - self.last_delta_time = delta_time.total_seconds().min(self.max_delta_time); + self.last_delta_time = delta_time.as_seconds().min(self.max_delta_time); self.last_delta_time } @@ -47,6 +47,6 @@ impl DeltaClock { pub fn total_time(&self) -> f32 { let now = TimeStamp::now(); - (now - self.start_time).total_seconds() + (now - self.start_time).as_seconds() } } diff --git a/common/src/maths/time/counters.rs b/common/src/maths/time/counters.rs index ac95de15..63868309 100644 --- a/common/src/maths/time/counters.rs +++ b/common/src/maths/time/counters.rs @@ -19,7 +19,7 @@ impl IntervalTimer { pub fn tick(&mut self, delta_time: f32) -> bool { self.time_elapsed += delta_time; - self.time_elapsed >= self.interval.total_seconds() + self.time_elapsed >= self.interval.as_seconds() } pub fn reset(&mut self) { diff --git a/common/src/maths/time/spans.rs b/common/src/maths/time/spans.rs index ef550dfa..0338d549 100644 --- a/common/src/maths/time/spans.rs +++ b/common/src/maths/time/spans.rs @@ -46,28 +46,28 @@ impl TimeSpan { } #[inline] - pub fn total_millis(&self) -> f32 { - self.total_seconds() * 1000. + pub fn as_millis(&self) -> f32 { + self.as_seconds() * 1000. } #[inline] - pub fn total_seconds(&self) -> f32 { + pub fn as_seconds(&self) -> f32 { self.seconds } #[inline] - pub fn total_minutes(&self) -> f32 { - self.total_seconds() / 60. + pub fn as_minutes(&self) -> f32 { + self.as_seconds() / 60. } #[inline] - pub fn total_hours(&self) -> f32 { - self.total_minutes() / 60. + pub fn as_hours(&self) -> f32 { + self.as_minutes() / 60. } #[inline] - pub fn total_days(&self) -> f32 { - self.total_hours() / 24. + pub fn as_days(&self) -> f32 { + self.as_hours() / 24. } } @@ -145,11 +145,11 @@ impl From for Duration { impl Display for TimeSpan { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { match self { - _ if self.total_days() > 1. => write!(formatter, "{} days", self.total_days()), - _ if self.total_hours() > 1. => write!(formatter, "{} hours", self.total_hours()), - _ if self.total_minutes() > 1. => write!(formatter, "{} minutes", self.total_minutes()), - _ if self.total_seconds() > 1. => write!(formatter, "{} seconds", self.total_seconds()), - _ => write!(formatter, "{} milliseconds", self.total_millis()), + _ if self.as_days() > 1. => write!(formatter, "{} days", self.as_days()), + _ if self.as_hours() > 1. => write!(formatter, "{} hours", self.as_hours()), + _ if self.as_minutes() > 1. => write!(formatter, "{} minutes", self.as_minutes()), + _ if self.as_seconds() > 1. => write!(formatter, "{} seconds", self.as_seconds()), + _ => write!(formatter, "{} milliseconds", self.as_millis()), } } } @@ -216,28 +216,28 @@ mod tests { fn test_time_span_from_millis() { let time_span = TimeSpan::from_millis(100.0); - assert_eq!(time_span.total_millis(), 100.0); + assert_eq!(time_span.as_millis(), 100.0); } #[test] fn test_time_span_from_seconds() { let time_span = TimeSpan::from_seconds(2.5); - assert_eq!(time_span.total_millis(), 2500.0); + assert_eq!(time_span.as_millis(), 2500.0); } #[test] fn test_time_span_from_minutes() { let time_span = TimeSpan::from_minutes(1.5); - assert_eq!(time_span.total_millis(), 90000.0); + assert_eq!(time_span.as_millis(), 90000.0); } #[test] fn test_time_span_from_hours() { let time_span = TimeSpan::from_hours(0.5); - assert_eq!(time_span.total_millis(), 1800000.0); + assert_eq!(time_span.as_millis(), 1800000.0); } #[test] @@ -245,6 +245,6 @@ mod tests { let value: u8 = 10; let time_span = value.milliseconds(); - assert_eq!(time_span.total_millis(), 10.0); + assert_eq!(time_span.as_millis(), 10.0); } } diff --git a/editor/src/lib.rs b/editor/src/lib.rs index a3ba9575..87d918b3 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -3,9 +3,7 @@ pub use documents::*; pub use hosting::*; pub use projects::*; -pub use windows::*; mod documents; mod hosting; mod projects; -mod windows; diff --git a/editor/src/main.rs b/editor/src/main.rs index 5dd9de9a..2561afb0 100644 --- a/editor/src/main.rs +++ b/editor/src/main.rs @@ -2,6 +2,8 @@ use surreal_editor::*; +mod windows; + fn main() { start_editor(EditorConfig { hosting_mode: HostingModel::OutOfProcess { diff --git a/examples/sprites.rs b/examples/sprites.rs deleted file mode 100644 index f5311273..00000000 --- a/examples/sprites.rs +++ /dev/null @@ -1,39 +0,0 @@ -use surreal::{backends::sdl::*, common::*, graphics::*}; - -fn main() { - let mut window = Window::new(WindowSettings { - title: "Sprite Example".to_string(), - ..Default::default() - }) - .expect("Failed to create window"); - - GraphicsServer::install(OpenGLGraphicsBackend::new(&window)); - - let mut batch = SpriteBatch::new().unwrap(); - let mut material = Material::from_template(&SHADER_SPRITE_STANDARD).unwrap(); - - material.set_blend_state(BlendState::Enabled { - source: BlendFactor::SrcAlpha, - destination: BlendFactor::OneMinusSrcAlpha, - }); - - material.set_uniform( - PROJECTION_VIEW, - &Mat4::orthographic_lh(0.0, 800.0, 600.0, 0.0, -1.0, 1.0), - ); - - let sprite = Texture::from_path("assets/sprites/bunny.png").unwrap().to_region(); - - while window.update() { - graphics().clear_color_buffer(Color::BLACK); - - batch.begin(&material); - batch.draw_sprite(&sprite, &SpriteOptions { - position: vec2(400.0, 300.0), - ..Default::default() - }); - batch.flush(); - - window.present(); - } -} diff --git a/modules/audio/src/openal.rs b/modules/audio/src/openal.rs index 3a8285a1..c555d813 100644 --- a/modules/audio/src/openal.rs +++ b/modules/audio/src/openal.rs @@ -1,7 +1,5 @@ //! The OpenAL backend implementation for the audio subsystem. -use common::profiling; - use super::*; /// A OpenAL-based [`AudioBackend`] implementation. @@ -22,57 +20,46 @@ impl Drop for OpenALAudioBackend { #[allow(unused_variables)] impl AudioBackend for OpenALAudioBackend { - #[profiling] fn clip_create(&self) -> Result { todo!() } - #[profiling] fn clip_write_data(&self, clip: ClipId, data: *const u8, length: usize) -> Result<(), ClipError> { todo!() } - #[profiling] fn clip_delete(&self, clip: ClipId) -> Result<(), ClipError> { todo!() } - #[profiling] fn source_create(&self) -> Result { todo!() } - #[profiling] fn source_is_playing(&self, source: SourceId) -> Option { todo!() } - #[profiling] fn source_get_volume(&self, source: SourceId) -> Option { todo!() } - #[profiling] fn source_set_volume(&self, source: SourceId, volume: f32) -> Result<(), SourceError> { todo!() } - #[profiling] fn source_get_clip(&self, source: SourceId) -> Option { todo!() } - #[profiling] fn source_set_clip(&self, source: SourceId, clip: ClipId) -> Result<(), SourceError> { todo!() } - #[profiling] fn source_play(&self, source: SourceId) -> Result<(), SourceError> { todo!() } - #[profiling] fn source_delete(&self, source: SourceId) -> Result<(), SourceError> { todo!() } diff --git a/modules/audio/src/sampling.rs b/modules/audio/src/sampling.rs index 1c074ba0..b76f2798 100644 --- a/modules/audio/src/sampling.rs +++ b/modules/audio/src/sampling.rs @@ -28,6 +28,6 @@ impl AudioSampleRate { /// Calculates the `Size` required for the given duration at this sample rate. pub fn calculate_size(&self, duration: TimeSpan) -> Size { - Size::from_bytes((duration.total_seconds() * self.bytes_per_second()).ceil() as usize) + Size::from_bytes((duration.as_seconds() * self.bytes_per_second()).ceil() as usize) } } diff --git a/modules/graphics/src/animations.rs b/modules/graphics/src/animations.rs index cf9d0225..9e4c1774 100644 --- a/modules/graphics/src/animations.rs +++ b/modules/graphics/src/animations.rs @@ -1,17 +1,17 @@ //! Animation support. -use common::{FastHashMap, StringName, TimeSpan, Vec2}; +use common::{FastHashMap, Identity, Lerp, Quat, StringName, TimeSpan, Vec2, Vec3}; -use crate::Color; +use crate::{Color, Color32}; /// An animation tree that can be used to drive animation state changes. /// /// The animation tree is a directed acyclic graph (DAG) where each node is an /// animation state. The root node is the current animation state, and the leaf /// nodes are the animation states that are being dynamically selected. -#[derive(Default)] pub struct AnimationTree { - state: T, + pub state: T, + nodes: FastHashMap>, current: Option, } @@ -26,12 +26,12 @@ pub struct AnimationState { } /// A condition that must be met for a transition to occur. -type Condition = Box, &T) -> bool>; +type AnimationCondition = Box, &T) -> bool>; /// A transition between two animation states. pub struct AnimationTransition { pub target: StringName, - pub condition: Condition, + pub condition: AnimationCondition, } /// A single clip of animation data. @@ -44,10 +44,12 @@ pub struct AnimationClip { /// A single track of animation data. #[derive(Clone)] pub enum AnimationTrack { - Position(AnimationTrackData), - Rotation(AnimationTrackData), - Scale(AnimationTrackData), + Scalar(AnimationTrackData), + Vec2(AnimationTrackData), + Vec3(AnimationTrackData), + Quat(AnimationTrackData), Color(AnimationTrackData), + Color32(AnimationTrackData), } /// Data for a single animation track. @@ -116,6 +118,18 @@ impl AnimationTree { state.time_elapsed = TimeSpan::ZERO; } + // evaluate all tracks and apply them to the state + for track in &state.clip.tracks { + match track { + AnimationTrack::Scalar(_) => {} + AnimationTrack::Vec2(_) => {} + AnimationTrack::Vec3(_) => {} + AnimationTrack::Quat(_) => {} + AnimationTrack::Color(_) => {} + AnimationTrack::Color32(_) => {} + } + } + // evaluate all transitions each tick for transition in &state.transitions { let AnimationTransition { condition, target } = transition; @@ -129,6 +143,24 @@ impl AnimationTree { } } +/// Evaluates the final value of the given keyframes by interpolation. +fn evaluate_keyframes(time: f32, keyframes: &[AnimationKeyFrame]) -> T { + for i in 0..keyframes.len() { + let keyframe = &keyframes[i]; + + if keyframe.time >= time { + let prev = &keyframes[i - 1]; + let next = keyframe; + + let t = (time - prev.time) / (next.time - prev.time); + + return T::lerp(prev.value, next.value, t); + } + } + + T::ZERO +} + #[cfg(test)] mod tests { use common::ToStringName; @@ -142,6 +174,30 @@ mod tests { pub is_jumping: bool, } + #[test] + fn it_should_evaluate_track_data() { + let keyframes = vec![ + AnimationKeyFrame { + time: 0.0, + value: Vec2::ZERO, + }, + AnimationKeyFrame { + time: 1.0, + value: Vec2::ONE, + }, + AnimationKeyFrame { + time: 2.0, + value: Vec2::ZERO, + }, + ]; + + println!("{:?}", evaluate_keyframes(0.5, &keyframes)); + println!("{:?}", evaluate_keyframes(1.0, &keyframes)); + println!("{:?}", evaluate_keyframes(1.5, &keyframes)); + println!("{:?}", evaluate_keyframes(2.0, &keyframes)); + println!("{:?}", evaluate_keyframes(2.5, &keyframes)); + } + #[test] fn it_should_support_basic_animations() { let mut tree = AnimationTree::new(AnimationParams::default()); @@ -151,7 +207,7 @@ mod tests { clip: AnimationClip { duration: TimeSpan::from_seconds(1.0), tracks: vec![ - AnimationTrack::Position(vec![ + AnimationTrack::Vec2(vec![ AnimationKeyFrame { time: 0.0, value: Vec2::ZERO, diff --git a/modules/input/src/lib.rs b/modules/input/src/lib.rs index f1908470..35f396cc 100644 --- a/modules/input/src/lib.rs +++ b/modules/input/src/lib.rs @@ -29,16 +29,3 @@ impl InputListener for F { self(event); } } - -/// Multiplexes events to multiple listeners. -pub struct InputMultiplexer { - listeners: Vec>, -} - -impl InputListener for InputMultiplexer { - fn on_event(&mut self, event: &InputEvent) { - for listener in &mut self.listeners { - listener.on_event(event); - } - } -} diff --git a/modules/scripting/Cargo.toml b/modules/scripting/Cargo.toml deleted file mode 100644 index d0419c76..00000000 --- a/modules/scripting/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "surreal-scripting" -description = "Input engine for Surreal" -authors.workspace = true -edition.workspace = true - -[dependencies] -common = { package = "surreal-common", path = "../../common" } diff --git a/modules/scripting/src/lib.rs b/modules/scripting/src/lib.rs deleted file mode 100644 index 9decb3b2..00000000 --- a/modules/scripting/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Scripting engine for Surreal - -pub trait ScriptBackend {} diff --git a/src/lib.rs b/src/lib.rs index 03cf8861..aa4e7e88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,8 +22,6 @@ pub extern crate editor; pub extern crate graphics; #[cfg(feature = "input")] pub extern crate input; -#[cfg(feature = "scripting")] -pub extern crate scripting; pub mod backends { #[cfg(feature = "sdl")]