Skip to main content

fae/
sprite.rs

1use crate::renderer::{DrawCallHandle, Renderer};
2use crate::types::*;
3
4// TODO(0.5.1): Add an anchoring system for sprites for smooth resizes.
5// - The simpler way, `with_anchor(x, y)`, would anchor the whole
6//   sprite to some corner.
7// - The more advanced version would involve specifying the
8//   top-left/bottom-right corners and their individual anchors.
9// - The anchors should be specified in 0..1 floats, describing the %
10//   of the way between the left/right and top/bottom edges of the
11//   window. See also: how Unity does its GUIs.
12
13/// Sprite builder struct. Call
14/// [`finish`](struct.Sprite.html#method.finish) to draw the sprite.
15///
16/// Created by
17/// [`Spritesheet::draw`](struct.Spritesheet.html#method.draw), and
18/// usually used as a temporary value, as this is a builder
19/// struct. See the
20/// [`Spritesheet::draw`](struct.Spritesheet.html#method.draw)
21/// documentation for examples.
22pub struct Sprite<'a, 'b> {
23    renderer: &'a mut Renderer,
24    call: &'b DrawCallHandle,
25    z: f32,
26    coords: (f32, f32, f32, f32),
27    texcoords: (f32, f32, f32, f32),
28    color: (f32, f32, f32, f32),
29    rotation: (f32, f32, f32),
30    clip_area: Option<(f32, f32, f32, f32)>,
31}
32
33impl<'a, 'b> Sprite<'a, 'b> {
34    pub(crate) fn new(renderer: &'a mut Renderer, call: &'b DrawCallHandle) -> Sprite<'a, 'b> {
35        Sprite {
36            renderer,
37            call,
38            z: 0.0,
39            coords: (0.0, 0.0, 0.0, 0.0),
40            texcoords: (-1.0, -1.0, -1.0, -1.0),
41            color: (1.0, 1.0, 1.0, 1.0),
42            rotation: (0.0, 0.0, 0.0),
43            clip_area: None,
44        }
45    }
46
47    /// Renders the quad specified by this struct.
48    pub fn finish(&mut self) {
49        if let Some(area) = self.clip_area {
50            self.renderer.draw_quad_clipped(
51                area,
52                self.coords,
53                self.texcoords,
54                self.color,
55                self.rotation,
56                self.z,
57                self.call,
58            );
59        } else {
60            self.renderer.draw_quad(
61                self.coords,
62                self.texcoords,
63                self.color,
64                self.rotation,
65                self.z,
66                self.call,
67            );
68        }
69    }
70
71    /// Specifies the Z-coordinate of the sprite. Sprites with a
72    /// higher Z-coordinate will be rendered over ones with a lower
73    /// Z-coordinate.
74    ///
75    /// ## Alpha blending and Z-coordinates
76    ///
77    /// When drawing sprites on top of each other, with
78    /// [`alpha_blending`][alpha_blending] set to true, draw the ones
79    /// with the highest Z-coordinate the last, and avoid overlapping
80    /// the minimum and maximum Z-coordinate ranges between draw
81    /// calls.
82    ///
83    /// Explanation: Draw call rendering order is decided by the
84    /// highest z-coordinate that each call has to draw. To get proper
85    /// blending, the sprites furthest back need to be rendered
86    /// first. Therefore, if a draw call is ordered to be rendered the
87    /// last, but has sprites behind some other sprites, they will not
88    /// get blended as hoped. However, this ordering only applies
89    /// between draw calls that have
90    /// [`alpha_blending`][alpha_blending] set to `true`: non-blended
91    /// draw calls are always drawn before blended ones.
92    ///
93    /// [alpha_blending]: struct.DrawCallParameters.html#structfield.alpha_blending
94    pub fn z(&mut self, z: f32) -> &mut Self {
95        self.z = z;
96        self
97    }
98
99    /// Specifies the screen coordinates (in logical pixels) where the
100    /// quad is drawn.
101    pub fn coordinates<R: Into<Rect>>(&mut self, rect: R) -> &mut Self {
102        self.coords = rect.into().into_corners();
103        self
104    }
105
106    /// Specifies the screen coordinates (in *physical* pixels) where
107    /// the quad is drawn.
108    pub fn physical_coordinates<R: Into<Rect>>(&mut self, rect: R) -> &mut Self {
109        let (x0, y0, x1, y1) = rect.into().into_corners();
110        let df = self.renderer.dpi_factor;
111        self.coords = (x0 / df, y0 / df, x1 / df, y1 / df);
112        self
113    }
114
115    /// Specifies the texture coordinates (in actual pixels, in the
116    /// texture's coordinate space) from where the quad is sampled.
117    pub fn texture_coordinates<R: Into<Rect>>(&mut self, rect: R) -> &mut Self {
118        let (tw, th) = self.renderer.get_texture_size(self.call);
119        let (tw, th) = (tw as f32, th as f32);
120        let (x0, y0, x1, y1) = rect.into().into_corners();
121        self.texcoords = (x0 / tw, y0 / th, x1 / tw, y1 / th);
122        self
123    }
124
125    /// Rounds previously set coordinates
126    /// ([`coordinates`](#method.coordinates)) so that they
127    /// align with the physical pixels of the monitor.
128    ///
129    /// This might help you with weird visual glitches, especially if
130    /// you're trying to render quads that have the same physical
131    /// pixel size as the texture it's sampling.
132    pub fn pixel_alignment(&mut self) -> &mut Self {
133        let (x0, y0, x1, y1) = self.coords;
134        let dpi_factor = self.renderer.dpi_factor;
135        let round_px = |x: f32| (x * dpi_factor).round() / dpi_factor;
136        let (w, h) = (round_px(x1 - x0), round_px(y1 - y0));
137        let (x0, y0) = (round_px(x0), round_px(y0));
138        let (x1, y1) = (x0 + w, y0 + h);
139        self.coords = (x0, y0, x1, y1);
140        self
141    }
142
143    /// Specifies the texture coordinates (as UVs, ie. 0.0 - 1.0) from
144    /// where the quad is sampled.
145    pub fn uvs<R: Into<Rect>>(&mut self, rect: R) -> &mut Self {
146        self.texcoords = rect.into().into_corners();
147        self
148    }
149
150    /// Specifies the clip area. Only the parts that overlap between
151    /// the clip area and the area specified by
152    /// [`coordinates`](#method.coordinates) are rendered.
153    pub fn clip_area<R: Into<Rect>>(&mut self, rect: R) -> &mut Self {
154        self.clip_area = Some(rect.into().into_corners());
155        self
156    }
157
158    /// Specifies the color tint of the quad.
159    pub fn color(&mut self, (red, green, blue, alpha): (f32, f32, f32, f32)) -> &mut Self {
160        self.color = (red, green, blue, alpha);
161        self
162    }
163
164    /// Specifies the rotation (in radians) and pivot of the quad,
165    /// relative to the sprite's origin.
166    pub fn rotation(&mut self, rotation: f32, pivot_x: f32, pivot_y: f32) -> &mut Self {
167        self.rotation = (rotation, pivot_x, pivot_y);
168        self
169    }
170}