Initial Comit: First commit.
[SauerEngine.git] / src / engine / rendertext.cpp
blob85465c01d713c8a630196a982473929a8131859f
1 #include "pch.h"
2 #include "engine.h"
4 static hashtable<const char *, font> fonts;
5 static font *fontdef = NULL;
7 font *curfont = NULL;
9 void newfont(char *name, char *tex, int *defaultw, int *defaulth, int *offsetx, int *offsety, int *offsetw, int *offseth)
11 font *f = fonts.access(name);
12 if(!f)
14 name = newstring(name);
15 f = &fonts[name];
16 f->name = name;
19 f->tex = textureload(tex);
20 f->chars.setsize(0);
21 f->defaultw = *defaultw;
22 f->defaulth = *defaulth;
23 f->offsetx = *offsetx;
24 f->offsety = *offsety;
25 f->offsetw = *offsetw;
26 f->offseth = *offseth;
28 fontdef = f;
31 void fontchar(int *x, int *y, int *w, int *h)
33 if(!fontdef) return;
35 font::charinfo &c = fontdef->chars.add();
36 c.x = *x;
37 c.y = *y;
38 c.w = *w ? *w : fontdef->defaultw;
39 c.h = *h ? *h : fontdef->defaulth;
42 COMMANDN(font, newfont, "ssiiiiii");
43 COMMAND(fontchar, "iiii");
45 bool setfont(const char *name)
47 font *f = fonts.access(name);
48 if(!f) return false;
49 curfont = f;
50 return true;
53 void gettextres(int &w, int &h)
55 if(w < MINRESW || h < MINRESH)
57 if(MINRESW > w*MINRESH/h)
59 h = h*MINRESW/w;
60 w = MINRESW;
62 else
64 w = w*MINRESH/h;
65 h = MINRESH;
70 #define PIXELTAB (4*curfont->defaultw)
72 int text_width(const char *str) { //@TODO deprecate in favour of text_bounds(..)
73 int width, height;
74 text_bounds(str, width, height);
75 return width;
78 void draw_textf(const char *fstr, int left, int top, ...)
80 s_sprintfdlv(str, top, fstr);
81 draw_text(str, left, top);
84 static int draw_char(int c, int x, int y)
86 font::charinfo &info = curfont->chars[c-33];
87 float tc_left = (info.x + curfont->offsetx) / float(curfont->tex->xs);
88 float tc_top = (info.y + curfont->offsety) / float(curfont->tex->ys);
89 float tc_right = (info.x + info.w + curfont->offsetw) / float(curfont->tex->xs);
90 float tc_bottom = (info.y + info.h + curfont->offseth) / float(curfont->tex->ys);
92 glTexCoord2f(tc_left, tc_top ); glVertex2f(x, y);
93 glTexCoord2f(tc_right, tc_top ); glVertex2f(x + info.w, y);
94 glTexCoord2f(tc_right, tc_bottom); glVertex2f(x + info.w, y + info.h);
95 glTexCoord2f(tc_left, tc_bottom); glVertex2f(x, y + info.h);
97 xtraverts += 4;
98 return info.w;
101 //stack[sp] is current color index
102 static void text_color(char c, char *stack, int size, int &sp, bvec color, int a)
104 if(c=='s') // save color
106 c = stack[sp];
107 if(sp<size-1) stack[sp++] = c;
109 else
111 if(c=='r') c = stack[(sp > 0) ? --sp : sp]; // restore color
112 else stack[sp] = c;
113 switch(c)
115 case '0': color = bvec( 64, 255, 128); break; // green: player talk
116 case '1': color = bvec( 96, 160, 255); break; // blue: "echo" command
117 case '2': color = bvec(255, 192, 64); break; // yellow: gameplay messages
118 case '3': color = bvec(255, 64, 64); break; // red: important errors
119 case '4': color = bvec(128, 128, 128); break; // gray
120 case '5': color = bvec(192, 64, 192); break; // magenta
121 case '6': color = bvec(255, 128, 0); break; // orange
122 // white (provided color): everything else
124 glColor4ub(color.x, color.y, color.z, a);
128 #define TEXTSKELETON \
129 int y = 0, x = 0;\
130 int i;\
131 for(i = 0; str[i]; i++)\
133 TEXTINDEX(i)\
134 int c = str[i];\
135 if(c=='\t') { x = ((x+PIXELTAB)/PIXELTAB)*PIXELTAB; TEXTWHITE(i) }\
136 else if(c==' ') { x += curfont->defaultw; TEXTWHITE(i) }\
137 else if(c=='\n') { TEXTLINE(i) x = 0; y += FONTH; }\
138 else if(c=='\f') { if(str[i+1]) { i++; TEXTCOLOR(i) }}\
139 else if(curfont->chars.inrange(c-33))\
141 if(maxwidth != -1)\
143 int j = i;\
144 int w = curfont->chars[c-33].w;\
145 for(; str[i+1]; i++)\
147 int c = str[i+1];\
148 if(c=='\f') { if(str[i+2]) i++; continue; }\
149 if(i-j > 16) break;\
150 if(!curfont->chars.inrange(c-33)) break;\
151 int cw = curfont->chars[c-33].w + 1;\
152 if(w + cw >= maxwidth) break;\
153 w += cw;\
155 if(x + w >= maxwidth && j!=0) { TEXTLINE(j-1) x = 0; y += FONTH; }\
156 TEXTWORD\
158 else\
159 { TEXTCHAR(i) }\
163 //all the chars are guaranteed to be either drawable or color commands
164 #define TEXTWORDSKELETON \
165 for(; j <= i; j++)\
167 TEXTINDEX(j)\
168 int c = str[j];\
169 if(c=='\f') { if(str[j+1]) { j++; TEXTCOLOR(j) }}\
170 else { TEXTCHAR(j) }\
173 int text_visible(const char *str, int hitx, int hity, int maxwidth)
175 #define TEXTINDEX(idx)
176 #define TEXTWHITE(idx) if(y+FONTH > hity && x >= hitx) return idx;
177 #define TEXTLINE(idx) if(y+FONTH > hity) return idx;
178 #define TEXTCOLOR(idx)
179 #define TEXTCHAR(idx) x += curfont->chars[c-33].w+1; TEXTWHITE(idx)
180 #define TEXTWORD TEXTWORDSKELETON
181 TEXTSKELETON
182 #undef TEXTINDEX
183 #undef TEXTWHITE
184 #undef TEXTLINE
185 #undef TEXTCOLOR
186 #undef TEXTCHAR
187 #undef TEXTWORD
188 return i;
191 //inverse of text_visible
192 void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth)
194 #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; break; }
195 #define TEXTWHITE(idx)
196 #define TEXTLINE(idx)
197 #define TEXTCOLOR(idx)
198 #define TEXTCHAR(idx) x += curfont->chars[c-33].w + 1;
199 #define TEXTWORD TEXTWORDSKELETON if(i >= cursor) break;
200 cx = INT_MIN;
201 cy = 0;
202 TEXTSKELETON
203 if(cx == INT_MIN) { cx = x; cy = y; }
204 #undef TEXTINDEX
205 #undef TEXTWHITE
206 #undef TEXTLINE
207 #undef TEXTCOLOR
208 #undef TEXTCHAR
209 #undef TEXTWORD
212 void text_bounds(const char *str, int &width, int &height, int maxwidth)
214 #define TEXTINDEX(idx)
215 #define TEXTWHITE(idx)
216 #define TEXTLINE(idx) if(x > width) width = x;
217 #define TEXTCOLOR(idx)
218 #define TEXTCHAR(idx) x += curfont->chars[c-33].w + 1;
219 #define TEXTWORD x += w + 1;
220 width = 0;
221 TEXTSKELETON
222 height = y + FONTH;
223 TEXTLINE(_)
224 #undef TEXTINDEX
225 #undef TEXTWHITE
226 #undef TEXTLINE
227 #undef TEXTCOLOR
228 #undef TEXTCHAR
229 #undef TEXTWORD
232 void draw_text(const char *str, int left, int top, int r, int g, int b, int a, int cursor, int maxwidth)
234 #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; }
235 #define TEXTWHITE(idx)
236 #define TEXTLINE(idx)
237 #define TEXTCOLOR(idx) text_color(str[idx], colorstack, sizeof(colorstack), colorpos, color, a);
238 #define TEXTCHAR(idx) x += draw_char(c, left+x, top+y)+1;
239 #define TEXTWORD TEXTWORDSKELETON
240 char colorstack[10];
241 bvec color(r, g, b);
242 int colorpos = 0, cx = INT_MIN, cy = 0;
243 colorstack[0] = 'c'; //indicate user color
244 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
245 glBindTexture(GL_TEXTURE_2D, curfont->tex->id);
246 glBegin(GL_QUADS);
247 glColor4ub(color.x, color.y, color.z, a);
248 TEXTSKELETON
249 if(cursor >= 0 && (totalmillis/250)&1)
251 glColor4ub(r, g, b, a);
252 if(cx == INT_MIN) { cx = x; cy = y; }
253 if(maxwidth != -1 && cx >= maxwidth) { cx = 0; cy += FONTH; }
254 draw_char('_', left+cx, top+cy);
256 glEnd();
257 #undef TEXTINDEX
258 #undef TEXTWHITE
259 #undef TEXTLINE
260 #undef TEXTCOLOR
261 #undef TEXTCHAR
262 #undef TEXTWORD
265 void reloadfonts()
267 enumerate(fonts, font, f,
268 if(!reloadtexture(*f.tex)) fatal("failed to reload font texture");