every malloc/calloc/free converted to qmalloc/qfree with tracking (memstats command...
[divverent/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 // draw.c -- this is the only file outside the refresh that touches the
22 // vid buffer
23
24 #include "quakedef.h"
25
26 #define GL_COLOR_INDEX8_EXT     0x80E5
27
28 cvar_t          qsg_version = {"qsg_version", "1"};
29 cvar_t          scr_conalpha = {"scr_conalpha", "1"};
30
31 byte            *draw_chars;                            // 8*8 graphic characters
32 qpic_t          *draw_disc;
33
34 int                     char_texture;
35
36 typedef struct
37 {
38         int             texnum;
39         float   sl, tl, sh, th;
40 } glpic_t;
41
42 int                     conbacktexnum;
43
44 /*
45 =============================================================================
46
47   scrap allocation
48
49   Allocate all the little status bar obejcts into a single texture
50   to crutch up stupid hardware / drivers
51
52 =============================================================================
53 */
54
55 /*
56 #define MAX_SCRAPS              2
57 #define BLOCK_WIDTH             256
58 #define BLOCK_HEIGHT    256
59
60 int                     scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH];
61 byte            scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4];
62 qboolean        scrap_dirty;
63
64 // returns a texture number and the position inside it
65 int Scrap_AllocBlock (int w, int h, int *x, int *y)
66 {
67         int             i, j;
68         int             best, best2;
69         int             texnum;
70
71         for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
72         {
73                 best = BLOCK_HEIGHT;
74
75                 for (i=0 ; i<BLOCK_WIDTH-w ; i++)
76                 {
77                         best2 = 0;
78
79                         for (j=0 ; j<w ; j++)
80                         {
81                                 if (scrap_allocated[texnum][i+j] >= best)
82                                         break;
83                                 if (scrap_allocated[texnum][i+j] > best2)
84                                         best2 = scrap_allocated[texnum][i+j];
85                         }
86                         if (j == w)
87                         {       // this is a valid spot
88                                 *x = i;
89                                 *y = best = best2;
90                         }
91                 }
92
93                 if (best + h > BLOCK_HEIGHT)
94                         continue;
95
96                 for (i=0 ; i<w ; i++)
97                         scrap_allocated[texnum][*x + i] = best + h;
98
99                 return texnum;
100         }
101
102         Sys_Error ("Scrap_AllocBlock: full");
103         return 0;
104 }
105
106 int     scrap_uploads;
107 int scraptexnum[MAX_SCRAPS];
108
109 void Scrap_Upload (void)
110 {
111         int             texnum;
112
113         scrap_uploads++;
114
115         for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
116                 scraptexnum[texnum] = GL_LoadTexture (va("scrapslot%d", texnum), BLOCK_WIDTH, BLOCK_HEIGHT, scrap_texels[texnum], false, true, 1);
117         scrap_dirty = false;
118 }
119 */
120
121 //=============================================================================
122 /* Support Routines */
123
124 typedef struct cachepic_s
125 {
126         char            name[MAX_QPATH];
127         qpic_t          pic;
128         byte            padding[32];    // for appended glpic
129 } cachepic_t;
130
131 #define MAX_CACHED_PICS         128
132 cachepic_t      menu_cachepics[MAX_CACHED_PICS];
133 int                     menu_numcachepics;
134
135 byte            menuplyr_pixels[4096];
136
137 int             pic_texels;
138 int             pic_count;
139
140 extern int GL_LoadPicTexture (qpic_t *pic);
141
142 qpic_t *Draw_PicFromWad (char *name)
143 {
144         qpic_t  *p;
145         glpic_t *gl;
146
147         p = W_GetLumpName (name);
148         gl = (glpic_t *)p->data;
149
150         // load little ones into the scrap
151         /*
152         if (p->width < 64 && p->height < 64)
153         {
154                 int             x, y;
155                 int             i, j, k;
156                 int             texnum;
157
158                 texnum = Scrap_AllocBlock (p->width, p->height, &x, &y);
159                 scrap_dirty = true;
160                 k = 0;
161                 for (i=0 ; i<p->height ; i++)
162                         for (j=0 ; j<p->width ; j++, k++)
163                                 scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k];
164                 if (!scraptexnum[texnum])
165                         scraptexnum[texnum] = GL_LoadTexture (va("scrapslot%d", texnum), BLOCK_WIDTH, BLOCK_HEIGHT, scrap_texels[texnum], false, true, 1);
166                 gl->texnum = scraptexnum[texnum];
167                 gl->sl = (x+0.01)/(float)BLOCK_WIDTH;
168                 gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH;
169                 gl->tl = (y+0.01)/(float)BLOCK_WIDTH;
170                 gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH;
171
172                 pic_count++;
173                 pic_texels += p->width*p->height;
174         }
175         else
176         {
177         */
178                 gl->texnum = GL_LoadPicTexture (p);
179                 gl->sl = 0;
180                 gl->sh = 1;
181                 gl->tl = 0;
182                 gl->th = 1;
183         //}
184         return p;
185 }
186
187
188 /*
189 ================
190 Draw_CachePic
191 ================
192 */
193 qpic_t  *Draw_CachePic (char *path)
194 {
195         cachepic_t      *pic;
196         int                     i;
197         qpic_t          *dat;
198         glpic_t         *gl;
199
200         for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++)
201                 if (!strcmp (path, pic->name))
202                         return &pic->pic;
203
204         if (menu_numcachepics == MAX_CACHED_PICS)
205                 Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
206         menu_numcachepics++;
207         strcpy (pic->name, path);
208
209 //
210 // load the pic from disk
211 //
212         dat = (qpic_t *)COM_LoadMallocFile (path, false);
213         if (!dat)
214                 Sys_Error ("Draw_CachePic: failed to load %s", path);
215         SwapPic (dat);
216
217         // HACK HACK HACK --- we need to keep the bytes for
218         // the translatable player picture just for the menu
219         // configuration dialog
220         if (!strcmp (path, "gfx/menuplyr.lmp"))
221                 memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
222
223         pic->pic.width = dat->width;
224         pic->pic.height = dat->height;
225
226         gl = (glpic_t *)pic->pic.data;
227         gl->texnum = loadtextureimage(path, 0, 0, false, false);
228         if (!gl->texnum)
229                 gl->texnum = GL_LoadPicTexture (dat);
230         gl->sl = 0;
231         gl->sh = 1;
232         gl->tl = 0;
233         gl->th = 1;
234
235         qfree(dat);
236
237         return &pic->pic;
238 }
239
240 extern void LoadSky_f(void);
241
242 /*
243 ===============
244 Draw_Init
245 ===============
246 */
247 void rmain_registercvars();
248 extern int buildnumber;
249
250 void gl_draw_start()
251 {
252         int             i;
253
254         char_texture = loadtextureimage ("conchars", 0, 0, false, false);
255         if (!char_texture)
256         {
257                 draw_chars = W_GetLumpName ("conchars");
258                 for (i=0 ; i<128*128 ; i++)
259                         if (draw_chars[i] == 0)
260                                 draw_chars[i] = 255;    // proper transparent color
261
262                 // now turn them into textures
263                 char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1);
264         }
265
266         conbacktexnum = loadtextureimage("gfx/conback", 0, 0, false, false);
267
268 //      memset(scraptexnum, 0, sizeof(scraptexnum));
269
270         // get the other pics we need
271         draw_disc = Draw_PicFromWad ("disc");
272 }
273
274 void gl_draw_shutdown()
275 {
276 }
277
278 char engineversion[40];
279 int engineversionx, engineversiony;
280
281 extern void GL_Textures_Init();
282 void GL_Draw_Init (void)
283 {
284         int i;
285         Cvar_RegisterVariable (&qsg_version);
286         Cvar_RegisterVariable (&scr_conalpha);
287
288         Cmd_AddCommand ("loadsky", &LoadSky_f);
289
290 #if defined(__linux__)
291         sprintf (engineversion, "DarkPlaces Linux   GL %.2f build %3i", (float) VERSION, buildnumber);
292 #elif defined(WIN32)
293         sprintf (engineversion, "DarkPlaces Windows GL %.2f build %3i", (float) VERSION, buildnumber);
294 #else
295         sprintf (engineversion, "DarkPlaces Unknown GL %.2f build %3i", (float) VERSION, buildnumber);
296 #endif
297         for (i = 0;i < 40 && engineversion[i];i++)
298                 engineversion[i] += 0x80; // shift to orange
299         engineversionx = vid.width - strlen(engineversion) * 8 - 8;
300         engineversiony = vid.height - 8;
301
302         GL_Textures_Init();
303         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown);
304 }
305
306 /*
307 ================
308 Draw_Character
309
310 Draws one 8*8 graphics character with 0 being transparent.
311 It can be clipped to the top of the screen to allow the console to be
312 smoothly scrolled off.
313 ================
314 */
315 void Draw_Character (int x, int y, int num)
316 {
317         int                             row, col;
318         float                   frow, fcol, size;
319
320         if (num == 32)
321                 return;         // space
322
323         num &= 255;
324         
325         if (y <= -8)
326                 return;                 // totally off screen
327
328         row = num>>4;
329         col = num&15;
330
331         frow = row*0.0625;
332         fcol = col*0.0625;
333         size = 0.0625;
334
335         if (!r_render.value)
336                 return;
337         glBindTexture(GL_TEXTURE_2D, char_texture);
338         // LordHavoc: NEAREST mode on text if not scaling up
339         if (glwidth < (int) vid.width)
340         {
341                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
342                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
343         }
344
345         glColor3f(1,1,1);
346         glBegin (GL_QUADS);
347         glTexCoord2f (fcol, frow);
348         glVertex2f (x, y);
349         glTexCoord2f (fcol + size, frow);
350         glVertex2f (x+8, y);
351         glTexCoord2f (fcol + size, frow + size);
352         glVertex2f (x+8, y+8);
353         glTexCoord2f (fcol, frow + size);
354         glVertex2f (x, y+8);
355         glEnd ();
356
357         // LordHavoc: revert to LINEAR mode
358         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
359         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
360 }
361
362 /*
363 ================
364 Draw_String
365 ================
366 */
367 // LordHavoc: sped this up a lot, and added maxlen
368 void Draw_String (int x, int y, char *str, int maxlen)
369 {
370         int num;
371         float frow, fcol;
372         if (!r_render.value)
373                 return;
374         if (y <= -8 || y >= (int) vid.height || x >= (int) vid.width || *str == 0) // completely offscreen or no text to print
375                 return;
376         if (maxlen < 1)
377                 maxlen = strlen(str);
378         else if (maxlen > (int) strlen(str))
379                 maxlen = strlen(str);
380         glBindTexture(GL_TEXTURE_2D, char_texture);
381
382         // LordHavoc: NEAREST mode on text if not scaling up
383         if (glwidth < (int) vid.width)
384         {
385                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
386                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
387         }
388
389         glColor3f(1,1,1);
390         glBegin (GL_QUADS);
391         while (maxlen-- && x < (int) vid.width) // stop rendering when out of characters or room
392         {
393                 if ((num = *str++) != 32) // skip spaces
394                 {
395                         frow = (float) ((int) num >> 4)*0.0625;
396                         fcol = (float) ((int) num & 15)*0.0625;
397                         glTexCoord2f (fcol         , frow         );glVertex2f (x, y);
398                         glTexCoord2f (fcol + 0.0625, frow         );glVertex2f (x+8, y);
399                         glTexCoord2f (fcol + 0.0625, frow + 0.0625);glVertex2f (x+8, y+8);
400                         glTexCoord2f (fcol         , frow + 0.0625);glVertex2f (x, y+8);
401                 }
402                 x += 8;
403         }
404         glEnd ();
405
406         // LordHavoc: revert to LINEAR mode
407         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
408         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
409 }
410
411 void Draw_GenericPic (int texnum, float red, float green, float blue, float alpha, int x, int y, int width, int height)
412 {
413         if (!r_render.value)
414                 return;
415         glColor4f(red,green,blue,alpha);
416         glBindTexture(GL_TEXTURE_2D, texnum);
417         glBegin (GL_QUADS);
418         glTexCoord2f (0, 0);glVertex2f (x, y);
419         glTexCoord2f (1, 0);glVertex2f (x+width, y);
420         glTexCoord2f (1, 1);glVertex2f (x+width, y+height);
421         glTexCoord2f (0, 1);glVertex2f (x, y+height);
422         glEnd ();
423 }
424
425 /*
426 =============
427 Draw_AlphaPic
428 =============
429 */
430 void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha)
431 {
432         glpic_t                 *gl;
433
434 //      if (scrap_dirty)
435 //              Scrap_Upload ();
436         gl = (glpic_t *)pic->data;
437         if (!r_render.value)
438                 return;
439         glColor4f(1,1,1,alpha);
440         glBindTexture(GL_TEXTURE_2D, gl->texnum);
441         glBegin (GL_QUADS);
442         glTexCoord2f (gl->sl, gl->tl);glVertex2f (x, y);
443         glTexCoord2f (gl->sh, gl->tl);glVertex2f (x+pic->width, y);
444         glTexCoord2f (gl->sh, gl->th);glVertex2f (x+pic->width, y+pic->height);
445         glTexCoord2f (gl->sl, gl->th);glVertex2f (x, y+pic->height);
446         glEnd ();
447 }
448
449
450 /*
451 =============
452 Draw_Pic
453 =============
454 */
455 void Draw_Pic (int x, int y, qpic_t *pic)
456 {
457         glpic_t                 *gl;
458
459 //      if (scrap_dirty)
460 //              Scrap_Upload ();
461         gl = (glpic_t *)pic->data;
462         if (!r_render.value)
463                 return;
464         glColor3f(1,1,1);
465         glBindTexture(GL_TEXTURE_2D, gl->texnum);
466         glBegin (GL_QUADS);
467         glTexCoord2f (gl->sl, gl->tl);glVertex2f (x, y);
468         glTexCoord2f (gl->sh, gl->tl);glVertex2f (x+pic->width, y);
469         glTexCoord2f (gl->sh, gl->th);glVertex2f (x+pic->width, y+pic->height);
470         glTexCoord2f (gl->sl, gl->th);glVertex2f (x, y+pic->height);
471         glEnd ();
472 }
473
474
475 /*
476 =============
477 Draw_PicTranslate
478
479 Only used for the player color selection menu
480 =============
481 */
482 void Draw_PicTranslate (int x, int y, qpic_t *pic, byte *translation)
483 {
484         int                             i, c;
485         byte                    *trans, *src, *dest;
486
487         c = pic->width * pic->height;
488         src = menuplyr_pixels;
489         dest = trans = qmalloc(c);
490         for (i = 0;i < c;i++)
491                 *dest++ = translation[*src++];
492
493         c = GL_LoadTexture ("translatedplayerpic", pic->width, pic->height, trans, false, true, 1);
494         qfree(trans);
495
496         if (!r_render.value)
497                 return;
498         Draw_GenericPic (c, 1,1,1,1, x, y, pic->width, pic->height);
499         /*
500         glBindTexture(GL_TEXTURE_2D, c);
501         glColor3f(1,1,1);
502         glBegin (GL_QUADS);
503         glTexCoord2f (0, 0);glVertex2f (x, y);
504         glTexCoord2f (1, 0);glVertex2f (x+pic->width, y);
505         glTexCoord2f (1, 1);glVertex2f (x+pic->width, y+pic->height);
506         glTexCoord2f (0, 1);glVertex2f (x, y+pic->height);
507         glEnd ();
508         */
509 }
510
511
512 /*
513 ================
514 Draw_ConsoleBackground
515
516 ================
517 */
518 void Draw_ConsoleBackground (int lines)
519 {
520         Draw_GenericPic (conbacktexnum, 1,1,1,scr_conalpha.value*lines/vid.height, 0, lines - vid.height, vid.width, vid.height);
521         // LordHavoc: draw version
522         Draw_String(engineversionx, lines - vid.height + engineversiony, engineversion, 9999);
523 }
524
525 /*
526 =============
527 Draw_Fill
528
529 Fills a box of pixels with a single color
530 =============
531 */
532 void Draw_Fill (int x, int y, int w, int h, int c)
533 {
534         if (!r_render.value)
535                 return;
536         glDisable (GL_TEXTURE_2D);
537         glColor3f (host_basepal[c*3]/255.0, host_basepal[c*3+1]/255.0, host_basepal[c*3+2]/255.0);
538
539         glBegin (GL_QUADS);
540
541         glVertex2f (x,y);
542         glVertex2f (x+w, y);
543         glVertex2f (x+w, y+h);
544         glVertex2f (x, y+h);
545
546         glEnd ();
547         glColor3f(1,1,1);
548         glEnable (GL_TEXTURE_2D);
549 }
550 //=============================================================================
551
552 //=============================================================================
553
554 /*
555 ================
556 GL_Set2D
557
558 Setup as if the screen was 320*200
559 ================
560 */
561 void GL_Set2D (void)
562 {
563         if (!r_render.value)
564                 return;
565         glViewport (glx, gly, glwidth, glheight);
566
567         glMatrixMode(GL_PROJECTION);
568     glLoadIdentity ();
569         glOrtho  (0, vid.width, vid.height, 0, -99999, 99999);
570
571         glMatrixMode(GL_MODELVIEW);
572     glLoadIdentity ();
573
574         glDisable (GL_DEPTH_TEST);
575         glDisable (GL_CULL_FACE);
576         glEnable (GL_BLEND);
577         glDisable (GL_ALPHA_TEST);
578         glEnable(GL_TEXTURE_2D);
579
580         // LordHavoc: added this
581         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
582         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
583
584         glColor3f(1,1,1);
585 }
586
587 // LordHavoc: SHOWLMP stuff
588 #define SHOWLMP_MAXLABELS 256
589 typedef struct showlmp_s
590 {
591         qboolean        isactive;
592         float           x;
593         float           y;
594         char            label[32];
595         char            pic[128];
596 } showlmp_t;
597
598 showlmp_t showlmp[SHOWLMP_MAXLABELS];
599
600 void SHOWLMP_decodehide()
601 {
602         int i;
603         byte *lmplabel;
604         lmplabel = MSG_ReadString();
605         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
606                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
607                 {
608                         showlmp[i].isactive = false;
609                         return;
610                 }
611 }
612
613 void SHOWLMP_decodeshow()
614 {
615         int i, k;
616         byte lmplabel[256], picname[256];
617         float x, y;
618         strcpy(lmplabel,MSG_ReadString());
619         strcpy(picname, MSG_ReadString());
620         x = MSG_ReadByte();
621         y = MSG_ReadByte();
622         k = -1;
623         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
624                 if (showlmp[i].isactive)
625                 {
626                         if (strcmp(showlmp[i].label, lmplabel) == 0)
627                         {
628                                 k = i;
629                                 break; // drop out to replace it
630                         }
631                 }
632                 else if (k < 0) // find first empty one to replace
633                         k = i;
634         if (k < 0)
635                 return; // none found to replace
636         // change existing one
637         showlmp[k].isactive = true;
638         strcpy(showlmp[k].label, lmplabel);
639         strcpy(showlmp[k].pic, picname);
640         showlmp[k].x = x;
641         showlmp[k].y = y;
642 }
643
644 void SHOWLMP_drawall()
645 {
646         int i;
647         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
648                 if (showlmp[i].isactive)
649                         Draw_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic));
650 }
651
652 void SHOWLMP_clear()
653 {
654         int i;
655         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
656                 showlmp[i].isactive = false;
657 }