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