added scr_lvled, a bunch of libraries, and improved framework code
[raydungeon] / libs / drawtext / drawgl.c
1 /*
2 libdrawtext - a simple library for fast text rendering in OpenGL
3 Copyright (C) 2011-2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "drawtext.h"
19 #include "drawtext_impl.h"
20
21 struct quad {
22         struct dtx_vertex v[6];
23 };
24
25 struct dtx_glyphmap *cur_gmap;
26
27 #define QBUF_SZ         512
28 static struct quad *qbuf;
29 static int num_quads;
30
31 static dtx_user_draw_func user_draw_func;
32 static void *user_cls;
33
34 static int dtx_draw_init(void);
35 static void cleanup(void);
36
37 static void set_glyphmap_texture(struct dtx_glyphmap *gmap);
38 static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush);
39 static void flush_user(void);
40 static void add_glyph(struct glyph *g, float x, float y);
41
42
43 #ifndef NO_OPENGL
44 #include <stdarg.h>
45 #include <math.h>
46 #include <ctype.h>
47 #include <stdlib.h>
48
49 #ifdef TARGET_IPHONE
50 #include <OpenGLES/ES2/gl.h>
51 #ifndef GL_ES
52 #define GL_ES
53 #endif
54
55 #elif defined(ANDROID)
56 #include <GLES2/gl2.h>
57 #ifndef GL_ES
58 #define GL_ES
59 #endif
60
61 #else   /* regular OpenGL */
62
63 #ifdef WIN32
64 #include <windows.h>
65 #endif
66
67 #ifdef __APPLE__
68 #include <OpenGL/gl.h>
69
70 #else
71
72 #define GL_GLEXT_LEGACY         /* don't include glext.h internally in gl.h */
73 #include <GL/gl.h>
74 #ifndef NO_GLU
75 #include <GL/glu.h>
76 #endif
77
78 #if defined(__unix__) || defined(unix)
79 #define GLX_GLXEXT_LEGACY       /* don't include glxext.h internally in glx.h */
80 #include <GL/glx.h>
81 #endif
82
83 #endif  /* !__APPLE__ */
84 #endif  /* !TARGET_IPHONE */
85
86 #ifdef GL_ES
87 #define GL_CLAMP GL_CLAMP_TO_EDGE
88 #endif
89
90 static void dtx_gl_init(void);
91 static void cleanup(void);
92 static void flush(void);
93
94 static int vattr = -1;
95 static int tattr = -1;
96 static int cattr = -1;
97 static unsigned int font_tex;
98
99 static int use_blend = 1;
100 static int use_atest;
101 static float atest_thres;
102
103 #ifndef APIENTRY
104 #define APIENTRY
105 #endif
106
107 #ifndef GL_ES
108 #ifndef GL_VERSION_1_5
109 #define GL_ARRAY_BUFFER 0x8892
110 #define GL_ARRAY_BUFFER_BINDING 0x8894
111 typedef void (APIENTRY *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
112 static PFNGLBINDBUFFERPROC glBindBuffer;
113 #endif
114
115 #ifndef GL_VERSION_2_0
116 typedef void (APIENTRY *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);
117 typedef void (APIENTRY *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);
118 typedef void (APIENTRY *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
119 static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
120 static PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
121 static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
122 #endif
123
124 #ifdef WIN32
125 #define load_glfunc(s)  wglGetProcAddress(s)
126 #elif defined(__unix__) || defined(unix)
127 #define load_glfunc(s)  glXGetProcAddress((unsigned char*)s)
128 #endif
129
130 #endif  /* !GL_ES */
131
132 void dtx_target_opengl(void)
133 {
134         dtx_draw_init();
135         dtx_drawchar = drawchar;
136         dtx_drawflush = flush;
137
138         user_draw_func = 0;
139 }
140
141 int dtx_gl_setopt(enum dtx_option opt, int val)
142 {
143         switch(opt) {
144         case DTX_GL_ATTR_VERTEX:
145                 vattr = val;
146                 break;
147         case DTX_GL_ATTR_TEXCOORD:
148                 tattr = val;
149                 break;
150         case DTX_GL_ATTR_COLOR:
151                 cattr = val;
152                 break;
153         case DTX_GL_BLEND:
154                 use_blend = val;
155                 break;
156         case DTX_GL_ALPHATEST:
157                 if(val) {
158                         use_atest = 1;
159                         atest_thres = val / 255.0f;
160                 } else {
161                         use_atest = 0;
162                         atest_thres = 0;
163                 }
164                 break;
165         default:
166                 return -1;
167         }
168         return 0;
169 }
170
171 int dtx_gl_getopt(enum dtx_option opt, int *res)
172 {
173         switch(opt) {
174         case DTX_GL_ATTR_VERTEX:
175                 *res = vattr;
176                 break;
177         case DTX_GL_ATTR_TEXCOORD:
178                 *res = tattr;
179                 break;
180         case DTX_GL_ATTR_COLOR:
181                 *res = cattr;
182                 break;
183         case DTX_GL_BLEND:
184                 *res = use_blend;
185                 break;
186         case DTX_GL_ALPHATEST:
187                 *res = use_atest ? (int)(atest_thres * 255.0f) : 0;
188                 break;
189         default:
190                 return -1;
191         }
192         return 0;
193 }
194
195 static void dtx_gl_init(void)
196 {
197 #ifndef GL_ES
198 #ifndef GL_VERSION_1_5
199         glBindBuffer = (PFNGLBINDBUFFERPROC)load_glfunc("glBindBuffer");
200 #endif
201 #ifndef GL_VERSION_2_0
202         glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load_glfunc("glEnableVertexAttribArray");
203         glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load_glfunc("glDisableVertexAttribArray");
204         glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load_glfunc("glVertexAttribPointer");
205 #endif
206 #endif  /* !GL_ES */
207 }
208
209
210 void dtx_vertex_attribs(int vert_attr, int tex_attr)
211 {
212         vattr = vert_attr;
213         tattr = tex_attr;
214 }
215
216 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap)
217 {
218         if(!gmap->tex) {
219                 glGenTextures(1, &gmap->tex);
220                 glBindTexture(GL_TEXTURE_2D, gmap->tex);
221 #if !defined(GL_ES) && defined(NO_GLU)
222                 /* TODO: ideally we want to have mipmaps even without GLU, and we should
223                  * just use SGIS_generate_mipmaps if available
224                  */
225                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
226 #else
227                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
228 #endif
229                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
230                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
231                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
232                 gmap->tex_valid = 0;
233         }
234
235         if(!gmap->tex_valid) {
236                 glBindTexture(GL_TEXTURE_2D, gmap->tex);
237 #ifdef GL_ES
238                 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
239                 glGenerateMipmap(GL_TEXTURE_2D);
240 #elif !defined(NO_GLU)
241                 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, gmap->xsz, gmap->ysz, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
242 #else
243                 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gmap->xsz, gmap->ysz, 0, GL_ALPHA, GL_UNSIGNED_BYTE, gmap->pixels);
244 #endif
245                 gmap->tex_valid = 1;
246         }
247
248         if(font_tex != gmap->tex) {
249                 dtx_flush();
250         }
251         font_tex = gmap->tex;
252 }
253
254 void dtxhack_replace_texture(struct dtx_glyphmap *gmap, unsigned int tex)
255 {
256         gmap->tex = tex;
257         gmap->tex_valid = 1;
258 }
259
260
261 static void flush(void)
262 {
263         int vbo;
264
265         if(!num_quads) {
266                 return;
267         }
268
269         if(glBindBuffer) {
270                 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
271                 glBindBuffer(GL_ARRAY_BUFFER, 0);
272         }
273
274 #ifndef GL_ES
275         glPushAttrib(GL_ENABLE_BIT);
276         glEnable(GL_TEXTURE_2D);
277 #endif
278         glBindTexture(GL_TEXTURE_2D, font_tex);
279
280         if(vattr != -1 && glEnableVertexAttribArray) {
281                 glEnableVertexAttribArray(vattr);
282                 glVertexAttribPointer(vattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), qbuf);
283 #ifndef GL_ES
284         } else {
285                 glEnableClientState(GL_VERTEX_ARRAY);
286                 glVertexPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), qbuf);
287 #endif
288         }
289         if(tattr != -1 && glEnableVertexAttribArray) {
290                 glEnableVertexAttribArray(tattr);
291                 glVertexAttribPointer(tattr, 2, GL_FLOAT, 0, sizeof(struct dtx_vertex), &qbuf->v[0].s);
292 #ifndef GL_ES
293         } else {
294                 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
295                 glTexCoordPointer(2, GL_FLOAT, sizeof(struct dtx_vertex), &qbuf->v[0].s);
296 #endif
297         }
298
299         if(use_blend) {
300                 glEnable(GL_BLEND);
301                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
302         } else {
303                 glDisable(GL_BLEND);
304         }
305         if(use_atest) {
306                 glEnable(GL_ALPHA_TEST);
307                 glAlphaFunc(GL_GREATER, atest_thres);
308         } else {
309                 glDisable(GL_ALPHA_TEST);
310         }
311
312         glDepthMask(0);
313
314         glDrawArrays(GL_TRIANGLES, 0, num_quads * 6);
315
316         glDepthMask(1);
317
318         if(vattr != -1 && glDisableVertexAttribArray) {
319                 glDisableVertexAttribArray(vattr);
320 #ifndef GL_ES
321         } else {
322                 glDisableClientState(GL_VERTEX_ARRAY);
323 #endif
324         }
325         if(tattr != -1 && glDisableVertexAttribArray) {
326                 glDisableVertexAttribArray(tattr);
327 #ifndef GL_ES
328         } else {
329                 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
330 #endif
331         }
332
333 #ifndef GL_ES
334         glPopAttrib();
335 #else
336         if(use_blend) {
337                 glDisable(GL_BLEND);
338         }
339         if(use_atest) {
340                 glDisable(GL_ALPHA_TEST);
341         }
342 #endif
343
344         if(glBindBuffer && vbo) {
345                 glBindBuffer(GL_ARRAY_BUFFER, vbo);
346         }
347
348         num_quads = 0;
349 }
350 #else
351
352 /* no-opengl build, define all public gl functions as stubs */
353 void dtx_target_opengl(void) {}
354 int dtx_gl_setopt(enum dtx_option opt, int val) { return -1; }
355 int dtx_gl_getopt(enum dtx_option opt, int *val) { return -1; }
356
357 static void set_glyphmap_texture_gl(struct dtx_glyphmap *gmap) {}
358
359 #endif  /* !def NO_OPENGL */
360
361 static int dtx_draw_init(void)
362 {
363         if(qbuf) {
364                 return 0;       /* already initialized */
365         }
366
367 #ifndef NO_OPENGL
368         dtx_gl_init();
369 #endif
370
371         if(!(qbuf = malloc(QBUF_SZ * sizeof *qbuf))) {
372                 return -1;
373         }
374         num_quads = 0;
375
376         atexit(cleanup);
377         return 0;
378 }
379
380 static void cleanup(void)
381 {
382         free(qbuf);
383 }
384
385
386 void dtx_target_user(dtx_user_draw_func func, void *cls)
387 {
388         dtx_draw_init();
389
390         user_draw_func = func;
391         user_cls = cls;
392
393         dtx_drawchar = drawchar;
394         dtx_drawflush = flush_user;
395 }
396
397 void dtx_glyph(int code)
398 {
399         struct dtx_glyphmap *gmap;
400
401         if(!dtx_font || !(gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
402                 return;
403         }
404         set_glyphmap_texture(gmap);
405
406         add_glyph(gmap->glyphs + code - gmap->cstart, 0, 0);
407         dtx_flush();
408 }
409
410 static void set_glyphmap_texture(struct dtx_glyphmap *gmap)
411 {
412         if(!user_draw_func) {
413                 set_glyphmap_texture_gl(gmap);
414         }
415
416         if(cur_gmap && gmap != cur_gmap) {
417                 dtx_flush();
418         }
419         cur_gmap = gmap;
420 }
421
422 static const char *drawchar(const char *str, float *pos_x, float *pos_y, int *should_flush)
423 {
424         struct dtx_glyphmap *gmap;
425         float px, py;
426         int code = dtx_utf8_char_code(str);
427         str = dtx_utf8_next_char((char*)str);
428
429         if(dtx_buf_mode == DTX_LBF && code == '\n') {
430                 *should_flush = 1;
431         }
432
433         px = *pos_x;
434         py = *pos_y;
435
436         if((gmap = dtx_proc_char(code, pos_x, pos_y))) {
437                 int idx = code - gmap->cstart;
438
439                 set_glyphmap_texture(gmap);
440                 add_glyph(gmap->glyphs + idx, px, py);
441         }
442         return str;
443 }
444
445 static void qvertex(struct dtx_vertex *v, float x, float y, float s, float t)
446 {
447         v->x = x;
448         v->y = y;
449         v->s = s;
450         v->t = t;
451 }
452
453 static void add_glyph(struct glyph *g, float x, float y)
454 {
455         struct quad *qptr = qbuf + num_quads;
456
457         x -= g->orig_x;
458         y -= g->orig_y;
459
460         qvertex(qptr->v + 0, x, y, g->nx, g->ny + g->nheight);
461         qvertex(qptr->v + 1, x + g->width, y, g->nx + g->nwidth, g->ny + g->nheight);
462         qvertex(qptr->v + 2, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
463
464         qvertex(qptr->v + 3, x, y, g->nx, g->ny + g->nheight);
465         qvertex(qptr->v + 4, x + g->width, y + g->height, g->nx + g->nwidth, g->ny);
466         qvertex(qptr->v + 5, x, y + g->height, g->nx, g->ny);
467
468         if(++num_quads >= QBUF_SZ) {
469                 dtx_flush();
470         }
471 }
472
473 static void flush_user(void)
474 {
475         struct dtx_pixmap pixmap;
476
477         if(!num_quads || !user_draw_func || !cur_gmap) {
478                 return;
479         }
480
481         pixmap.pixels = cur_gmap->pixels;
482         pixmap.width = cur_gmap->xsz;
483         pixmap.height = cur_gmap->ysz;
484         pixmap.udata = cur_gmap->udata;
485
486         user_draw_func((struct dtx_vertex*)qbuf, num_quads * 6, &pixmap, user_cls);
487         cur_gmap->udata = pixmap.udata;
488
489         num_quads = 0;
490 }