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