]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/draw.qc
drawstring_aspect: fix wrong horizontal string alignment sometimes due to r_font_size...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / draw.qc
1 #include "draw.qh"
2
3 #include <client/hud/hud.qh>
4
5 void drawborderlines(float thickness, vector pos, vector dim, vector color, float theAlpha, float drawflag)
6 {
7         vector line_dim = '0 0 0';
8
9         // left and right lines
10         pos.x -= thickness;
11         line_dim.x = thickness;
12         line_dim.y = dim.y;
13         drawfill(pos, line_dim, color, theAlpha, drawflag);
14         drawfill(pos + (dim.x + thickness) * '1 0 0', line_dim, color, theAlpha, drawflag);
15
16         // upper and lower lines
17         pos.y -= thickness;
18         line_dim.x = dim.x + thickness * 2; // make upper and lower lines longer
19         line_dim.y = thickness;
20         drawfill(pos, line_dim, color, theAlpha, drawflag);
21         drawfill(pos + (dim.y + thickness) * '0 1 0', line_dim, color, theAlpha, drawflag);
22 }
23
24 void drawpic_tiled(vector pos, string pic, vector sz, vector area, vector color, float theAlpha, float drawflag)
25 {
26         pos = HUD_Shift(pos);
27         sz = HUD_Scale(sz);
28         area = HUD_Scale(area);
29
30         vector current_pos = '0 0 0', end_pos, new_size = '0 0 0', ratio = '0 0 0';
31         end_pos = pos + area;
32
33         current_pos.y = pos.y;
34         while (current_pos.y < end_pos.y)
35         {
36                 current_pos.x = pos.x;
37                 while (current_pos.x < end_pos.x)
38                 {
39                         new_size.x = min(sz.x, end_pos.x - current_pos.x);
40                         new_size.y = min(sz.y, end_pos.y - current_pos.y);
41                         ratio.x = new_size.x / sz.x;
42                         ratio.y = new_size.y / sz.y;
43                         drawsubpic(current_pos, new_size, pic, '0 0 0', ratio, color, theAlpha, drawflag);
44                         current_pos.x += sz.x;
45                 }
46                 current_pos.y += sz.y;
47         }
48 }
49
50 float expandingbox_sizefactor_from_fadelerp(float fadelerp)
51 {
52         return 1.2 / (1.2 - fadelerp);
53 }
54
55 vector expandingbox_resize_centered_box_offset(float sz, vector boxsize, float boxxsizefactor)
56 {
57         boxsize.x *= boxxsizefactor; // easier interface for text
58         return boxsize * (0.5 * (1 - sz));
59 }
60
61 void drawpic_aspect_skin_expanding(vector position, string pic, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp)
62 {
63         float sz;
64         sz = expandingbox_sizefactor_from_fadelerp(fadelerp);
65
66         drawpic_aspect_skin(position + expandingbox_resize_centered_box_offset(sz, theScale, 1), pic, theScale * sz, rgb, theAlpha * (1 - fadelerp), flag);
67 }
68
69 void drawpic_aspect_skin_expanding_two(vector position, string pic, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp)
70 {
71         drawpic_aspect_skin_expanding(position, pic, theScale, rgb, theAlpha, flag, fadelerp);
72         drawpic_skin(position, pic, theScale, rgb, theAlpha * fadelerp, flag);
73 }
74
75 float stringwidth(string text, float handleColors, vector sz)
76 {
77         vector dfs = drawfontscale;
78         drawfontscale = '1 1 0';
79         float r = stringwidth_builtin(text, handleColors, sz);
80         drawfontscale = dfs;
81         return r;
82 }
83
84 // it scales text up to box width
85 // NOTE it doesn't work perfectly because r_font_size_snapping 4 (default value)
86 // may render text with a slightly different size making text bigger or smaller
87 // NOTE this is implemented as a macro to reduce number of function calls per frame
88 #define DRAWSTRING_ASPECT_SCALE(pos, text, sz, allow_colors) MACRO_BEGIN \
89         float textaspect, oldsz; \
90         vector dfs = drawfontscale; \
91         drawfontscale = '1 1 0'; \
92         textaspect = stringwidth(text, allow_colors, '1 1 1' * sz.y) / sz.y; \
93         if(sz.x/sz.y > textaspect) { \
94                 oldsz = sz.x; \
95                 sz.x = sz.y * textaspect; \
96                 pos.x += (oldsz - sz.x) * 0.5; \
97         } else { \
98                 oldsz = sz.y; \
99                 sz.y = sz.x / textaspect; \
100                 pos.y += (oldsz - sz.y) * 0.5; \
101                 /* in case text is rendered with a different size, at least recenter it horizontally */ \
102                 /* unfortunately there is no way to correctly recenter it vertically */ \
103                 float new_textwidth = stringwidth(text, allow_colors, '1 1 1' * sz.y); \
104                 pos.x += (sz.x - new_textwidth) * 0.5; \
105         } \
106         drawfontscale = dfs; \
107 MACRO_END
108
109 // drawstring wrapper to draw a string as large as possible with preserved aspect ratio into a box
110 void drawstring_aspect(vector pos, string text, vector sz, vector color, float theAlpha, float drawflag) {
111         DRAWSTRING_ASPECT_SCALE(pos, text, sz, false);
112         drawstring(pos, text, '1 1 0' * sz.y, color, theAlpha, drawflag);
113 }
114
115 // drawstring wrapper to draw a colorcodedstring as large as possible with preserved aspect ratio into a box
116 void drawcolorcodedstring_aspect(vector pos, string text, vector sz, float theAlpha, float drawflag) {
117         DRAWSTRING_ASPECT_SCALE(pos, text, sz, true);
118         drawcolorcodedstring(pos, text, '1 1 0' * sz.y, theAlpha, drawflag);
119 }
120
121 void drawstring_expanding(vector position, string text, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp)
122 {
123         float sz;
124         sz = expandingbox_sizefactor_from_fadelerp(fadelerp);
125
126         drawfontscale = hud_scale * sz;
127         vector dfs = drawfontscale;
128         drawfontscale = sz * '1 1 0';
129         float textaspect = stringwidth_builtin(text, false, theScale * (sz / drawfontscale.x)) / (theScale.x * sz);
130         drawfontscale = dfs;
131         drawstring(position + expandingbox_resize_centered_box_offset(sz, theScale, textaspect), text, HUD_Scale(theScale * (sz / drawfontscale.x)), rgb, theAlpha * (1 - fadelerp), flag);
132         // width parameter:
133         //    (scale_x * sz / drawfontscale.x) * drawfontscale.x * SIZE1 / (scale_x * sz)
134         //    SIZE1
135         drawfontscale = hud_scale;
136 }
137
138 // drawstring wrapper to draw a string as large as possible with preserved aspect ratio into a box
139 void drawstring_aspect_expanding(vector pos, string text, vector sz, vector color, float theAlpha, float drawflag, float fadelerp) {
140         DRAWSTRING_ASPECT_SCALE(pos, text, sz, false);
141         drawstring_expanding(pos, text, '1 1 0' * sz.y, color, theAlpha, drawflag, fadelerp);
142 }
143
144 void drawcolorcodedstring_expanding(vector position, string text, vector theScale, float theAlpha, float flag, float fadelerp)
145 {
146         float sz;
147         sz = expandingbox_sizefactor_from_fadelerp(fadelerp);
148
149         drawfontscale = hud_scale * sz;
150         // eventually replace with drawcolorcodedstring
151         drawcolorcodedstring(position + expandingbox_resize_centered_box_offset(sz, theScale, stringwidth_builtin(text, true, theScale * (sz / drawfontscale.x)) / (theScale.x * sz)), text, theScale * (sz / drawfontscale.x), theAlpha * (1 - fadelerp), flag);
152         drawfontscale = hud_scale;
153 }
154
155 void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, float theAlpha, float drawflag, float fadelerp) {
156         DRAWSTRING_ASPECT_SCALE(pos, text, sz, true);
157         drawcolorcodedstring_expanding(pos, text, '1 1 0' * sz.y, theAlpha, drawflag, fadelerp);
158 }
159
160 // this draws the triangles of a model DIRECTLY. Don't expect high performance, really...
161 float PolyDrawModelSurface(entity e, float i_s)
162 {
163         float i_t;
164         float n_t;
165         vector tri;
166         string tex;
167         tex = getsurfacetexture(e, i_s);
168         if (!tex)
169                 return 0; // this is beyond the last one
170         n_t = getsurfacenumtriangles(e, i_s);
171         for(i_t = 0; i_t < n_t; ++i_t)
172         {
173                 tri = getsurfacetriangle(e, i_s, i_t);
174                 R_BeginPolygon(tex, 0, false);
175                 R_PolygonVertex(getsurfacepoint(e, i_s, tri.x), getsurfacepointattribute(e, i_s, tri.x, SPA_TEXCOORDS0), '1 1 1', 1);
176                 R_PolygonVertex(getsurfacepoint(e, i_s, tri.y), getsurfacepointattribute(e, i_s, tri.y, SPA_TEXCOORDS0), '1 1 1', 1);
177                 R_PolygonVertex(getsurfacepoint(e, i_s, tri.z), getsurfacepointattribute(e, i_s, tri.z, SPA_TEXCOORDS0), '1 1 1', 1);
178                 R_EndPolygon();
179         }
180         return 1;
181 }
182 void PolyDrawModel(entity e)
183 {
184         float i_s;
185         for(i_s = 0; ; ++i_s)
186                 if(!PolyDrawModelSurface(e, i_s))
187                         break;
188 }
189
190 void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector rgb, float a, float drawflag)
191 {
192         vector ringsize, v, t;
193         ringsize = radi * '1 1 0';
194         centre = HUD_Shift(centre);
195         ringsize = HUD_Scale(ringsize);
196
197         if(f >= 1)
198         {
199                 // draw full rectangle
200                 R_BeginPolygon(pic, drawflag, true);
201                         v = centre;                     t = '0.5 0.5 0';
202                         v.x += 0.5 * ringsize.x;        t += '0.5 0.5 0';
203                         R_PolygonVertex(v, t, rgb, a);
204
205                         v = centre;                     t = '0.5 0.5 0';
206                         v.y += 0.5 * ringsize.y;        t += '0.5 -0.5 0';
207                         R_PolygonVertex(v, t, rgb, a);
208
209                         v = centre;                     t = '0.5 0.5 0';
210                         v.x -= 0.5 * ringsize.x;        t -= '0.5 0.5 0';
211                         R_PolygonVertex(v, t, rgb, a);
212
213                         v = centre;                     t = '0.5 0.5 0';
214                         v.y -= 0.5 * ringsize.y;        t -= '0.5 -0.5 0';
215                         R_PolygonVertex(v, t, rgb, a);
216                 R_EndPolygon();
217                 return;  // Complete rectangle, nothing more needed.
218         }
219
220         float co = cos(f * 2 * M_PI);
221         float si = sin(f * 2 * M_PI);
222         float q = fabs(co) + fabs(si);
223         co /= q;
224         si /= q;
225
226         if(f > 0.75)
227         {
228                 // draw upper half in full
229                 R_BeginPolygon(pic, drawflag, true);
230                         v = centre;                     t = '0.5 0.5 0';
231                         v.x += 0.5 * ringsize.x;        t += '0.5 0.5 0';
232                         R_PolygonVertex(v, t, rgb, a);
233
234                         v = centre;                     t = '0.5 0.5 0';
235                         v.y += 0.5 * ringsize.y;        t += '0.5 -0.5 0';
236                         R_PolygonVertex(v, t, rgb, a);
237
238                         v = centre;                     t = '0.5 0.5 0';
239                         v.x -= 0.5 * ringsize.x;        t -= '0.5 0.5 0';
240                         R_PolygonVertex(v, t, rgb, a);
241                 R_EndPolygon();
242                 // draw clipped lower half as a quad
243                 R_BeginPolygon(pic, drawflag, true);
244                         v = centre;                     t = '0.5 0.5 0';
245                         R_PolygonVertex(v, t, rgb, a);
246
247                         v = centre;                     t = '0.5 0.5 0';
248                         v.x -= 0.5 * ringsize.x;        t -= '0.5 0.5 0';
249                         R_PolygonVertex(v, t, rgb, a);
250
251                         v = centre;                     t = '0.5 0.5 0';
252                         v.y -= 0.5 * ringsize.y;        t -= '0.5 -0.5 0';
253                         R_PolygonVertex(v, t, rgb, a);
254         }
255         else if(f > 0.5)
256         {
257                 // draw upper half in full
258                 R_BeginPolygon(pic, drawflag, true);
259                         v = centre;                     t = '0.5 0.5 0';
260                         v.x += 0.5 * ringsize.x;        t += '0.5 0.5 0';
261                         R_PolygonVertex(v, t, rgb, a);
262
263                         v = centre;                     t = '0.5 0.5 0';
264                         v.y += 0.5 * ringsize.y;        t += '0.5 -0.5 0';
265                         R_PolygonVertex(v, t, rgb, a);
266
267                         v = centre;                     t = '0.5 0.5 0';
268                         v.x -= 0.5 * ringsize.x;        t -= '0.5 0.5 0';
269                         R_PolygonVertex(v, t, rgb, a);
270                 R_EndPolygon();
271                 // draw clipped lower half as a triangle
272                 R_BeginPolygon(pic, drawflag, true);
273                         v = centre;                     t = '0.5 0.5 0';
274                         R_PolygonVertex(v, t, rgb, a);
275
276                         v = centre;                     t = '0.5 0.5 0';
277                         v.x -= 0.5 * ringsize.x;        t -= '0.5 0.5 0';
278                         R_PolygonVertex(v, t, rgb, a);
279         }
280         else if(f > 0.25)
281         {
282                 // draw clipped lower half as a quad
283                 R_BeginPolygon(pic, drawflag, true);
284                         v = centre;                     t = '0.5 0.5 0';
285                         R_PolygonVertex(v, t, rgb, a);
286
287                         v = centre;                     t = '0.5 0.5 0';
288                         v.x += 0.5 * ringsize.x;        t += '0.5 0.5 0';
289                         R_PolygonVertex(v, t, rgb, a);
290
291                         v = centre;                     t = '0.5 0.5 0';
292                         v.y += 0.5 * ringsize.y;        t += '0.5 -0.5 0';
293                         R_PolygonVertex(v, t, rgb, a);
294         }
295         else if (f > 0)
296         {
297                 // draw clipped lower half as a triangle
298                 R_BeginPolygon(pic, drawflag, true);
299                         v = centre;                     t = '0.5 0.5 0';
300                         R_PolygonVertex(v, t, rgb, a);
301
302                         v = centre;                     t = '0.5 0.5 0';
303                         v.x += 0.5 * ringsize.x;        t += '0.5 0.5 0';
304                         R_PolygonVertex(v, t, rgb, a);
305         }
306         else
307         {
308                 // Nothing to draw.
309                 return;
310         }
311
312         // The last, moving vertex.
313                 v = centre;                     t = '0.5 0.5 0';
314                 v.x += co * 0.5 * ringsize.x;   t += co * '0.5 0.5 0';
315                 v.y += si * 0.5 * ringsize.y;   t += si * '0.5 -0.5 0';
316                 R_PolygonVertex(v, t, rgb, a);
317         R_EndPolygon();
318 }