1 /*
2 Copyright (c) 2017-2022 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.graphics.shapes;
29 
30 import std.math;
31 
32 import dlib.core.memory;
33 import dlib.core.ownership;
34 import dlib.math.vector;
35 import dlib.container.array;
36 
37 import dagon.core.bindings;
38 import dagon.graphics.drawable;
39 import dagon.graphics.mesh;
40 
41 class ShapePlane: Mesh
42 {
43     this(float sx, float sz, uint numTiles, Owner owner)
44     {
45         super(owner);
46 
47         float px = -sx * 0.5f;
48         float py = -sz * 0.5f;
49 
50         float tileWidth = sx / numTiles;
51         float tileHeight = sz / numTiles;
52 
53         Vector3f start = Vector3f(px, 0.0f, py);
54 
55         uint gridSize = numTiles + 1;
56 
57         vertices = New!(Vector3f[])(gridSize * gridSize);
58         normals = New!(Vector3f[])(gridSize * gridSize);
59         texcoords = New!(Vector2f[])(gridSize * gridSize);
60 
61         for (uint i = 0, y = 0; y < gridSize; y++)
62         for (uint x = 0; x < gridSize; x++, i++)
63         {
64             vertices[i] = start + Vector3f(x * tileWidth, 0, y * tileHeight);
65             normals[i] = Vector3f(0, 1, 0);
66             texcoords[i] = Vector2f(x, y);
67         }
68 
69         indices = New!(uint[3][])(gridSize * gridSize * 2);
70 
71         uint index = 0;
72         for (uint y = 0; y < gridSize - 1; y++)
73         for (uint x = 0; x < gridSize - 1; x++)
74         {
75             uint offset = y * gridSize + x;
76             indices[index][2] = (offset + 0);
77             indices[index][1] = (offset + 1);
78             indices[index][0] = (offset + gridSize);
79 
80             indices[index+1][2] = (offset + 1);
81             indices[index+1][1] = (offset + gridSize + 1);
82             indices[index+1][0] = (offset + gridSize);
83 
84             index += 2;
85         }
86 
87         dataReady = true;
88         prepareVAO();
89     }
90 }
91 
92 class ShapeQuad: Owner, Drawable
93 {
94     Vector2f[4] vertices;
95     Vector2f[4] texcoords;
96     uint[3][2] indices;
97 
98     GLuint vao = 0;
99     GLuint vbo = 0;
100     GLuint tbo = 0;
101     GLuint eao = 0;
102 
103     this(Owner owner)
104     {
105         super(owner);
106 
107         vertices[0] = Vector2f(0, 1);
108         vertices[1] = Vector2f(0, 0);
109         vertices[2] = Vector2f(1, 0);
110         vertices[3] = Vector2f(1, 1);
111 
112         texcoords[0] = Vector2f(0, 1);
113         texcoords[1] = Vector2f(0, 0);
114         texcoords[2] = Vector2f(1, 0);
115         texcoords[3] = Vector2f(1, 1);
116 
117         indices[0][0] = 0;
118         indices[0][1] = 1;
119         indices[0][2] = 2;
120 
121         indices[1][0] = 0;
122         indices[1][1] = 2;
123         indices[1][2] = 3;
124 
125         glGenBuffers(1, &vbo);
126         glBindBuffer(GL_ARRAY_BUFFER, vbo);
127         glBufferData(GL_ARRAY_BUFFER, vertices.length * float.sizeof * 2, vertices.ptr, GL_STATIC_DRAW);
128 
129         glGenBuffers(1, &tbo);
130         glBindBuffer(GL_ARRAY_BUFFER, tbo);
131         glBufferData(GL_ARRAY_BUFFER, texcoords.length * float.sizeof * 2, texcoords.ptr, GL_STATIC_DRAW);
132 
133         glGenBuffers(1, &eao);
134         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
135         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.length * uint.sizeof * 3, indices.ptr, GL_STATIC_DRAW);
136 
137         glGenVertexArrays(1, &vao);
138         glBindVertexArray(vao);
139         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
140 
141         glEnableVertexAttribArray(VertexAttrib.Vertices);
142         glBindBuffer(GL_ARRAY_BUFFER, vbo);
143         glVertexAttribPointer(VertexAttrib.Vertices, 2, GL_FLOAT, GL_FALSE, 0, null);
144 
145         glEnableVertexAttribArray(VertexAttrib.Texcoords);
146         glBindBuffer(GL_ARRAY_BUFFER, tbo);
147         glVertexAttribPointer(VertexAttrib.Texcoords, 2, GL_FLOAT, GL_FALSE, 0, null);
148 
149         glBindVertexArray(0);
150     }
151 
152     ~this()
153     {
154         glDeleteVertexArrays(1, &vao);
155         glDeleteBuffers(1, &vbo);
156         glDeleteBuffers(1, &tbo);
157         glDeleteBuffers(1, &eao);
158     }
159 
160     void render(GraphicsState* state)
161     {
162         glDepthMask(0);
163         glBindVertexArray(vao);
164         glDrawElements(GL_TRIANGLES, cast(uint)indices.length * 3, GL_UNSIGNED_INT, cast(void*)0);
165         glBindVertexArray(0);
166         glDepthMask(1);
167     }
168 }
169 
170 class ShapeBox: Mesh
171 {
172     this(Vector3f extents, Owner owner)
173     {
174         super(owner);
175 
176         vertices = New!(Vector3f[])(24);
177         normals = New!(Vector3f[])(24);
178         texcoords = New!(Vector2f[])(24);
179         indices = New!(uint[3][])(12);
180 
181         Vector3f pmax = +extents;
182         Vector3f pmin = -extents;
183 
184         texcoords[0] = Vector2f(1, 0); normals[0] = Vector3f(0,0,1); vertices[0] = Vector3f(pmax.x, pmax.y, pmax.z);
185         texcoords[1] = Vector2f(0, 0); normals[1] = Vector3f(0,0,1); vertices[1] = Vector3f(pmin.x, pmax.y, pmax.z);
186         texcoords[2] = Vector2f(0, 1); normals[2] = Vector3f(0,0,1); vertices[2] = Vector3f(pmin.x, pmin.y, pmax.z);
187         texcoords[3] = Vector2f(1, 1); normals[3] = Vector3f(0,0,1); vertices[3] = Vector3f(pmax.x, pmin.y, pmax.z);
188         indices[0][0] = 0; indices[0][1] = 1; indices[0][2] = 2;
189         indices[1][0] = 2; indices[1][1] = 3; indices[1][2] = 0;
190 
191         texcoords[4] = Vector2f(0, 0); normals[4] = Vector3f(1,0,0); vertices[4] = Vector3f(pmax.x, pmax.y, pmax.z);
192         texcoords[5] = Vector2f(0, 1); normals[5] = Vector3f(1,0,0); vertices[5] = Vector3f(pmax.x, pmin.y, pmax.z);
193         texcoords[6] = Vector2f(1, 1); normals[6] = Vector3f(1,0,0); vertices[6] = Vector3f(pmax.x, pmin.y, pmin.z);
194         texcoords[7] = Vector2f(1, 0); normals[7] = Vector3f(1,0,0); vertices[7] = Vector3f(pmax.x, pmax.y, pmin.z);
195         indices[2][0] = 4; indices[2][1] = 5; indices[2][2] = 6;
196         indices[3][0] = 6; indices[3][1] = 7; indices[3][2] = 4;
197 
198         texcoords[8] = Vector2f(1, 1); normals[8] = Vector3f(0,1,0); vertices[8] = Vector3f(pmax.x, pmax.y, pmax.z);
199         texcoords[9] = Vector2f(1, 0); normals[9] = Vector3f(0,1,0); vertices[9] = Vector3f(pmax.x, pmax.y, pmin.z);
200         texcoords[10] = Vector2f(0, 0); normals[10] = Vector3f(0,1,0); vertices[10] = Vector3f(pmin.x, pmax.y, pmin.z);
201         texcoords[11] = Vector2f(0, 1); normals[11] = Vector3f(0,1,0); vertices[11] = Vector3f(pmin.x, pmax.y, pmax.z);
202         indices[4][0] = 8; indices[4][1] = 9; indices[4][2] = 10;
203         indices[5][0] = 10; indices[5][1] = 11; indices[5][2] = 8;
204 
205         texcoords[12] = Vector2f(1, 0); normals[12] = Vector3f(-1,0,0); vertices[12] = Vector3f(pmin.x, pmax.y, pmax.z);
206         texcoords[13] = Vector2f(0, 0); normals[13] = Vector3f(-1,0,0); vertices[13] = Vector3f(pmin.x, pmax.y, pmin.z);
207         texcoords[14] = Vector2f(0, 1); normals[14] = Vector3f(-1,0,0); vertices[14] = Vector3f(pmin.x, pmin.y, pmin.z);
208         texcoords[15] = Vector2f(1, 1); normals[15] = Vector3f(-1,0,0); vertices[15] = Vector3f(pmin.x, pmin.y, pmax.z);
209         indices[6][0] = 12; indices[6][1] = 13; indices[6][2] = 14;
210         indices[7][0] = 14; indices[7][1] = 15; indices[7][2] = 12;
211 
212         texcoords[16] = Vector2f(0, 1); normals[16] = Vector3f(0,-1,0); vertices[16] = Vector3f(pmin.x, pmin.y, pmin.z);
213         texcoords[17] = Vector2f(1, 1); normals[17] = Vector3f(0,-1,0); vertices[17] = Vector3f(pmax.x, pmin.y, pmin.z);
214         texcoords[18] = Vector2f(1, 0); normals[18] = Vector3f(0,-1,0); vertices[18] = Vector3f(pmax.x, pmin.y, pmax.z);
215         texcoords[19] = Vector2f(0, 0); normals[19] = Vector3f(0,-1,0); vertices[19] = Vector3f(pmin.x, pmin.y, pmax.z);
216         indices[8][0] = 16; indices[8][1] = 17; indices[8][2] = 18;
217         indices[9][0] = 18; indices[9][1] = 19; indices[9][2] = 16;
218 
219         texcoords[20] = Vector2f(0, 1); normals[20] = Vector3f(0,0,-1); vertices[20] = Vector3f(pmax.x, pmin.y, pmin.z);
220         texcoords[21] = Vector2f(1, 1); normals[21] = Vector3f(0,0,-1); vertices[21] = Vector3f(pmin.x, pmin.y, pmin.z);
221         texcoords[22] = Vector2f(1, 0); normals[22] = Vector3f(0,0,-1); vertices[22] = Vector3f(pmin.x, pmax.y, pmin.z);
222         texcoords[23] = Vector2f(0, 0); normals[23] = Vector3f(0,0,-1); vertices[23] = Vector3f(pmax.x, pmax.y, pmin.z);
223         indices[10][0] = 20; indices[10][1] = 21; indices[10][2] = 22;
224         indices[11][0] = 22; indices[11][1] = 23; indices[11][2] = 20;
225 
226         dataReady = true;
227         prepareVAO();
228     }
229     
230     this(float hw, float hh, float hd, Owner owner)
231     {
232         this(Vector3f(hw, hh, hd), owner);
233     }
234 }
235 
236 enum PI2 = PI * 2.0f;
237 enum HALF_PI = PI * 0.5f;
238 
239 Vector2f envMapEquirect(Vector3f dir)
240 {
241     Vector2f uv;
242     uv.y = acos(dir.y) / PI;
243     uv.x = (PI + atan2(dir.x, dir.z)) / PI2;
244     return uv;
245 }
246 
247 class ShapeSphere: Mesh
248 {
249     Array!Vector3f daVertices;
250     Array!Vector3f daNormals;
251     Array!Vector2f daTexcoords;
252     Array!(uint[3]) daIndices;
253 
254     this(float radius, int slices, int stacks, bool invNormals, Owner owner)
255     {
256         super(owner);
257 
258         float X1, Y1, X2, Y2, Z1, Z2;
259         float inc1, inc2, inc3, inc4, inc5, radius1, radius2;
260         uint[3] tri;
261         uint i = 0;
262 
263         float cuts = stacks;
264         float invCuts = 1.0f / cuts;
265         float heightStep = 2.0f * invCuts;
266 
267         float invSlices = 1.0f / slices;
268         float angleStep = (2.0f * PI) * invSlices;
269 
270         for(int h = 0; h < stacks; h++)
271         {
272             float h1Norm = cast(float)h * invCuts * 2.0f - 1.0f;
273             float h2Norm = cast(float)(h+1) * invCuts * 2.0f - 1.0f;
274             float y1 = sin(HALF_PI * h1Norm);
275             float y2 = sin(HALF_PI * h2Norm);
276 
277             float circleRadius1 = cos(HALF_PI * y1);
278             float circleRadius2 = cos(HALF_PI * y2);
279 
280             for(int a = 0; a < slices; a++)
281             {
282                 float x1a = sin(angleStep * a) * circleRadius1;
283                 float z1a = cos(angleStep * a) * circleRadius1;
284                 float x2a = sin(angleStep * (a + 1)) * circleRadius1;
285                 float z2a = cos(angleStep * (a + 1)) * circleRadius1;
286 
287                 float x1b = sin(angleStep * a) * circleRadius2;
288                 float z1b = cos(angleStep * a) * circleRadius2;
289                 float x2b = sin(angleStep * (a + 1)) * circleRadius2;
290                 float z2b = cos(angleStep * (a + 1)) * circleRadius2;
291 
292                 Vector3f v1 = Vector3f(x1a, y1, z1a);
293                 Vector3f v2 = Vector3f(x2a, y1, z2a);
294                 Vector3f v3 = Vector3f(x1b, y2, z1b);
295                 Vector3f v4 = Vector3f(x2b, y2, z2b);
296 
297                 Vector3f n1 = v1.normalized;
298                 Vector3f n2 = v2.normalized;
299                 Vector3f n3 = v3.normalized;
300                 Vector3f n4 = v4.normalized;
301 
302                 daVertices.append(n1 * radius);
303                 daVertices.append(n2 * radius);
304                 daVertices.append(n3 * radius);
305 
306                 daVertices.append(n3 * radius);
307                 daVertices.append(n2 * radius);
308                 daVertices.append(n4 * radius);
309 
310                 float sign = invNormals? -1.0f : 1.0f;
311 
312                 daNormals.append(n1 * sign);
313                 daNormals.append(n2 * sign);
314                 daNormals.append(n3 * sign);
315 
316                 daNormals.append(n3 * sign);
317                 daNormals.append(n2 * sign);
318                 daNormals.append(n4 * sign);
319 
320                 auto uv1 = Vector2f(0, 1);
321                 auto uv2 = Vector2f(1, 1);
322                 auto uv3 = Vector2f(0, 0);
323                 auto uv4 = Vector2f(1, 0);
324 
325                 daTexcoords.append(uv1);
326                 daTexcoords.append(uv2);
327                 daTexcoords.append(uv3);
328 
329                 daTexcoords.append(uv3);
330                 daTexcoords.append(uv2);
331                 daTexcoords.append(uv4);
332 
333                 if (invNormals)
334                 {
335                     tri[0] = i+2;
336                     tri[1] = i+1;
337                     tri[2] = i;
338                     daIndices.append(tri);
339 
340                     tri[0] = i+5;
341                     tri[1] = i+4;
342                     tri[2] = i+3;
343                     daIndices.append(tri);
344                 }
345                 else
346                 {
347                     tri[0] = i;
348                     tri[1] = i+1;
349                     tri[2] = i+2;
350                     daIndices.append(tri);
351 
352                     tri[0] = i+3;
353                     tri[1] = i+4;
354                     tri[2] = i+5;
355                     daIndices.append(tri);
356                 }
357 
358                 i += 6;
359             }
360         }
361 
362         vertices = New!(Vector3f[])(daVertices.length);
363         vertices[] = daVertices.data[];
364 
365         normals = New!(Vector3f[])(daNormals.length);
366         normals[] = daNormals.data[];
367 
368         texcoords = New!(Vector2f[])(daTexcoords.length);
369         texcoords[] = daTexcoords.data[];
370 
371         indices = New!(uint[3][])(daIndices.length);
372         indices[] = daIndices.data[];
373 
374         daVertices.free();
375         daNormals.free();
376         daTexcoords.free();
377         daIndices.free();
378 
379         dataReady = true;
380         prepareVAO();
381     }
382 }
383 
384 class ShapeDisk: Mesh
385 {
386     this(float radius, uint slices, Owner owner)
387     {
388         super(owner);
389         
390         vertices = New!(Vector3f[])(slices + 1);
391         normals = New!(Vector3f[])(slices + 1);
392         texcoords = New!(Vector2f[])(slices + 1);
393         indices = New!(uint[3][])(slices);
394         
395         float angleStep = (2.0f * PI) / slices;
396         float angle = 0.0f;
397         uint vi = 0;
398         uint i = 0;
399         
400         vertices[vi] = Vector3f(0.0f, 0.0f, 0.0f);
401         normals[vi] = Vector3f(0.0f, 1.0f, 0.0f);
402         texcoords[vi] = Vector2f(0.5f, 0.5f);
403         vi++;
404         
405         for(uint s = 0; s < slices; s++)
406         {
407             float x = cos(angle);
408             float z = sin(angle);
409             
410             vertices[vi] = Vector3f(x, 0.0f, z) * radius;
411             normals[vi] = Vector3f(0.0f, 1.0f, 0.0f);
412             texcoords[vi] = Vector2f(x * 0.5f + 0.5f, z * 0.5f + 0.5f);
413             
414             indices[i][0] = 0;
415             if (s < slices - 1)
416                 indices[i][1] = vi + 1;
417             else
418                 indices[i][1] = indices[0][2];
419             indices[i][2] = vi;
420             
421             vi++;
422             i++;
423             
424             angle += angleStep;
425         }
426         
427         dataReady = true;
428         prepareVAO();
429     }
430 }
431 
432 // TODO: ShapeCylinder, ShapeCone, ShapeCapsule