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 }