1 /++
2 The module for generating vertices for drawing open graphics by the library.
3 
4 By means of $(HREF shape.html, Shape)'s, you can generate its vertices for a convenient 
5 representation and transfer such vertices to the shader to draw 
6 the corresponding shapes.
7 
8 Macros:
9     LREF = <a href="#$1">$1</a>
10     HREF = <a href="$1">$2</a>
11 
12 Authors: $(HREF https://github.com/TodNaz,TodNaz)
13 Copyright: Copyright (c) 2020 - 2021, TodNaz.
14 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT)
15 +/
16 module tida.vertgen;
17 
18 import tida.shape;
19 import tida.vector;
20 import std.traits;
21 import tida.color;
22 
23 /++
24 a template for translating a type into a type identifier that is 
25 understandable for the OpenGL interface.
26 
27 Params:
28     T = Type;
29 +/
30 template glType(T)
31 {
32     import tida.gl;
33 
34     static if (is(T : float))
35         enum glType = GL_FLOAT;
36     else
37     static if (is(T : double))
38         enum glType = GL_DOUBLE;
39     else
40     static if (is(T : byte))
41         enum glType = GL_BYTE;
42     else
43     static if (is(T : ubyte))
44         enum glType = GL_UNSIGNED_BYTE;
45     else
46     static if (is(T : int))
47         enum glType = GL_INT;
48     else
49     static if (is(T : uint))
50         enum glType = GL_UNSIGNED_INT;
51     else
52         static assert(null, "The `" ~ T.stringof ~ "` type cannot be translated into an interface that OpenGL understands.");
53 }
54 
55 /++
56 A storage object for the identifiers of the buffers stored in memory.
57 +/
58 class VertexInfo(T)
59 if (isNumeric!T)
60 {
61     import tida.gl;
62 
63 package(tida):
64     uint id = 0;
65 
66 public:
67     BufferInfo!(T) buffer = null;
68     ElementInfo!(uint) elements = null;
69 
70     this() @trusted
71     {
72         glGenVertexArrays(1, &id);
73     }
74 
75     void bind() @trusted
76     {
77         glBindVertexArray(id);
78     }
79 
80     static void unbind() @trusted
81     {
82         glBindVertexArray(0);
83     }
84 
85     static void vertexAttribPointer(uint location, uint components, uint sample, uint offset) @trusted
86     {
87         glVertexAttribPointer(
88             location,
89             components,
90             glType!T,
91             false,
92             sample * cast(uint) T.sizeof,
93             cast(void*) (T.sizeof * offset)
94         );
95     }
96 
97     static void positionAttribPointer(uint location, uint sample, uint offset = 0) @safe
98     {
99         vertexAttribPointer(location, 2, sample, offset);
100     }
101 
102     static void colorAttibPointer(uint location, uint sample, uint offset = 2) @safe
103     {
104         vertexAttribPointer(location, 4, sample, offset);
105     }
106 
107     static void enableVertex (uint location) @trusted
108     {
109         glEnableVertexAttribArray(location);
110     }
111 
112     void draw (ShapeType drawType, uint count = 1) @trusted
113     {
114         switch (drawType)
115         {
116             case ShapeType.point:
117             {
118                 if (elements is null)
119                 {
120                     glDrawArrays(GL_POINTS, 0, count);
121                 }
122                 else
123                 {
124                     glDrawElements(
125                         GL_POINTS,
126                         cast(uint) elements.length,
127                         GL_UNSIGNED_INT,
128                         null
129                     );
130                 }
131             }
132             break;
133 
134             case ShapeType.line:
135             {
136                 if (elements is null)
137                 {
138                     glDrawArrays(GL_LINES, 0, 2 * count);
139                 }
140                 else
141                 {
142                     glDrawElements(
143                         GL_LINES,
144                         cast(uint) elements.length,
145                         GL_UNSIGNED_INT,
146                         null
147                     );
148                 }
149             }
150             break;
151 
152             case ShapeType.triangle:
153             {
154                 if (elements is null)
155                 {
156                     glDrawArrays(GL_TRIANGLES, 0, 3 * count);
157                 }
158                 else
159                 {
160                     glDrawElements(
161                         GL_TRIANGLES,
162                         cast(uint) elements.length,
163                         GL_UNSIGNED_INT,
164                         null
165                     );
166                 }
167             }
168             break;
169 
170             case ShapeType.rectangle:
171             {
172                 if (elements is null)
173                 {
174                     glDrawArrays(GL_TRIANGLES, 0, 6 * count);
175                 }
176                 else
177                 {
178                     glDrawElements(
179                         GL_TRIANGLES,
180                         cast(uint) elements.length,
181                         GL_UNSIGNED_INT,
182                         null
183                     );
184                 }
185             }
186             break;
187 
188             case ShapeType.roundrect:
189             {
190                 if (elements is null)
191                 {
192                     glDrawArrays(GL_TRIANGLES, 0, cast(uint) buffer.length / (1 * count));
193                 }
194                 else
195                 {
196                     glDrawElements(
197                         GL_TRIANGLES,
198                         cast(uint) elements.length,
199                         GL_UNSIGNED_INT,
200                         null
201                     );
202                 }
203             }
204             break;
205 
206             case ShapeType.circle:
207             {
208                 if (elements is null)
209                 {
210                     glDrawArrays(GL_TRIANGLE_FAN, 0, cast(uint) buffer.length / 4);
211                 }
212                 else
213                 {
214                     glDrawElements(
215                         GL_TRIANGLE_FAN,
216                         cast(uint) elements.length,
217                         GL_UNSIGNED_INT,
218                         null
219                     );
220                 }
221             }
222             break;
223 
224             case ShapeType.polygon:
225             {
226                 if (elements is null)
227                 {
228                     glDrawArrays(GL_TRIANGLE_FAN, 0, cast(uint) buffer.length);
229                 }
230                 else
231                 {
232                     glDrawElements(
233                         GL_TRIANGLE_FAN,
234                         cast(uint) elements.length,
235                         GL_UNSIGNED_INT,
236                         null
237                     );
238                 }
239             }
240             break;
241 
242             case ShapeType.multi:
243                 return;
244 
245             case ShapeType.unknown:
246                 return;
247 
248             default:
249                 return;
250         }
251     }
252 
253     void clear() @trusted
254     {
255         glDeleteVertexArrays(1, &id);
256     }
257 
258     ~this() @trusted
259     {
260         clear();
261     }
262 }
263 
264 /++
265 Figure description structure for binding and drawing.
266 +/
267 class BufferInfo(T)
268 {
269     import tida.gl;
270 
271 package(tida):
272     uint id = 0;
273     T[] contextData;
274 
275 public:
276     Vector!T[] vertexData; /// Vertices.
277 
278     /// The remaining data.
279     /// Each element refers to a certain top.
280     T[][] attachData;
281 
282     this() @safe
283     {
284         generateBuffer();
285     }
286 
287     /// How many vertices are in the buffer
288     @property size_t length() @safe inout
289     {
290         return vertexData.length;
291     }
292 
293     /// How much data to attach (to all vertices).
294     @property size_t attachDataLength() @safe inout
295     {
296         size_t result;
297 
298         foreach (e; attachData)
299         {
300             result += e.length;
301         }
302 
303         return result;
304     }
305 
306     /// The final length of the buffer.
307     @property size_t rawLength() @safe inout
308     {
309         return length() + attachDataLength();
310     }
311 
312     /++
313     The function of accepting a vertex to the buffer, along with its attached 
314     data (position, color, texture coordinates, etc.)
315 
316     Params:
317         vertex = Vertex position.
318         attached = Enumerated data that can be attached to the node.
319 
320     Example:
321     ---
322     //             vertex position --  color ------------
323     buffer.append (vec!float (32, 32), 1.0, 0.0, 0.0, 1.0);
324     buffer.append (vec!float (64, 64), 0.0, 1.0, 0.0, 0.5);
325     ---
326     +/
327     void append (Args...) (Vector!float vertex, Args attached) @safe
328     {
329         vertexData ~= vertex;
330 
331         if (attached.length > attachData.length)
332             attachData.length = attached.length;
333 
334         size_t i = 0;
335         foreach (e; attached)
336         {
337             attachData[i] ~= e;
338             i++;
339         }
340     }
341 
342     /++
343     Issuance of the final data that should be obtained.
344     +/
345     @property T[] rawData() @safe
346     {
347         T[] result;
348 
349         foreach (size_t i, e; vertexData)
350         {
351             result ~= e.array;
352 
353             foreach (ae; attachData)
354                 result ~= ae[i];
355         }
356 
357         return result;
358     }
359 
360     /// Generate a buffer for the GPU.
361     void generateBuffer() @trusted
362     {
363         glGenBuffers(1, &id);
364     }
365 
366     /// Copy data to GPU.
367     void attach() @trusted
368     {
369         contextData = rawData();
370 
371         glBufferData(GL_ARRAY_BUFFER, T.sizeof * contextData.length, contextData.ptr, GL_STATIC_DRAW);
372     }
373 
374     /// Move data to GPU.
375     void move() @trusted
376     {
377         contextData = rawData();
378 
379         glBufferData(GL_ARRAY_BUFFER, T.sizeof * contextData.length, contextData.ptr, GL_STATIC_DRAW);
380         
381         contextData.length = 0;
382         vertexData.length = 0;
383         attachData.length = 0;
384     }
385 
386     /// Bind opengl buffer.
387     void bind() @trusted
388     {
389         glBindBuffer(GL_ARRAY_BUFFER, id);
390     }
391 
392     /// Unbind opengl buffer.
393     static void unbind() @trusted
394     {
395         glBindBuffer(GL_ARRAY_BUFFER, 0);
396     }
397 
398     /// Delete opengl buffer.
399     void deleteBuffer() @trusted
400     {
401         glDeleteBuffers(1, &id);
402     }
403 
404     /// Clear data.
405     void clear() @safe
406     {
407         if (id != 0)
408         {
409             deleteBuffer();
410         }
411 
412         vertexData.length = 0;
413         attachData.length = 0;
414         contextData.length = 0;
415     }
416 
417     ~this()
418     {
419         clear();
420     }
421 }
422 
423 /++
424 Vertex element data.
425 +/
426 class ElementInfo(T)
427 {
428     import tida.gl;
429 
430 package(tida):
431     uint id;
432 
433 public:
434     T[] data; /// Elements data
435 
436     this() @safe
437     {
438         generateBuffer();
439     }
440 
441     /// Elements length.
442     @property size_t length() @trusted
443     {
444         return data.length;
445     }
446 
447     /// Bind opengl element buffer.
448     void bind() @trusted
449     {
450         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id);
451     }
452 
453     /// Unbind opengl element buffer.
454     static void unbind() @trusted
455     {
456         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
457     }
458 
459     /// Generate element buffer for GPU.
460     void generateBuffer() @trusted
461     {
462         glGenBuffers(1, &id);
463     }
464 
465     /// Copy elements to GPU.
466     void attach() @trusted
467     {
468         glBufferData(GL_ELEMENT_ARRAY_BUFFER, T.sizeof * data.length, data.ptr, GL_STATIC_DRAW);
469     }
470 
471     /// Delete opengl buffer.
472     void deleteBuffer() @trusted
473     {
474         glDeleteBuffers(1, &id);
475     }
476 
477     /// Clear data.
478     void clear() @safe
479     {
480         deleteBuffer();
481         data.length = 0;
482     }
483 
484     ~this() @trusted
485     {
486         clear();
487     }
488 }
489 
490 /++
491 Generates the vertices of shapes to be rendered using hardware acceleration.
492 
493 Params:
494     T = Type.
495     shape = Shape information.
496     textureSize =   Texture size. If not specified, will not generate texture 
497                     vertices for vertices.
498 +/
499 Vector!T[] generateBuffer(T)(Shape!T shape) @safe nothrow pure
500 {
501     import std.math : cos, sin;
502 
503     Vector!T[] vertexs;
504 
505     switch (shape.type)
506     {
507         case ShapeType.point:
508             vertexs = [shape.begin];
509         break;
510 
511         case ShapeType.line:
512             vertexs = [shape.begin, shape.end];
513         break;
514 
515         case ShapeType.rectangle:
516             vertexs =   [
517                             vec!T(shape.end.x, shape.begin.y),
518                             shape.end,
519                             vec!T(shape.begin.x, shape.end.y),
520                             shape.begin
521                         ];
522         break;
523 
524         case ShapeType.roundrect:
525             immutable pos1 = shape.begin + vec!T(shape.radius, 0);
526             immutable pos2 = shape.begin + vec!T(0, shape.radius);
527 
528             immutable size = vec!T(shape.width, shape.height);
529 
530             vertexs =   [
531                             // FIRST RECTANGLE
532                             pos1,
533                             pos1 + vec!T(size.x - shape.radius * 2, 0),
534                             pos1 + size - vec!T(shape.radius * 2, 0),
535 
536                             pos1,
537                             pos1 + vec!T(0, size.y),
538                             pos1 + size - vec!T(shape.radius * 2, 0),
539 
540                             // SECOND RECTANGLE
541                             pos2,
542                             pos2 + vec!T(size.x, 0),
543                             pos2 + size - vec!T(0, shape.radius * 2),
544 
545                             pos2,
546                             pos2 + vec!T(0, size.y - shape.radius * 2),
547                             pos2 + size - vec!T(0, shape.radius * 2)
548                         ];
549 
550             void rounded(vec!T pos, T a1, T a2, T iter)
551             {
552                 for (T i = a1; i <= a2;)
553                 {
554                     vertexs ~= pos + vec!T(cos(i), sin(i)) * shape.radius;
555 
556                     i += iter;
557                     vertexs ~= pos + vec!T(cos(i), sin(i)) * shape.radius;
558                     vertexs ~= pos;
559 
560                     i += iter;
561                 }
562             }
563 
564             rounded(shape.begin + vec!T(shape.radius, shape.radius), 270, 360, 0.25);
565             rounded(shape.begin + vec!T(size.x - shape.radius, shape.radius), 0, 90, 0.25);
566             rounded(shape.begin + vec!T(shape.radius, size.y - shape.radius), 180, 270, 0.25);
567             rounded(shape.begin + vec!T(size.x - shape.radius, size.y - shape.radius), 90, 180, 0.25);
568         break;
569 
570         case ShapeType.circle:
571             for (T i = 0; i <= 360;)
572             {
573                 vertexs ~= shape.begin + vec!T(cos(i), sin(i)) * shape.radius;
574 
575                 i += 0.25;
576                 vertexs ~= shape.begin + vec!T(cos(i), sin(i)) * shape.radius;
577                 vertexs ~= shape.begin;
578 
579                 i += 0.25;
580             }
581         break;
582 
583         case ShapeType.triangle:
584             vertexs = shape.vertexs;
585         break;
586 
587         case ShapeType.polygon:
588             foreach (e; shape.data)
589                 vertexs ~= shape.begin + e;
590             vertexs ~= shape.begin + shape.data[0];
591         break;
592 
593         case ShapeType.multi:
594             foreach (cs; shape.shapes)
595             {
596                 vertexs ~= generateBuffer!T(cs);
597             }
598         break;
599 
600         default:
601             return null;
602     }
603 
604     return vertexs;
605 }
606 
607 unittest
608 {
609     immutable buffer = generateBuffer!(float)(
610         Shape!(float).Rectangle(vec!float(32.0f, 32.0f),
611                                 vec!float(96.0f, 96.0f)));
612 
613     immutable checkedBuffer =
614     [
615         vec!float(96.0f, 32.0f),
616         vec!float(96.0f, 96.0f),
617         vec!float(32.0f, 96.0f),
618         vec!float(32.0f, 32.0f)
619     ];
620 
621     assert(buffer == (checkedBuffer));
622 }