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 }