use color::Color;
use draw::{BlendMode, Context, Drawable, DrawableMut, Drawer};
use nalgebra;
use nalgebra::*;
use resources::Resource;
use shader::DEFAULT_SHADER;
use std::convert::From;
use std::error::Error;
use std::fmt;
use texture::Texture;
use transform::{Movable, Rotable, Scalable, Transformable};
use vertex::Vertex;
use vertex::*;
use vertex_buffer::{Primitive, VertexBuffer};
#[derive(Debug, Clone, PartialEq)]
pub struct Sprite {
pos: Vector2<f32>,
scale: Vector2<f32>,
rotation: f32,
origin: Vector2<f32>,
vertice: VertexBuffer,
texture: Option<Resource<Texture>>,
model: Matrix4<f32>,
need_update: bool,
}
impl Sprite {
pub fn new() -> Sprite {
Sprite {
pos: Vector2::new(0.0, 0.0),
scale: Vector2::new(1.0, 1.0),
vertice: VertexBuffer::new(
Primitive::TrianglesStrip,
VertexArray::from(
vec![
Vertex::default(),
Vertex::default(),
Vertex::default(),
Vertex::default(),
]
.as_slice(),
),
),
need_update: true,
texture: None,
origin: Vector2::new(0.0, 0.0),
model: Matrix4::identity(),
rotation: 0.0,
}
}
pub fn set_color(&mut self, color: &Color) {
self.vertice[0].color = *color;
self.vertice[1].color = *color;
self.vertice[2].color = *color;
self.vertice[3].color = *color;
self.vertice.update();
}
pub fn get_sizes(&self) -> Vector2<u32> {
if let Some(ref texture) = self.texture {
Vector2::new(texture.width() as u32, texture.height() as u32)
} else {
Vector2::new(0, 0)
}
}
pub fn set_origin_to_center(&mut self) -> Result<(), SpriteError> {
if self.texture.is_some() {
let mut center = Vector2::new(0.0, 0.0);
let sizes = self.get_sizes();
center.x = sizes.x as f32 / 2.0;
center.y = sizes.y as f32 / 2.0;
self.set_origin(center);
Ok(())
} else {
Err(SpriteError::NoTexture)
}
}
pub fn set_texture(&mut self, texture: &Resource<Texture>) {
self.texture = Some(Resource::clone(texture));
self.need_update = true;
}
}
impl<'a> From<&'a Resource<Texture>> for Sprite {
fn from(tex: &'a Resource<Texture>) -> Sprite {
let width = tex.width() as f32;
let height = tex.height() as f32;
let pos = Vector2::new(0.0, 0.0);
Sprite {
pos,
scale: Vector2::new(1.0, 1.0),
vertice: VertexBuffer::new(
Primitive::TrianglesStrip,
VertexArray::from(
vec![
Vertex::new(
Vector2::new(0.0, 0.0),
Vector2::new(0.0, 0.0),
Color::white(),
),
Vertex::new(
Vector2::new(0.0, height),
Vector2::new(0.0, 1.0),
Color::white(),
),
Vertex::new(
Vector2::new(width, 0.0),
Vector2::new(1.0, 0.0),
Color::white(),
),
Vertex::new(
Vector2::new(width, height),
Vector2::new(1.0, 1.0),
Color::white(),
),
]
.as_slice(),
),
),
texture: Some(Resource::clone(tex)),
need_update: true,
model: Matrix4::identity().append_translation(&Vector3::new(pos.x, pos.y, 0.0)),
rotation: 0.0,
origin: Vector2::new(0.0, 0.0),
}
}
}
impl Transformable for Sprite {
fn contain<T: nalgebra::Scalar + Into<f32>>(&self, _point: ::Point<T>) -> bool {
true
}
fn set_origin<T: nalgebra::Scalar + Into<f32>>(&mut self, origin: Vector2<T>) {
self.origin.x = origin.x.into();
self.origin.y = origin.y.into();
self.need_update = true;
}
fn get_origin(&self) -> Vector2<f32> {
self.origin
}
}
impl Scalable for Sprite {
fn set_scale<T>(&mut self, vec: Vector2<T>)
where
T: Scalar + Into<f32>,
{
self.scale.x = vec.x.into();
self.scale.y = vec.y.into();
self.need_update = true;
}
fn get_scale(&self) -> Vector2<f32> {
self.scale
}
fn scale<T>(&mut self, factor: Vector2<T>)
where
T: Scalar + Into<f32>,
{
self.scale.x += factor.x.into();
self.scale.y += factor.y.into();
self.need_update = true;
}
}
impl Rotable for Sprite {
fn rotate<T>(&mut self, angle: T)
where
T: Scalar + Into<f32>,
{
self.rotation += angle.into();
self.need_update = true;
}
fn set_rotation<T>(&mut self, angle: T)
where
T: Scalar + Into<f32>,
{
self.rotation = angle.into();
self.need_update = true;
}
fn get_rotation(&self) -> f32 {
self.rotation
}
}
impl Movable for Sprite {
fn translate<T>(&mut self, vec: Vector2<T>)
where
T: Scalar + Into<f32>,
{
self.pos.x += vec.x.into();
self.pos.y += vec.y.into();
self.need_update = true;
}
fn get_position(&self) -> Vector2<f32> {
self.pos
}
fn set_position<T>(&mut self, vec: Vector2<T>)
where
T: Scalar + Into<f32>,
{
self.pos.x = vec.x.into();
self.pos.y = vec.y.into();
self.need_update = true;
}
}
impl Default for Sprite {
fn default() -> Self {
Sprite {
pos: Vector2::new(0.0, 0.0),
scale: Vector2::new(0.0, 0.0),
rotation: 0.0,
origin: Vector2::new(0.0, 0.0),
vertice: VertexBuffer::default(),
texture: Some(Resource::new(Texture::default())),
model: Matrix4::<f32>::identity(),
need_update: false,
}
}
}
impl DrawableMut for Sprite {
fn draw_mut<T: Drawer>(&mut self, window: &mut T) {
self.update();
self.draw(window);
}
fn draw_with_context_mut<'a>(&mut self, context: &'a mut Context) {
self.update();
self.vertice.draw_with_context(context);
}
}
impl Drawable for Sprite {
fn draw<T: Drawer>(&self, window: &mut T) {
let texture = if let Some(ref rc_texture) = self.texture {
Some(rc_texture.as_ref())
} else {
None
};
let mut context = Context::new(
texture,
&*DEFAULT_SHADER,
vec![
("transform".to_string(), &self.model),
("projection".to_string(), window.projection()),
],
BlendMode::Alpha,
);
self.vertice.draw_with_context(&mut context);
}
fn draw_with_context<'a>(&self, context: &'a mut Context) {
self.vertice.draw_with_context(context);
}
fn update(&mut self) {
if !self.need_update {
return;
}
self.model = Matrix4::<f32>::identity().append_translation(&Vector3::new(
self.pos.x - self.origin.x,
self.pos.y - self.origin.y,
0.0,
));
if self.origin.x != 0.0 && self.origin.y != 0.0 {
self.model
.append_translation_mut(&Vector3::new(self.origin.x, self.origin.y, 0.0));
self.model *= Matrix4::from_euler_angles(0.0, 0.0, self.rotation * (3.14116 * 180.0));
self.model
.prepend_translation_mut(&Vector3::new(-self.origin.x, -self.origin.y, 0.0));
} else {
self.model *= Matrix4::from_euler_angles(0.0, 0.0, self.rotation * (3.14116 * 180.0));
}
self.model
.append_nonuniform_scaling_mut(&Vector3::new(self.scale.x, self.scale.y, 0.0));
if self.rotation > 360.0 {
self.rotation = 0.0;
}
self.need_update = false;
}
}
#[derive(Debug)]
pub enum SpriteError {
NoTexture,
}
impl fmt::Display for SpriteError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SpriteError::NoTexture => write!(f, "There is no texture linked to this Sprite"),
}
}
}
impl Error for SpriteError {
fn cause(&self) -> Option<&Error> {
match self {
SpriteError::NoTexture => None,
}
}
}