1 /* 2 * Copyright (c) 2009-2010 Mikko Mononen memon@inside.org 3 * 4 * This software is provided 'as-is', without any express or implied 5 * warranty. In no event will the authors be held liable for any damages 6 * arising from the use of this software. 7 * Permission is granted to anyone to use this software for any purpose, 8 * including commercial applications, and to alter it and redistribute it 9 * freely, subject to the following restrictions: 10 * 1. The origin of this software must not be misrepresented; you must not 11 * claim that you wrote the original software. If you use this software 12 * in a product, an acknowledgment in the product documentation would be 13 * appreciated but is not required. 14 * 2. Altered source versions must be plainly marked as such, and must not be 15 * misrepresented as being the original software. 16 * 3. This notice may not be removed or altered from any source distribution. 17 */ 18 module imgui.gl3_renderer; 19 20 import core.stdc.stdlib; 21 import core.stdc..string; 22 23 import std.math; 24 import std.stdio; 25 26 import glad.gl.all; 27 import glad.gl.loader; 28 29 import imgui.api; 30 import imgui.engine; 31 import imgui.stdb_truetype; 32 33 private: 34 // Draw up to 65536 unicode glyphs. What this will actually do is draw *only glyphs the 35 // font supports* until it will run out of glyphs or texture space (determined by 36 // g_font_texture_size). The actual number of glyphs will be in thousands (ASCII is 37 // guaranteed, the rest will depend mainly on what the font supports, e.g. if it 38 // supports common European characters such as á or š they will be there because they 39 // are "early" in Unicode) 40 // 41 // Note that g_cdata uses memory of stbtt_bakedchar.sizeof * MAX_CHARACTER_COUNT which 42 // at the moment is 20 * 65536 or 1.25 MiB. 43 enum MAX_CHARACTER_COUNT = 1024 * 16 * 4; 44 enum FIRST_CHARACTER = 32; 45 46 47 48 /** Globals start. */ 49 50 // A 1024x1024 font texture takes 1MiB of memory, and should be enough for thousands of 51 // glyphs (at the fixed 15.0f size imgui uses). 52 // 53 // Some examples: 54 // 55 // =================================================== ============ ============================= 56 // Font Texture size Glyps fit 57 // =================================================== ============ ============================= 58 // GentiumPlus-R 512x512 2550 (all glyphs in the font) 59 // GentiumPlus-R 256x256 709 60 // DroidSans (the small version included for examples) 512x512 903 (all glyphs in the font) 61 // DroidSans (the small version included for examples) 256x256 497 62 // =================================================== ============ ============================= 63 // 64 // This was measured after the optimization to reuse null character glyph, which is in 65 // BakeFontBitmap in stdb_truetype.d 66 __gshared uint g_font_texture_size = 1024; 67 __gshared float[TEMP_COORD_COUNT * 2] g_tempCoords; 68 __gshared float[TEMP_COORD_COUNT * 2] g_tempNormals; 69 __gshared float[TEMP_COORD_COUNT * 12 + (TEMP_COORD_COUNT - 2) * 6] g_tempVertices; 70 __gshared float[TEMP_COORD_COUNT * 12 + (TEMP_COORD_COUNT - 2) * 6] g_tempTextureCoords; 71 __gshared float[TEMP_COORD_COUNT * 24 + (TEMP_COORD_COUNT - 2) * 12] g_tempColors; 72 __gshared float[CIRCLE_VERTS * 2] g_circleVerts; 73 __gshared uint g_max_character_count = MAX_CHARACTER_COUNT; 74 __gshared stbtt_bakedchar[MAX_CHARACTER_COUNT] g_cdata; 75 __gshared GLuint g_ftex = 0; 76 __gshared GLuint g_whitetex = 0; 77 __gshared GLuint g_vao = 0; 78 __gshared GLuint[3] g_vbos = [0, 0, 0]; 79 __gshared GLuint g_program = 0; 80 __gshared GLuint g_programViewportLocation = 0; 81 __gshared GLuint g_programTextureLocation = 0; 82 83 /** Globals end. */ 84 85 enum TEMP_COORD_COUNT = 100; 86 enum int CIRCLE_VERTS = 8 * 4; 87 immutable float[4] g_tabStops = [150, 210, 270, 330]; 88 89 package: 90 91 uint maxCharacterCount() @trusted nothrow @nogc 92 { 93 return g_max_character_count; 94 } 95 96 void imguifree(void* ptr, void* /*userptr*/) 97 { 98 free(ptr); 99 } 100 101 void* imguimalloc(size_t size, void* /*userptr*/) 102 { 103 return malloc(size); 104 } 105 106 uint toPackedRGBA(RGBA color) 107 { 108 return (color.r) | (color.g << 8) | (color.b << 16) | (color.a << 24); 109 } 110 111 void drawPolygon(const(float)* coords, uint numCoords, float r, uint col) 112 { 113 if (numCoords > TEMP_COORD_COUNT) 114 numCoords = TEMP_COORD_COUNT; 115 116 for (uint i = 0, j = numCoords - 1; i < numCoords; j = i++) 117 { 118 const(float)* v0 = &coords[j * 2]; 119 const(float)* v1 = &coords[i * 2]; 120 float dx = v1[0] - v0[0]; 121 float dy = v1[1] - v0[1]; 122 float d = sqrt(dx * dx + dy * dy); 123 124 if (d > 0) 125 { 126 d = 1.0f / d; 127 dx *= d; 128 dy *= d; 129 } 130 g_tempNormals[j * 2 + 0] = dy; 131 g_tempNormals[j * 2 + 1] = -dx; 132 } 133 134 const float[4] colf = [cast(float)(col & 0xff) / 255.0, cast(float)((col >> 8) & 0xff) / 255.0, cast(float)((col >> 16) & 0xff) / 255.0, cast(float)((col >> 24) & 0xff) / 255.0]; 135 const float[4] colTransf = [cast(float)(col & 0xff) / 255.0, cast(float)((col >> 8) & 0xff) / 255.0, cast(float)((col >> 16) & 0xff) / 255.0, 0]; 136 137 for (uint i = 0, j = numCoords - 1; i < numCoords; j = i++) 138 { 139 float dlx0 = g_tempNormals[j * 2 + 0]; 140 float dly0 = g_tempNormals[j * 2 + 1]; 141 float dlx1 = g_tempNormals[i * 2 + 0]; 142 float dly1 = g_tempNormals[i * 2 + 1]; 143 float dmx = (dlx0 + dlx1) * 0.5f; 144 float dmy = (dly0 + dly1) * 0.5f; 145 float dmr2 = dmx * dmx + dmy * dmy; 146 147 if (dmr2 > 0.000001f) 148 { 149 float scale = 1.0f / dmr2; 150 151 if (scale > 10.0f) 152 scale = 10.0f; 153 dmx *= scale; 154 dmy *= scale; 155 } 156 g_tempCoords[i * 2 + 0] = coords[i * 2 + 0] + dmx * r; 157 g_tempCoords[i * 2 + 1] = coords[i * 2 + 1] + dmy * r; 158 } 159 160 int vSize = numCoords * 12 + (numCoords - 2) * 6; 161 int uvSize = numCoords * 2 * 6 + (numCoords - 2) * 2 * 3; 162 int cSize = numCoords * 4 * 6 + (numCoords - 2) * 4 * 3; 163 float* v = g_tempVertices.ptr; 164 float* uv = g_tempTextureCoords.ptr; 165 memset(uv, 0, uvSize * float.sizeof); 166 float* c = g_tempColors.ptr; 167 memset(c, 1, cSize * float.sizeof); 168 169 float* ptrV = v; 170 float* ptrC = c; 171 172 for (uint i = 0, j = numCoords - 1; i < numCoords; j = i++) 173 { 174 *ptrV = coords[i * 2]; 175 *(ptrV + 1) = coords[i * 2 + 1]; 176 ptrV += 2; 177 *ptrV = coords[j * 2]; 178 *(ptrV + 1) = coords[j * 2 + 1]; 179 ptrV += 2; 180 *ptrV = g_tempCoords[j * 2]; 181 *(ptrV + 1) = g_tempCoords[j * 2 + 1]; 182 ptrV += 2; 183 *ptrV = g_tempCoords[j * 2]; 184 *(ptrV + 1) = g_tempCoords[j * 2 + 1]; 185 ptrV += 2; 186 *ptrV = g_tempCoords[i * 2]; 187 *(ptrV + 1) = g_tempCoords[i * 2 + 1]; 188 ptrV += 2; 189 *ptrV = coords[i * 2]; 190 *(ptrV + 1) = coords[i * 2 + 1]; 191 ptrV += 2; 192 193 *ptrC = colf[0]; 194 *(ptrC + 1) = colf[1]; 195 *(ptrC + 2) = colf[2]; 196 *(ptrC + 3) = colf[3]; 197 ptrC += 4; 198 *ptrC = colf[0]; 199 *(ptrC + 1) = colf[1]; 200 *(ptrC + 2) = colf[2]; 201 *(ptrC + 3) = colf[3]; 202 ptrC += 4; 203 *ptrC = colTransf[0]; 204 *(ptrC + 1) = colTransf[1]; 205 *(ptrC + 2) = colTransf[2]; 206 *(ptrC + 3) = colTransf[3]; 207 ptrC += 4; 208 *ptrC = colTransf[0]; 209 *(ptrC + 1) = colTransf[1]; 210 *(ptrC + 2) = colTransf[2]; 211 *(ptrC + 3) = colTransf[3]; 212 ptrC += 4; 213 *ptrC = colTransf[0]; 214 *(ptrC + 1) = colTransf[1]; 215 *(ptrC + 2) = colTransf[2]; 216 *(ptrC + 3) = colTransf[3]; 217 ptrC += 4; 218 *ptrC = colf[0]; 219 *(ptrC + 1) = colf[1]; 220 *(ptrC + 2) = colf[2]; 221 *(ptrC + 3) = colf[3]; 222 ptrC += 4; 223 } 224 225 for (uint i = 2; i < numCoords; ++i) 226 { 227 *ptrV = coords[0]; 228 *(ptrV + 1) = coords[1]; 229 ptrV += 2; 230 *ptrV = coords[(i - 1) * 2]; 231 *(ptrV + 1) = coords[(i - 1) * 2 + 1]; 232 ptrV += 2; 233 *ptrV = coords[i * 2]; 234 *(ptrV + 1) = coords[i * 2 + 1]; 235 ptrV += 2; 236 237 *ptrC = colf[0]; 238 *(ptrC + 1) = colf[1]; 239 *(ptrC + 2) = colf[2]; 240 *(ptrC + 3) = colf[3]; 241 ptrC += 4; 242 *ptrC = colf[0]; 243 *(ptrC + 1) = colf[1]; 244 *(ptrC + 2) = colf[2]; 245 *(ptrC + 3) = colf[3]; 246 ptrC += 4; 247 *ptrC = colf[0]; 248 *(ptrC + 1) = colf[1]; 249 *(ptrC + 2) = colf[2]; 250 *(ptrC + 3) = colf[3]; 251 ptrC += 4; 252 } 253 254 glBindTexture(GL_TEXTURE_2D, g_whitetex); 255 256 glBindVertexArray(g_vao); 257 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]); 258 glBufferData(GL_ARRAY_BUFFER, vSize * float.sizeof, v, GL_STATIC_DRAW); 259 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]); 260 glBufferData(GL_ARRAY_BUFFER, uvSize * float.sizeof, uv, GL_STATIC_DRAW); 261 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]); 262 glBufferData(GL_ARRAY_BUFFER, cSize * float.sizeof, c, GL_STATIC_DRAW); 263 glDrawArrays(GL_TRIANGLES, 0, (numCoords * 2 + numCoords - 2) * 3); 264 } 265 266 void drawRect(float x, float y, float w, float h, float fth, uint col) 267 { 268 const float[4 * 2] verts = 269 [ 270 x + 0.5f, y + 0.5f, 271 x + w - 0.5f, y + 0.5f, 272 x + w - 0.5f, y + h - 0.5f, 273 x + 0.5f, y + h - 0.5f, 274 ]; 275 drawPolygon(verts.ptr, 4, fth, col); 276 } 277 278 /* 279 void drawEllipse(float x, float y, float w, float h, float fth, uint col) 280 { 281 float verts[CIRCLE_VERTS*2]; 282 const(float)* cverts = g_circleVerts; 283 float* v = verts; 284 285 for (int i = 0; i < CIRCLE_VERTS; ++i) 286 { 287 * v++ = x + cverts[i*2]*w; 288 * v++ = y + cverts[i*2+1]*h; 289 } 290 291 drawPolygon(verts, CIRCLE_VERTS, fth, col); 292 } 293 */ 294 295 void drawRoundedRect(float x, float y, float w, float h, float r, float fth, uint col) 296 { 297 const uint n = CIRCLE_VERTS / 4; 298 float[(n + 1) * 4 * 2] verts; 299 const(float)* cverts = g_circleVerts.ptr; 300 float* v = verts.ptr; 301 302 for (uint i = 0; i <= n; ++i) 303 { 304 *v++ = x + w - r + cverts[i * 2] * r; 305 *v++ = y + h - r + cverts[i * 2 + 1] * r; 306 } 307 308 for (uint i = n; i <= n * 2; ++i) 309 { 310 *v++ = x + r + cverts[i * 2] * r; 311 *v++ = y + h - r + cverts[i * 2 + 1] * r; 312 } 313 314 for (uint i = n * 2; i <= n * 3; ++i) 315 { 316 *v++ = x + r + cverts[i * 2] * r; 317 *v++ = y + r + cverts[i * 2 + 1] * r; 318 } 319 320 for (uint i = n * 3; i < n * 4; ++i) 321 { 322 *v++ = x + w - r + cverts[i * 2] * r; 323 *v++ = y + r + cverts[i * 2 + 1] * r; 324 } 325 326 *v++ = x + w - r + cverts[0] * r; 327 *v++ = y + r + cverts[1] * r; 328 329 drawPolygon(verts.ptr, (n + 1) * 4, fth, col); 330 } 331 332 void drawLine(float x0, float y0, float x1, float y1, float r, float fth, uint col) 333 { 334 float dx = x1 - x0; 335 float dy = y1 - y0; 336 float d = sqrt(dx * dx + dy * dy); 337 338 if (d > 0.0001f) 339 { 340 d = 1.0f / d; 341 dx *= d; 342 dy *= d; 343 } 344 float nx = dy; 345 float ny = -dx; 346 float[4 * 2] verts; 347 r -= fth; 348 r *= 0.5f; 349 350 if (r < 0.01f) 351 r = 0.01f; 352 dx *= r; 353 dy *= r; 354 nx *= r; 355 ny *= r; 356 357 verts[0] = x0 - dx - nx; 358 verts[1] = y0 - dy - ny; 359 360 verts[2] = x0 - dx + nx; 361 verts[3] = y0 - dy + ny; 362 363 verts[4] = x1 + dx + nx; 364 verts[5] = y1 + dy + ny; 365 366 verts[6] = x1 + dx - nx; 367 verts[7] = y1 + dy - ny; 368 369 drawPolygon(verts.ptr, 4, fth, col); 370 } 371 372 bool imguiRenderGLInit(const(char)[] fontpath, const uint fontTextureSize) 373 { 374 for (int i = 0; i < CIRCLE_VERTS; ++i) 375 { 376 float a = cast(float)i / cast(float)CIRCLE_VERTS * PI * 2; 377 g_circleVerts[i * 2 + 0] = cos(a); 378 g_circleVerts[i * 2 + 1] = sin(a); 379 } 380 381 // Load font. 382 auto file = File(cast(string)fontpath, "rb"); 383 g_font_texture_size = fontTextureSize; 384 FILE* fp = file.getFP(); 385 386 if (!fp) 387 return false; 388 fseek(fp, 0, SEEK_END); 389 size_t size = cast(size_t)ftell(fp); 390 fseek(fp, 0, SEEK_SET); 391 392 ubyte* ttfBuffer = cast(ubyte*)malloc(size); 393 394 if (!ttfBuffer) 395 { 396 return false; 397 } 398 399 fread(ttfBuffer, 1, size, fp); 400 // fclose(fp); 401 fp = null; 402 403 ubyte* bmap = cast(ubyte*)malloc(g_font_texture_size * g_font_texture_size); 404 405 if (!bmap) 406 { 407 free(ttfBuffer); 408 return false; 409 } 410 411 const result = stbtt_BakeFontBitmap(ttfBuffer, 0, 15.0f, bmap, 412 g_font_texture_size, g_font_texture_size, 413 FIRST_CHARACTER, g_max_character_count, g_cdata.ptr); 414 // If result is negative, we baked less than max characters so update the max 415 // character count. 416 if(result < 0) 417 { 418 g_max_character_count = -result; 419 } 420 421 // can free ttf_buffer at this point 422 glGenTextures(1, &g_ftex); 423 glBindTexture(GL_TEXTURE_2D, g_ftex); 424 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 425 g_font_texture_size, g_font_texture_size, 426 0, GL_RED, GL_UNSIGNED_BYTE, bmap); 427 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 428 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 429 430 // can free ttf_buffer at this point 431 ubyte white_alpha = 255; 432 glGenTextures(1, &g_whitetex); 433 glBindTexture(GL_TEXTURE_2D, g_whitetex); 434 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &white_alpha); 435 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 436 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 437 438 glGenVertexArrays(1, &g_vao); 439 glGenBuffers(3, g_vbos.ptr); 440 441 glBindVertexArray(g_vao); 442 glEnableVertexAttribArray(0); 443 glEnableVertexAttribArray(1); 444 glEnableVertexAttribArray(2); 445 446 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]); 447 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, GL_FLOAT.sizeof * 2, null); 448 glBufferData(GL_ARRAY_BUFFER, 0, null, GL_STATIC_DRAW); 449 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]); 450 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, GL_FLOAT.sizeof * 2, null); 451 glBufferData(GL_ARRAY_BUFFER, 0, null, GL_STATIC_DRAW); 452 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]); 453 glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, GL_FLOAT.sizeof * 4, null); 454 glBufferData(GL_ARRAY_BUFFER, 0, null, GL_STATIC_DRAW); 455 g_program = glCreateProgram(); 456 457 string vs = 458 "#version 150\n" ~ 459 "uniform vec2 Viewport;\n" ~ 460 "in vec2 VertexPosition;\n" ~ 461 "in vec2 VertexTexCoord;\n" ~ 462 "in vec4 VertexColor;\n" ~ 463 "out vec2 texCoord;\n" ~ 464 "out vec4 vertexColor;\n" ~ 465 "void main(void)\n" ~ 466 "{\n" ~ 467 " vertexColor = VertexColor;\n" ~ 468 " texCoord = VertexTexCoord;\n" ~ 469 " gl_Position = vec4(VertexPosition * 2.0 / Viewport - 1.0, 0.f, 1.0);\n" ~ 470 "}\n"; 471 GLuint vso = glCreateShader(GL_VERTEX_SHADER); 472 auto vsPtr = vs.ptr; 473 glShaderSource(vso, 1, &vsPtr, null); 474 glCompileShader(vso); 475 glAttachShader(g_program, vso); 476 477 string fs = 478 "#version 150\n" ~ 479 "in vec2 texCoord;\n" ~ 480 "in vec4 vertexColor;\n" ~ 481 "uniform sampler2D Texture;\n" ~ 482 "out vec4 Color;\n" ~ 483 "void main(void)\n" ~ 484 "{\n" ~ 485 " float alpha = texture(Texture, texCoord).r;\n" ~ 486 " Color = vec4(vertexColor.rgb, vertexColor.a * alpha);\n" ~ 487 "}\n"; 488 GLuint fso = glCreateShader(GL_FRAGMENT_SHADER); 489 490 auto fsPtr = fs.ptr; 491 glShaderSource(fso, 1, &fsPtr, null); 492 glCompileShader(fso); 493 glAttachShader(g_program, fso); 494 495 glBindAttribLocation(g_program, 0, "VertexPosition"); 496 glBindAttribLocation(g_program, 1, "VertexTexCoord"); 497 glBindAttribLocation(g_program, 2, "VertexColor"); 498 glBindFragDataLocation(g_program, 0, "Color"); 499 glLinkProgram(g_program); 500 glDeleteShader(vso); 501 glDeleteShader(fso); 502 503 glUseProgram(g_program); 504 g_programViewportLocation = glGetUniformLocation(g_program, "Viewport"); 505 g_programTextureLocation = glGetUniformLocation(g_program, "Texture"); 506 507 glUseProgram(0); 508 509 free(ttfBuffer); 510 free(bmap); 511 512 return true; 513 } 514 515 void imguiRenderGLDestroy() 516 { 517 if (g_ftex) 518 { 519 glDeleteTextures(1, &g_ftex); 520 g_ftex = 0; 521 } 522 523 if (g_vao) 524 { 525 glDeleteVertexArrays(1, &g_vao); 526 glDeleteBuffers(3, g_vbos.ptr); 527 g_vao = 0; 528 } 529 530 if (g_program) 531 { 532 glDeleteProgram(g_program); 533 g_program = 0; 534 } 535 } 536 537 void getBakedQuad(stbtt_bakedchar* chardata, int pw, int ph, int char_index, 538 float* xpos, float* ypos, stbtt_aligned_quad* q) 539 { 540 stbtt_bakedchar* b = chardata + char_index; 541 int round_x = STBTT_ifloor(*xpos + b.xoff); 542 int round_y = STBTT_ifloor(*ypos - b.yoff); 543 544 q.x0 = cast(float)round_x; 545 q.y0 = cast(float)round_y; 546 q.x1 = cast(float)round_x + b.x1 - b.x0; 547 q.y1 = cast(float)round_y - b.y1 + b.y0; 548 549 q.s0 = b.x0 / cast(float)pw; 550 q.t0 = b.y0 / cast(float)pw; 551 q.s1 = b.x1 / cast(float)ph; 552 q.t1 = b.y1 / cast(float)ph; 553 554 *xpos += b.xadvance; 555 } 556 557 float getTextLength(stbtt_bakedchar* chardata, const(char)[] text) 558 { 559 float xpos = 0; 560 float len = 0; 561 562 // The cast(string) is only there for UTF-8 decoding. 563 foreach (dchar c; cast(string)text) 564 { 565 if (c == '\t') 566 { 567 for (int i = 0; i < 4; ++i) 568 { 569 if (xpos < g_tabStops[i]) 570 { 571 xpos = g_tabStops[i]; 572 break; 573 } 574 } 575 } 576 else if (cast(int)c >= FIRST_CHARACTER && cast(int)c < FIRST_CHARACTER + g_max_character_count) 577 { 578 stbtt_bakedchar* b = chardata + c - FIRST_CHARACTER; 579 int round_x = STBTT_ifloor((xpos + b.xoff) + 0.5); 580 len = round_x + b.x1 - b.x0 + 0.5f; 581 xpos += b.xadvance; 582 } 583 } 584 585 return len; 586 } 587 588 float getTextLength(const(char)[] text) 589 { 590 return getTextLength(g_cdata.ptr, text); 591 } 592 593 void drawText(float x, float y, const(char)[] text, int align_, uint col) 594 { 595 if (!g_ftex) 596 return; 597 598 if (!text) 599 return; 600 601 if (align_ == TextAlign.center) 602 x -= getTextLength(g_cdata.ptr, text) / 2; 603 else if (align_ == TextAlign.right) 604 x -= getTextLength(g_cdata.ptr, text); 605 606 float r = cast(float)(col & 0xff) / 255.0; 607 float g = cast(float)((col >> 8) & 0xff) / 255.0; 608 float b = cast(float)((col >> 16) & 0xff) / 255.0; 609 float a = cast(float)((col >> 24) & 0xff) / 255.0; 610 611 // assume orthographic projection with units = screen pixels, origin at top left 612 glBindTexture(GL_TEXTURE_2D, g_ftex); 613 614 const float ox = x; 615 616 // The cast(string) is only there for UTF-8 decoding. 617 foreach (ubyte c; cast(ubyte[])text) 618 { 619 if (c == '\t') 620 { 621 for (int i = 0; i < 4; ++i) 622 { 623 if (x < g_tabStops[i] + ox) 624 { 625 x = g_tabStops[i] + ox; 626 break; 627 } 628 } 629 } 630 else if (c >= FIRST_CHARACTER && c < FIRST_CHARACTER + g_max_character_count) 631 { 632 stbtt_aligned_quad q; 633 getBakedQuad(g_cdata.ptr, g_font_texture_size, g_font_texture_size, 634 c - FIRST_CHARACTER, &x, &y, &q); 635 636 float[12] v = [ 637 q.x0, q.y0, 638 q.x1, q.y1, 639 q.x1, q.y0, 640 q.x0, q.y0, 641 q.x0, q.y1, 642 q.x1, q.y1, 643 ]; 644 float[12] uv = [ 645 q.s0, q.t0, 646 q.s1, q.t1, 647 q.s1, q.t0, 648 q.s0, q.t0, 649 q.s0, q.t1, 650 q.s1, q.t1, 651 ]; 652 float[24] cArr = [ 653 r, g, b, a, 654 r, g, b, a, 655 r, g, b, a, 656 r, g, b, a, 657 r, g, b, a, 658 r, g, b, a, 659 ]; 660 glBindVertexArray(g_vao); 661 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]); 662 glBufferData(GL_ARRAY_BUFFER, 12 * float.sizeof, v.ptr, GL_STATIC_DRAW); 663 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]); 664 glBufferData(GL_ARRAY_BUFFER, 12 * float.sizeof, uv.ptr, GL_STATIC_DRAW); 665 glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]); 666 glBufferData(GL_ARRAY_BUFFER, 24 * float.sizeof, cArr.ptr, GL_STATIC_DRAW); 667 glDrawArrays(GL_TRIANGLES, 0, 6); 668 } 669 } 670 671 // glEnd(); 672 // glDisable(GL_TEXTURE_2D); 673 } 674 675 void imguiRenderGLDraw(int width, int height) 676 { 677 const imguiGfxCmd* q = imguiGetRenderQueue(); 678 int nq = imguiGetRenderQueueSize(); 679 680 const float s = 1.0f / 8.0f; 681 682 glViewport(0, 0, width, height); 683 glUseProgram(g_program); 684 glActiveTexture(GL_TEXTURE0); 685 glUniform2f(g_programViewportLocation, cast(float)width, cast(float)height); 686 glUniform1i(g_programTextureLocation, 0); 687 688 glDisable(GL_SCISSOR_TEST); 689 690 for (int i = 0; i < nq; ++i) 691 { 692 auto cmd = &q[i]; 693 694 if (cmd.type == IMGUI_GFXCMD_RECT) 695 { 696 if (cmd.rect.r == 0) 697 { 698 drawRect(cast(float)cmd.rect.x * s + 0.5f, cast(float)cmd.rect.y * s + 0.5f, 699 cast(float)cmd.rect.w * s - 1, cast(float)cmd.rect.h * s - 1, 700 1.0f, cmd.col); 701 } 702 else 703 { 704 drawRoundedRect(cast(float)cmd.rect.x * s + 0.5f, cast(float)cmd.rect.y * s + 0.5f, 705 cast(float)cmd.rect.w * s - 1, cast(float)cmd.rect.h * s - 1, 706 cast(float)cmd.rect.r * s, 1.0f, cmd.col); 707 } 708 } 709 else if (cmd.type == IMGUI_GFXCMD_LINE) 710 { 711 drawLine(cmd.line.x0 * s, cmd.line.y0 * s, cmd.line.x1 * s, cmd.line.y1 * s, cmd.line.r * s, 1.0f, cmd.col); 712 } 713 else if (cmd.type == IMGUI_GFXCMD_TRIANGLE) 714 { 715 if (cmd.flags == 1) 716 { 717 const float[3 * 2] verts = 718 [ 719 cast(float)cmd.rect.x * s + 0.5f, cast(float)cmd.rect.y * s + 0.5f, 720 cast(float)cmd.rect.x * s + 0.5f + cast(float)cmd.rect.w * s - 1, cast(float)cmd.rect.y * s + 0.5f + cast(float)cmd.rect.h * s / 2 - 0.5f, 721 cast(float)cmd.rect.x * s + 0.5f, cast(float)cmd.rect.y * s + 0.5f + cast(float)cmd.rect.h * s - 1, 722 ]; 723 drawPolygon(verts.ptr, 3, 1.0f, cmd.col); 724 } 725 726 if (cmd.flags == 2) 727 { 728 const float[3 * 2] verts = 729 [ 730 cast(float)cmd.rect.x * s + 0.5f, cast(float)cmd.rect.y * s + 0.5f + cast(float)cmd.rect.h * s - 1, 731 cast(float)cmd.rect.x * s + 0.5f + cast(float)cmd.rect.w * s / 2 - 0.5f, cast(float)cmd.rect.y * s + 0.5f, 732 cast(float)cmd.rect.x * s + 0.5f + cast(float)cmd.rect.w * s - 1, cast(float)cmd.rect.y * s + 0.5f + cast(float)cmd.rect.h * s - 1, 733 ]; 734 drawPolygon(verts.ptr, 3, 1.0f, cmd.col); 735 } 736 } 737 else if (cmd.type == IMGUI_GFXCMD_TEXT) 738 { 739 drawText(cmd.text.x, cmd.text.y, cmd.text.text, cmd.text.align_, cmd.col); 740 } 741 else if (cmd.type == IMGUI_GFXCMD_SCISSOR) 742 { 743 if (cmd.flags) 744 { 745 glEnable(GL_SCISSOR_TEST); 746 glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h); 747 } 748 else 749 { 750 glDisable(GL_SCISSOR_TEST); 751 } 752 } 753 } 754 755 glDisable(GL_SCISSOR_TEST); 756 }