]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
527db40d237a4f7e03ff6d917e13fb848045fa17
[xonotic/darkplaces.git] / gl_draw.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "wad.h"
24
25 #include "cl_video.h"
26 #include "cl_dyntexture.h"
27
28 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
29 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
30
31 static rtexture_t *char_texture;
32 cachepic_t *r_crosshairs[NUMCROSSHAIRS+1];
33
34 //=============================================================================
35 /* Support Routines */
36
37 #define FONT_FILESIZE 13468
38 #define MAX_CACHED_PICS 1024
39 #define CACHEPICHASHSIZE 256
40 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
41 static cachepic_t cachepics[MAX_CACHED_PICS];
42 static int numcachepics;
43
44 static rtexturepool_t *drawtexturepool;
45
46 static unsigned char concharimage[FONT_FILESIZE] =
47 {
48 #include "lhfont.h"
49 };
50
51 static rtexture_t *draw_generateconchars(void)
52 {
53         int i;
54         unsigned char buffer[65536][4], *data = NULL;
55         double random;
56
57         data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
58 // Gold numbers
59         for (i = 0;i < 8192;i++)
60         {
61                 random = lhrandom (0.0,1.0);
62                 buffer[i][2] = 83 + (unsigned char)(random * 64);
63                 buffer[i][1] = 71 + (unsigned char)(random * 32);
64                 buffer[i][0] = 23 + (unsigned char)(random * 16);
65                 buffer[i][3] = data[i*4+0];
66         }
67 // White chars
68         for (i = 8192;i < 32768;i++)
69         {
70                 random = lhrandom (0.0,1.0);
71                 buffer[i][2] = 95 + (unsigned char)(random * 64);
72                 buffer[i][1] = 95 + (unsigned char)(random * 64);
73                 buffer[i][0] = 95 + (unsigned char)(random * 64);
74                 buffer[i][3] = data[i*4+0];
75         }
76 // Gold numbers
77         for (i = 32768;i < 40960;i++)
78         {
79                 random = lhrandom (0.0,1.0);
80                 buffer[i][2] = 83 + (unsigned char)(random * 64);
81                 buffer[i][1] = 71 + (unsigned char)(random * 32);
82                 buffer[i][0] = 23 + (unsigned char)(random * 16);
83                 buffer[i][3] = data[i*4+0];
84         }
85 // Red chars
86         for (i = 40960;i < 65536;i++)
87         {
88                 random = lhrandom (0.0,1.0);
89                 buffer[i][2] = 96 + (unsigned char)(random * 64);
90                 buffer[i][1] = 43 + (unsigned char)(random * 32);
91                 buffer[i][0] = 27 + (unsigned char)(random * 32);
92                 buffer[i][3] = data[i*4+0];
93         }
94
95 #if 0
96         Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
97 #endif
98
99         Mem_Free(data);
100         return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
101 }
102
103 static char *pointerimage =
104         "333333332......."
105         "26777761........"
106         "2655541........."
107         "265541.........."
108         "2654561........."
109         "26414561........"
110         "251.14561......."
111         "21...14561......"
112         "1.....141......."
113         ".......1........"
114         "................"
115         "................"
116         "................"
117         "................"
118         "................"
119         "................"
120 ;
121
122 static rtexture_t *draw_generatemousepointer(void)
123 {
124         int i;
125         unsigned char buffer[256][4];
126         for (i = 0;i < 256;i++)
127         {
128                 if (pointerimage[i] == '.')
129                 {
130                         buffer[i][0] = 0;
131                         buffer[i][1] = 0;
132                         buffer[i][2] = 0;
133                         buffer[i][3] = 0;
134                 }
135                 else
136                 {
137                         buffer[i][0] = (pointerimage[i] - '0') * 16;
138                         buffer[i][1] = (pointerimage[i] - '0') * 16;
139                         buffer[i][2] = (pointerimage[i] - '0') * 16;
140                         buffer[i][3] = 255;
141                 }
142         }
143         return R_LoadTexture2D(drawtexturepool, "mousepointer", 16, 16, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
144 }
145
146 static char *crosshairtexdata[NUMCROSSHAIRS] =
147 {
148         "................"
149         "................"
150         "................"
151         "...33......33..."
152         "...355....553..."
153         "....577..775...."
154         ".....77..77....."
155         "................"
156         "................"
157         ".....77..77....."
158         "....577..775...."
159         "...355....553..."
160         "...33......33..."
161         "................"
162         "................"
163         "................"
164         ,
165         "................"
166         "................"
167         "................"
168         "...3........3..."
169         "....5......5...."
170         ".....7....7....."
171         "......7..7......"
172         "................"
173         "................"
174         "......7..7......"
175         ".....7....7....."
176         "....5......5...."
177         "...3........3..."
178         "................"
179         "................"
180         "................"
181         ,
182         "................"
183         ".......77......."
184         ".......77......."
185         "................"
186         "................"
187         ".......44......."
188         ".......44......."
189         ".77..44..44..77."
190         ".77..44..44..77."
191         ".......44......."
192         ".......44......."
193         "................"
194         "................"
195         ".......77......."
196         ".......77......."
197         "................"
198         ,
199         "................"
200         "................"
201         "................"
202         "................"
203         "................"
204         "................"
205         "................"
206         "................"
207         "........7777777."
208         "........752....."
209         "........72......"
210         "........7......."
211         "........7......."
212         "........7......."
213         "........7......."
214         "................"
215         ,
216         "................"
217         "................"
218         "................"
219         "................"
220         "................"
221         "........7......."
222         "................"
223         "........4......."
224         ".....7.4.4.7...."
225         "........4......."
226         "................"
227         "........7......."
228         "................"
229         "................"
230         "................"
231         "................"
232         ,
233         "................"
234         "................"
235         "................"
236         "................"
237         "................"
238         "................"
239         "................"
240         ".......55......."
241         ".......55......."
242         "................"
243         "................"
244         "................"
245         "................"
246         "................"
247         "................"
248         "................"
249 };
250
251 static rtexture_t *draw_generatecrosshair(int num)
252 {
253         int i;
254         char *in;
255         unsigned char data[16*16][4];
256         in = crosshairtexdata[num];
257         for (i = 0;i < 16*16;i++)
258         {
259                 if (in[i] == '.')
260                 {
261                         data[i][0] = 255;
262                         data[i][1] = 255;
263                         data[i][2] = 255;
264                         data[i][3] = 0;
265                 }
266                 else
267                 {
268                         data[i][0] = data[i][1] = data[i][2] = (unsigned char) ((int) (in[i] - '0') * 127 / 7 + 128);
269                         data[i][3] = 255;
270                 }
271         }
272         return R_LoadTexture2D(drawtexturepool, va("crosshair%i", num+1), 16, 16, &data[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
273 }
274
275 static rtexture_t *draw_generateditherpattern(void)
276 {
277 #if 1
278         int x, y;
279         unsigned char data[8*8*4];
280         for (y = 0;y < 8;y++)
281         {
282                 for (x = 0;x < 8;x++)
283                 {
284                         data[(y*8+x)*4+0] = data[(y*8+x)*4+1] = data[(y*8+x)*4+2] = ((x^y) & 4) ? 255 : 0;
285                         data[(y*8+x)*4+3] = 255;
286                 }
287         }
288         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, data, TEXTYPE_BGRA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
289 #else
290         unsigned char data[16];
291         memset(data, 255, sizeof(data));
292         data[0] = data[1] = data[2] = data[12] = data[13] = data[14] = 0;
293         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 2, 2, data, TEXTYPE_BGRA, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
294 #endif
295 }
296
297 /*
298 ================
299 Draw_CachePic
300 ================
301 */
302 // FIXME: move this to client somehow
303 cachepic_t      *Draw_CachePic (const char *path, qboolean persistent)
304 {
305         int crc, hashkey;
306         cachepic_t *pic;
307         int flags;
308         fs_offset_t lmpsize;
309         unsigned char *lmpdata;
310         char lmpname[MAX_QPATH];
311
312         // check whether the picture has already been cached
313         crc = CRC_Block((unsigned char *)path, strlen(path));
314         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
315         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
316                 if (!strcmp (path, pic->name))
317                         return pic;
318
319         if (numcachepics == MAX_CACHED_PICS)
320         {
321                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
322                 // FIXME: support NULL in callers?
323                 return cachepics; // return the first one
324         }
325         pic = cachepics + (numcachepics++);
326         strlcpy (pic->name, path, sizeof(pic->name));
327         // link into list
328         pic->chain = cachepichash[hashkey];
329         cachepichash[hashkey] = pic;
330
331         // check whether it is an dynamic texture (if so, we can directly use its texture handler)
332         pic->tex = CL_GetDynTexture( path );
333         // if so, set the width/height, too
334         if( pic->tex ) {
335                 pic->width = R_TextureWidth(pic->tex);
336                 pic->height = R_TextureHeight(pic->tex);
337                 // we're done now (early-out)
338                 return pic;
339         }
340
341         flags = TEXF_ALPHA;
342         if (persistent)
343                 flags |= TEXF_PRECACHE;
344         if (!strcmp(path, "gfx/colorcontrol/ditherpattern"))
345                 flags |= TEXF_CLAMP;
346
347         // load a high quality image from disk if possible
348         pic->tex = loadtextureimage(drawtexturepool, path, false, flags | (gl_texturecompression_2d.integer ? TEXF_COMPRESS : 0), true);
349         if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
350         {
351                 // compatibility with older versions which did not require gfx/ prefix
352                 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags | (gl_texturecompression_2d.integer ? TEXF_COMPRESS : 0), true);
353         }
354         // if a high quality image was loaded, set the pic's size to match it, just
355         // in case there's no low quality version to get the size from
356         if (pic->tex)
357         {
358                 pic->width = R_TextureWidth(pic->tex);
359                 pic->height = R_TextureHeight(pic->tex);
360         }
361
362         // now read the low quality version (wad or lmp file), and take the pic
363         // size from that even if we don't upload the texture, this way the pics
364         // show up the right size in the menu even if they were replaced with
365         // higher or lower resolution versions
366         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
367         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
368         {
369                 if (lmpsize >= 9)
370                 {
371                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
372                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
373                         // if no high quality replacement image was found, upload the original low quality texture
374                         if (!pic->tex)
375                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags, palette_bgra_transparent);
376                 }
377                 Mem_Free(lmpdata);
378         }
379         else if ((lmpdata = W_GetLumpName (path + 4)))
380         {
381                 if (!strcmp(path, "gfx/conchars"))
382                 {
383                         // conchars is a raw image and with color 0 as transparent instead of 255
384                         pic->width = 128;
385                         pic->height = 128;
386                         // if no high quality replacement image was found, upload the original low quality texture
387                         if (!pic->tex)
388                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags, palette_bgra_font);
389                 }
390                 else
391                 {
392                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
393                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
394                         // if no high quality replacement image was found, upload the original low quality texture
395                         if (!pic->tex)
396                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags, palette_bgra_transparent);
397                 }
398         }
399
400         // if it's not found on disk, check if it's one of the builtin images
401         if (pic->tex == NULL)
402         {
403                 if (pic->tex == NULL && !strcmp(path, "gfx/conchars"))
404                         pic->tex = draw_generateconchars();
405                 if (pic->tex == NULL && !strcmp(path, "ui/mousepointer"))
406                         pic->tex = draw_generatemousepointer();
407                 if (pic->tex == NULL && !strcmp(path, "gfx/prydoncursor001"))
408                         pic->tex = draw_generatemousepointer();
409                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair1"))
410                         pic->tex = draw_generatecrosshair(0);
411                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair2"))
412                         pic->tex = draw_generatecrosshair(1);
413                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair3"))
414                         pic->tex = draw_generatecrosshair(2);
415                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair4"))
416                         pic->tex = draw_generatecrosshair(3);
417                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair5"))
418                         pic->tex = draw_generatecrosshair(4);
419                 if (pic->tex == NULL && !strcmp(path, "gfx/crosshair6"))
420                         pic->tex = draw_generatecrosshair(5);
421                 if (pic->tex == NULL && !strcmp(path, "gfx/colorcontrol/ditherpattern"))
422                         pic->tex = draw_generateditherpattern();
423                 // default textures for light sprites
424                 // todo: improve them
425                 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/cursor"))
426                         pic->tex = draw_generatecrosshair(0);
427                 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/light"))
428                         pic->tex = draw_generatecrosshair(0);
429                 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/noshadow"))
430                         pic->tex = draw_generatecrosshair(0);
431                 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/cubemap"))
432                         pic->tex = draw_generatecrosshair(0);
433                 if (pic->tex == NULL && !strcmp(path, "gfx/editlights/selection"))
434                         pic->tex = draw_generatecrosshair(0);
435                 if (pic->tex == NULL)
436                 {
437                         // don't complain about missing gfx/crosshair images
438                         if (strncmp(path, "gfx/crosshair", 13))
439                                 Con_Printf("Draw_CachePic: failed to load %s\n", path);
440                         pic->tex = r_texture_notexture;
441                 }
442                 pic->width = R_TextureWidth(pic->tex);
443                 pic->height = R_TextureHeight(pic->tex);
444         }
445
446         return pic;
447 }
448
449 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
450 {
451         int crc, hashkey;
452         cachepic_t *pic;
453
454         crc = CRC_Block((unsigned char *)picname, strlen(picname));
455         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
456         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
457                 if (!strcmp (picname, pic->name))
458                         break;
459
460         if (pic)
461         {
462                 if (pic->tex && pic->width == width && pic->height == height)
463                 {
464                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
465                         return pic;
466                 }
467         }
468         else
469         {
470                 if (pic == NULL)
471                 {
472                         if (numcachepics == MAX_CACHED_PICS)
473                         {
474                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
475                                 // FIXME: support NULL in callers?
476                                 return cachepics; // return the first one
477                         }
478                         pic = cachepics + (numcachepics++);
479                         strlcpy (pic->name, picname, sizeof(pic->name));
480                         // link into list
481                         pic->chain = cachepichash[hashkey];
482                         cachepichash[hashkey] = pic;
483                 }
484         }
485
486         pic->width = width;
487         pic->height = height;
488         if (pic->tex)
489                 R_FreeTexture(pic->tex);
490         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
491         return pic;
492 }
493
494 void Draw_FreePic(const char *picname)
495 {
496         int crc;
497         int hashkey;
498         cachepic_t *pic;
499         // this doesn't really free the pic, but does free it's texture
500         crc = CRC_Block((unsigned char *)picname, strlen(picname));
501         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
502         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
503         {
504                 if (!strcmp (picname, pic->name) && pic->tex)
505                 {
506                         R_FreeTexture(pic->tex);
507                         pic->width = 0;
508                         pic->height = 0;
509                         return;
510                 }
511         }
512 }
513
514 /*
515 ===============
516 Draw_Init
517 ===============
518 */
519 static void gl_draw_start(void)
520 {
521         int i;
522         drawtexturepool = R_AllocTexturePool();
523
524         numcachepics = 0;
525         memset(cachepichash, 0, sizeof(cachepichash));
526
527         char_texture = Draw_CachePic("gfx/conchars", true)->tex;
528         for (i = 1;i <= NUMCROSSHAIRS;i++)
529                 r_crosshairs[i] = Draw_CachePic(va("gfx/crosshair%i", i), true);
530
531         // draw the loading screen so people have something to see in the newly opened window
532         SCR_UpdateLoadingScreen(true);
533 }
534
535 static void gl_draw_shutdown(void)
536 {
537         R_FreeTexturePool(&drawtexturepool);
538
539         numcachepics = 0;
540         memset(cachepichash, 0, sizeof(cachepichash));
541 }
542
543 static void gl_draw_newmap(void)
544 {
545 }
546
547 void GL_Draw_Init (void)
548 {
549         Cvar_RegisterVariable(&r_textshadow);
550         Cvar_RegisterVariable(&r_textbrightness);
551         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
552 }
553
554 static void _DrawQ_Setup(void)
555 {
556         if (r_refdef.draw2dstage)
557                 return;
558         r_refdef.draw2dstage = true;
559         CHECKGLERROR
560         qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
561         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
562         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
563         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
564         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
565         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
566         R_Mesh_Matrix(&identitymatrix);
567
568         GL_DepthMask(true);
569         GL_DepthRange(0, 1);
570         GL_PolygonOffset(0, 0);
571         GL_DepthTest(false);
572         GL_Color(1,1,1,1);
573         GL_AlphaTest(false);
574         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
575
576         if (gl_support_fragment_shader)
577         {
578                 qglUseProgramObjectARB(0);CHECKGLERROR
579         }
580 }
581
582 static void _DrawQ_ProcessDrawFlag(int flags)
583 {
584         _DrawQ_Setup();
585         CHECKGLERROR
586         if(flags == DRAWFLAG_ADDITIVE)
587                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
588         else if(flags == DRAWFLAG_MODULATE)
589                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
590         else if(flags == DRAWFLAG_2XMODULATE)
591                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
592         else
593                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
594 }
595
596 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
597 {
598         float floats[20];
599
600         _DrawQ_ProcessDrawFlag(flags);
601         GL_Color(red, green, blue, alpha);
602
603         R_Mesh_VertexPointer(floats, 0, 0);
604         R_Mesh_ColorPointer(NULL, 0, 0);
605         R_Mesh_ResetTextureState();
606         if (pic)
607         {
608                 if (width == 0)
609                         width = pic->width;
610                 if (height == 0)
611                         height = pic->height;
612                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
613                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
614
615       // AK07: lets be texel correct on the corners
616       {
617          float horz_offset = 0.5f / pic->width;
618          float vert_offset = 0.5f / pic->height;
619
620                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
621                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
622                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
623                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
624       }
625         }
626
627         floats[2] = floats[5] = floats[8] = floats[11] = 0;
628         floats[0] = floats[9] = x;
629         floats[1] = floats[4] = y;
630         floats[3] = floats[6] = x + width;
631         floats[7] = floats[10] = y + height;
632
633         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
634 }
635
636 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
637 {
638         float floats[12];
639
640         _DrawQ_ProcessDrawFlag(flags);
641         GL_Color(red, green, blue, alpha);
642
643         R_Mesh_VertexPointer(floats, 0, 0);
644         R_Mesh_ColorPointer(NULL, 0, 0);
645         R_Mesh_ResetTextureState();
646
647         floats[2] = floats[5] = floats[8] = floats[11] = 0;
648         floats[0] = floats[9] = x;
649         floats[1] = floats[4] = y;
650         floats[3] = floats[6] = x + width;
651         floats[7] = floats[10] = y + height;
652
653         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
654 }
655
656 // color tag printing
657 static vec4_t string_colors[] =
658 {
659         // Quake3 colors
660         // LordHavoc: why on earth is cyan before magenta in Quake3?
661         // LordHavoc: note: Doom3 uses white for [0] and [7]
662         {0.0, 0.0, 0.0, 1.0}, // black
663         {1.0, 0.0, 0.0, 1.0}, // red
664         {0.0, 1.0, 0.0, 1.0}, // green
665         {1.0, 1.0, 0.0, 1.0}, // yellow
666         {0.0, 0.0, 1.0, 1.0}, // blue
667         {0.0, 1.0, 1.0, 1.0}, // cyan
668         {1.0, 0.0, 1.0, 1.0}, // magenta
669         {1.0, 1.0, 1.0, 1.0}, // white
670         // [515]'s BX_COLOREDTEXT extension
671         {1.0, 1.0, 1.0, 0.5}, // half transparent
672         {0.5, 0.5, 0.5, 1.0}  // half brightness
673         // Black's color table
674         //{1.0, 1.0, 1.0, 1.0},
675         //{1.0, 0.0, 0.0, 1.0},
676         //{0.0, 1.0, 0.0, 1.0},
677         //{0.0, 0.0, 1.0, 1.0},
678         //{1.0, 1.0, 0.0, 1.0},
679         //{0.0, 1.0, 1.0, 1.0},
680         //{1.0, 0.0, 1.0, 1.0},
681         //{0.1, 0.1, 0.1, 1.0}
682 };
683
684 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
685
686 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
687 {
688         float v = r_textbrightness.value;
689         Vector4Copy(string_colors[colorindex], color);
690         Vector4Set(color, (color[0] * (1-v) + v) * r, (color[1] * (1-v) + v) * g, (color[2] * (1-v) + v) * b, color[3] * a);
691         if (shadow)
692         {
693                 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
694                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
695         }
696 }
697
698 float DrawQ_String(float startx, float starty, const char *text, int maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes)
699 {
700         int i, num, shadow, colorindex = STRING_COLOR_DEFAULT;
701         float x = startx, y, s, t, u, v;
702         float *av, *at, *ac;
703         float color[4];
704         int batchcount;
705         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
706         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
707         float color4f[QUADELEMENTS_MAXQUADS*4*4];
708
709         if (maxlen < 1)
710                 maxlen = 1<<30;
711
712         _DrawQ_ProcessDrawFlag(flags);
713
714         R_Mesh_ColorPointer(color4f, 0, 0);
715         R_Mesh_ResetTextureState();
716         R_Mesh_TexBind(0, R_GetTexture(char_texture));
717         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
718         R_Mesh_VertexPointer(vertex3f, 0, 0);
719
720         ac = color4f;
721         at = texcoord2f;
722         av = vertex3f;
723         batchcount = 0;
724
725         for (shadow = r_textshadow.value != 0;shadow >= 0;shadow--)
726         {
727                 if (!outcolor || *outcolor == -1)
728                         colorindex = STRING_COLOR_DEFAULT;
729                 else
730                         colorindex = *outcolor;
731                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
732
733                 x = startx;
734                 y = starty;
735                 if (shadow)
736                 {
737                         x += r_textshadow.value;
738                         y += r_textshadow.value;
739                 }
740                 for (i = 0;i < maxlen && text[i];i++, x += w)
741                 {
742                         if (text[i] == ' ')
743                                 continue;
744                         if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
745                         {
746                                 if (text[i+1] == STRING_COLOR_TAG)
747                                 {
748                                         i++;
749                                         if (text[i] == ' ')
750                                                 continue;
751                                 }
752                                 else if (text[i+1] >= '0' && text[i+1] <= '9')
753                                 {
754                                         colorindex = text[i+1] - '0';
755                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
756                                         i++;
757                                         x -= w;
758                                         continue;
759                                 }
760                         }
761                         num = text[i];
762                         s = (num & 15)*0.0625f + (0.5f / 256.0f);
763                         t = (num >> 4)*0.0625f + (0.5f / 256.0f);
764                         u = 0.0625f - (1.0f / 256.0f);
765                         v = 0.0625f - (1.0f / 256.0f);
766                         ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
767                         ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
768                         ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
769                         ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
770                         at[ 0] = s  ;at[ 1] = t  ;
771                         at[ 2] = s+u;at[ 3] = t  ;
772                         at[ 4] = s+u;at[ 5] = t+v;
773                         at[ 6] = s  ;at[ 7] = t+v;
774                         av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
775                         av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
776                         av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
777                         av[ 9] = x  ;av[10] = y+h;av[11] = 10;
778                         ac += 16;
779                         at += 8;
780                         av += 12;
781                         batchcount++;
782                         if (batchcount >= QUADELEMENTS_MAXQUADS)
783                         {
784                                 if (basealpha >= (1.0f / 255.0f))
785                                 {
786                                         GL_LockArrays(0, batchcount * 4);
787                                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
788                                         GL_LockArrays(0, 0);
789                                 }
790                                 batchcount = 0;
791                                 ac = color4f;
792                                 at = texcoord2f;
793                                 av = vertex3f;
794                         }
795                 }
796         }
797         if (batchcount > 0)
798         {
799                 if (basealpha >= (1.0f / 255.0f))
800                 {
801                         GL_LockArrays(0, batchcount * 4);
802                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
803                         GL_LockArrays(0, 0);
804                 }
805         }
806
807         if (outcolor)
808                 *outcolor = colorindex;
809
810         // note: this relies on the proper text (not shadow) being drawn last
811         return x;
812 }
813
814 #if 0
815 // not used
816 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
817 {
818         int color, numchars = 0;
819         char *outputend2c = output2c + maxoutchars - 2;
820         if (!outcolor || *outcolor == -1)
821                 color = STRING_COLOR_DEFAULT;
822         else
823                 color = *outcolor;
824         if (!maxreadchars)
825                 maxreadchars = 1<<30;
826         textend = text + maxreadchars;
827         while (text != textend && *text)
828         {
829                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
830                 {
831                         if (text[1] == STRING_COLOR_TAG)
832                                 text++;
833                         else if (text[1] >= '0' && text[1] <= '9')
834                         {
835                                 color = text[1] - '0';
836                                 text += 2;
837                                 continue;
838                         }
839                 }
840                 if (output2c >= outputend2c)
841                         break;
842                 *output2c++ = *text++;
843                 *output2c++ = color;
844                 numchars++;
845         }
846         output2c[0] = output2c[1] = 0;
847         if (outcolor)
848                 *outcolor = color;
849         return numchars;
850 }
851 #endif
852
853 void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height, float s1, float t1, float r1, float g1, float b1, float a1, float s2, float t2, float r2, float g2, float b2, float a2, float s3, float t3, float r3, float g3, float b3, float a3, float s4, float t4, float r4, float g4, float b4, float a4, int flags)
854 {
855         float floats[36];
856
857         _DrawQ_ProcessDrawFlag(flags);
858
859         R_Mesh_VertexPointer(floats, 0, 0);
860         R_Mesh_ColorPointer(floats + 20, 0, 0);
861         R_Mesh_ResetTextureState();
862         if (pic)
863         {
864                 if (width == 0)
865                         width = pic->width;
866                 if (height == 0)
867                         height = pic->height;
868                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
869                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
870                 floats[12] = s1;floats[13] = t1;
871                 floats[14] = s2;floats[15] = t2;
872                 floats[16] = s4;floats[17] = t4;
873                 floats[18] = s3;floats[19] = t3;
874         }
875
876         floats[2] = floats[5] = floats[8] = floats[11] = 0;
877         floats[0] = floats[9] = x;
878         floats[1] = floats[4] = y;
879         floats[3] = floats[6] = x + width;
880         floats[7] = floats[10] = y + height;
881         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
882         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
883         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
884         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
885
886         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
887 }
888
889 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
890 {
891         _DrawQ_ProcessDrawFlag(flags);
892
893         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
894         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
895         R_Mesh_ResetTextureState();
896         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
897         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
898
899         GL_LockArrays(0, mesh->num_vertices);
900         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
901         GL_LockArrays(0, 0);
902 }
903
904 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
905 {
906         int num;
907
908         _DrawQ_ProcessDrawFlag(flags);
909
910         GL_Color(1,1,1,1);
911         CHECKGLERROR
912         qglBegin(GL_LINE_LOOP);
913         for (num = 0;num < mesh->num_vertices;num++)
914         {
915                 if (mesh->data_color4f)
916                         GL_Color(mesh->data_color4f[num*4+0], mesh->data_color4f[num*4+1], mesh->data_color4f[num*4+2], mesh->data_color4f[num*4+3]);
917                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
918         }
919         qglEnd();
920         CHECKGLERROR
921 }
922
923 //[515]: this is old, delete
924 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
925 {
926         _DrawQ_ProcessDrawFlag(flags);
927
928         CHECKGLERROR
929         qglLineWidth(width);CHECKGLERROR
930
931         GL_Color(r,g,b,alpha);
932         CHECKGLERROR
933         qglBegin(GL_LINES);
934         qglVertex2f(x1, y1);
935         qglVertex2f(x2, y2);
936         qglEnd();
937         CHECKGLERROR
938 }
939
940 void DrawQ_SetClipArea(float x, float y, float width, float height)
941 {
942         _DrawQ_Setup();
943
944         // We have to convert the con coords into real coords
945         // OGL uses top to bottom
946         GL_Scissor((int)(x * ((float)vid.width / vid_conwidth.integer)), (int)(y * ((float) vid.height / vid_conheight.integer)), (int)(width * ((float)vid.width / vid_conwidth.integer)), (int)(height * ((float)vid.height / vid_conheight.integer)));
947
948         GL_ScissorTest(true);
949 }
950
951 void DrawQ_ResetClipArea(void)
952 {
953         _DrawQ_Setup();
954         GL_ScissorTest(false);
955 }
956
957 void DrawQ_Finish(void)
958 {
959         r_refdef.draw2dstage = false;
960 }
961
962 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
963 void R_DrawGamma(void)
964 {
965         float c[4];
966         if (!vid_usinghwgamma)
967         {
968                 // all the blends ignore depth
969                 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
970                 R_Mesh_ColorPointer(NULL, 0, 0);
971                 R_Mesh_ResetTextureState();
972                 GL_DepthMask(true);
973                 GL_DepthRange(0, 1);
974                 GL_PolygonOffset(0, 0);
975                 GL_DepthTest(false);
976                 if (v_color_enable.integer)
977                 {
978                         c[0] = v_color_white_r.value;
979                         c[1] = v_color_white_g.value;
980                         c[2] = v_color_white_b.value;
981                 }
982                 else
983                         c[0] = c[1] = c[2] = v_contrast.value;
984                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
985                 {
986                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
987                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
988                         {
989                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
990                                 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
991                                 VectorScale(c, 0.5, c);
992                         }
993                 }
994                 if (v_color_enable.integer)
995                 {
996                         c[0] = v_color_black_r.value;
997                         c[1] = v_color_black_g.value;
998                         c[2] = v_color_black_b.value;
999                 }
1000                 else
1001                         c[0] = c[1] = c[2] = v_brightness.value;
1002                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1003                 {
1004                         GL_BlendFunc(GL_ONE, GL_ONE);
1005                         GL_Color(c[0], c[1], c[2], 1);
1006                         R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1007                 }
1008         }
1009 }
1010