diff --git a/src/lib.rs b/src/lib.rs index 35c5ff5..910c665 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,9 +29,9 @@ pub mod math; /// Models abstractions pub mod models; pub use models::{ - Animation, Armature, Color, Image, Joint, Material, Mesh, Model, RenderModels, Transform, - VertexAttribute, VertexBitangent, VertexJoints, VertexNormal, VertexPosition, VertexTangent, - VertexTexture, VertexWeights, + Animation, AnimationPlayer, AnimationState, Armature, Color, Image, Joint, Material, Mesh, + Model, RenderModels, Transform, VertexAttribute, VertexBitangent, VertexJoints, VertexNormal, + VertexPosition, VertexTangent, VertexTexture, VertexWeights, }; /// Rendering tools and routines diff --git a/src/models.rs b/src/models.rs index b40252d..e673e99 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,5 +1,5 @@ mod animations; -pub use animations::{Animation, Interpolation}; +pub use animations::{Animation, AnimationPlayer, AnimationState, Interpolation}; mod armatures; pub use armatures::{Armature, Joint}; @@ -42,11 +42,13 @@ pub struct Model { pub scale: Vec3, pub rotate: Quat, pub pose: Vec, + pub animation: Option, } impl From for Entity { fn from(model: Model) -> Self { - Entity::new(( + let animation = model.animation; + let mut entity = Entity::new(( model.mesh, model.material, model.armature, @@ -54,7 +56,13 @@ impl From for Entity { Transform3D::new(model.translate, model.rotate, model.scale), model.pose, ), - )) + )); + + if let Some(player) = animation { + entity = entity.with(player); + } + + entity } } @@ -68,6 +76,7 @@ impl Default for Model { scale: Vec3::new(1.0, 1.0, 1.0), rotate: Quat::IDENTITY, pose: Vec::new(), + animation: None, } } } diff --git a/src/models/animations.rs b/src/models/animations.rs index 21887f7..59b5596 100644 --- a/src/models/animations.rs +++ b/src/models/animations.rs @@ -26,6 +26,10 @@ impl Animation { } } + pub fn name(&self) -> &str { + self.name.as_str() + } + /// Returns [`Duration`] of the animation pub fn duration(&self) -> Duration { self.duration @@ -88,8 +92,15 @@ impl Animation { /// Samples the animeation at some keyframe (s) and returns a HashMap of /// [`crate::assets::Skin`] joint id to [`TransformBuilder`] - pub fn sample(&self, keyframe: f32) -> HashMap, TransformBuilder> { + pub fn sample(&self, timestamp: f32) -> HashMap, TransformBuilder> { let mut result = HashMap::new(); + let duration_secs = self.duration.as_secs_f32(); + + let keyframe = if timestamp > duration_secs { + timestamp % duration_secs + } else { + timestamp + }; for channel in &self.translation_channels { if let Some(transform) = channel.sample(keyframe) { @@ -220,3 +231,125 @@ impl Channel { None } } + +/// Animation player state +/// +/// [`Duration`] contains current time offset from the beginning of the animation +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum AnimationState { + /// Animation is playing + Play(Duration), + /// Animation is looped + Loop(Duration), + /// Animation is stopped + Stop, +} + +impl AnimationState { + pub fn play() -> Self { + AnimationState::Play(Duration::from_secs(0)) + } + + pub fn play_loop() -> Self { + AnimationState::Loop(Duration::from_secs(0)) + } + + pub fn stop() -> Self { + AnimationState::Stop + } +} + +/// Component to control model animation +pub struct AnimationPlayer { + animation: Id, + state: AnimationState, + /// animation speed + speed: f32, +} + +impl AnimationPlayer { + /// creates new component instance with specified animation with [`State::Stop`] + pub fn new(animation: Id) -> Self { + Self { + animation, + state: AnimationState::stop(), + speed: 1.0, + } + } + + /// creates new component instance with specified animation with [`State::Play`] + pub fn play(animation: Id) -> Self { + Self { + animation, + state: AnimationState::play(), + speed: 1.0, + } + } + + /// creates new component instance with specified animation with [`State::Loop`] + pub fn looped(animation: Id) -> Self { + Self { + animation, + state: AnimationState::play_loop(), + speed: 1.0, + } + } + + /// Starts current animation + pub fn start(&mut self) { + self.state = AnimationState::play(); + } + + /// Starts current animation looped + pub fn start_loop(&mut self) { + self.state = AnimationState::play_loop(); + } + + /// Stops current animation + pub fn stop(&mut self) { + self.state = AnimationState::stop(); + } + + /// Changes current animation + pub fn animate(&mut self, animation: Id) { + self.animation = animation; + self.state = AnimationState::stop(); + } + + /// Returns current animation [`Id`] + pub fn animation(&self) -> Id { + self.animation + } + + /// Returns current [`State`] + pub fn state(&self) -> AnimationState { + self.state + } + + pub fn update(&mut self, delta: Duration, duration: Duration) -> Option { + let (state, duration) = match self.state { + AnimationState::Play(current) => { + let new_duration = current + delta.mul_f32(self.speed); + let state = if new_duration < duration { + AnimationState::Play(new_duration) + } else { + AnimationState::Stop + }; + (state, Some(new_duration)) + } + AnimationState::Loop(current) => { + let mut new_duration = current + delta.mul_f32(self.speed); + if !(new_duration < duration) { + new_duration = Duration::from_secs_f32( + new_duration.as_secs_f32() % duration.as_secs_f32(), + ); + } + let state = AnimationState::Loop(new_duration); + (state, Some(new_duration)) + } + AnimationState::Stop => (AnimationState::Stop, None), + }; + self.state = state; + duration + } +} diff --git a/src/models/armatures.rs b/src/models/armatures.rs index ce700fb..4dedec2 100644 --- a/src/models/armatures.rs +++ b/src/models/armatures.rs @@ -34,7 +34,6 @@ impl Armature { pub fn transform( &self, - // model_transform: &Mat4, joint_local_transforms: Option, TransformBuilder>>, ) -> Vec { let mut result = vec![Mat4::IDENTITY; self.joints.len()]; @@ -59,11 +58,6 @@ impl Armature { .matrix(); result[joint_index] = global_joint_transform; - // joint - // .inverse_bind_matrix - // .as_ref() - // .map(|&inverse_bind_matrix| global_joint_transform * inverse_bind_matrix) - // .unwrap_or(global_joint_transform); } result @@ -84,34 +78,6 @@ impl Armature { .collect::>() } } -/* -pub fn transform( - &self, - skin_transform: &mut Pose, - model_transform: &Mat4, - local_transforms: Option>, - ) { - for (i, joint) in self.joints.iter().enumerate() { - let parent_transform = joint - .parent_id - .map(|parent_id| skin_transform.joints[self.index(parent_id)].global_transform) - .or(Some(*model_transform)) - .unwrap(); - - let local_transform = local_transforms - .as_ref() - .map(|l| l.get(&joint.id)) - .unwrap_or(None); - - if i < skin_transform.joints.len() { - skin_transform.joints[i] = joint.transform(&parent_transform, local_transform); - } else { - panic!("Joints count exceeds limit of {:?}", MAX_JOINTS); - } - } - } - - */ #[derive(Default, Debug, Clone)] /// Joint data structure