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}