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