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