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