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     import tida.shape;
63 
64 private:
65     uint bid;
66     uint vid;
67     uint eid;
68 
69     size_t blength;
70     size_t elength;
71 
72 public:
73     Shape!T shapeinfo; /// Shape information
74 
75 @trusted:
76     /++
77     Enters data into a separate memory.
78 
79     Params:
80         buffer = Binded buffer.
81     +/
82     void bindFromBuffer(T[] buffer)
83     {
84         glGenVertexArrays(1, &vid);
85         glGenBuffers(1, &bid);
86         glBindVertexArray(vid);
87 
88         glBindBuffer(GL_ARRAY_BUFFER, bid);
89         glBufferData(GL_ARRAY_BUFFER, T.sizeof * buffer.length, buffer.ptr, GL_STATIC_DRAW);
90 
91         glBindBuffer(GL_ARRAY_BUFFER, 0);
92         glBindVertexArray(0);
93 
94         blength = buffer.length;
95     }
96 
97     /++
98     Enters data into a separate memory.
99 
100     Params:
101         buffer = Binded buffer.
102         element = Binded element buffer.
103     +/
104     void bindFromBufferAndElem(T[] buffer, uint[] element)
105     {
106         glGenVertexArrays(1, &vid);
107         glGenBuffers(1, &bid);
108         glBindVertexArray(vid);
109 
110         glBindBuffer(GL_ARRAY_BUFFER, bid);
111         glBufferData(GL_ARRAY_BUFFER, T.sizeof * buffer.length, buffer.ptr, GL_STATIC_DRAW);
112 
113         glGenBuffers(1, &eid);
114 
115         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eid);
116         glBufferData(GL_ELEMENT_ARRAY_BUFFER, uint.sizeof * buffer.length, element.ptr, GL_STATIC_DRAW);
117 
118         glBindBuffer(GL_ARRAY_BUFFER, 0);
119         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
120         glBindVertexArray(0);
121 
122         blength = buffer.length;
123         elength = element.length;
124     }
125 
126     /// Binds an array of vertices to the current render cycle.
127     void bindVertexArray() nothrow
128     {
129         glBindVertexArray(vid);
130     }
131 
132     /// Binds an buffer of vertices to the current render cycle.
133     void bindBuffer() nothrow
134     {
135         glBindBuffer(GL_ARRAY_BUFFER, bid);
136     }
137 
138     /// Binds an element buffer of vertices to the current render cycle.
139     void bindElementBuffer() nothrow
140     {
141         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eid);
142     }
143 
144     /// Unbinds an buffer of vertices to the current render cycle.
145     static void unbindBuffer() nothrow
146     {
147         glBindBuffer(GL_ARRAY_BUFFER, 0);
148     }
149 
150     /// Unbinds an element buffer of vertices to the current render cycle.
151     static void unbindElementBuffer() nothrow
152     {
153         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
154     }
155 
156     /// Unbinds an array of vertices to the current render cycle.
157     static void unbindVertexArray() nothrow
158     {
159         glBindVertexArray(0);
160     }
161 
162     /// Define an array of generic vertex attribute data
163     void vertexAttribPointer(uint vertLocation, int sample = 2) nothrow
164     {
165         glVertexAttribPointer(vertLocation, 2, glType!T, false, sample * cast(int) T.sizeof, null);
166     }
167 
168     /// Define an array of generic vertex attribute data
169     void textureAttribPointer(uint location, int sample = 4) nothrow
170     {
171         glVertexAttribPointer(location, 2, glType!T, false, sample * cast(int) T.sizeof, cast(void*) (T.sizeof * 2));
172     }
173 
174     void offsetAttribPointer(uint location, uint offset, uint sample) nothrow
175     {
176         glVertexAttribPointer(location, 2, glType!T, false, sample * cast(int) T.sizeof, cast(void*) (T.sizeof * offset));
177     }
178 
179     /// Define an array of generic vertex attribute data
180     void colorAttribPointer(uint location, uint sample = 6) nothrow
181     {
182         glVertexAttribPointer(location, 4, glType!T, false, sample * cast(int) T.sizeof, cast(void*) (T.sizeof * 2));
183     }
184 
185     /// ID of the generated vertex array.
186     @property uint idVertexArray() nothrow inout
187     {
188         return vid;
189     }
190 
191     /// The identifier of the buffer in memory.
192     @property uint idBufferArray() nothrow inout
193     {
194         return bid;
195     }
196 
197     /// The identifier of the elements in memory.
198     @property uint idElementArray() nothrow inout
199     {
200         return eid;
201     }
202 
203     /// Buffer length.
204     @property size_t length() nothrow inout
205     {
206         return blength;
207     }
208 
209     /// Element length
210     @property size_t elementLength() nothrow inout
211     {
212         return elength;
213     }
214 
215     /++
216     Outputs the rendering of the buffer.
217 
218     Params:
219         type = Shape type
220         count = Count rendering shapes
221     +/
222     void draw(ShapeType type, int count = 1)
223     {
224         switch(type)
225         {
226             case ShapeType.line:
227                 glDrawArrays(GL_LINES, 0, 2 * count);
228             break;
229 
230             case ShapeType.rectangle:
231                 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, null);
232             break;
233 
234             case ShapeType.roundrect:
235                 glDrawArrays(GL_TRIANGLES, 0, cast(uint) (blength / 2 * count));
236             break;
237 
238             case ShapeType.circle:
239                 glDrawArrays(GL_TRIANGLE_FAN, 0, cast(uint) (blength / 4 * count));
240             break;
241 
242             case ShapeType.triangle:
243                 glDrawArrays(GL_TRIANGLES, 0, cast(uint) blength);
244             break;
245 
246             case ShapeType.polygon:
247                 glDrawArrays(GL_TRIANGLE_FAN, 0, cast(uint) blength);
248             break;
249 
250             default:
251                 assert(null, "Unknown type!");
252         }
253     }
254 
255     /// Destroys vertex information.
256     void deleting()
257     {
258         if(idBufferArray != 0) glDeleteBuffers(1, &bid);
259         if(idVertexArray != 0) glDeleteVertexArrays(1, &vid);
260         if(idElementArray != 0) glDeleteBuffers(1, &eid);
261 
262         bid = 0;
263         vid = 0;
264         eid = 0;
265     }
266 
267     ~this()
268     {
269         this.deleting();
270     }
271 }
272 
273 /++
274 Generates the vertices of shapes to be rendered using hardware acceleration.
275 
276 Params:
277     T = Type.
278     shape = Shape information.
279     textureSize =   Texture size. If not specified, will not generate texture 
280                     vertices for vertices.
281 +/
282 Vector!T[] generateBuffer(T)(Shape!T shape, Vector!T textureSize = vecNaN!T) @safe nothrow pure
283 {
284     import std.math : cos, sin;
285 
286     Vector!T[] vertexs;
287 
288     switch (shape.type)
289     {
290         case ShapeType.point:
291             vertexs = [shape.begin];
292         break;
293 
294         case ShapeType.line:
295             vertexs = [shape.begin, shape.end];
296         break;
297 
298         case ShapeType.rectangle:
299             vertexs =   [
300                             vec!T(shape.end.x, shape.begin.y),
301                             shape.end,
302                             vec!T(shape.begin.x, shape.end.y),
303                             shape.begin
304                         ];
305         break;
306 
307         case ShapeType.roundrect:
308             immutable pos1 = shape.begin + vec!T(shape.radius, 0);
309             immutable pos2 = shape.begin + vec!T(0, shape.radius);
310 
311             immutable size = vec!T(shape.width, shape.height);
312 
313             vertexs =   [
314                             // FIRST RECTANGLE
315                             pos1,
316                             pos1 + vec!T(size.x - shape.radius * 2, 0),
317                             pos1 + size - vec!T(shape.radius * 2, 0),
318 
319                             pos1,
320                             pos1 + vec!T(0, size.y),
321                             pos1 + size - vec!T(shape.radius * 2, 0),
322 
323                             // SECOND RECTANGLE
324                             pos2,
325                             pos2 + vec!T(size.x, 0),
326                             pos2 + size - vec!T(0, shape.radius * 2),
327 
328                             pos2,
329                             pos2 + vec!T(0, size.y - shape.radius * 2),
330                             pos2 + size - vec!T(0, shape.radius * 2)
331                         ];
332 
333             void rounded(vec!T pos, T a1, T a2, T iter)
334             {
335                 for (T i = a1; i <= a2;)
336                 {
337                     vertexs ~= pos + vec!T(cos(i), sin(i)) * shape.radius;
338 
339                     i += iter;
340                     vertexs ~= pos + vec!T(cos(i), sin(i)) * shape.radius;
341                     vertexs ~= pos;
342 
343                     i += iter;
344                 }
345             }
346 
347             rounded(shape.begin + vec!T(shape.radius, shape.radius), 270, 360, 0.25);
348             rounded(shape.begin + vec!T(size.x - shape.radius, shape.radius), 0, 90, 0.25);
349             rounded(shape.begin + vec!T(shape.radius, size.y - shape.radius), 180, 270, 0.25);
350             rounded(shape.begin + vec!T(size.x - shape.radius, size.y - shape.radius), 90, 180, 0.25);
351         break;
352 
353         case ShapeType.circle:
354             for (T i = 0; i <= 360;)
355             {
356                 vertexs ~= shape.begin + vec!T(cos(i), sin(i)) * shape.radius;
357 
358                 i += 0.25;
359                 vertexs ~= shape.begin + vec!T(cos(i), sin(i)) * shape.radius;
360                 vertexs ~= shape.begin;
361 
362                 i += 0.25;
363             }
364         break;
365 
366         case ShapeType.triangle:
367             vertexs = shape.vertexs;
368         break;
369 
370         case ShapeType.polygon:
371             vertexs = shape.data;
372             vertexs ~= shape.data[0];
373         break;
374 
375         case ShapeType.multi:
376             foreach (cs; shape.shapes)
377             {
378                 vertexs ~= generateBuffer!T(cs);
379             }
380         break;
381 
382         default:
383             return null;
384     }
385 
386     if (!isVectorNaN!T(textureSize))
387     {
388         auto vertDump = vertexs.dup;
389         vertexs.length = 0;
390 
391         const clip = rectVertexs!T(vertDump);
392 
393         foreach (e; vertDump) {
394             vertexs ~= [e,
395                         vec!T   ((e.x - clip.x) / clip.width,
396                                 (e.y - clip.y) / clip.height)];
397         }
398     }
399 
400     return vertexs;
401 }
402 
403 unittest
404 {
405     immutable buffer = generateBuffer!(float)(
406         Shape!(float).Rectangle(vec!float(32.0f, 32.0f),
407                                 vec!float(96.0f, 96.0f)));
408 
409     immutable checkedBuffer =
410     [
411         vec!float(96.0f, 32.0f),
412         vec!float(96.0f, 96.0f),
413         vec!float(32.0f, 96.0f),
414         vec!float(32.0f, 32.0f)
415     ];
416 
417     assert(buffer == (checkedBuffer));
418 }
419 
420 /// ditto
421 VertexInfo!T generateVertex(T)(Shape!T shape, Vector!T textSize = vecNaN!T) @trusted
422 {
423     T[] buffer;
424 
425     buffer = generateBuffer!T(shape, textSize).generateArray;
426 
427     VertexInfo!T info = new VertexInfo!T();
428 
429     if (shape.type == ShapeType.rectangle)
430     {
431         uint[] elements = [0 ,1, 2, 2, 3 ,0];
432         info.bindFromBufferAndElem(buffer, elements);
433     }else
434     {
435         info.bindFromBuffer(buffer);
436     }
437 
438     info.shapeinfo = shape;
439 
440     destroy(buffer);
441 
442     return info;
443 }
444 
445 /// ditto
446 VertexInfo!T generateVertexColor(T)(Shape!T shape, Color!ubyte[] colors) @trusted
447 {
448     T[] buffer;
449     Color!float[] fcolors;
450 
451     foreach (e; colors)
452         fcolors ~= e.convert!(ubyte, float);
453 
454     buffer = generateBuffer!T(shape).generateArray;
455     T[] buffDump = buffer.dup;
456     buffer = [];
457 
458     VertexInfo!T info = new VertexInfo!T();
459 
460     size_t j = 0;
461     for (size_t i = 0; i < buffDump.length; i += 2)
462     {
463         buffer ~= buffDump[i .. i + 2] ~ fcolors[j].toBytes!(PixelFormat.RGBA);
464         j++;
465     }
466 
467     if (shape.type == ShapeType.rectangle)
468     {
469         uint[] elements = [0 ,1, 2, 2, 3 ,0];
470         info.bindFromBufferAndElem(buffer, elements);
471     }else
472     {
473         info.bindFromBuffer(buffer);
474     }
475 
476     info.shapeinfo = shape;
477 
478     destroy(buffer);
479 
480     return info;
481 }