1 /*
2 Copyright (c) 2017-2020 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 
231 enum PI2 = PI * 2.0f;
232 enum HALF_PI = PI * 0.5f;
233 
234 Vector2f envMapEquirect(Vector3f dir)
235 {
236     Vector2f uv;
237     uv.y = acos(dir.y) / PI;
238     uv.x = (PI + atan2(dir.x, dir.z)) / PI2;
239     return uv;
240 }
241 
242 class ShapeSphere: Mesh
243 {
244     Array!Vector3f daVertices;
245     Array!Vector3f daNormals;
246     Array!Vector2f daTexcoords;
247     Array!(uint[3]) daIndices;
248 
249     this(float radius, int slices, int stacks, bool invNormals, Owner owner)
250     {
251         super(owner);
252 
253         float X1, Y1, X2, Y2, Z1, Z2;
254         float inc1, inc2, inc3, inc4, inc5, radius1, radius2;
255         uint[3] tri;
256         uint i = 0;
257 
258         float cuts = stacks;
259         float invCuts = 1.0f / cuts;
260         float heightStep = 2.0f * invCuts;
261 
262         float invSlices = 1.0f / slices;
263         float angleStep = (2.0f * PI) * invSlices;
264 
265         for(int h = 0; h < stacks; h++)
266         {
267             float h1Norm = cast(float)h * invCuts * 2.0f - 1.0f;
268             float h2Norm = cast(float)(h+1) * invCuts * 2.0f - 1.0f;
269             float y1 = sin(HALF_PI * h1Norm);
270             float y2 = sin(HALF_PI * h2Norm);
271 
272             float circleRadius1 = cos(HALF_PI * y1);
273             float circleRadius2 = cos(HALF_PI * y2);
274 
275             for(int a = 0; a < slices; a++)
276             {
277                 float x1a = sin(angleStep * a) * circleRadius1;
278                 float z1a = cos(angleStep * a) * circleRadius1;
279                 float x2a = sin(angleStep * (a + 1)) * circleRadius1;
280                 float z2a = cos(angleStep * (a + 1)) * circleRadius1;
281 
282                 float x1b = sin(angleStep * a) * circleRadius2;
283                 float z1b = cos(angleStep * a) * circleRadius2;
284                 float x2b = sin(angleStep * (a + 1)) * circleRadius2;
285                 float z2b = cos(angleStep * (a + 1)) * circleRadius2;
286 
287                 Vector3f v1 = Vector3f(x1a, y1, z1a);
288                 Vector3f v2 = Vector3f(x2a, y1, z2a);
289                 Vector3f v3 = Vector3f(x1b, y2, z1b);
290                 Vector3f v4 = Vector3f(x2b, y2, z2b);
291 
292                 Vector3f n1 = v1.normalized;
293                 Vector3f n2 = v2.normalized;
294                 Vector3f n3 = v3.normalized;
295                 Vector3f n4 = v4.normalized;
296 
297                 daVertices.append(n1 * radius);
298                 daVertices.append(n2 * radius);
299                 daVertices.append(n3 * radius);
300 
301                 daVertices.append(n3 * radius);
302                 daVertices.append(n2 * radius);
303                 daVertices.append(n4 * radius);
304 
305                 float sign = invNormals? -1.0f : 1.0f;
306 
307                 daNormals.append(n1 * sign);
308                 daNormals.append(n2 * sign);
309                 daNormals.append(n3 * sign);
310 
311                 daNormals.append(n3 * sign);
312                 daNormals.append(n2 * sign);
313                 daNormals.append(n4 * sign);
314 
315                 auto uv1 = Vector2f(0, 1);
316                 auto uv2 = Vector2f(1, 1);
317                 auto uv3 = Vector2f(0, 0);
318                 auto uv4 = Vector2f(1, 0);
319 
320                 daTexcoords.append(uv1);
321                 daTexcoords.append(uv2);
322                 daTexcoords.append(uv3);
323 
324                 daTexcoords.append(uv3);
325                 daTexcoords.append(uv2);
326                 daTexcoords.append(uv4);
327 
328                 if (invNormals)
329                 {
330                     tri[0] = i+2;
331                     tri[1] = i+1;
332                     tri[2] = i;
333                     daIndices.append(tri);
334 
335                     tri[0] = i+5;
336                     tri[1] = i+4;
337                     tri[2] = i+3;
338                     daIndices.append(tri);
339                 }
340                 else
341                 {
342                     tri[0] = i;
343                     tri[1] = i+1;
344                     tri[2] = i+2;
345                     daIndices.append(tri);
346 
347                     tri[0] = i+3;
348                     tri[1] = i+4;
349                     tri[2] = i+5;
350                     daIndices.append(tri);
351                 }
352 
353                 i += 6;
354             }
355         }
356 
357         vertices = New!(Vector3f[])(daVertices.length);
358         vertices[] = daVertices.data[];
359 
360         normals = New!(Vector3f[])(daNormals.length);
361         normals[] = daNormals.data[];
362 
363         texcoords = New!(Vector2f[])(daTexcoords.length);
364         texcoords[] = daTexcoords.data[];
365 
366         indices = New!(uint[3][])(daIndices.length);
367         indices[] = daIndices.data[];
368 
369         daVertices.free();
370         daNormals.free();
371         daTexcoords.free();
372         daIndices.free();
373 
374         dataReady = true;
375         prepareVAO();
376     }
377 }
378 
379 class ShapeDisk: Mesh
380 {
381     this(float radius, uint slices, Owner owner)
382     {
383         super(owner);
384         
385         vertices = New!(Vector3f[])(slices + 1);
386         normals = New!(Vector3f[])(slices + 1);
387         texcoords = New!(Vector2f[])(slices + 1);
388         indices = New!(uint[3][])(slices);
389         
390         float angleStep = (2.0f * PI) / slices;
391         float angle = 0.0f;
392         uint vi = 0;
393         uint i = 0;
394         
395         vertices[vi] = Vector3f(0.0f, 0.0f, 0.0f);
396         normals[vi] = Vector3f(0.0f, 1.0f, 0.0f);
397         texcoords[vi] = Vector2f(0.5f, 0.5f);
398         vi++;
399         
400         for(uint s = 0; s < slices; s++)
401         {
402             float x = cos(angle);
403             float z = sin(angle);
404             
405             vertices[vi] = Vector3f(x, 0.0f, z) * radius;
406             normals[vi] = Vector3f(0.0f, 1.0f, 0.0f);
407             texcoords[vi] = Vector2f(x * 0.5f + 0.5f, z * 0.5f + 0.5f);
408             
409             indices[i][0] = 0;
410             if (s < slices - 1)
411                 indices[i][1] = vi + 1;
412             else
413                 indices[i][1] = indices[0][2];
414             indices[i][2] = vi;
415             
416             vi++;
417             i++;
418             
419             angle += angleStep;
420         }
421         
422         dataReady = true;
423         prepareVAO();
424     }
425 }
426 
427 // TODO: other shapes from original Dagon