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