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 }