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);