1 module evael.graphics.Spritesheet;
2 
3 import std.typecons;
4 import std.math;
5 
6 import evael.graphics.GL;
7 
8 import evael.graphics.GraphicsDevice;
9 import evael.graphics.Texture;
10 import evael.graphics.Vertex;
11 import evael.graphics.shapes.Quad;
12 import evael.graphics.models.Animation;
13 
14 import evael.utils.Math;
15 import evael.utils.Color;
16 import evael.utils.Size;
17 import evael.utils.Rectangle;
18 
19 import dnogc.DynamicArray;
20 import dnogc.Utils;
21 
22 /**
23  * Spritesheet
24  * Draw animated sprite.
25  */
26 class Spritesheet : Quad!Vertex2PositionColorTexture
27 {
28 	/// Sprite size
29 	private Size!int m_spriteSize;
30 
31 	private vec2 m_currentSpritePosition;
32 
33 	private Animation[string] m_animations;
34 
35 	private Animation m_currentAnimation;
36 	private float m_currentAnimationFrame;
37 
38 	/**
39 	 * Spritesheet constructor.
40 	 */
41 	public this()(GraphicsDevice graphics, Texture texture, in auto ref Size!int spriteSize)
42 	{
43 		this.m_texture = texture;
44 		this.m_spriteSize = spriteSize;
45 		this.m_currentSpritePosition = vec2(0, 0);
46 		this.m_currentAnimationFrame = 0.0f;
47 		
48 		immutable float v = 1.0f / this.m_texture.size.width;
49 
50 		super(
51 			graphics, 
52 			[
53 				// Bottom-left vertex
54 				Vertex2PositionColorTexture(vec2(0, 0), Color.White, vec2(0, (this.m_currentSpritePosition.y + spriteSize.height) * v)),
55 				// Top-left vertex
56 				Vertex2PositionColorTexture(vec2(0, spriteSize.height), Color.White, vec2(0, 0)),
57 				// Bottom-right vertex
58 				Vertex2PositionColorTexture(vec2(spriteSize.width, 0), Color.White, vec2((this.m_currentSpritePosition.x + spriteSize.width) * v, (this.m_currentSpritePosition.y + spriteSize.height) * v)),
59 				// Top-right vertex
60 				Vertex2PositionColorTexture(vec2(spriteSize.width, spriteSize.height), Color.White, vec2((this.m_currentSpritePosition.x + spriteSize.width) * v, 0))
61 			]
62 			,
63 			[0, 1, 2, 2, 1, 3]
64 		);
65 	}
66 
67 	public override void draw(in float deltaTime, mat4 view, mat4 projection)				
68 	{
69 		if (this.m_animations.length)
70 		{
71 			this.update(deltaTime);
72 		}
73 
74 		super.draw(deltaTime, view, projection);
75 	}
76 
77 	public void update(in float deltaTime)
78 	{
79 		this.m_currentAnimationFrame += this.m_currentAnimation.speed * deltaTime;
80 
81 		if (this.m_currentAnimationFrame >= this.m_currentAnimation.endFrame)
82 		{
83 			this.m_currentAnimationFrame = this.m_currentAnimation.startFrame;
84 		}
85 
86 		auto ss = this.m_spriteSize;
87 		auto frame = cast(int)this.m_currentAnimationFrame - 1;
88 
89 		auto framePerWidth = this.m_texture.size.width / cast(float)ss.width;
90 
91 		auto x = frame % framePerWidth;
92 		auto y = floor(frame / framePerWidth);
93 
94 		this.m_currentSpritePosition.x = x * ss.width;
95 		this.m_currentSpritePosition.y = y * ss.height;
96 
97 		immutable float v = 1.0f / this.m_texture.size.width;
98 
99 		auto vertices =
100 		[
101 			// Bottom-left vertex
102 			Vertex2PositionColorTexture(vec2(0, 0), Color.White, 
103 				vec2(this.m_currentSpritePosition.x * v, (this.m_currentSpritePosition.y + ss.height) * v)),
104 
105 			// Top-left vertex
106 			Vertex2PositionColorTexture(vec2(0, this.m_spriteSize.height), Color.White, 
107 				vec2(this.m_currentSpritePosition.x * v, this.m_currentSpritePosition.y * v)),
108 
109 			// Bottom-right vertex
110 			Vertex2PositionColorTexture(vec2(this.m_spriteSize.width, 0), Color.White, 
111 				vec2((this.m_currentSpritePosition.x + ss.width) * v, (this.m_currentSpritePosition.y + ss.height) * v)),
112 
113 			// Top-right vertex
114 			Vertex2PositionColorTexture(vec2(this.m_spriteSize.width, this.m_spriteSize.height), Color.White, 
115 				vec2((this.m_currentSpritePosition.x + ss.width) * v, this.m_currentSpritePosition.y * v))
116 		];
117 
118 		this.m_graphicsDevice.sendVertexBufferData(this.m_vertexBuffer, 0, Vertex2PositionColorTexture.sizeof * vertices.length, vertices.ptr);
119 	}
120 
121 	/**
122 	 * Adds animation.
123 	 * Params:
124 	 *      name: animation name
125 	 *      animation : animation
126 	 */
127 	public void addAnimation()(in string name, in auto ref Animation animation)
128 	{
129 		this.m_animations[name] = animation;
130 	}
131 
132 	/**
133 	 * Sets current animation.
134 	 * Params:
135 	 *      name: animation name
136 	 */
137 	public void setAnimation(in string name)
138 	{
139 		assert(name in this.m_animations);
140 		this.m_currentAnimation = this.m_animations[name];
141 		this.m_currentAnimationFrame = this.m_currentAnimation.startFrame;
142 	}
143 
144 	/**
145 	 * Sets current sprite texture coords.
146 	 * Params:
147 	 *      rect : texture rect
148 	 */
149 	public void setTextureCoords()(in auto ref Rectangle!float rect)
150 	{
151 		immutable float v = 1.0f / this.m_texture.size.width;
152 
153 		auto vertices =
154 		[
155 			// Bottom-left vertex
156 			Vertex2PositionColorTexture(vec2(0, 0), Color.White, vec2(rect.left * v, rect.top * v)),
157 
158 			// Top-left vertex
159 			Vertex2PositionColorTexture(vec2(0, rect.size.height), Color.White, vec2(rect.left * v, rect.bottom * v)),
160 
161 			// Bottom-right vertex
162 			Vertex2PositionColorTexture(vec2(rect.size.width, 0), Color.White, vec2(rect.right * v, rect.top * v)),
163 
164 			// Top-right vertex
165 			Vertex2PositionColorTexture(vec2(rect.size.width, rect.size.height), Color.White, vec2(rect.right * v, rect.bottom * v))
166 		];
167 
168 		this.m_graphicsDevice.sendVertexBufferData(this.m_vertexBuffer, 0, Vertex2PositionColorTexture.sizeof * vertices.length, vertices.ptr);   
169 	}
170 
171 	@nogc @safe
172 	@property pure nothrow
173 	{
174 
175 	}
176 }