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 }