1 /* 2 Copyright © 2020, Luna Nielsen 3 Distributed under the 2-Clause BSD License, see LICENSE file. 4 5 Authors: Luna Nielsen 6 */ 7 module engine.render.tile; 8 import engine; 9 import std.conv; 10 import std.exception; 11 import std.format : format; 12 13 private { 14 static Shader TileShader; 15 static GLint TileShaderMVP; 16 static GLint TileAvailability; 17 } 18 19 /** 20 Initializes the tile mesh system 21 */ 22 void initTileMesh() { 23 24 // Load tile shader if needed 25 if (TileShader is null) { 26 TileShader = new Shader(import("shaders/tile.vert"), import("shaders/tile.frag")); 27 TileShader.use(); 28 TileShaderMVP = TileShader.getUniformLocation("mvp"); 29 TileAvailability = TileShader.getUniformLocation("available"); 30 } 31 } 32 33 /** 34 The side of the tile mesh 35 */ 36 enum TileMeshSide { 37 /// Font of the tile mesh 38 Front, 39 40 /// Back of the tile mesh 41 Back, 42 43 // Left/right sides of the tile mesh 44 Side, 45 46 // Top/bottom sides of the tile mesh 47 Cap 48 } 49 50 private static GLuint vao; 51 52 /** 53 A mesh for a tile 54 */ 55 class TileMesh { 56 private: 57 TextureAtlas atlas; 58 59 60 vec3 size; 61 float[12*3*3] verts; 62 float[12*2*3] uvs; 63 GLuint uvId; 64 GLuint vdId; 65 66 void genVerts() { 67 68 // We want to generate the model so that origin is in the center 69 immutable(float) relWidth = size.x/2; 70 immutable(float) relHeight = size.y/2; 71 immutable(float) relLength = size.z/2; 72 73 // Populate verticies 74 verts = [ 75 // Front Face Tri 1 76 -relWidth, -relHeight, relLength, 77 relWidth, -relHeight, relLength, 78 -relWidth, relHeight, relLength, 79 80 // Front Face Tri 2 81 relWidth, -relHeight, relLength, 82 relWidth, relHeight, relLength, 83 -relWidth, relHeight, relLength, 84 85 // Top Face Tri 1 86 relWidth, relHeight, -relLength, 87 -relWidth, relHeight, -relLength, 88 relWidth, relHeight, relLength, 89 90 // Top Face Tri 2 91 -relWidth, relHeight, -relLength, 92 -relWidth, relHeight, relLength, 93 relWidth, relHeight, relLength, 94 95 // Back Face Tri 1 96 relWidth, -relHeight, -relLength, 97 -relWidth, -relHeight, -relLength, 98 relWidth, relHeight, -relLength, 99 100 // Back Face Tri 2 101 -relWidth, -relHeight, -relLength, 102 -relWidth, relHeight, -relLength, 103 relWidth, relHeight, -relLength, 104 105 // Bottom Face Tri 1 106 -relWidth, -relHeight, -relLength, 107 relWidth, -relHeight, -relLength, 108 -relWidth, -relHeight, relLength, 109 110 // Bottom Face Tri 2 111 relWidth, -relHeight, -relLength, 112 relWidth, -relHeight, relLength, 113 -relWidth, -relHeight, relLength, 114 115 // Left Face Tri 1 116 -relWidth, relHeight, -relLength, 117 -relWidth, -relHeight, -relLength, 118 -relWidth, relHeight, relLength, 119 120 // Left Face Tri 2 121 -relWidth, -relHeight, -relLength, 122 -relWidth, -relHeight, relLength, 123 -relWidth, relHeight, relLength, 124 125 // Right Face Tri 1 126 relWidth, -relHeight, -relLength, 127 relWidth, relHeight, -relLength, 128 relWidth, -relHeight, relLength, 129 130 // Right Face Tri 2 131 relWidth, relHeight, -relLength, 132 relWidth, relHeight, relLength, 133 relWidth, -relHeight, relLength, 134 ]; 135 136 } 137 138 void genUV(TileMeshSide side, AtlasArea area) { 139 immutable(vec4) faceUV = area.uv; 140 switch(side) { 141 case TileMeshSide.Front: 142 uvs[0..12] = [ 143 faceUV.x, faceUV.w, 144 faceUV.z, faceUV.w, 145 faceUV.x, faceUV.y, 146 147 faceUV.z, faceUV.w, 148 faceUV.z, faceUV.y, 149 faceUV.x, faceUV.y, 150 ]; 151 break; 152 153 case TileMeshSide.Back: 154 uvs[24..36] = [ 155 faceUV.x, faceUV.w, 156 faceUV.z, faceUV.w, 157 faceUV.x, faceUV.y, 158 159 faceUV.z, faceUV.w, 160 faceUV.z, faceUV.y, 161 faceUV.x, faceUV.y, 162 ]; 163 164 break; 165 166 case TileMeshSide.Side: 167 uvs[48..60] = [ 168 faceUV.x, faceUV.w, 169 faceUV.z, faceUV.w, 170 faceUV.x, faceUV.y, 171 172 faceUV.z, faceUV.w, 173 faceUV.z, faceUV.y, 174 faceUV.x, faceUV.y, 175 ]; 176 uvs[60..72] = [ 177 faceUV.x, faceUV.w, 178 faceUV.z, faceUV.w, 179 faceUV.x, faceUV.y, 180 181 faceUV.z, faceUV.w, 182 faceUV.z, faceUV.y, 183 faceUV.x, faceUV.y, 184 ]; 185 break; 186 187 case TileMeshSide.Cap: 188 uvs[12..24] = [ 189 faceUV.x, faceUV.w, 190 faceUV.z, faceUV.w, 191 faceUV.x, faceUV.y, 192 193 faceUV.z, faceUV.w, 194 faceUV.z, faceUV.y, 195 faceUV.x, faceUV.y, 196 ]; 197 uvs[36..48] = [ 198 faceUV.x, faceUV.w, 199 faceUV.z, faceUV.w, 200 faceUV.x, faceUV.y, 201 202 faceUV.z, faceUV.w, 203 faceUV.z, faceUV.y, 204 faceUV.x, faceUV.y, 205 ]; 206 break; 207 default: break; 208 } 209 } 210 211 void initTile(string front, string back, string side, string cap) { 212 213 // Gen new vao if needed 214 if (vao == 0) glGenVertexArrays(1, &vao); 215 glBindVertexArray(vao); 216 217 glGenBuffers(1, &vdId); 218 glGenBuffers(1, &uvId); 219 220 genVerts(); 221 glBindBuffer(GL_ARRAY_BUFFER, vdId); 222 glBufferData(GL_ARRAY_BUFFER, float.sizeof*verts.length, verts.ptr, GL_STATIC_DRAW); 223 224 genUV(TileMeshSide.Front, atlas[front]); 225 genUV(TileMeshSide.Back, atlas[back]); 226 genUV(TileMeshSide.Side, atlas[side]); 227 genUV(TileMeshSide.Cap, atlas[cap]); 228 glBindBuffer(GL_ARRAY_BUFFER, uvId); 229 glBufferData(GL_ARRAY_BUFFER, float.sizeof*uvs.length, uvs.ptr, GL_STATIC_DRAW); 230 } 231 232 void bind() { 233 234 // Set vertex attributes 235 glEnableVertexAttribArray(0); 236 glEnableVertexAttribArray(1); 237 glBindBuffer(GL_ARRAY_BUFFER, vdId); 238 glVertexAttribPointer( 239 0, 240 3, 241 GL_FLOAT, 242 GL_FALSE, 243 0, 244 null 245 ); 246 247 glBindBuffer(GL_ARRAY_BUFFER, uvId); 248 glVertexAttribPointer( 249 1, 250 2, 251 GL_FLOAT, 252 GL_FALSE, 253 0, 254 null 255 ); 256 } 257 258 public: 259 260 /** 261 Availability 262 */ 263 bool available = true; 264 265 /** 266 Destructor 267 */ 268 ~this() { 269 glDeleteBuffers(1, &uvId); 270 glDeleteBuffers(1, &vdId); 271 } 272 273 /** 274 Construct a new tile 275 */ 276 this(vec3 size, TextureAtlas atlas, string frontTexture, string backTexture, string sideTexture, string capTexture) { 277 this.atlas = atlas; 278 this.size = size; 279 initTile(frontTexture, backTexture, sideTexture, capTexture); 280 } 281 282 /** 283 Changes the texture of a side of the tile 284 */ 285 void setTexture(TileMeshSide side, AtlasArea tex) { 286 genUV(side, tex); 287 glBindBuffer(GL_ARRAY_BUFFER, uvId); 288 glBufferData(GL_ARRAY_BUFFER, float.sizeof*uvs.length, uvs.ptr, GL_STATIC_DRAW); 289 } 290 291 /** 292 Begin rendering tiles 293 */ 294 static void begin() { 295 296 // Bind the vao 297 glBindVertexArray(vao); 298 } 299 300 /** 301 End rendering tiles 302 */ 303 static void end() { 304 glDisableVertexAttribArray(0); 305 glDisableVertexAttribArray(1); 306 } 307 308 /** 309 Draws the tile 310 */ 311 void draw(Camera camera, mat4 transform) { 312 313 bind(); 314 TileShader.use(); 315 atlas.bind(); 316 TileShader.setUniform(TileShaderMVP, camera.matrix*transform); 317 TileShader.setUniform(TileAvailability, available); 318 glDrawArrays(GL_TRIANGLES, 0, cast(int)verts.length); 319 } 320 321 /** 322 Draws the tile on a 2D plane 323 */ 324 void draw2d(Camera2D camera, vec2 position, float scale = 1, quat rotation = quat.identity) { 325 326 bind(); 327 TileShader.use(); 328 atlas.bind(); 329 TileShader.setUniform(TileShaderMVP, 330 camera.matrix * 331 mat4.translation(position.x, position.y, -(size.z*scale)) * 332 rotation.to_matrix!(4, 4) * 333 mat4.scaling(scale, -scale, scale) 334 ); 335 336 glDrawArrays(GL_TRIANGLES, 0, cast(int)verts.length); 337 } 338 }