1
use figures::{Displayable, Pixels, Points, Scaled};
2

            
3
use crate::math::{Angle, ExtentsRect, Point, Rect, Size};
4
use crate::texture::Texture;
5
use crate::Error;
6
mod batch;
7
mod collection;
8
mod gpu_batch;
9
mod pipeline;
10
mod sheet;
11
pub(crate) use self::batch::Batch;
12
pub(crate) use self::gpu_batch::{BatchBuffers, GpuBatch};
13
pub(crate) use self::pipeline::Pipeline;
14

            
15
mod source;
16
use std::collections::HashMap;
17
use std::iter::IntoIterator;
18
use std::sync::Arc;
19
use std::time::Duration;
20

            
21
pub use self::collection::*;
22
pub use self::pipeline::VertexShaderSource;
23
pub use self::sheet::*;
24
pub use self::source::*;
25

            
26
/// Includes an [Aseprite](https://www.aseprite.org/) sprite sheet and Json
27
/// export. For more information, see [`Sprite::load_aseprite_json`]. This macro
28
/// will append ".png" and ".json" to the path provided and include both files
29
/// in your binary.
30
#[macro_export]
31
macro_rules! include_aseprite_sprite {
32
    ($path:expr) => {{
33
        $crate::include_texture!(concat!($path, ".png")).and_then(|texture| {
34
            $crate::sprite::Sprite::load_aseprite_json(
35
                include_str!(concat!($path, ".json")),
36
                &texture,
37
            )
38
        })
39
    }};
40
}
41

            
42
/// The animation mode of the sprite.
43
#[derive(Debug, Clone)]
44
pub enum AnimationMode {
45
    /// Iterate frames in order. When at the end, reset to the start.
46
    Forward,
47
    /// Iterate frames in reverse order. When at the start, reset to the end.
48
    Reverse,
49
    /// Iterate frames starting at the beginning and continuously iterating
50
    /// forwards and backwards across the frames, changing direction whenever
51
    /// the start or end are reached.
52
    PingPong,
53
}
54

            
55
impl AnimationMode {
56
    const fn default_direction(&self) -> AnimationDirection {
57
        match self {
58
            AnimationMode::Forward | AnimationMode::PingPong => AnimationDirection::Forward,
59
            AnimationMode::Reverse => AnimationDirection::Reverse,
60
        }
61
    }
62
}
63

            
64
#[derive(Debug, Clone)]
65
enum AnimationDirection {
66
    Forward,
67
    Reverse,
68
}
69

            
70
/// A sprite is a renderable graphic with optional animations.
71
///
72
/// Cloning a sprite is cheap. When cloning, the animations will be shared
73
/// between all clones of the sprite, but each sprite will track its current
74
/// frame/tag independently.
75
#[derive(Debug, Clone)]
76
pub struct Sprite {
77
    /// The animations that form this sprite.
78
    pub animations: SpriteAnimations,
79
    elapsed_since_frame_change: Duration,
80
    current_tag: Option<String>,
81
    current_frame: usize,
82
    current_animation_direction: AnimationDirection,
83
}
84

            
85
impl From<SpriteAnimations> for Sprite {
86
    fn from(animations: SpriteAnimations) -> Self {
87
        Self::new(animations)
88
    }
89
}
90

            
91
impl Sprite {
92
    /// Returns a new sprite with `animations`.
93
    #[must_use]
94
    pub const fn new(animations: SpriteAnimations) -> Self {
95
        Self {
96
            animations,
97
            current_frame: 0,
98
            current_tag: None,
99
            elapsed_since_frame_change: Duration::from_millis(0),
100
            current_animation_direction: AnimationDirection::Forward,
101
        }
102
    }
103

            
104
    /// For merging multiple Sprites that have no tags within them
105
    #[must_use]
106
    pub fn merged<S: Into<String>, I: IntoIterator<Item = (S, Self)>>(source: I) -> Self {
107
        let mut combined = HashMap::new();
108
        for (name, sprite) in source {
109
            combined.insert(
110
                Some(name.into()),
111
                sprite
112
                    .animations
113
                    .animation_for(&Option::<&str>::None)
114
                    .unwrap()
115
                    .clone(),
116
            );
117
        }
118
        Self::new(SpriteAnimations::new(combined))
119
    }
120

            
121
    /// Creates an instance from a texture. This creates a `SpriteAnimation`
122
    /// with no tag and a single frame.
123
    #[must_use]
124
    pub fn single_frame(texture: Texture) -> Self {
125
        let source = SpriteSource::entire_texture(texture);
126
        let mut frames = HashMap::new();
127
        frames.insert(
128
            None,
129
            SpriteAnimation::new(vec![SpriteFrame {
130
                source,
131
                duration: None,
132
            }])
133
            .with_mode(AnimationMode::Forward),
134
        );
135
        let frames = SpriteAnimations::new(frames);
136

            
137
        Self::new(frames)
138
    }
139

            
140
    /// Loads [Aseprite](https://www.aseprite.org/) JSON export format, when
141
    /// using the correct settings.
142
    ///
143
    /// For the JSON data, use the Hash export option (default), and use either
144
    /// spaces or underscores (_) inbetween the fields in the name. Ensure
145
    /// `{frame}` is the last field in the name before the extension. E.g.,
146
    /// `{tag}_{frame}.{extension}`
147
    #[allow(clippy::too_many_lines)]
148
    // TODO refactor. Now that I know more about serde, this probably can be parsed
149
    // with a complex serde type.
150
    pub fn load_aseprite_json(raw_json: &str, texture: &Texture) -> crate::Result<Self> {
151
        let json = json::parse(raw_json)?;
152

            
153
        // Validate the data
154
        let meta = &json["meta"];
155
        if !meta.is_object() {
156
            return Err(Error::SpriteParse(
157
                "invalid aseprite json: No `meta` section".to_owned(),
158
            ));
159
        }
160

            
161
        let texture_size = texture.size();
162
        if meta["size"]["w"] != texture_size.width || meta["size"]["h"] != texture_size.height {
163
            return Err(Error::SpriteParse(
164
                "invalid aseprite json: Size did not match input texture".to_owned(),
165
            ));
166
        }
167

            
168
        let mut frames = HashMap::new();
169
        for (name, frame) in json["frames"].entries() {
170
            // Remove the extension, if present
171
            let name = name.split('.').next().unwrap();
172
            // Split by _ or ' 'as per the documentation of this method.
173
            let name_parts = name.split(|c| c == '_' || c == ' ').collect::<Vec<_>>();
174
            let frame_number = name_parts[name_parts.len() - 1]
175
                .parse::<usize>()
176
                .or_else(|_| {
177
                    if json["frames"].len() == 1 {
178
                        Ok(0)
179
                    } else {
180
                        Err(Error::SpriteParse(
181
                            "invalid aseprite json: frame was not numeric.".to_owned(),
182
                        ))
183
                    }
184
                })?;
185

            
186
            let duration = match frame["duration"].as_u64() {
187
                Some(millis) => Duration::from_millis(millis),
188
                None => {
189
                    return Err(Error::SpriteParse(
190
                        "invalid aseprite json: invalid duration".to_owned(),
191
                    ))
192
                }
193
            };
194

            
195
            let frame = Rect::new(
196
                Point::new(
197
                    frame["frame"]["x"].as_u32().ok_or_else(|| {
198
                        Error::SpriteParse(
199
                            "invalid aseprite json: frame x was not valid".to_owned(),
200
                        )
201
                    })?,
202
                    frame["frame"]["y"].as_u32().ok_or_else(|| {
203
                        Error::SpriteParse(
204
                            "invalid aseprite json: frame y was not valid".to_owned(),
205
                        )
206
                    })?,
207
                ),
208
                Size::new(
209
                    frame["frame"]["w"].as_u32().ok_or_else(|| {
210
                        Error::SpriteParse(
211
                            "invalid aseprite json: frame w was not valid".to_owned(),
212
                        )
213
                    })?,
214
                    frame["frame"]["h"].as_u32().ok_or_else(|| {
215
                        Error::SpriteParse(
216
                            "invalid aseprite json: frame h was not valid".to_owned(),
217
                        )
218
                    })?,
219
                ),
220
            );
221

            
222
            let source = SpriteSource::new(frame, texture.clone());
223

            
224
            frames.insert(
225
                frame_number,
226
                SpriteFrame {
227
                    duration: Some(duration),
228
                    source,
229
                },
230
            );
231
        }
232

            
233
        let mut animations = HashMap::new();
234
        for tag in meta["frameTags"].members() {
235
            let direction = if tag["direction"] == "forward" {
236
                AnimationMode::Forward
237
            } else if tag["direction"] == "reverse" {
238
                AnimationMode::Reverse
239
            } else if tag["direction"] == "pingpong" {
240
                AnimationMode::PingPong
241
            } else {
242
                return Err(Error::SpriteParse(
243
                    "invalid aseprite json: frameTags direction is an unknown value".to_owned(),
244
                ));
245
            };
246

            
247
            let name = tag["name"].as_str().map(str::to_owned);
248

            
249
            let start_frame = tag["from"].as_usize().ok_or_else(|| {
250
                Error::SpriteParse(
251
                    "invalid aseprite json: frameTags from was not numeric".to_owned(),
252
                )
253
            })?;
254
            let end_frame = tag["to"].as_usize().ok_or_else(|| {
255
                Error::SpriteParse(
256
                    "invalid aseprite json: frameTags from was not numeric".to_owned(),
257
                )
258
            })?;
259
            let mut animation_frames = Vec::new();
260
            for i in start_frame..=end_frame {
261
                let frame = frames.get(&i).ok_or_else(|| {
262
                    Error::SpriteParse(
263
                        "invalid aseprite json: frameTags frame was out of bounds".to_owned(),
264
                    )
265
                })?;
266
                animation_frames.push(frame.clone());
267
            }
268

            
269
            animations.insert(
270
                name,
271
                SpriteAnimation::new(animation_frames).with_mode(direction),
272
            );
273
        }
274

            
275
        let mut frames: Vec<_> = frames.into_iter().collect();
276
        frames.sort_by(|a, b| a.0.cmp(&b.0));
277

            
278
        animations.insert(
279
            None,
280
            SpriteAnimation::new(frames.iter().map(|(_, f)| f.clone()).collect())
281
                .with_mode(AnimationMode::Forward),
282
        );
283

            
284
        Ok(Self::new(SpriteAnimations::new(animations)))
285
    }
286

            
287
    /// Sets the current tag for the animation. If the tag currently matches,
288
    /// nothing will happen. If it is a new tag, the current frame and animation
289
    /// direction will be switched to the values from the new tag.
290
    pub fn set_current_tag<S: Into<String>>(&mut self, tag: Option<S>) -> crate::Result<()> {
291
        let new_tag = tag.map(Into::into);
292
        if self.current_tag != new_tag {
293
            self.current_animation_direction = {
294
                let animation = self
295
                    .animations
296
                    .animations
297
                    .get(&new_tag)
298
                    .ok_or(Error::InvalidSpriteTag)?;
299
                animation.mode.default_direction()
300
            };
301
            self.current_frame = 0;
302
            self.current_tag = new_tag;
303
        }
304

            
305
        Ok(())
306
    }
307

            
308
    /// Returns the current tag.
309
    #[must_use]
310
    pub fn current_tag(&self) -> Option<&'_ str> {
311
        self.current_tag.as_deref()
312
    }
313

            
314
    /// Gets the current frame after advancing the animation for `elapsed`
315
    /// duration. If you need to invoke this multiple times in a single frame,
316
    /// pass `None` on subsequent calls. In general, you should clone sprites
317
    /// rather than reuse them. Kludgine ensures that your texture and animation
318
    /// data will be shared and not cloned.
319
    pub fn get_frame(&mut self, elapsed: Option<Duration>) -> crate::Result<SpriteSource> {
320
        if let Some(elapsed) = elapsed {
321
            self.elapsed_since_frame_change += elapsed;
322

            
323
            let current_frame_duration = self.with_current_frame(|frame| frame.duration)?;
324
            if let Some(frame_duration) = current_frame_duration {
325
                if self.elapsed_since_frame_change > frame_duration {
326
                    self.elapsed_since_frame_change = Duration::from_nanos(
327
                        (self.elapsed_since_frame_change.as_nanos() % frame_duration.as_nanos())
328
                            as u64,
329
                    );
330
                    self.advance_frame()?;
331
                }
332
            }
333
        }
334

            
335
        self.current_frame()
336
    }
337

            
338
    /// Retrieve the current animation frame, if set and valid.
339
    #[inline]
340
    pub fn current_frame(&self) -> crate::Result<SpriteSource> {
341
        self.with_current_frame(|frame| frame.source.clone())
342
    }
343

            
344
    /// Returns the amount of time remaining until the next frame is due to be
345
    /// shown for this sprite. Can be used to calculate redraws more efficiently
346
    /// if you're not rendering at a constant framerate.
347
    pub fn remaining_frame_duration(&self) -> crate::Result<Option<Duration>> {
348
        let duration = self
349
            .with_current_frame(|frame| frame.duration)?
350
            .map(|frame_duration| {
351
                frame_duration
352
                    .checked_sub(self.elapsed_since_frame_change)
353
                    .unwrap_or_default()
354
            });
355

            
356
        Ok(duration)
357
    }
358

            
359
    fn advance_frame(&mut self) -> crate::Result<()> {
360
        self.current_frame = self.next_frame()?;
361
        Ok(())
362
    }
363

            
364
    #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
365
    fn next_frame(&mut self) -> crate::Result<usize> {
366
        let starting_frame = self.current_frame as i32;
367
        let animation = self
368
            .animations
369
            .animations
370
            .get(&self.current_tag)
371
            .ok_or(Error::InvalidSpriteTag)?;
372

            
373
        let next_frame = match self.current_animation_direction {
374
            AnimationDirection::Forward => starting_frame + 1,
375
            AnimationDirection::Reverse => starting_frame - 1,
376
        };
377

            
378
        Ok(if next_frame < 0 {
379
            match animation.mode {
380
                AnimationMode::Forward => unreachable!(),
381
                AnimationMode::Reverse => {
382
                    // Cycle back to the last frame
383
                    animation.frames.len() - 1
384
                }
385
                AnimationMode::PingPong => {
386
                    self.current_animation_direction = AnimationDirection::Forward;
387
                    1
388
                }
389
            }
390
        } else if next_frame as usize >= animation.frames.len() {
391
            match animation.mode {
392
                AnimationMode::Reverse => unreachable!(),
393
                AnimationMode::Forward => 0,
394
                AnimationMode::PingPong => {
395
                    self.current_animation_direction = AnimationDirection::Reverse;
396
                    (animation.frames.len() - 2).max(0)
397
                }
398
            }
399
        } else {
400
            next_frame as usize
401
        })
402
    }
403

            
404
    /// If tag is valid, invoke `f` with the current animation frame.
405
    fn with_current_frame<F, R>(&self, f: F) -> crate::Result<R>
406
    where
407
        F: Fn(&SpriteFrame) -> R,
408
    {
409
        let animation = self
410
            .animations
411
            .animations
412
            .get(&self.current_tag)
413
            .ok_or(Error::InvalidSpriteTag)?;
414

            
415
        Ok(f(&animation.frames[self.current_frame]))
416
    }
417
}
418

            
419
/// A collection of [`SpriteAnimation`]s. This is an immutable object that
420
/// shares data when cloned to minimize data copies.
421
#[derive(Clone, Debug)]
422
pub struct SpriteAnimations {
423
    animations: Arc<HashMap<Option<String>, SpriteAnimation>>,
424
}
425

            
426
impl SpriteAnimations {
427
    /// Creates a new collection from `animations`.
428
    #[must_use]
429
    pub fn new(animations: HashMap<Option<String>, SpriteAnimation>) -> Self {
430
        Self {
431
            animations: Arc::new(animations),
432
        }
433
    }
434

            
435
    /// Returns the animation for `tag`.
436
    #[must_use]
437
    pub fn animation_for(&self, tag: &Option<impl ToString>) -> Option<&'_ SpriteAnimation> {
438
        self.animations.get(&tag.as_ref().map(ToString::to_string))
439
    }
440
}
441

            
442
/// An animation of one or more [`SpriteFrame`]s.
443
#[derive(Debug, Clone)]
444
pub struct SpriteAnimation {
445
    /// The frames of the animation.
446
    pub frames: Vec<SpriteFrame>,
447
    /// The mode of the animation.
448
    pub mode: AnimationMode,
449
}
450

            
451
impl SpriteAnimation {
452
    /// Creates a new animation with `frames` and [`AnimationMode::Forward`].
453
    #[must_use]
454
    pub fn new(frames: Vec<SpriteFrame>) -> Self {
455
        Self {
456
            frames,
457
            mode: AnimationMode::Forward,
458
        }
459
    }
460

            
461
    /// Builder-style function. Sets `mode` and returns self.
462
    #[must_use]
463
    pub const fn with_mode(mut self, mode: AnimationMode) -> Self {
464
        self.mode = mode;
465
        self
466
    }
467
}
468

            
469
/// A single frame for a [`SpriteAnimation`].
470
#[derive(Debug, Clone)]
471
pub struct SpriteFrame {
472
    /// The source to render.
473
    pub source: SpriteSource,
474
    /// The length the frame should be displayed. `None` will act as an infinite
475
    /// duration.
476
    pub duration: Option<Duration>,
477
}
478

            
479
impl SpriteFrame {
480
    /// Creates a new frame with `source` and no duration.
481
    #[must_use]
482
    pub const fn new(source: SpriteSource) -> Self {
483
        Self {
484
            source,
485
            duration: None,
486
        }
487
    }
488

            
489
    /// Builder-style function. Sets `duration` and returns self.
490
    #[must_use]
491
    pub const fn with_duration(mut self, duration: Duration) -> Self {
492
        self.duration = Some(duration);
493
        self
494
    }
495
}
496

            
497
/// A rendered sprite.
498
#[derive(Clone, Debug)]
499
pub struct RenderedSprite {
500
    pub(crate) data: Arc<RenderedSpriteData>,
501
}
502

            
503
impl RenderedSprite {
504
    #[must_use]
505
    pub(crate) fn new(
506
        render_at: ExtentsRect<f32, Pixels>,
507
        rotation: SpriteRotation<Pixels>,
508
        alpha: f32,
509
        source: SpriteSource,
510
    ) -> Self {
511
        Self {
512
            data: Arc::new(RenderedSpriteData {
513
                render_at,
514
                rotation,
515
                alpha,
516
                source,
517
            }),
518
        }
519
    }
520
}
521

            
522
#[derive(Debug)]
523
pub(crate) struct RenderedSpriteData {
524
    pub render_at: ExtentsRect<f32, Pixels>,
525
    pub rotation: SpriteRotation<Pixels>,
526
    pub alpha: f32,
527
    pub source: SpriteSource,
528
}
529

            
530
/// A rotation of a sprite.
531
#[derive(Copy, Clone, Debug)]
532
#[must_use]
533
pub struct SpriteRotation<Unit = Scaled> {
534
    /// The angle to rotate around `screen_location`.
535
    pub angle: Option<Angle>,
536
    /// The location to rotate the sprite around. If not specified, the center
537
    /// of the sprite is used.
538
    pub location: Option<Point<f32, Unit>>,
539
}
540

            
541
impl SpriteRotation<Pixels> {
542
    /// Returns a value that performs no rotation.
543
    pub const fn none() -> Self {
544
        Self {
545
            angle: None,
546
            location: None,
547
        }
548
    }
549

            
550
    /// Returns a rotation around the center of the shape.
551
    pub const fn around_center(angle: Angle) -> Self {
552
        Self {
553
            angle: Some(angle),
554
            location: None,
555
        }
556
    }
557
}
558

            
559
impl<Unit> Default for SpriteRotation<Unit> {
560
    fn default() -> Self {
561
        Self {
562
            angle: None,
563
            location: None,
564
        }
565
    }
566
}
567

            
568
impl<Unit> SpriteRotation<Unit> {
569
    /// Returns a rotation around `location`.
570
    pub const fn around(angle: Angle, location: Point<f32, Unit>) -> Self {
571
        Self {
572
            angle: Some(angle),
573
            location: Some(location),
574
        }
575
    }
576
}
577

            
578
impl Displayable<f32> for SpriteRotation<Pixels> {
579
    type Pixels = Self;
580
    type Points = SpriteRotation<Points>;
581
    type Scaled = SpriteRotation<Scaled>;
582

            
583
    fn to_pixels(&self, _scale: &figures::DisplayScale<f32>) -> Self::Pixels {
584
        *self
585
    }
586

            
587
    fn to_points(&self, scale: &figures::DisplayScale<f32>) -> Self::Points {
588
        SpriteRotation {
589
            angle: self.angle,
590
            location: self.location.map(|l| l.to_points(scale)),
591
        }
592
    }
593

            
594
    fn to_scaled(&self, scale: &figures::DisplayScale<f32>) -> Self::Scaled {
595
        SpriteRotation {
596
            angle: self.angle,
597
            location: self.location.map(|l| l.to_scaled(scale)),
598
        }
599
    }
600
}
601

            
602
impl Displayable<f32> for SpriteRotation<Points> {
603
    type Pixels = SpriteRotation<Pixels>;
604
    type Points = Self;
605
    type Scaled = SpriteRotation<Scaled>;
606

            
607
    fn to_pixels(&self, scale: &figures::DisplayScale<f32>) -> Self::Pixels {
608
        SpriteRotation {
609
            angle: self.angle,
610
            location: self.location.map(|l| l.to_pixels(scale)),
611
        }
612
    }
613

            
614
    fn to_points(&self, _scale: &figures::DisplayScale<f32>) -> Self::Points {
615
        *self
616
    }
617

            
618
    fn to_scaled(&self, scale: &figures::DisplayScale<f32>) -> Self::Scaled {
619
        SpriteRotation {
620
            angle: self.angle,
621
            location: self.location.map(|l| l.to_scaled(scale)),
622
        }
623
    }
624
}
625

            
626
impl Displayable<f32> for SpriteRotation<Scaled> {
627
    type Pixels = SpriteRotation<Pixels>;
628
    type Points = SpriteRotation<Points>;
629
    type Scaled = Self;
630

            
631
    fn to_pixels(&self, scale: &figures::DisplayScale<f32>) -> Self::Pixels {
632
        SpriteRotation {
633
            angle: self.angle,
634
            location: self.location.map(|l| l.to_pixels(scale)),
635
        }
636
    }
637

            
638
    fn to_points(&self, scale: &figures::DisplayScale<f32>) -> Self::Points {
639
        SpriteRotation {
640
            angle: self.angle,
641
            location: self.location.map(|l| l.to_points(scale)),
642
        }
643
    }
644

            
645
    fn to_scaled(&self, _scale: &figures::DisplayScale<f32>) -> Self::Scaled {
646
        *self
647
    }
648
}
649

            
650
/// The Srgb colorspace. Used as a `VertexShaderSource` in
651
/// [`FrameRenderer`](crate::frame_renderer::FrameRenderer).
652
pub struct Srgb;
653
/// The uncorrected Rgb colorspace. Used as a
654
/// [`VertexShaderSource`](crate::sprite::VertexShaderSource) in
655
/// [`FrameRenderer`](crate::frame_renderer::FrameRenderer).
656
pub struct Normal;