minor redesign of console loading/drawing, cruft removal, change to Draw_GenericPic...
[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 = GL_LoadPicTexture (dat);
226         gl->sl = 0;
227         gl->sh = 1;
228         gl->tl = 0;
229         gl->th = 1;
230
231         return &pic->pic;
232 }
233
234 extern void LoadSky_f(void);
235
236 extern char *QSG_EXTENSIONS;
237
238 /*
239 ===============
240 Draw_Init
241 ===============
242 */
243 void rmain_registercvars();
244 extern int buildnumber;
245
246 void gl_draw_start()
247 {
248         int             i;
249
250         char_texture = loadtextureimage ("conchars", 0, 0, false, false);
251         if (!char_texture)
252         {
253                 draw_chars = W_GetLumpName ("conchars");
254                 for (i=0 ; i<128*128 ; i++)
255                         if (draw_chars[i] == 0)
256                                 draw_chars[i] = 255;    // proper transparent color
257
258                 // now turn them into textures
259                 char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1);
260         }
261
262         conbacktexnum = loadtextureimage("gfx/conback", 0, 0, false, false);
263
264         memset(scraptexnum, 0, sizeof(scraptexnum));
265
266         // get the other pics we need
267         draw_disc = Draw_PicFromWad ("disc");
268 }
269
270 void gl_draw_shutdown()
271 {
272 }
273
274 char engineversion[40];
275 int engineversionx, engineversiony;
276
277 extern void GL_Textures_Init();
278 void GL_Draw_Init (void)
279 {
280         int i;
281         Cvar_RegisterVariable (&qsg_version);
282         Cvar_RegisterVariable (&scr_conalpha);
283
284         Cmd_AddCommand ("loadsky", &LoadSky_f);
285
286 #ifdef NEHAHRA
287 #if defined(__linux__)
288         sprintf (engineversion, "DPNehahra Linux   GL %.2f build %3i", (float) VERSION, buildnumber);
289 #elif defined(WIN32)
290         sprintf (engineversion, "DPNehahra Windows GL %.2f build %3i", (float) VERSION, buildnumber);
291 #else
292         sprintf (engineversion, "DPNehahra Unknown GL %.2f build %3i", (float) VERSION, buildnumber);
293 #endif
294 #else
295 #if defined(__linux__)
296         sprintf (engineversion, "DarkPlaces Linux   GL %.2f build %3i", (float) VERSION, buildnumber);
297 #elif defined(WIN32)
298         sprintf (engineversion, "DarkPlaces Windows GL %.2f build %3i", (float) VERSION, buildnumber);
299 #else
300         sprintf (engineversion, "DarkPlaces Unknown GL %.2f build %3i", (float) VERSION, buildnumber);
301 #endif
302 #endif
303         for (i = 0;i < 40 && engineversion[i];i++)
304                 engineversion[i] += 0x80; // shift to orange
305         engineversionx = vid.width - strlen(engineversion) * 8 - 8;
306         engineversiony = vid.height - 8;
307
308         GL_Textures_Init();
309         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown);
310 }
311
312 /*
313 ================
314 Draw_Character
315
316 Draws one 8*8 graphics character with 0 being transparent.
317 It can be clipped to the top of the screen to allow the console to be
318 smoothly scrolled off.
319 ================
320 */
321 void Draw_Character (int x, int y, int num)
322 {
323         int                             row, col;
324         float                   frow, fcol, size;
325
326         if (num == 32)
327                 return;         // space
328
329         num &= 255;
330         
331         if (y <= -8)
332                 return;                 // totally off screen
333
334         row = num>>4;
335         col = num&15;
336
337         frow = row*0.0625;
338         fcol = col*0.0625;
339         size = 0.0625;
340
341         if (!r_render.value)
342                 return;
343         glBindTexture(GL_TEXTURE_2D, char_texture);
344         // LordHavoc: NEAREST mode on text if not scaling up
345         if ((int) vid.width < glwidth)
346         {
347                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
348                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
349         }
350
351         glColor3f(1,1,1);
352         glBegin (GL_QUADS);
353         glTexCoord2f (fcol, frow);
354         glVertex2f (x, y);
355         glTexCoord2f (fcol + size, frow);
356         glVertex2f (x+8, y);
357         glTexCoord2f (fcol + size, frow + size);
358         glVertex2f (x+8, y+8);
359         glTexCoord2f (fcol, frow + size);
360         glVertex2f (x, y+8);
361         glEnd ();
362
363         // LordHavoc: revert to LINEAR mode
364         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
365         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
366 }
367
368 /*
369 ================
370 Draw_String
371 ================
372 */
373 // LordHavoc: sped this up a lot, and added maxlen
374 void Draw_String (int x, int y, char *str, int maxlen)
375 {
376         int num;
377         float frow, fcol;
378         if (!r_render.value)
379                 return;
380         if (y <= -8 || y >= (int) vid.height || x >= (int) vid.width || *str == 0) // completely offscreen or no text to print
381                 return;
382         if (maxlen < 1)
383                 maxlen = strlen(str);
384         else if (maxlen > (int) strlen(str))
385                 maxlen = strlen(str);
386         glBindTexture(GL_TEXTURE_2D, char_texture);
387
388         // LordHavoc: NEAREST mode on text if not scaling up
389         if ((int) vid.width < glwidth)
390         {
391                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
392                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
393         }
394
395         glColor3f(1,1,1);
396         glBegin (GL_QUADS);
397         while (maxlen-- && x < (int) vid.width) // stop rendering when out of characters or room
398         {
399                 if ((num = *str++) != 32) // skip spaces
400                 {
401                         frow = (float) ((int) num >> 4)*0.0625;
402                         fcol = (float) ((int) num & 15)*0.0625;
403                         glTexCoord2f (fcol         , frow         );glVertex2f (x, y);
404                         glTexCoord2f (fcol + 0.0625, frow         );glVertex2f (x+8, y);
405                         glTexCoord2f (fcol + 0.0625, frow + 0.0625);glVertex2f (x+8, y+8);
406                         glTexCoord2f (fcol         , frow + 0.0625);glVertex2f (x, y+8);
407                 }
408                 x += 8;
409         }
410         glEnd ();
411
412         // LordHavoc: revert to LINEAR mode
413         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
414         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
415 }
416
417 void Draw_GenericPic (int texnum, float red, float green, float blue, float alpha, int x, int y, int width, int height)
418 {
419         if (!r_render.value)
420                 return;
421         glColor4f(red,green,blue,alpha);
422         glBindTexture(GL_TEXTURE_2D, texnum);
423         glBegin (GL_QUADS);
424         glTexCoord2f (0, 0);glVertex2f (x, y);
425         glTexCoord2f (1, 0);glVertex2f (x+width, y);
426         glTexCoord2f (1, 1);glVertex2f (x+width, y+height);
427         glTexCoord2f (0, 1);glVertex2f (x, y+height);
428         glEnd ();
429 }
430
431 /*
432 =============
433 Draw_AlphaPic
434 =============
435 */
436 void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha)
437 {
438         glpic_t                 *gl;
439
440         if (scrap_dirty)
441                 Scrap_Upload ();
442         gl = (glpic_t *)pic->data;
443         if (!r_render.value)
444                 return;
445         glColor4f(1,1,1,alpha);
446         glBindTexture(GL_TEXTURE_2D, gl->texnum);
447         glBegin (GL_QUADS);
448         glTexCoord2f (gl->sl, gl->tl);glVertex2f (x, y);
449         glTexCoord2f (gl->sh, gl->tl);glVertex2f (x+pic->width, y);
450         glTexCoord2f (gl->sh, gl->th);glVertex2f (x+pic->width, y+pic->height);
451         glTexCoord2f (gl->sl, gl->th);glVertex2f (x, y+pic->height);
452         glEnd ();
453 }
454
455
456 /*
457 =============
458 Draw_Pic
459 =============
460 */
461 void Draw_Pic (int x, int y, qpic_t *pic)
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         glColor3f(1,1,1);
471         glBindTexture(GL_TEXTURE_2D, gl->texnum);
472         glBegin (GL_QUADS);
473         glTexCoord2f (gl->sl, gl->tl);glVertex2f (x, y);
474         glTexCoord2f (gl->sh, gl->tl);glVertex2f (x+pic->width, y);
475         glTexCoord2f (gl->sh, gl->th);glVertex2f (x+pic->width, y+pic->height);
476         glTexCoord2f (gl->sl, gl->th);glVertex2f (x, y+pic->height);
477         glEnd ();
478 }
479
480
481 /*
482 =============
483 Draw_PicTranslate
484
485 Only used for the player color selection menu
486 =============
487 */
488 void Draw_PicTranslate (int x, int y, qpic_t *pic, byte *translation)
489 {
490         int                             i, c;
491         byte                    *trans, *src, *dest;
492
493         c = pic->width * pic->height;
494         src = menuplyr_pixels;
495         dest = trans = malloc(c);
496         for (i = 0;i < c;i++)
497                 *dest++ = translation[*src++];
498
499         c = GL_LoadTexture ("translatedplayerpic", pic->width, pic->height, trans, false, true, 1);
500         free(trans);
501
502         if (!r_render.value)
503                 return;
504         Draw_GenericPic (c, 1,1,1,1, x, y, pic->width, pic->height);
505         /*
506         glBindTexture(GL_TEXTURE_2D, c);
507         glColor3f(1,1,1);
508         glBegin (GL_QUADS);
509         glTexCoord2f (0, 0);glVertex2f (x, y);
510         glTexCoord2f (1, 0);glVertex2f (x+pic->width, y);
511         glTexCoord2f (1, 1);glVertex2f (x+pic->width, y+pic->height);
512         glTexCoord2f (0, 1);glVertex2f (x, y+pic->height);
513         glEnd ();
514         */
515 }
516
517
518 /*
519 ================
520 Draw_ConsoleBackground
521
522 ================
523 */
524 void Draw_ConsoleBackground (int lines)
525 {
526         Draw_GenericPic (conbacktexnum, 1,1,1,scr_conalpha.value*lines/vid.height, 0, lines - vid.height, vid.width, vid.height);
527         // LordHavoc: draw version
528         Draw_String(engineversionx, lines - vid.height + engineversiony, engineversion, 9999);
529 }
530
531 /*
532 =============
533 Draw_Fill
534
535 Fills a box of pixels with a single color
536 =============
537 */
538 void Draw_Fill (int x, int y, int w, int h, int c)
539 {
540         if (!r_render.value)
541                 return;
542         glDisable (GL_TEXTURE_2D);
543         glColor3f (host_basepal[c*3]/255.0, host_basepal[c*3+1]/255.0, host_basepal[c*3+2]/255.0);
544
545         glBegin (GL_QUADS);
546
547         glVertex2f (x,y);
548         glVertex2f (x+w, y);
549         glVertex2f (x+w, y+h);
550         glVertex2f (x, y+h);
551
552         glEnd ();
553         glColor3f(1,1,1);
554         glEnable (GL_TEXTURE_2D);
555 }
556 //=============================================================================
557
558 //=============================================================================
559
560 /*
561 ================
562 GL_Set2D
563
564 Setup as if the screen was 320*200
565 ================
566 */
567 void GL_Set2D (void)
568 {
569         if (!r_render.value)
570                 return;
571         glViewport (glx, gly, glwidth, glheight);
572
573         glMatrixMode(GL_PROJECTION);
574     glLoadIdentity ();
575         glOrtho  (0, vid.width, vid.height, 0, -99999, 99999);
576
577         glMatrixMode(GL_MODELVIEW);
578     glLoadIdentity ();
579
580         glDisable (GL_DEPTH_TEST);
581         glDisable (GL_CULL_FACE);
582         glEnable (GL_BLEND);
583         glDisable (GL_ALPHA_TEST);
584         glEnable(GL_TEXTURE_2D);
585
586         // LordHavoc: added this
587         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
588         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
589
590         glColor3f(1,1,1);
591 }
592
593 // LordHavoc: SHOWLMP stuff
594 #define SHOWLMP_MAXLABELS 256
595 typedef struct showlmp_s
596 {
597         qboolean        isactive;
598         float           x;
599         float           y;
600         char            label[32];
601         char            pic[128];
602 } showlmp_t;
603
604 showlmp_t showlmp[SHOWLMP_MAXLABELS];
605
606 void SHOWLMP_decodehide()
607 {
608         int i;
609         byte *lmplabel;
610         lmplabel = MSG_ReadString();
611         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
612                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
613                 {
614                         showlmp[i].isactive = false;
615                         return;
616                 }
617 }
618
619 void SHOWLMP_decodeshow()
620 {
621         int i, k;
622         byte lmplabel[256], picname[256];
623         float x, y;
624         strcpy(lmplabel,MSG_ReadString());
625         strcpy(picname, MSG_ReadString());
626         x = MSG_ReadByte();
627         y = MSG_ReadByte();
628         k = -1;
629         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
630                 if (showlmp[i].isactive)
631                 {
632                         if (strcmp(showlmp[i].label, lmplabel) == 0)
633                         {
634                                 k = i;
635                                 break; // drop out to replace it
636                         }
637                 }
638                 else if (k < 0) // find first empty one to replace
639                         k = i;
640         if (k < 0)
641                 return; // none found to replace
642         // change existing one
643         showlmp[k].isactive = true;
644         strcpy(showlmp[k].label, lmplabel);
645         strcpy(showlmp[k].pic, picname);
646         showlmp[k].x = x;
647         showlmp[k].y = y;
648 }
649
650 void SHOWLMP_drawall()
651 {
652         int i;
653         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
654                 if (showlmp[i].isactive)
655                         Draw_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic));
656 }
657
658 void SHOWLMP_clear()
659 {
660         int i;
661         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
662                 showlmp[i].isactive = false;
663 }