rewrote memory system entirely (hunk, cache, and zone are gone, memory pools replaced...
[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
23 //#define GL_COLOR_INDEX8_EXT     0x80E5
24
25 cvar_t          scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1"};
26
27 rtexture_t      *char_texture;
28
29 typedef struct
30 {
31         rtexture_t      *tex;
32 } glpic_t;
33
34 rtexture_t      *conbacktex;
35
36 //=============================================================================
37 /* Support Routines */
38
39 typedef struct cachepic_s
40 {
41         char            name[MAX_QPATH];
42         // FIXME: qpic is evil
43         qpic_t          pic;
44         byte            padding[32];    // for appended glpic
45 }
46 cachepic_t;
47
48 #define MAX_CACHED_PICS         256
49 cachepic_t      menu_cachepics[MAX_CACHED_PICS];
50 int                     menu_numcachepics;
51
52 byte            menuplyr_pixels[4096];
53
54 int                     pic_texels;
55 int                     pic_count;
56
57 rtexturepool_t *drawtexturepool;
58
59 /*
60 ================
61 Draw_CachePic
62 ================
63 */
64 // FIXME: qpic is evil
65 qpic_t  *Draw_CachePic (char *path)
66 {
67         cachepic_t      *pic;
68         int                     i;
69         qpic_t          *dat;
70         glpic_t         *gl;
71         rtexture_t      *tex;
72
73         for (pic = menu_cachepics, i = 0;i < menu_numcachepics;pic++, i++)
74                 if (!strcmp (path, pic->name))
75                         return &pic->pic;
76
77         if (menu_numcachepics == MAX_CACHED_PICS)
78                 Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
79         menu_numcachepics++;
80         strcpy (pic->name, path);
81
82         // FIXME: move this to menu code
83         // HACK HACK HACK --- we need to keep the bytes for
84         // the translatable player picture just for the menu
85         // configuration dialog
86         if (!strcmp (path, "gfx/menuplyr.lmp"))
87         {
88                 dat = (qpic_t *)COM_LoadFile (path, false);
89                 if (!dat)
90                         Sys_Error("unable to load gfx/menuplyr.lmp");
91                 SwapPic (dat);
92
93                 memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
94         }
95
96         // load the pic from disk
97         if ((tex = loadtextureimage(drawtexturepool, path, 0, 0, false, false, true)))
98         {
99                 // load the pic from an image file
100                 pic->pic.width = image_width;
101                 pic->pic.height = image_height;
102                 gl = (glpic_t *)pic->pic.data;
103                 gl->tex = tex;
104                 return &pic->pic;
105         }
106         else
107         {
108                 qpic_t *p;
109                 // load the pic from gfx.wad
110                 p = W_GetLumpName (path);
111                 if (!p)
112                         Sys_Error ("Draw_CachePic: failed to load %s", path);
113                 pic->pic.width = p->width;
114                 pic->pic.height = p->height;
115                 gl = (glpic_t *)pic->pic.data;
116                 gl->tex = R_LoadTexture (drawtexturepool, path, p->width, p->height, p->data, TEXTYPE_QPALETTE, TEXF_ALPHA | TEXF_PRECACHE);
117                 return &pic->pic;
118         }
119 }
120
121 /*
122 ===============
123 Draw_Init
124 ===============
125 */
126 static void gl_draw_start(void)
127 {
128         int i;
129         byte *draw_chars;
130
131         menu_numcachepics = 0;
132
133         drawtexturepool = R_AllocTexturePool();
134         char_texture = loadtextureimage (drawtexturepool, "conchars", 0, 0, false, false, true);
135         if (!char_texture)
136         {
137                 draw_chars = W_GetLumpName ("conchars");
138                 // convert font to proper transparent color
139                 for (i = 0;i < 128 * 128;i++)
140                         if (draw_chars[i] == 0)
141                                 draw_chars[i] = 255;
142
143                 // now turn into texture
144                 char_texture = R_LoadTexture (drawtexturepool, "charset", 128, 128, draw_chars, TEXTYPE_QPALETTE, TEXF_ALPHA | TEXF_PRECACHE);
145         }
146
147         conbacktex = loadtextureimage(drawtexturepool, "gfx/conback", 0, 0, false, false, true);
148 }
149
150 static void gl_draw_shutdown(void)
151 {
152         R_FreeTexturePool(&drawtexturepool);
153
154         menu_numcachepics = 0;
155 }
156
157 void SHOWLMP_clear(void);
158 static void gl_draw_newmap(void)
159 {
160         SHOWLMP_clear();
161 }
162
163 extern char engineversion[40];
164 int engineversionx, engineversiony;
165
166 void GL_Draw_Init (void)
167 {
168         int i;
169         Cvar_RegisterVariable (&scr_conalpha);
170
171         for (i = 0;i < 40 && engineversion[i];i++)
172                 engineversion[i] |= 0x80; // shift to orange
173         engineversionx = vid.conwidth - strlen(engineversion) * 8 - 8;
174         engineversiony = vid.conheight - 8;
175
176         menu_numcachepics = 0;
177
178         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
179 }
180
181 /*
182 ================
183 Draw_Character
184
185 Draws one 8*8 graphics character with 0 being transparent.
186 It can be clipped to the top of the screen to allow the console to be
187 smoothly scrolled off.
188 ================
189 */
190 void Draw_Character (int x, int y, int num)
191 {
192         int                             row, col;
193         float                   frow, fcol, size;
194
195         if (num == 32)
196                 return;         // space
197
198         num &= 255;
199         
200         if (y <= -8)
201                 return;                 // totally off screen
202
203         row = num>>4;
204         col = num&15;
205
206         frow = row*0.0625;
207         fcol = col*0.0625;
208         size = 0.0625;
209
210         if (!r_render.integer)
211                 return;
212         glBindTexture(GL_TEXTURE_2D, R_GetTexture(char_texture));
213         CHECKGLERROR
214         // LordHavoc: NEAREST mode on text if not scaling up
215         if (vid.realwidth <= (int) vid.conwidth)
216         {
217                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
218                 CHECKGLERROR
219                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
220                 CHECKGLERROR
221         }
222         else
223         {
224                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
225                 CHECKGLERROR
226                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
227                 CHECKGLERROR
228         }
229
230         if (lighthalf)
231                 glColor3f(0.5f,0.5f,0.5f);
232         else
233                 glColor3f(1.0f,1.0f,1.0f);
234         CHECKGLERROR
235         glBegin (GL_QUADS);
236         glTexCoord2f (fcol, frow);
237         glVertex2f (x, y);
238         glTexCoord2f (fcol + size, frow);
239         glVertex2f (x+8, y);
240         glTexCoord2f (fcol + size, frow + size);
241         glVertex2f (x+8, y+8);
242         glTexCoord2f (fcol, frow + size);
243         glVertex2f (x, y+8);
244         glEnd ();
245         CHECKGLERROR
246
247         // LordHavoc: revert to LINEAR mode
248 //      if (vid.realwidth <= (int) vid.conwidth)
249 //      {
250 //              glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
251 //              glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
252 //      }
253 }
254
255 /*
256 ================
257 Draw_String
258 ================
259 */
260 // LordHavoc: sped this up a lot, and added maxlen
261 void Draw_String (int x, int y, char *str, int maxlen)
262 {
263         int num;
264         float frow, fcol;
265         if (!r_render.integer)
266                 return;
267         if (y <= -8 || y >= (int) vid.conheight || x >= (int) vid.conwidth || *str == 0) // completely offscreen or no text to print
268                 return;
269         if (maxlen < 1)
270                 maxlen = strlen(str);
271         else if (maxlen > (int) strlen(str))
272                 maxlen = strlen(str);
273         glBindTexture(GL_TEXTURE_2D, R_GetTexture(char_texture));
274
275         // LordHavoc: NEAREST mode on text if not scaling up
276         if (vid.realwidth <= (int) vid.conwidth)
277         {
278                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
279                 CHECKGLERROR
280                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
281                 CHECKGLERROR
282         }
283         else
284         {
285                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
286                 CHECKGLERROR
287                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
288                 CHECKGLERROR
289         }
290
291         if (lighthalf)
292                 glColor3f(0.5f,0.5f,0.5f);
293         else
294                 glColor3f(1.0f,1.0f,1.0f);
295         CHECKGLERROR
296         glBegin (GL_QUADS);
297         while (maxlen-- && x < (int) vid.conwidth) // stop rendering when out of characters or room
298         {
299                 if ((num = *str++) != 32) // skip spaces
300                 {
301                         frow = (float) ((int) num >> 4)*0.0625;
302                         fcol = (float) ((int) num & 15)*0.0625;
303                         glTexCoord2f (fcol         , frow         );glVertex2f (x, y);
304                         glTexCoord2f (fcol + 0.0625, frow         );glVertex2f (x+8, y);
305                         glTexCoord2f (fcol + 0.0625, frow + 0.0625);glVertex2f (x+8, y+8);
306                         glTexCoord2f (fcol         , frow + 0.0625);glVertex2f (x, y+8);
307                 }
308                 x += 8;
309         }
310         glEnd ();
311         CHECKGLERROR
312
313         // LordHavoc: revert to LINEAR mode
314 //      if (vid.realwidth < (int) vid.conwidth)
315 //      {
316 //              glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
317 //              glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
318 //      }
319 }
320
321 void Draw_AdditiveString (int x, int y, char *str, int maxlen)
322 {
323         if (!r_render.integer)
324                 return;
325         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
326         CHECKGLERROR
327         Draw_String(x, y, str, maxlen);
328         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
329         CHECKGLERROR
330 }
331
332 void Draw_GenericPic (rtexture_t *tex, float red, float green, float blue, float alpha, int x, int y, int width, int height)
333 {
334         if (!r_render.integer)
335                 return;
336         if (lighthalf)
337                 glColor4f(red * 0.5f, green * 0.5f, blue * 0.5f, alpha);
338         else
339                 glColor4f(red, green, blue, alpha);
340         CHECKGLERROR
341         glBindTexture(GL_TEXTURE_2D, R_GetTexture(tex));
342         CHECKGLERROR
343         glBegin (GL_QUADS);
344         glTexCoord2f (0, 0);glVertex2f (x, y);
345         glTexCoord2f (1, 0);glVertex2f (x+width, y);
346         glTexCoord2f (1, 1);glVertex2f (x+width, y+height);
347         glTexCoord2f (0, 1);glVertex2f (x, y+height);
348         glEnd ();
349         CHECKGLERROR
350 }
351
352 /*
353 =============
354 Draw_AlphaPic
355 =============
356 */
357 void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha)
358 {
359         if (pic)
360                 Draw_GenericPic(((glpic_t *)pic->data)->tex, 1,1,1,alpha, x,y,pic->width, pic->height);
361 }
362
363
364 /*
365 =============
366 Draw_Pic
367 =============
368 */
369 void Draw_Pic (int x, int y, qpic_t *pic)
370 {
371         if (pic)
372                 Draw_GenericPic(((glpic_t *)pic->data)->tex, 1,1,1,1, x,y,pic->width, pic->height);
373 }
374
375
376 void Draw_AdditivePic (int x, int y, qpic_t *pic)
377 {
378         if (pic)
379         {
380                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
381                 CHECKGLERROR
382                 Draw_GenericPic(((glpic_t *)pic->data)->tex, 1,1,1,1, x,y,pic->width, pic->height);
383                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
384                 CHECKGLERROR
385         }
386 }
387
388
389 /*
390 =============
391 Draw_PicTranslate
392
393 Only used for the player color selection menu
394 =============
395 */
396 void Draw_PicTranslate (int x, int y, qpic_t *pic, byte *translation)
397 {
398         int                             i, c;
399         byte                    *trans, *src, *dest;
400         rtexture_t              *rt;
401
402         if (pic == NULL)
403                 return;
404
405         c = pic->width * pic->height;
406         src = menuplyr_pixels;
407         dest = trans = Mem_Alloc(tempmempool, c);
408         for (i = 0;i < c;i++)
409                 *dest++ = translation[*src++];
410
411         rt = R_LoadTexture (drawtexturepool, "translatedplayerpic", pic->width, pic->height, trans, TEXTYPE_QPALETTE, TEXF_ALPHA | TEXF_PRECACHE);
412         Mem_Free(trans);
413
414         if (!r_render.integer)
415                 return;
416         Draw_GenericPic (rt, 1,1,1,1, x, y, pic->width, pic->height);
417 }
418
419
420 /*
421 ================
422 Draw_ConsoleBackground
423
424 ================
425 */
426 void Draw_ConsoleBackground (int lines)
427 {
428         Draw_GenericPic (conbacktex, 1,1,1,scr_conalpha.value * lines / vid.conheight, 0, lines - vid.conheight, vid.conwidth, vid.conheight);
429         // LordHavoc: draw version
430         Draw_String(engineversionx, lines - vid.conheight + engineversiony, engineversion, 9999);
431 }
432
433 /*
434 =============
435 Draw_Fill
436
437 Fills a box of pixels with a single color
438 =============
439 */
440 void Draw_Fill (int x, int y, int w, int h, int c)
441 {
442         if (!r_render.integer)
443                 return;
444         glDisable (GL_TEXTURE_2D);
445         CHECKGLERROR
446         if (lighthalf)
447         {
448                 byte *tempcolor = (byte *)&d_8to24table[c];
449                 glColor4ub ((byte) (tempcolor[0] >> 1), (byte) (tempcolor[1] >> 1), (byte) (tempcolor[2] >> 1), tempcolor[3]);
450         }
451         else
452                 glColor4ubv ((byte *)&d_8to24table[c]);
453         CHECKGLERROR
454
455         glBegin (GL_QUADS);
456
457         glVertex2f (x,y);
458         glVertex2f (x+w, y);
459         glVertex2f (x+w, y+h);
460         glVertex2f (x, y+h);
461
462         glEnd ();
463         CHECKGLERROR
464         glColor3f(1,1,1);
465         CHECKGLERROR
466         glEnable (GL_TEXTURE_2D);
467         CHECKGLERROR
468 }
469 //=============================================================================
470
471 //=============================================================================
472
473 /*
474 ================
475 GL_Set2D
476
477 Setup as if the screen was 320*200
478 ================
479 */
480 void GL_Set2D (void)
481 {
482         if (!r_render.integer)
483                 return;
484         glViewport (vid.realx, vid.realy, vid.realwidth, vid.realheight);
485         CHECKGLERROR
486
487         glMatrixMode(GL_PROJECTION);
488         CHECKGLERROR
489     glLoadIdentity ();
490         CHECKGLERROR
491         glOrtho  (0, vid.conwidth, vid.conheight, 0, -99999, 99999);
492         CHECKGLERROR
493
494         glMatrixMode(GL_MODELVIEW);
495         CHECKGLERROR
496     glLoadIdentity ();
497         CHECKGLERROR
498
499         glDisable (GL_DEPTH_TEST);
500         CHECKGLERROR
501         glDisable (GL_CULL_FACE);
502         CHECKGLERROR
503         glEnable (GL_BLEND);
504         CHECKGLERROR
505         glEnable(GL_TEXTURE_2D);
506         CHECKGLERROR
507
508         // LordHavoc: added this
509         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
510         CHECKGLERROR
511         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
512         CHECKGLERROR
513
514         glColor3f(1,1,1);
515         CHECKGLERROR
516 }
517
518 // LordHavoc: SHOWLMP stuff
519 #define SHOWLMP_MAXLABELS 256
520 typedef struct showlmp_s
521 {
522         qboolean        isactive;
523         float           x;
524         float           y;
525         char            label[32];
526         char            pic[128];
527 } showlmp_t;
528
529 showlmp_t showlmp[SHOWLMP_MAXLABELS];
530
531 void SHOWLMP_decodehide(void)
532 {
533         int i;
534         byte *lmplabel;
535         lmplabel = MSG_ReadString();
536         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
537                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
538                 {
539                         showlmp[i].isactive = false;
540                         return;
541                 }
542 }
543
544 void SHOWLMP_decodeshow(void)
545 {
546         int i, k;
547         byte lmplabel[256], picname[256];
548         float x, y;
549         strcpy(lmplabel,MSG_ReadString());
550         strcpy(picname, MSG_ReadString());
551         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
552         {
553                 x = MSG_ReadByte();
554                 y = MSG_ReadByte();
555         }
556         else
557         {
558                 x = MSG_ReadShort();
559                 y = MSG_ReadShort();
560         }
561         k = -1;
562         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
563                 if (showlmp[i].isactive)
564                 {
565                         if (strcmp(showlmp[i].label, lmplabel) == 0)
566                         {
567                                 k = i;
568                                 break; // drop out to replace it
569                         }
570                 }
571                 else if (k < 0) // find first empty one to replace
572                         k = i;
573         if (k < 0)
574                 return; // none found to replace
575         // change existing one
576         showlmp[k].isactive = true;
577         strcpy(showlmp[k].label, lmplabel);
578         strcpy(showlmp[k].pic, picname);
579         showlmp[k].x = x;
580         showlmp[k].y = y;
581 }
582
583 void SHOWLMP_drawall(void)
584 {
585         int i;
586         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
587                 if (showlmp[i].isactive)
588                         Draw_Pic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic));
589 }
590
591 void SHOWLMP_clear(void)
592 {
593         int i;
594         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
595                 showlmp[i].isactive = false;
596 }