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