imguiRenderGL.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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. #define _USE_MATH_DEFINES
  19. #include <cmath>
  20. #include <cstdio>
  21. #include "imgui.h"
  22. #include "SDL.h"
  23. #include "SDL_opengl.h"
  24. // Some math headers don't have PI defined.
  25. static const float PI = 3.14159265f;
  26. void imguifree(void* ptr, void* userptr);
  27. void* imguimalloc(size_t size, void* userptr);
  28. #define STBTT_malloc(x,y) imguimalloc(x,y)
  29. #define STBTT_free(x,y) imguifree(x,y)
  30. #define STB_TRUETYPE_IMPLEMENTATION
  31. #include "stb_truetype.h"
  32. void imguifree(void* ptr, void* /*userptr*/)
  33. {
  34. free(ptr);
  35. }
  36. void* imguimalloc(size_t size, void* /*userptr*/)
  37. {
  38. return malloc(size);
  39. }
  40. static const unsigned TEMP_COORD_COUNT = 100;
  41. static float g_tempCoords[TEMP_COORD_COUNT*2];
  42. static float g_tempNormals[TEMP_COORD_COUNT*2];
  43. static const int CIRCLE_VERTS = 8*4;
  44. static float g_circleVerts[CIRCLE_VERTS*2];
  45. static stbtt_bakedchar g_cdata[96]; // ASCII 32..126 is 95 glyphs
  46. static GLuint g_ftex = 0;
  47. inline unsigned int RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
  48. {
  49. return (r) | (g << 8) | (b << 16) | (a << 24);
  50. }
  51. static void drawPolygon(const float* coords, unsigned numCoords, float r, unsigned int col)
  52. {
  53. if (numCoords > TEMP_COORD_COUNT) numCoords = TEMP_COORD_COUNT;
  54. for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
  55. {
  56. const float* v0 = &coords[j*2];
  57. const float* v1 = &coords[i*2];
  58. float dx = v1[0] - v0[0];
  59. float dy = v1[1] - v0[1];
  60. float d = sqrtf(dx*dx+dy*dy);
  61. if (d > 0)
  62. {
  63. d = 1.0f/d;
  64. dx *= d;
  65. dy *= d;
  66. }
  67. g_tempNormals[j*2+0] = dy;
  68. g_tempNormals[j*2+1] = -dx;
  69. }
  70. for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
  71. {
  72. float dlx0 = g_tempNormals[j*2+0];
  73. float dly0 = g_tempNormals[j*2+1];
  74. float dlx1 = g_tempNormals[i*2+0];
  75. float dly1 = g_tempNormals[i*2+1];
  76. float dmx = (dlx0 + dlx1) * 0.5f;
  77. float dmy = (dly0 + dly1) * 0.5f;
  78. float dmr2 = dmx*dmx + dmy*dmy;
  79. if (dmr2 > 0.000001f)
  80. {
  81. float scale = 1.0f / dmr2;
  82. if (scale > 10.0f) scale = 10.0f;
  83. dmx *= scale;
  84. dmy *= scale;
  85. }
  86. g_tempCoords[i*2+0] = coords[i*2+0]+dmx*r;
  87. g_tempCoords[i*2+1] = coords[i*2+1]+dmy*r;
  88. }
  89. unsigned int colTrans = RGBA(col&0xff, (col>>8)&0xff, (col>>16)&0xff, 0);
  90. glBegin(GL_TRIANGLES);
  91. glColor4ubv((GLubyte*)&col);
  92. for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
  93. {
  94. glVertex2fv(&coords[i*2]);
  95. glVertex2fv(&coords[j*2]);
  96. glColor4ubv((GLubyte*)&colTrans);
  97. glVertex2fv(&g_tempCoords[j*2]);
  98. glVertex2fv(&g_tempCoords[j*2]);
  99. glVertex2fv(&g_tempCoords[i*2]);
  100. glColor4ubv((GLubyte*)&col);
  101. glVertex2fv(&coords[i*2]);
  102. }
  103. glColor4ubv((GLubyte*)&col);
  104. for (unsigned i = 2; i < numCoords; ++i)
  105. {
  106. glVertex2fv(&coords[0]);
  107. glVertex2fv(&coords[(i-1)*2]);
  108. glVertex2fv(&coords[i*2]);
  109. }
  110. glEnd();
  111. }
  112. static void drawRect(float x, float y, float w, float h, float fth, unsigned int col)
  113. {
  114. float verts[4*2] =
  115. {
  116. x+0.5f, y+0.5f,
  117. x+w-0.5f, y+0.5f,
  118. x+w-0.5f, y+h-0.5f,
  119. x+0.5f, y+h-0.5f,
  120. };
  121. drawPolygon(verts, 4, fth, col);
  122. }
  123. /*
  124. static void drawEllipse(float x, float y, float w, float h, float fth, unsigned int col)
  125. {
  126. float verts[CIRCLE_VERTS*2];
  127. const float* cverts = g_circleVerts;
  128. float* v = verts;
  129. for (int i = 0; i < CIRCLE_VERTS; ++i)
  130. {
  131. *v++ = x + cverts[i*2]*w;
  132. *v++ = y + cverts[i*2+1]*h;
  133. }
  134. drawPolygon(verts, CIRCLE_VERTS, fth, col);
  135. }
  136. */
  137. static void drawRoundedRect(float x, float y, float w, float h, float r, float fth, unsigned int col)
  138. {
  139. const unsigned n = CIRCLE_VERTS/4;
  140. float verts[(n+1)*4*2];
  141. const float* cverts = g_circleVerts;
  142. float* v = verts;
  143. for (unsigned i = 0; i <= n; ++i)
  144. {
  145. *v++ = x+w-r + cverts[i*2]*r;
  146. *v++ = y+h-r + cverts[i*2+1]*r;
  147. }
  148. for (unsigned i = n; i <= n*2; ++i)
  149. {
  150. *v++ = x+r + cverts[i*2]*r;
  151. *v++ = y+h-r + cverts[i*2+1]*r;
  152. }
  153. for (unsigned i = n*2; i <= n*3; ++i)
  154. {
  155. *v++ = x+r + cverts[i*2]*r;
  156. *v++ = y+r + cverts[i*2+1]*r;
  157. }
  158. for (unsigned i = n*3; i < n*4; ++i)
  159. {
  160. *v++ = x+w-r + cverts[i*2]*r;
  161. *v++ = y+r + cverts[i*2+1]*r;
  162. }
  163. *v++ = x+w-r + cverts[0]*r;
  164. *v++ = y+r + cverts[1]*r;
  165. drawPolygon(verts, (n+1)*4, fth, col);
  166. }
  167. static void drawLine(float x0, float y0, float x1, float y1, float r, float fth, unsigned int col)
  168. {
  169. float dx = x1-x0;
  170. float dy = y1-y0;
  171. float d = sqrtf(dx*dx+dy*dy);
  172. if (d > 0.0001f)
  173. {
  174. d = 1.0f/d;
  175. dx *= d;
  176. dy *= d;
  177. }
  178. float nx = dy;
  179. float ny = -dx;
  180. float verts[4*2];
  181. r -= fth;
  182. r *= 0.5f;
  183. if (r < 0.01f) r = 0.01f;
  184. dx *= r;
  185. dy *= r;
  186. nx *= r;
  187. ny *= r;
  188. verts[0] = x0-dx-nx;
  189. verts[1] = y0-dy-ny;
  190. verts[2] = x0-dx+nx;
  191. verts[3] = y0-dy+ny;
  192. verts[4] = x1+dx+nx;
  193. verts[5] = y1+dy+ny;
  194. verts[6] = x1+dx-nx;
  195. verts[7] = y1+dy-ny;
  196. drawPolygon(verts, 4, fth, col);
  197. }
  198. bool imguiRenderGLInit(const char* fontpath)
  199. {
  200. for (int i = 0; i < CIRCLE_VERTS; ++i)
  201. {
  202. float a = (float)i/(float)CIRCLE_VERTS * PI*2;
  203. g_circleVerts[i*2+0] = cosf(a);
  204. g_circleVerts[i*2+1] = sinf(a);
  205. }
  206. // Load font.
  207. FILE* fp = fopen(fontpath, "rb");
  208. if (!fp) return false;
  209. if (fseek(fp, 0, SEEK_END) != 0)
  210. {
  211. fclose(fp);
  212. return false;
  213. }
  214. long size = ftell(fp);
  215. if (size < 0)
  216. {
  217. fclose(fp);
  218. return false;
  219. }
  220. if (fseek(fp, 0, SEEK_SET) != 0)
  221. {
  222. fclose(fp);
  223. return false;
  224. }
  225. unsigned char* ttfBuffer = (unsigned char*)malloc(size);
  226. if (!ttfBuffer)
  227. {
  228. fclose(fp);
  229. return false;
  230. }
  231. size_t readLen = fread(ttfBuffer, 1, size, fp);
  232. fclose(fp);
  233. if (readLen != static_cast<size_t>(size))
  234. {
  235. free(ttfBuffer);
  236. return false;
  237. }
  238. fp = 0;
  239. unsigned char* bmap = (unsigned char*)malloc(512*512);
  240. if (!bmap)
  241. {
  242. free(ttfBuffer);
  243. return false;
  244. }
  245. stbtt_BakeFontBitmap(ttfBuffer,0, 15.0f, bmap,512,512, 32,96, g_cdata);
  246. // can free ttf_buffer at this point
  247. glGenTextures(1, &g_ftex);
  248. glBindTexture(GL_TEXTURE_2D, g_ftex);
  249. glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bmap);
  250. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  251. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  252. free(ttfBuffer);
  253. free(bmap);
  254. return true;
  255. }
  256. void imguiRenderGLDestroy()
  257. {
  258. if (g_ftex)
  259. {
  260. glDeleteTextures(1, &g_ftex);
  261. g_ftex = 0;
  262. }
  263. }
  264. static void getBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index,
  265. float *xpos, float *ypos, stbtt_aligned_quad *q)
  266. {
  267. stbtt_bakedchar *b = chardata + char_index;
  268. int round_x = STBTT_ifloor(*xpos + b->xoff);
  269. int round_y = STBTT_ifloor(*ypos - b->yoff);
  270. q->x0 = (float)round_x;
  271. q->y0 = (float)round_y;
  272. q->x1 = (float)round_x + b->x1 - b->x0;
  273. q->y1 = (float)round_y - b->y1 + b->y0;
  274. q->s0 = b->x0 / (float)pw;
  275. q->t0 = b->y0 / (float)pw;
  276. q->s1 = b->x1 / (float)ph;
  277. q->t1 = b->y1 / (float)ph;
  278. *xpos += b->xadvance;
  279. }
  280. static const float g_tabStops[4] = {150, 210, 270, 330};
  281. static float getTextLength(stbtt_bakedchar *chardata, const char* text)
  282. {
  283. float xpos = 0;
  284. float len = 0;
  285. while (*text)
  286. {
  287. int c = (unsigned char)*text;
  288. if (c == '\t')
  289. {
  290. for (int i = 0; i < 4; ++i)
  291. {
  292. if (xpos < g_tabStops[i])
  293. {
  294. xpos = g_tabStops[i];
  295. break;
  296. }
  297. }
  298. }
  299. else if (c >= 32 && c < 128)
  300. {
  301. stbtt_bakedchar *b = chardata + c-32;
  302. int round_x = STBTT_ifloor((xpos + b->xoff) + 0.5);
  303. len = round_x + b->x1 - b->x0 + 0.5f;
  304. xpos += b->xadvance;
  305. }
  306. ++text;
  307. }
  308. return len;
  309. }
  310. static void drawText(float x, float y, const char *text, int align, unsigned int col)
  311. {
  312. if (!g_ftex) return;
  313. if (!text) return;
  314. if (align == IMGUI_ALIGN_CENTER)
  315. x -= getTextLength(g_cdata, text)/2;
  316. else if (align == IMGUI_ALIGN_RIGHT)
  317. x -= getTextLength(g_cdata, text);
  318. glColor4ub(col&0xff, (col>>8)&0xff, (col>>16)&0xff, (col>>24)&0xff);
  319. glEnable(GL_TEXTURE_2D);
  320. // assume orthographic projection with units = screen pixels, origin at top left
  321. glBindTexture(GL_TEXTURE_2D, g_ftex);
  322. glBegin(GL_TRIANGLES);
  323. const float ox = x;
  324. while (*text)
  325. {
  326. int c = (unsigned char)*text;
  327. if (c == '\t')
  328. {
  329. for (int i = 0; i < 4; ++i)
  330. {
  331. if (x < g_tabStops[i]+ox)
  332. {
  333. x = g_tabStops[i]+ox;
  334. break;
  335. }
  336. }
  337. }
  338. else if (c >= 32 && c < 128)
  339. {
  340. stbtt_aligned_quad q;
  341. getBakedQuad(g_cdata, 512,512, c-32, &x,&y,&q);
  342. glTexCoord2f(q.s0, q.t0);
  343. glVertex2f(q.x0, q.y0);
  344. glTexCoord2f(q.s1, q.t1);
  345. glVertex2f(q.x1, q.y1);
  346. glTexCoord2f(q.s1, q.t0);
  347. glVertex2f(q.x1, q.y0);
  348. glTexCoord2f(q.s0, q.t0);
  349. glVertex2f(q.x0, q.y0);
  350. glTexCoord2f(q.s0, q.t1);
  351. glVertex2f(q.x0, q.y1);
  352. glTexCoord2f(q.s1, q.t1);
  353. glVertex2f(q.x1, q.y1);
  354. }
  355. ++text;
  356. }
  357. glEnd();
  358. glDisable(GL_TEXTURE_2D);
  359. }
  360. void imguiRenderGLDraw()
  361. {
  362. const imguiGfxCmd* q = imguiGetRenderQueue();
  363. int nq = imguiGetRenderQueueSize();
  364. const float s = 1.0f/8.0f;
  365. glDisable(GL_SCISSOR_TEST);
  366. for (int i = 0; i < nq; ++i)
  367. {
  368. const imguiGfxCmd& cmd = q[i];
  369. if (cmd.type == IMGUI_GFXCMD_RECT)
  370. {
  371. if (cmd.rect.r == 0)
  372. {
  373. drawRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
  374. (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1,
  375. 1.0f, cmd.col);
  376. }
  377. else
  378. {
  379. drawRoundedRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
  380. (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1,
  381. (float)cmd.rect.r*s, 1.0f, cmd.col);
  382. }
  383. }
  384. else if (cmd.type == IMGUI_GFXCMD_LINE)
  385. {
  386. 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);
  387. }
  388. else if (cmd.type == IMGUI_GFXCMD_TRIANGLE)
  389. {
  390. if (cmd.flags == 1)
  391. {
  392. const float verts[3*2] =
  393. {
  394. (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
  395. (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s/2-0.5f,
  396. (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
  397. };
  398. drawPolygon(verts, 3, 1.0f, cmd.col);
  399. }
  400. if (cmd.flags == 2)
  401. {
  402. const float verts[3*2] =
  403. {
  404. (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
  405. (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s/2-0.5f, (float)cmd.rect.y*s+0.5f,
  406. (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
  407. };
  408. drawPolygon(verts, 3, 1.0f, cmd.col);
  409. }
  410. }
  411. else if (cmd.type == IMGUI_GFXCMD_TEXT)
  412. {
  413. drawText(cmd.text.x, cmd.text.y, cmd.text.text, cmd.text.align, cmd.col);
  414. }
  415. else if (cmd.type == IMGUI_GFXCMD_SCISSOR)
  416. {
  417. if (cmd.flags)
  418. {
  419. glEnable(GL_SCISSOR_TEST);
  420. glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h);
  421. }
  422. else
  423. {
  424. glDisable(GL_SCISSOR_TEST);
  425. }
  426. }
  427. }
  428. glDisable(GL_SCISSOR_TEST);
  429. }