Skip to main content

gust_render/
sprite.rs

1//! Module to handle drawable texture that are called Sprite
2
3use color::Color;
4use draw::{BlendMode, Context, Drawable, DrawableMut, Drawer};
5use nalgebra;
6use nalgebra::*;
7use resources::Resource;
8use shader::DEFAULT_SHADER;
9use std::convert::From;
10use std::error::Error;
11use std::fmt;
12use texture::Texture;
13use transform::{Movable, Rotable, Scalable, Transformable};
14use vertex::Vertex;
15use vertex::*;
16use vertex_buffer::{Primitive, VertexBuffer};
17
18/// A sprite is a transformable
19/// drawable sprite
20/// > Display a sprite from a texture
21/// ```Rust
22/// use texture::Texture;
23/// use sprite::Sprite;
24///
25/// let texture = Texture::new("assets/texture.jpg");
26/// let sprite = Sprite::from_texture(Rc::clone(&texutre));
27/// sprite.rotate(45.0);
28/// sprite.set_position(Vector2::new(100.0, 200.0));
29/// ```
30/// > A sprite is just attributes for textures to become printable ...
31#[derive(Debug, Clone, PartialEq)]
32pub struct Sprite {
33    pos: Vector2<f32>,
34    scale: Vector2<f32>,
35    rotation: f32,
36    origin: Vector2<f32>,
37    vertice: VertexBuffer,
38    texture: Option<Resource<Texture>>,
39    model: Matrix4<f32>,
40    need_update: bool,
41}
42
43impl Sprite {
44    /// Create a empty sprite
45    /// It's not very useful but you can assign texture later
46    pub fn new() -> Sprite {
47        Sprite {
48            pos: Vector2::new(0.0, 0.0),
49            scale: Vector2::new(1.0, 1.0),
50            vertice: VertexBuffer::new(
51                Primitive::TrianglesStrip,
52                VertexArray::from(
53                    vec![
54                        Vertex::default(),
55                        Vertex::default(),
56                        Vertex::default(),
57                        Vertex::default(),
58                    ]
59                    .as_slice(),
60                ),
61            ),
62            need_update: true,
63            texture: None,
64            origin: Vector2::new(0.0, 0.0),
65            model: Matrix4::identity(),
66            rotation: 0.0,
67        }
68    }
69
70    /// Set texture color
71    pub fn set_color(&mut self, color: &Color) {
72        self.vertice[0].color = *color;
73        self.vertice[1].color = *color;
74        self.vertice[2].color = *color;
75        self.vertice[3].color = *color;
76        self.vertice.update();
77    }
78
79    /// Get texture sizes
80    pub fn get_sizes(&self) -> Vector2<u32> {
81        if let Some(ref texture) = self.texture {
82            Vector2::new(texture.width() as u32, texture.height() as u32)
83        } else {
84            Vector2::new(0, 0)
85        }
86    }
87
88    /// Set origin to center of the sprite. Can fail because a sprite sizes
89    /// are defined by it's texture, sometimes it can happend that there isn't one.
90    /// So it return an SpriteError::NoTexture
91    pub fn set_origin_to_center(&mut self) -> Result<(), SpriteError> {
92        if self.texture.is_some() {
93            let mut center = Vector2::new(0.0, 0.0);
94            let sizes = self.get_sizes();
95            center.x = sizes.x as f32 / 2.0;
96            center.y = sizes.y as f32 / 2.0;
97            self.set_origin(center);
98            Ok(())
99        } else {
100            Err(SpriteError::NoTexture)
101        }
102    }
103
104    /// Set a new texture and set the sprite to update state.
105    pub fn set_texture(&mut self, texture: &Resource<Texture>) {
106        self.texture = Some(Resource::clone(texture));
107        self.need_update = true;
108    }
109}
110
111impl<'a> From<&'a Resource<Texture>> for Sprite {
112    /// You can create sprite from texture (precisly Rc<Texture>)
113    /// ```no_run
114    /// use gust::texture::Texture;
115    /// use gust::sprite::Sprite;
116    /// use std::rc::Rc;
117    ///
118    /// let texture = Rc::new(Texture::new("My great texture"));
119    /// let personnage = Sprite::from(&texture);
120    /// ```
121    fn from(tex: &'a Resource<Texture>) -> Sprite {
122        let width = tex.width() as f32;
123        let height = tex.height() as f32;
124        let pos = Vector2::new(0.0, 0.0);
125        Sprite {
126            pos,
127            scale: Vector2::new(1.0, 1.0),
128            vertice: VertexBuffer::new(
129                Primitive::TrianglesStrip,
130                VertexArray::from(
131                    vec![
132                        Vertex::new(
133                            Vector2::new(0.0, 0.0),
134                            Vector2::new(0.0, 0.0),
135                            Color::white(),
136                        ),
137                        Vertex::new(
138                            Vector2::new(0.0, height),
139                            Vector2::new(0.0, 1.0),
140                            Color::white(),
141                        ),
142                        Vertex::new(
143                            Vector2::new(width, 0.0),
144                            Vector2::new(1.0, 0.0),
145                            Color::white(),
146                        ),
147                        Vertex::new(
148                            Vector2::new(width, height),
149                            Vector2::new(1.0, 1.0),
150                            Color::white(),
151                        ),
152                    ]
153                    .as_slice(),
154                ),
155            ),
156            texture: Some(Resource::clone(tex)),
157            need_update: true,
158            model: Matrix4::identity().append_translation(&Vector3::new(pos.x, pos.y, 0.0)),
159            rotation: 0.0,
160            origin: Vector2::new(0.0, 0.0),
161        }
162    }
163}
164
165impl Transformable for Sprite {
166    /// TODO: Transform the point tested.
167    fn contain<T: nalgebra::Scalar + Into<f32>>(&self, _point: ::Point<T>) -> bool {
168        //let sizes = self.get_sizes();
169        //let b: Vector4<f32> = Matrix4::inverse(self.model) * Vector4::new(point.x.into(), point.y.into(), 0.0, 1.0);
170        //let vec: Vector2<f32> = Vector2::new(b.x, b.y);
171        //println!("OldVec {:?}", point);
172        //println!("NewVec {}", vec);
173
174        //let a = Rect::new(self.pos.x as f32, self.pos.y as f32, sizes.x as f32, sizes.y as f32);
175        //a.contain(vec)
176        true
177    }
178
179    fn set_origin<T: nalgebra::Scalar + Into<f32>>(&mut self, origin: Vector2<T>) {
180        self.origin.x = origin.x.into();
181        self.origin.y = origin.y.into();
182        self.need_update = true;
183    }
184
185    fn get_origin(&self) -> Vector2<f32> {
186        self.origin
187    }
188}
189
190impl Scalable for Sprite {
191    fn set_scale<T>(&mut self, vec: Vector2<T>)
192    where
193        T: Scalar + Into<f32>,
194    {
195        self.scale.x = vec.x.into();
196        self.scale.y = vec.y.into();
197        self.need_update = true;
198    }
199
200    fn get_scale(&self) -> Vector2<f32> {
201        self.scale
202    }
203
204    fn scale<T>(&mut self, factor: Vector2<T>)
205    where
206        T: Scalar + Into<f32>,
207    {
208        self.scale.x += factor.x.into();
209        self.scale.y += factor.y.into();
210        self.need_update = true;
211    }
212}
213
214impl Rotable for Sprite {
215    fn rotate<T>(&mut self, angle: T)
216    where
217        T: Scalar + Into<f32>,
218    {
219        self.rotation += angle.into();
220        self.need_update = true;
221    }
222
223    fn set_rotation<T>(&mut self, angle: T)
224    where
225        T: Scalar + Into<f32>,
226    {
227        self.rotation = angle.into();
228        self.need_update = true;
229    }
230
231    fn get_rotation(&self) -> f32 {
232        self.rotation
233    }
234}
235
236impl Movable for Sprite {
237    fn translate<T>(&mut self, vec: Vector2<T>)
238    where
239        T: Scalar + Into<f32>,
240    {
241        self.pos.x += vec.x.into();
242        self.pos.y += vec.y.into();
243        self.need_update = true;
244    }
245
246    fn get_position(&self) -> Vector2<f32> {
247        self.pos
248    }
249
250    fn set_position<T>(&mut self, vec: Vector2<T>)
251    where
252        T: Scalar + Into<f32>,
253    {
254        self.pos.x = vec.x.into();
255        self.pos.y = vec.y.into();
256        self.need_update = true;
257    }
258}
259
260impl Default for Sprite {
261    fn default() -> Self {
262        Sprite {
263            pos: Vector2::new(0.0, 0.0),
264            scale: Vector2::new(0.0, 0.0),
265            rotation: 0.0,
266            origin: Vector2::new(0.0, 0.0),
267            vertice: VertexBuffer::default(),
268            texture: Some(Resource::new(Texture::default())),
269            model: Matrix4::<f32>::identity(),
270            need_update: false,
271        }
272    }
273}
274
275impl DrawableMut for Sprite {
276    /// Draw the actual sprite on a context
277    fn draw_mut<T: Drawer>(&mut self, window: &mut T) {
278        self.update();
279        self.draw(window);
280    }
281
282    fn draw_with_context_mut<'a>(&mut self, context: &'a mut Context) {
283        self.update();
284        self.vertice.draw_with_context(context);
285    }
286}
287
288/// Drawing trait for sprite sturct
289impl Drawable for Sprite {
290    /// Draw the actual sprite on a context
291    fn draw<T: Drawer>(&self, window: &mut T) {
292        let texture = if let Some(ref rc_texture) = self.texture {
293            Some(rc_texture.as_ref())
294        } else {
295            None
296        };
297
298        let mut context = Context::new(
299            texture,
300            &*DEFAULT_SHADER,
301            vec![
302                ("transform".to_string(), &self.model),
303                ("projection".to_string(), window.projection()),
304            ],
305            BlendMode::Alpha,
306        );
307        self.vertice.draw_with_context(&mut context);
308    }
309
310    /// Draw the actual sprite with your own context.
311    fn draw_with_context<'a>(&self, context: &'a mut Context) {
312        self.vertice.draw_with_context(context);
313    }
314
315    /// Update the sprite, this is a heavy operation because it's an operation that reconstruct
316    /// the model matrix (that represent transformation of the sprite) from scratch.
317    /// However this function is computed only when it's necessary. (self.need_update == true)
318    /// TODO: Make this computation in shader program.
319    fn update(&mut self) {
320        if !self.need_update {
321            return;
322        }
323        //translate to position
324        self.model = Matrix4::<f32>::identity().append_translation(&Vector3::new(
325            self.pos.x - self.origin.x,
326            self.pos.y - self.origin.y,
327            0.0,
328        ));
329
330        if self.origin.x != 0.0 && self.origin.y != 0.0 {
331            self.model
332                .append_translation_mut(&Vector3::new(self.origin.x, self.origin.y, 0.0));
333            self.model *= Matrix4::from_euler_angles(0.0, 0.0, self.rotation * (3.14116 * 180.0));
334            self.model
335                .prepend_translation_mut(&Vector3::new(-self.origin.x, -self.origin.y, 0.0));
336        } else {
337            self.model *= Matrix4::from_euler_angles(0.0, 0.0, self.rotation * (3.14116 * 180.0));
338        }
339        self.model
340            .append_nonuniform_scaling_mut(&Vector3::new(self.scale.x, self.scale.y, 0.0));
341
342        if self.rotation > 360.0 {
343            self.rotation = 0.0;
344        }
345
346        self.need_update = false;
347    }
348}
349
350#[derive(Debug)]
351/// All error trigerable in sprite
352pub enum SpriteError {
353    NoTexture,
354}
355
356impl fmt::Display for SpriteError {
357    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
358        match self {
359            SpriteError::NoTexture => write!(f, "There is no texture linked to this Sprite"),
360        }
361    }
362}
363
364impl Error for SpriteError {
365    fn cause(&self) -> Option<&Error> {
366        match self {
367            SpriteError::NoTexture => None,
368        }
369    }
370}