1 module d2d.rendering.spritebatch;
2 
3 import crunch;
4 
5 import d2d;
6 
7 enum DrawOrigin
8 {
9 	topLeft = 0,
10 	topCenter = 1,
11 	topRight = 2,
12 	middleLeft = 4,
13 	middleCenter = 5,
14 	middleRight = 6,
15 	bottomLeft = 8,
16 	bottomCenter = 9,
17 	bottomRight = 10
18 }
19 
20 class SpriteBatchImpl(uint maxVertexCount) : IDisposable, IVerifiable, IDrawable
21 {
22 	struct Vertex
23 	{
24 		vec2 position;
25 		vec2 texCoord;
26 		vec4 color;
27 	}
28 
29 	Vertex[maxVertexCount] vertices;
30 	RenderableMesh renderable;
31 
32 	private Texture texture;
33 	private uint index;
34 	private uint vao;
35 	private uint vbo;
36 	private bool running;
37 
38 	/// Regular texture shader.
39 	public static ShaderProgram spriteShader;
40 
41 	static void load()
42 	{
43 		spriteShader = new ShaderProgram();
44 		Shader vertex = new Shader();
45 		vertex.load(ShaderType.Vertex, "#version 330
46 layout(location = 0) in vec3 in_position;
47 layout(location = 1) in vec2 in_tex;
48 layout(location = 2) in vec4 in_color;
49 
50 uniform mat4 transform;
51 uniform mat4 projection;
52 
53 out vec2 texCoord;
54 out vec4 color;
55 
56 void main()
57 {
58 	gl_Position = projection * transform * vec4(in_position, 1);
59 
60 	texCoord = in_tex;
61 	color = in_color;
62 }
63 ");
64 		Shader fragment = new Shader();
65 		fragment.load(ShaderType.Fragment, "#version 330
66 uniform sampler2D tex;
67 
68 in vec2 texCoord;
69 in vec4 color;
70 
71 layout(location = 0) out vec4 out_frag_color;
72 
73 void main()
74 {
75 	out_frag_color = texture(tex, texCoord) * color;
76 }
77 ");
78 		spriteShader.attach(vertex);
79 		spriteShader.attach(fragment);
80 		spriteShader.link();
81 		spriteShader.bind();
82 		spriteShader.registerUniform("tex");
83 		spriteShader.registerUniform("transform");
84 		spriteShader.registerUniform("projection");
85 		spriteShader.set("tex", 0);
86 	}
87 
88 	this()
89 	{
90 		glGenVertexArrays(1, &vao);
91 		glBindVertexArray(vao);
92 
93 		glGenBuffers(1, &vbo);
94 		glBindBuffer(GL_ARRAY_BUFFER, vbo);
95 		glBufferData(GL_ARRAY_BUFFER, maxVertexCount * Vertex.sizeof, &vertices[0], GL_DYNAMIC_DRAW);
96 		glEnableVertexAttribArray(0);
97 		glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, Vertex.sizeof,
98 				cast(void*)(Vertex.position.offsetof));
99 		glEnableVertexAttribArray(1);
100 		glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, Vertex.sizeof,
101 				cast(void*)(Vertex.texCoord.offsetof));
102 		glEnableVertexAttribArray(2);
103 		glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, Vertex.sizeof,
104 				cast(void*)(Vertex.color.offsetof));
105 
106 		glBindVertexArray(0);
107 
108 		renderable = new RenderableMesh(vao, 0, false);
109 
110 		if (spriteShader is null)
111 			load();
112 	}
113 
114 	void dispose()
115 	{
116 		if (valid)
117 		{
118 			renderable = null;
119 		}
120 	}
121 
122 	@property bool valid()
123 	{
124 		return renderable !is null;
125 	}
126 
127 	void draw(IRenderTarget target, ShaderProgram shader = spriteShader)
128 	{
129 		texture.bind();
130 		target.draw(renderable, shader);
131 	}
132 
133 	void begin(Texture texture)
134 	{
135 		if (running)
136 			throw new Exception("Tried to begin when begin was already called");
137 
138 		index = 0;
139 		running = true;
140 		this.texture = texture;
141 	}
142 
143 	void end()
144 	{
145 		if (!running)
146 			throw new Exception("Tried to end when begin was not called");
147 
148 		running = false;
149 		glBindBuffer(GL_ARRAY_BUFFER, vbo);
150 		glBufferSubData(GL_ARRAY_BUFFER, 0, index * Vertex.sizeof, &vertices[0]);
151 		renderable.count = index;
152 	}
153 
154 	void drawSprite(Crunch.Image image, vec2 position, vec4 color = vec4(1))
155 	{
156 		drawSprite(image, position, vec2(1, 1), color);
157 	}
158 
159 	void drawSprite(Crunch.Image image, vec2 position, float rotation,
160 			DrawOrigin origin, vec2 originOffset = vec2(0), vec4 color = vec4(1))
161 	{
162 		drawSprite(image, position, vec2(1, 1), rotation, origin, originOffset, color);
163 	}
164 
165 	void drawSprite(Crunch.Image image, vec2 position, float scale, vec4 color = vec4(1))
166 	{
167 		drawSprite(image, position, vec2(scale, scale), color);
168 	}
169 
170 	void drawSprite(Crunch.Image image, vec2 position, float scale, float rotation,
171 			DrawOrigin origin, vec2 originOffset = vec2(0), vec4 color = vec4(1))
172 	{
173 		drawSprite(image, position, vec2(scale, scale), rotation, origin, originOffset, color);
174 	}
175 
176 	void drawSprite(Crunch.Image sprite, vec2 position, vec2 scale, vec4 color = vec4(1))
177 	{
178 		vec2 uvScale = vec2(1.0f / texture.width, 1.0f / texture.height);
179 		vec2 size = vec2(sprite.width, sprite.height);
180 		vec2 drawSize = vec2(size.x * scale.x, size.y * scale.y);
181 
182 		drawRectangle(position, drawSize, rect.dim(sprite.x, sprite.y, size).scale(uvScale), color);
183 	}
184 
185 	void drawSprite(Crunch.Image sprite, vec2 position, vec2 scale, float rotation,
186 			DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1))
187 	{
188 		vec2 uvScale = vec2(1.0f / texture.width, 1.0f / texture.height);
189 		vec2 size = vec2(sprite.width, sprite.height);
190 		vec2 drawSize = vec2(size.x * scale.x, size.y * scale.y);
191 
192 		drawRectangle(position, drawSize, rect.dim(sprite.x, sprite.y, size)
193 				.scale(uvScale), rotation, origin, originOffset, color);
194 	}
195 
196 	void drawSprite(Crunch.Image sprite, mat3 transformation, vec4 color = vec4(1))
197 	{
198 		vec2 uvScale = vec2(1.0f / texture.width, 1.0f / texture.height);
199 		vec2 size = vec2(sprite.width, sprite.height);
200 
201 		drawRectangle(rect.dim(size), rect.dim(sprite.x, sprite.y, size)
202 				.scale(uvScale), transformation, color);
203 	}
204 
205 	void drawRectangle(vec2 position, vec2 size, vec4 color = vec4(1))
206 	{
207 		drawRectangle(rect.dim(position, size), color);
208 	}
209 
210 	void drawRectangle(vec2 position, vec2 size, float rotation,
211 			DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1))
212 	{
213 		drawRectangle(rect.dim(position, size), rotation, origin, originOffset, color);
214 	}
215 
216 	void drawRectangle(rect rectangle, vec4 color = vec4(1))
217 	{
218 		drawRectangle(rectangle, rect(0, 0, 1, 1), color);
219 	}
220 
221 	void drawRectangle(rect rectangle, float rotation,
222 			DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1))
223 	{
224 		drawRectangle(rectangle, rect(0, 0, 1, 1), rotation, origin, originOffset, color);
225 	}
226 
227 	void drawRectangle(vec2 position, vec2 size, rect uv, vec4 color = vec4(1))
228 	{
229 		drawRectangle(rect.dim(position, size), uv, color);
230 	}
231 
232 	void drawRectangle(vec2 position, vec2 size, rect uv, float rotation,
233 			DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1))
234 	{
235 		drawRectangle(rect.dim(position, size), uv, rotation, origin, originOffset, color);
236 	}
237 
238 	void drawRectangle(rect rectangle, rect uv, vec4 color = vec4(1))
239 	{
240 		if (!running)
241 			throw new Exception("Tried to draw when begin was not called");
242 
243 		vertices[index++] = Vertex(rectangle.pos00, uv.pos00, color);
244 		vertices[index++] = Vertex(rectangle.pos10, uv.pos10, color);
245 		vertices[index++] = Vertex(rectangle.pos01, uv.pos01, color);
246 
247 		vertices[index++] = Vertex(rectangle.pos01, uv.pos01, color);
248 		vertices[index++] = Vertex(rectangle.pos10, uv.pos10, color);
249 		vertices[index++] = Vertex(rectangle.pos11, uv.pos11, color);
250 	}
251 
252 	void drawRectangle(rect rectangle, rect uv, mat3 transformation, vec4 color = vec4(1))
253 	{
254 		if (!running)
255 			throw new Exception("Tried to draw when begin was not called");
256 
257 		vertices[index++] = Vertex((transformation * vec3(rectangle.pos00, 1)).xy, uv.pos00, color);
258 		vertices[index++] = Vertex((transformation * vec3(rectangle.pos10, 1)).xy, uv.pos10, color);
259 		vertices[index++] = Vertex((transformation * vec3(rectangle.pos01, 1)).xy, uv.pos01, color);
260 
261 		vertices[index++] = Vertex((transformation * vec3(rectangle.pos01, 1)).xy, uv.pos01, color);
262 		vertices[index++] = Vertex((transformation * vec3(rectangle.pos10, 1)).xy, uv.pos10, color);
263 		vertices[index++] = Vertex((transformation * vec3(rectangle.pos11, 1)).xy, uv.pos11, color);
264 	}
265 
266 	void drawRectangle(rect rectangle, rect uv, float rotation,
267 			DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1))
268 	{
269 		if (!running)
270 			throw new Exception("Tried to draw when begin was not called");
271 
272 		vec2 offset = originOffset;
273 
274 		auto horizontal = cast(int) origin & 0b11;
275 		auto vertical = cast(int) origin & 0b1100;
276 
277 		offset.x -= rectangle.x;
278 		offset.y -= rectangle.y;
279 
280 		vec2 drawOffset = vec2(0);
281 
282 		if (horizontal == 1)
283 		{
284 			drawOffset.x = -rectangle.width * 0.5;
285 			offset.x -= rectangle.width * 0.5;
286 		}
287 		else if (horizontal == 2)
288 		{
289 			drawOffset.x = -rectangle.width;
290 			offset.x -= rectangle.width;
291 		}
292 
293 		if (vertical == 4)
294 		{
295 			drawOffset.y = -rectangle.height * 0.5;
296 			offset.y -= rectangle.height * 0.5;
297 		}
298 		else if (vertical == 8)
299 		{
300 			drawOffset.y = -rectangle.height;
301 			offset.y -= rectangle.height;
302 		}
303 
304 		float s = -sin(rotation);
305 		float c = cos(rotation);
306 
307 		mat2 m = mat2(c, -s, s, c);
308 
309 		vertices[index++] = Vertex((rectangle.pos00 + offset) * m - offset + drawOffset,
310 				uv.pos00, color);
311 		vertices[index++] = Vertex((rectangle.pos10 + offset) * m - offset + drawOffset,
312 				uv.pos10, color);
313 		vertices[index++] = Vertex((rectangle.pos01 + offset) * m - offset + drawOffset,
314 				uv.pos01, color);
315 
316 		vertices[index++] = Vertex((rectangle.pos01 + offset) * m - offset + drawOffset,
317 				uv.pos01, color);
318 		vertices[index++] = Vertex((rectangle.pos10 + offset) * m - offset + drawOffset,
319 				uv.pos10, color);
320 		vertices[index++] = Vertex((rectangle.pos11 + offset) * m - offset + drawOffset,
321 				uv.pos11, color);
322 	}
323 
324 	void drawTriangle(Vertex[3] tri)
325 	{
326 		vertices[index++] = tri[0];
327 		vertices[index++] = tri[1];
328 		vertices[index++] = tri[2];
329 	}
330 }
331 
332 alias SpriteBatch = SpriteBatchImpl!(65536);