1#![warn(missing_docs)]
25
26use crate::{
27 core::{
28 algebra::Vector2,
29 math::Rect,
30 reflect::prelude::*,
31 uuid::{uuid, Uuid},
32 uuid_provider,
33 visitor::prelude::*,
34 TypeUuidProvider,
35 },
36 spritesheet::signal::Signal,
37};
38use std::collections::vec_deque::VecDeque;
39use strum_macros::{AsRefStr, EnumString, VariantNames};
40
41pub mod signal;
42
43pub trait SpriteSheetTexture: PartialEq + Clone + Visit + Reflect + Default {}
45
46impl<T: PartialEq + Clone + Visit + Reflect + Default> SpriteSheetTexture for T {}
47
48#[derive(
50 Visit, Reflect, Copy, Clone, Eq, PartialEq, Debug, AsRefStr, EnumString, VariantNames, Default,
51)]
52pub enum Status {
53 Playing,
55
56 #[default]
59 Stopped,
60
61 Paused,
63}
64
65uuid_provider!(Status = "74a31122-a7a8-476c-ab87-77e53cf0523c");
66
67#[derive(Visit, Reflect, Clone, Debug, Eq, PartialEq)]
69#[non_exhaustive]
70pub enum Event {
71 Signal(u64),
73}
74
75impl Default for Event {
76 fn default() -> Self {
77 Self::Signal(0)
78 }
79}
80
81#[derive(Reflect, Visit, Clone, Debug, PartialEq, Eq)]
83pub struct SpriteSheetFramesContainer<T>
84where
85 T: SpriteSheetTexture,
86{
87 size: Vector2<u32>,
88 frames: Vec<Vector2<u32>>,
89 #[visit(optional)]
90 texture: Option<T>,
91}
92
93impl<T> SpriteSheetFramesContainer<T>
94where
95 T: SpriteSheetTexture,
96{
97 pub fn push(&mut self, bounds: Vector2<u32>) {
99 self.frames.push(bounds)
100 }
101
102 pub fn remove(&mut self, index: usize) -> Vector2<u32> {
104 self.frames.remove(index)
105 }
106
107 pub fn len(&self) -> usize {
109 self.frames.len()
110 }
111
112 pub fn is_empty(&self) -> bool {
114 self.frames.is_empty()
115 }
116
117 pub fn get(&self, index: usize) -> Option<&Vector2<u32>> {
119 self.frames.get(index)
120 }
121
122 pub fn set_size(&mut self, size: Vector2<u32>) {
124 self.size = Vector2::new(size.x.max(1), size.y.max(1));
125 }
126
127 pub fn size(&self) -> Vector2<u32> {
129 self.size
130 }
131
132 pub fn sort_by_position(&mut self) {
136 self.frames.sort_by_key(|p| p.y * self.size.x + p.x)
137 }
138
139 pub fn iter(&self) -> impl Iterator<Item = &Vector2<u32>> {
141 self.frames.iter()
142 }
143
144 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Vector2<u32>> {
146 self.frames.iter_mut()
147 }
148
149 pub fn texture(&self) -> Option<T> {
151 self.texture.clone()
152 }
153}
154
155impl<T> Default for SpriteSheetFramesContainer<T>
156where
157 T: SpriteSheetTexture,
158{
159 fn default() -> Self {
160 Self {
161 size: Vector2::new(1, 1),
162 frames: vec![],
163 texture: None,
164 }
165 }
166}
167
168#[derive(Visit, Reflect, Clone, Debug)]
171pub struct SpriteSheetAnimation<T>
172where
173 T: SpriteSheetTexture,
174{
175 #[visit(rename = "Frames")]
176 frames_container: SpriteSheetFramesContainer<T>,
177 current_frame: f32,
178 speed: f32,
179 status: Status,
180 looping: bool,
181 signals: Vec<Signal>,
182 #[visit(optional)]
183 #[reflect(setter = "set_texture")]
184 texture: Option<T>,
185 #[reflect(hidden)]
186 #[visit(skip)]
187 events: VecDeque<Event>,
188 #[visit(optional)]
189 max_event_capacity: usize,
190}
191
192impl<T: SpriteSheetTexture> PartialEq for SpriteSheetAnimation<T> {
193 fn eq(&self, other: &Self) -> bool {
194 self.frames_container == other.frames_container
195 && self.current_frame == other.current_frame
196 && self.speed == other.speed
197 && self.looping == other.looping
198 && self.signals == other.signals
199 && self.texture == other.texture
200 }
201}
202
203impl<T> TypeUuidProvider for SpriteSheetAnimation<T>
204where
205 T: SpriteSheetTexture,
206{
207 fn type_uuid() -> Uuid {
208 uuid!("1fa13feb-a16d-4539-acde-672aaeb0f62b")
209 }
210}
211
212impl<T> Default for SpriteSheetAnimation<T>
213where
214 T: SpriteSheetTexture,
215{
216 fn default() -> Self {
217 Self {
218 frames_container: Default::default(),
219 current_frame: 0.0,
220 speed: 10.0,
221 status: Default::default(),
222 looping: true,
223 signals: Default::default(),
224 texture: None,
225 events: Default::default(),
226 max_event_capacity: 32,
227 }
228 }
229}
230
231#[derive(Clone, Debug, PartialEq, Eq)]
234pub struct ImageParameters {
235 pub width: u32,
237
238 pub height: u32,
240
241 pub frame_width: u32,
243
244 pub frame_height: u32,
246
247 pub first_frame: u32,
249
250 pub last_frame: u32,
252
253 pub column_major: bool,
255}
256
257impl<T> SpriteSheetAnimation<T>
258where
259 T: SpriteSheetTexture,
260{
261 pub fn new() -> Self {
263 Self::default()
264 }
265
266 pub fn new_from_image_parameters(params: ImageParameters) -> Self {
334 let ImageParameters {
335 width,
336 height,
337 frame_width,
338 frame_height,
339 first_frame,
340 last_frame,
341 column_major,
342 } = params;
343
344 let width_in_frames = width / frame_width;
345 let height_in_frames = height / frame_height;
346
347 let frames = (first_frame..last_frame)
348 .map(|n| {
349 let x = if column_major {
350 n / width_in_frames
351 } else {
352 n % width_in_frames
353 };
354 let y = if column_major {
355 n % height_in_frames
356 } else {
357 n / height_in_frames
358 };
359
360 Vector2::new(x, y)
361 })
362 .collect::<Vec<_>>();
363
364 Self {
365 frames_container: SpriteSheetFramesContainer {
366 frames,
367 size: Vector2::new(width_in_frames, height_in_frames),
368 texture: None,
369 },
370 ..Default::default()
371 }
372 }
373
374 pub fn with_container(container: SpriteSheetFramesContainer<T>) -> Self {
376 Self {
377 frames_container: container,
378 ..Default::default()
379 }
380 }
381
382 pub fn set_texture(&mut self, texture: Option<T>) -> Option<T> {
384 self.frames_container.texture.clone_from(&texture);
385 std::mem::replace(&mut self.texture, texture)
386 }
387
388 pub fn texture(&self) -> Option<T> {
390 self.texture.clone()
391 }
392
393 pub fn get_max_event_capacity(&self) -> usize {
395 self.max_event_capacity
396 }
397
398 pub fn set_max_event_capacity(&mut self, max_event_capacity: usize) {
400 self.max_event_capacity = max_event_capacity;
401 }
402
403 pub fn frames(&self) -> &SpriteSheetFramesContainer<T> {
405 &self.frames_container
406 }
407
408 pub fn frames_mut(&mut self) -> &mut SpriteSheetFramesContainer<T> {
410 &mut self.frames_container
411 }
412
413 pub fn add_frame(&mut self, frame: Vector2<u32>) {
415 self.frames_container.push(frame);
416 }
417
418 pub fn remove_frame(&mut self, index: usize) -> Option<Vector2<u32>> {
420 if index < self.frames_container.len() {
421 self.current_frame = self.current_frame.min(self.frames_container.len() as f32);
422 Some(self.frames_container.remove(index))
423 } else {
424 None
425 }
426 }
427
428 pub fn update(&mut self, dt: f32) {
430 if self.status != Status::Playing {
431 return;
432 }
433
434 if self.frames_container.is_empty() {
435 self.status = Status::Stopped;
436 return;
437 }
438
439 let next_frame = self.current_frame + self.speed * dt;
440
441 for signal in self.signals.iter_mut().filter(|s| s.enabled) {
442 let signal_frame = signal.frame as f32;
443
444 if (self.speed >= 0.0
445 && (self.current_frame < signal_frame && next_frame >= signal_frame)
446 || self.speed < 0.0
447 && (self.current_frame > signal_frame && next_frame <= signal_frame))
448 && self.events.len() < self.max_event_capacity
449 {
450 self.events.push_back(Event::Signal(signal.id));
451 }
452 }
453
454 self.current_frame = next_frame;
455 if self.current_frame >= self.frames_container.len() as f32 {
456 if self.looping {
457 self.current_frame = 0.0;
459 } else {
460 self.current_frame = self.frames_container.len().saturating_sub(1) as f32;
462 self.status = Status::Stopped;
463 }
464 } else if self.current_frame <= 0.0 {
465 if self.looping {
466 self.current_frame = self.frames_container.len().saturating_sub(1) as f32;
468 } else {
469 self.current_frame = 0.0;
471 self.status = Status::Stopped;
472 }
473 }
474 }
475
476 pub fn current_frame(&self) -> usize {
478 self.current_frame as usize
479 }
480
481 pub fn frame_uv_rect(&self, i: usize) -> Option<Rect<f32>> {
483 assert_ne!(self.frames_container.size.x, 0);
484 assert_ne!(self.frames_container.size.y, 0);
485
486 self.frames_container.get(i).map(|pos| Rect {
487 position: Vector2::new(
488 pos.x as f32 / self.frames_container.size.x as f32,
489 pos.y as f32 / self.frames_container.size.y as f32,
490 ),
491 size: Vector2::new(
492 1.0 / self.frames_container.size.x as f32,
493 1.0 / self.frames_container.size.y as f32,
494 ),
495 })
496 }
497
498 pub fn current_frame_uv_rect(&self) -> Option<Rect<f32>> {
500 self.frame_uv_rect(self.current_frame())
501 }
502
503 pub fn set_current_frame(&mut self, current_frame: usize) {
505 self.current_frame = current_frame.min(self.frames_container.len()) as f32;
506 }
507
508 pub fn is_looping(&self) -> bool {
510 self.looping
511 }
512
513 pub fn set_looping(&mut self, looping: bool) {
515 self.looping = looping;
516 }
517
518 pub fn speed(&self) -> f32 {
520 self.speed
521 }
522
523 pub fn set_speed(&mut self, speed: f32) {
526 self.speed = speed;
527 }
528
529 pub fn rewind_to_beginning(&mut self) {
531 self.current_frame = 0.0;
532 }
533
534 pub fn rewind_to_end(&mut self) {
536 self.current_frame = self.frames_container.len().saturating_sub(1) as f32;
537 }
538
539 pub fn status(&self) -> Status {
541 self.status
542 }
543
544 pub fn play(&mut self) {
546 self.status = Status::Playing;
547 }
548
549 pub fn is_playing(&self) -> bool {
551 self.status == Status::Playing
552 }
553
554 pub fn stop(&mut self) {
556 self.status = Status::Stopped;
557 self.rewind_to_beginning();
558 }
559
560 pub fn is_stopped(&self) -> bool {
562 self.status == Status::Stopped
563 }
564
565 pub fn pause(&mut self) {
567 self.status = Status::Paused;
568 }
569
570 pub fn is_paused(&self) -> bool {
572 self.status == Status::Paused
573 }
574
575 pub fn add_signal(&mut self, signal: Signal) {
577 self.signals.push(signal)
578 }
579
580 pub fn remove_signal(&mut self, id: u64) {
582 self.signals.retain(|s| s.id != id)
583 }
584
585 pub fn pop_event(&mut self) -> Option<Event> {
587 self.events.pop_front()
588 }
589}
590
591#[cfg(test)]
592mod test {
593 use crate::spritesheet::{
594 signal::Signal, Event, ImageParameters, SpriteSheetAnimation, Status,
595 };
596 use fyrox_core::{algebra::Vector2, math::Rect, reflect::prelude::*, visitor::prelude::*};
597
598 #[derive(PartialEq, Clone, Reflect, Visit, Debug, Default)]
599 struct MyTexture {}
600
601 #[test]
602 fn test_sprite_sheet_one_row() {
603 let animation =
604 SpriteSheetAnimation::<MyTexture>::new_from_image_parameters(ImageParameters {
605 width: 128,
606 height: 128,
607 frame_width: 32,
608 frame_height: 32,
609 first_frame: 0,
610 last_frame: 4,
611 column_major: false,
612 });
613 assert_eq!(
614 animation.frame_uv_rect(0),
615 Some(Rect::new(0.0, 0.0, 0.25, 0.25))
616 );
617 assert_eq!(
618 animation.frame_uv_rect(1),
619 Some(Rect::new(0.25, 0.0, 0.25, 0.25))
620 );
621 assert_eq!(
622 animation.frame_uv_rect(2),
623 Some(Rect::new(0.5, 0.0, 0.25, 0.25))
624 );
625 assert_eq!(
626 animation.frame_uv_rect(3),
627 Some(Rect::new(0.75, 0.0, 0.25, 0.25))
628 );
629 }
630
631 #[test]
632 fn test_sprite_sheet_one_column() {
633 let animation =
634 SpriteSheetAnimation::<MyTexture>::new_from_image_parameters(ImageParameters {
635 width: 128,
636 height: 128,
637 frame_width: 32,
638 frame_height: 32,
639 first_frame: 0,
640 last_frame: 4,
641 column_major: true,
642 });
643 assert_eq!(
644 animation.frame_uv_rect(0),
645 Some(Rect::new(0.0, 0.0, 0.25, 0.25))
646 );
647 assert_eq!(
648 animation.frame_uv_rect(1),
649 Some(Rect::new(0.0, 0.25, 0.25, 0.25))
650 );
651 assert_eq!(
652 animation.frame_uv_rect(2),
653 Some(Rect::new(0.0, 0.5, 0.25, 0.25))
654 );
655 assert_eq!(
656 animation.frame_uv_rect(3),
657 Some(Rect::new(0.0, 0.75, 0.25, 0.25))
658 );
659 }
660
661 #[test]
662 fn test_sprite_sheet_row_partial() {
663 let animation =
664 SpriteSheetAnimation::<MyTexture>::new_from_image_parameters(ImageParameters {
665 width: 128,
666 height: 128,
667 frame_width: 32,
668 frame_height: 32,
669 first_frame: 2,
670 last_frame: 6,
671 column_major: false,
672 });
673 assert_eq!(
674 animation.frame_uv_rect(0),
675 Some(Rect::new(0.5, 0.0, 0.25, 0.25))
676 );
677 assert_eq!(
678 animation.frame_uv_rect(1),
679 Some(Rect::new(0.75, 0.0, 0.25, 0.25))
680 );
681 assert_eq!(
682 animation.frame_uv_rect(2),
683 Some(Rect::new(0.0, 0.25, 0.25, 0.25))
684 );
685 assert_eq!(
686 animation.frame_uv_rect(3),
687 Some(Rect::new(0.25, 0.25, 0.25, 0.25))
688 );
689 }
690
691 #[test]
692 fn test_sprite_sheet_column_partial() {
693 let animation =
694 SpriteSheetAnimation::<MyTexture>::new_from_image_parameters(ImageParameters {
695 width: 128,
696 height: 128,
697 frame_width: 32,
698 frame_height: 32,
699 first_frame: 2,
700 last_frame: 6,
701 column_major: true,
702 });
703 assert_eq!(
704 animation.frame_uv_rect(0),
705 Some(Rect::new(0.0, 0.5, 0.25, 0.25))
706 );
707 assert_eq!(
708 animation.frame_uv_rect(1),
709 Some(Rect::new(0.0, 0.75, 0.25, 0.25))
710 );
711 assert_eq!(
712 animation.frame_uv_rect(2),
713 Some(Rect::new(0.25, 0.0, 0.25, 0.25))
714 );
715 assert_eq!(
716 animation.frame_uv_rect(3),
717 Some(Rect::new(0.25, 0.25, 0.25, 0.25))
718 );
719 }
720
721 #[test]
722 fn test_sprite_sheet_playback() {
723 let mut animation =
724 SpriteSheetAnimation::<MyTexture>::new_from_image_parameters(ImageParameters {
725 width: 128,
726 height: 128,
727 frame_width: 32,
728 frame_height: 32,
729 first_frame: 2,
730 last_frame: 6,
731 column_major: true,
732 });
733
734 animation.speed = 1.0; animation.looping = false;
736
737 assert_eq!(animation.status, Status::Stopped);
738
739 animation.play();
740
741 assert_eq!(animation.status, Status::Playing);
742
743 let expected_output = [
744 Rect::new(0.0, 0.5, 0.25, 0.25),
745 Rect::new(0.0, 0.75, 0.25, 0.25),
746 Rect::new(0.25, 0.0, 0.25, 0.25),
747 Rect::new(0.25, 0.25, 0.25, 0.25),
748 ];
749
750 for &expected_frame in &expected_output {
751 assert_eq!(animation.current_frame_uv_rect(), Some(expected_frame));
752 animation.update(1.0);
753 }
754
755 assert_eq!(animation.status, Status::Stopped);
756
757 animation.speed = -1.0; animation.play();
760
761 for &expected_frame in expected_output.iter().rev() {
762 assert_eq!(animation.current_frame_uv_rect(), Some(expected_frame));
763 animation.update(1.0);
764 }
765 }
766
767 #[test]
768 fn test_signals() {
769 let mut animation = SpriteSheetAnimation::<MyTexture>::new();
770
771 animation.add_frame(Vector2::new(0, 0));
772 animation.add_frame(Vector2::new(1, 0));
773 animation.add_frame(Vector2::new(2, 0));
774
775 animation.set_speed(1.0);
776 animation.set_looping(false);
777 animation.play();
778
779 animation.add_signal(Signal {
780 id: 0,
781 frame: 1,
782 enabled: true,
783 });
784
785 animation.add_signal(Signal {
786 id: 1,
787 frame: 1,
788 enabled: false,
789 });
790
791 animation.add_signal(Signal {
792 id: 2,
793 frame: 2,
794 enabled: true,
795 });
796
797 for _ in 0..3 {
798 animation.update(1.0);
799 }
800
801 assert_eq!(animation.pop_event(), Some(Event::Signal(0)));
802 assert_eq!(animation.pop_event(), Some(Event::Signal(2)));
804 assert_eq!(animation.pop_event(), None);
806 }
807}