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