varray_* rewritten to remove padding (varray_vertex3f, varray_texcoord2f, varray_texc...
[xonotic/darkplaces.git] / cl_screen.c
1
2 #include "quakedef.h"
3 #include "cl_video.h"
4
5 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
6 cvar_t scr_fov = {CVAR_SAVE, "fov","90"};       // 10 - 170
7 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
8 cvar_t scr_centertime = {0, "scr_centertime","2"};
9 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
10 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
11 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
12 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
13 cvar_t scr_2dresolution = {CVAR_SAVE, "scr_2dresolution", "1"};
14 cvar_t cl_avidemo = {0, "cl_avidemo", "0"};
15
16 qboolean        scr_initialized;                // ready to draw
17
18 float           scr_con_current;
19 float           scr_conlines;           // lines of console to display
20
21 int                     clearconsole;
22 int                     clearnotify;
23
24 qboolean        scr_drawloading = false;
25
26 static qbyte menuplyr_pixels[4096];
27
28 void DrawCrosshair(int num);
29 void V_CalcRefdef (void);
30 static void SCR_ScreenShot_f (void);
31 static void R_Envmap_f (void);
32
33 // backend
34 void R_ClearScreen(void);
35
36 /*
37 ===============================================================================
38
39 CENTER PRINTING
40
41 ===============================================================================
42 */
43
44 char            scr_centerstring[1024];
45 float           scr_centertime_start;   // for slow victory printing
46 float           scr_centertime_off;
47 int                     scr_center_lines;
48 int                     scr_erase_lines;
49 int                     scr_erase_center;
50
51 /*
52 ==============
53 SCR_CenterPrint
54
55 Called for important messages that should stay in the center of the screen
56 for a few moments
57 ==============
58 */
59 void SCR_CenterPrint (char *str)
60 {
61         strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
62         scr_centertime_off = scr_centertime.value;
63         scr_centertime_start = cl.time;
64
65 // count the number of lines for centering
66         scr_center_lines = 1;
67         while (*str)
68         {
69                 if (*str == '\n')
70                         scr_center_lines++;
71                 str++;
72         }
73 }
74
75
76 void SCR_DrawCenterString (void)
77 {
78         char    *start;
79         int             l;
80         int             x, y;
81         int             remaining;
82
83 // the finale prints the characters one at a time
84         if (cl.intermission)
85                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
86         else
87                 remaining = 9999;
88
89         scr_erase_center = 0;
90         start = scr_centerstring;
91
92         if (scr_center_lines <= 4)
93                 y = vid.conheight*0.35;
94         else
95                 y = 48;
96
97         do
98         {
99         // scan the width of the line
100                 for (l=0 ; l<40 ; l++)
101                         if (start[l] == '\n' || !start[l])
102                                 break;
103                 x = (vid.conwidth - l*8)/2;
104                 if (l > 0)
105                 {
106                         if (remaining < l)
107                                 l = remaining;
108                         DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
109                         remaining -= l;
110                         if (remaining <= 0)
111                                 return;
112                 }
113
114                 y += 8;
115
116                 while (*start && *start != '\n')
117                         start++;
118
119                 if (!*start)
120                         break;
121                 start++;                // skip the \n
122         } while (1);
123 }
124
125 void SCR_CheckDrawCenterString (void)
126 {
127         if (scr_center_lines > scr_erase_lines)
128                 scr_erase_lines = scr_center_lines;
129
130         scr_centertime_off -= host_frametime;
131
132         // don't draw if this is a normal stats-screen intermission,
133         // only if it is not an intermission, or a finale intermission
134         if (cl.intermission == 1)
135                 return;
136         if (scr_centertime_off <= 0 && !cl.intermission)
137                 return;
138         if (key_dest != key_game)
139                 return;
140
141         SCR_DrawCenterString ();
142 }
143
144 /*
145 ==============
146 SCR_DrawTurtle
147 ==============
148 */
149 void SCR_DrawTurtle (void)
150 {
151         static int      count;
152
153         if (cls.state != ca_connected)
154                 return;
155
156         if (!scr_showturtle.integer)
157                 return;
158
159         if (host_frametime < 0.1)
160         {
161                 count = 0;
162                 return;
163         }
164
165         count++;
166         if (count < 3)
167                 return;
168
169         DrawQ_Pic (0, 0, "turtle", 0, 0, 1, 1, 1, 1, 0);
170 }
171
172 /*
173 ==============
174 SCR_DrawNet
175 ==============
176 */
177 void SCR_DrawNet (void)
178 {
179         if (cls.state != ca_connected)
180                 return;
181         if (realtime - cl.last_received_message < 0.3)
182                 return;
183         if (cls.demoplayback)
184                 return;
185
186         DrawQ_Pic (64, 0, "net", 0, 0, 1, 1, 1, 1, 0);
187 }
188
189 /*
190 ==============
191 DrawPause
192 ==============
193 */
194 void SCR_DrawPause (void)
195 {
196         cachepic_t      *pic;
197
198         if (cls.state != ca_connected)
199                 return;
200
201         if (!scr_showpause.integer)             // turn off for screenshots
202                 return;
203
204         if (!cl.paused)
205                 return;
206
207         pic = Draw_CachePic ("gfx/pause.lmp");
208         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
209 }
210
211
212
213 /*
214 ==============
215 SCR_DrawLoading
216 ==============
217 */
218 void SCR_DrawLoading (void)
219 {
220         cachepic_t      *pic;
221
222         pic = Draw_CachePic ("gfx/loading.lmp");
223         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/loading.lmp", 0, 0, 1, 1, 1, 1, 0);
224 }
225
226
227
228 //=============================================================================
229
230
231 /*
232 ==================
233 SCR_SetUpToDrawConsole
234 ==================
235 */
236 void SCR_SetUpToDrawConsole (void)
237 {
238         Con_CheckResize ();
239
240         if (key_dest == key_game && cls.signon != SIGNONS)
241                 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
242         else
243                 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
244
245 // decide on the height of the console
246         if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
247                 scr_conlines = vid.conheight; // full screen
248         else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
249                 scr_conlines = vid.conheight/2; // half screen
250         else
251                 scr_conlines = 0;                               // none visible
252
253         if (scr_conspeed.value)
254         {
255                 if (scr_conlines < scr_con_current)
256                 {
257                         scr_con_current -= scr_conspeed.value*host_realframetime;
258                         if (scr_conlines > scr_con_current)
259                                 scr_con_current = scr_conlines;
260
261                 }
262                 else if (scr_conlines > scr_con_current)
263                 {
264                         scr_con_current += scr_conspeed.value*host_realframetime;
265                         if (scr_conlines < scr_con_current)
266                                 scr_con_current = scr_conlines;
267                 }
268         }
269         else
270                 scr_con_current = scr_conlines;
271 }
272
273 /*
274 ==================
275 SCR_DrawConsole
276 ==================
277 */
278 void SCR_DrawConsole (void)
279 {
280         if (scr_con_current)
281         {
282                 Con_DrawConsole (scr_con_current);
283                 clearconsole = 0;
284         }
285         else
286         {
287                 if (key_dest == key_game || key_dest == key_message)
288                         Con_DrawNotify ();      // only draw notify in game
289         }
290 }
291
292 /*
293 ===============
294 SCR_BeginLoadingPlaque
295
296 ================
297 */
298 void SCR_BeginLoadingPlaque (void)
299 {
300         if (scr_drawloading)
301                 return;
302
303         S_StopAllSounds (true);
304
305         scr_drawloading = true;
306         CL_UpdateScreen ();
307         scr_drawloading = true;
308         CL_UpdateScreen ();
309 }
310
311 //=============================================================================
312
313 char r_speeds_string[1024];
314 int speedstringcount, r_timereport_active;
315 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
316
317 void R_TimeReport(char *desc)
318 {
319         char tempbuf[256];
320         int length;
321         int t;
322
323         if (!r_timereport_active)
324                 return;
325
326         r_timereport_temp = r_timereport_current;
327         r_timereport_current = Sys_DoubleTime();
328         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
329
330         sprintf(tempbuf, "%8i %s", t, desc);
331         length = strlen(tempbuf);
332         while (length < 20)
333                 tempbuf[length++] = ' ';
334         tempbuf[length] = 0;
335         if (speedstringcount + length > (vid.conwidth / 8))
336         {
337                 strcat(r_speeds_string, "\n");
338                 speedstringcount = 0;
339         }
340         // skip the space at the beginning if it's the first on the line
341         if (speedstringcount == 0)
342         {
343                 strcat(r_speeds_string, tempbuf + 1);
344                 speedstringcount = length - 1;
345         }
346         else
347         {
348                 strcat(r_speeds_string, tempbuf);
349                 speedstringcount += length;
350         }
351 }
352
353 extern int c_rt_lights, c_rt_clears, c_rt_scissored;
354 extern int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
355 extern int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
356 extern int r_shadow_lightingmode;
357 void R_TimeReport_Start(void)
358 {
359         r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
360         r_speeds_string[0] = 0;
361         if (r_timereport_active)
362         {
363                 speedstringcount = 0;
364                 AngleVectors (r_refdef.viewangles, vpn, NULL, NULL);
365                 sprintf(r_speeds_string,
366                         "org:'%+8.2f %+8.2f %+8.2f' ang:'%+4.0f %+4.0f %+4.0f' dir:'%+2.3f %+2.3f %+2.3f'\n"
367                         "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
368                         "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
369                         "%6i modeltris%6i meshs%6i meshtris\n",
370                         r_refdef.vieworg[0], r_refdef.vieworg[1], r_refdef.vieworg[2], r_refdef.viewangles[0], r_refdef.viewangles[1], r_refdef.viewangles[2], vpn[0], vpn[1], vpn[2],
371                         c_faces, c_nodes, c_leafs, c_light_polys,
372                         c_models, c_bmodels, c_sprites, c_particles, c_dlights,
373                         c_alias_polys, c_meshs, c_meshelements / 3);
374                 if (r_shadow_lightingmode)
375                 {
376                         sprintf(r_speeds_string + strlen(r_speeds_string),
377                                 "realtime lighting:%4i lights%4i clears%4i scissored\n"
378                                 "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n"
379                                 "precomputed: %6i shadowmeshes%6i shadowtris\n",
380                                 c_rt_lights, c_rt_clears, c_rt_scissored,
381                                 c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris,
382                                 c_rtcached_shadowmeshes, c_rtcached_shadowtris);
383                 }
384
385                 c_alias_polys = 0;
386                 c_light_polys = 0;
387                 c_faces = 0;
388                 c_nodes = 0;
389                 c_leafs = 0;
390                 c_models = 0;
391                 c_bmodels = 0;
392                 c_sprites = 0;
393                 c_particles = 0;
394                 c_meshs = 0;
395                 c_meshelements = 0;
396
397                 r_timereport_start = Sys_DoubleTime();
398         }
399 }
400
401 void R_TimeReport_End(void)
402 {
403         r_timereport_current = r_timereport_start;
404         R_TimeReport("total");
405
406         if (r_timereport_active)
407         {
408                 int i, j, lines, y;
409                 lines = 1;
410                 for (i = 0;r_speeds_string[i];i++)
411                         if (r_speeds_string[i] == '\n')
412                                 lines++;
413                 y = vid.conheight - sb_lines - lines * 8;
414                 i = j = 0;
415                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
416                 while (r_speeds_string[i])
417                 {
418                         j = i;
419                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
420                                 i++;
421                         if (i - j > 0)
422                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
423                         if (r_speeds_string[i] == '\n')
424                                 i++;
425                         y += 8;
426                 }
427         }
428 }
429
430 /*
431 =================
432 SCR_SizeUp_f
433
434 Keybinding command
435 =================
436 */
437 void SCR_SizeUp_f (void)
438 {
439         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
440 }
441
442
443 /*
444 =================
445 SCR_SizeDown_f
446
447 Keybinding command
448 =================
449 */
450 void SCR_SizeDown_f (void)
451 {
452         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
453 }
454
455 void CL_Screen_Init(void)
456 {
457         qpic_t *dat;
458
459         Cvar_RegisterVariable (&scr_fov);
460         Cvar_RegisterVariable (&scr_viewsize);
461         Cvar_RegisterVariable (&scr_conspeed);
462         Cvar_RegisterVariable (&scr_showram);
463         Cvar_RegisterVariable (&scr_showturtle);
464         Cvar_RegisterVariable (&scr_showpause);
465         Cvar_RegisterVariable (&scr_centertime);
466         Cvar_RegisterVariable (&scr_printspeed);
467         Cvar_RegisterVariable (&scr_2dresolution);
468         Cvar_RegisterVariable (&cl_avidemo);
469
470         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
471         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
472         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
473         Cmd_AddCommand ("envmap", R_Envmap_f);
474
475         scr_initialized = true;
476
477         // HACK HACK HACK
478         // load the image data for the player image in the config menu
479         dat = (qpic_t *)COM_LoadFile ("gfx/menuplyr.lmp", false);
480         if (!dat)
481                 Sys_Error("unable to load gfx/menuplyr.lmp");
482         SwapPic (dat);
483
484         if (dat->width*dat->height <= 4096)
485                 memcpy (menuplyr_pixels, dat->data, dat->width * dat->height);
486         else
487                 Con_Printf("gfx/menuplyr.lmp larger than 4k buffer");
488         Mem_Free(dat);
489 }
490
491 void DrawQ_Clear(void)
492 {
493         r_refdef.drawqueuesize = 0;
494 }
495
496 static int picelements[6] = {0, 1, 2, 0, 2, 3};
497 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
498 {
499 #if 1
500         DrawQ_SuperPic(x,y,picname,width,height,0,0,red,green,blue,alpha,1,0,red,green,blue,alpha,0,1,red,green,blue,alpha,1,1,red,green,blue,alpha,flags);
501 #elif 1
502         float floats[48];
503         cachepic_t *pic;
504         drawqueuemesh_t mesh;
505         if (alpha < (1.0f / 255.0f) || !picname || !picname[0])
506                 return;
507         pic = Draw_CachePic(picname);
508         if (width == 0)
509                 width = pic->width;
510         if (height == 0)
511                 height = pic->height;
512         mesh.texture = pic->tex;
513         mesh.numtriangles = 2;
514         mesh.numvertices = 4;
515         mesh.indices = picelements;
516         mesh.vertex3f = floats;
517         mesh.texcoord2f = floats + 16;
518         mesh.color4f = floats + 32;
519         memset(floats, 0, sizeof(floats));
520         mesh.vertex3f[0] = mesh.vertex3f[12] = x;
521         mesh.vertex3f[1] = mesh.vertex3f[5] = y;
522         mesh.vertex3f[4] = mesh.vertex3f[8] = x + width;
523         mesh.vertex3f[9] = mesh.vertex3f[13] = y + height;
524         mesh.texcoord2f[4] = mesh.texcoord2f[8] = mesh.texcoord2f[9] = mesh.texcoord2f[13] = 1;
525         mesh.color4f[0] = mesh.color4f[4] = mesh.color4f[8] = mesh.color4f[12] = red;
526         mesh.color4f[1] = mesh.color4f[5] = mesh.color4f[9] = mesh.color4f[13] = green;
527         mesh.color4f[2] = mesh.color4f[6] = mesh.color4f[10] = mesh.color4f[14] = blue;
528         mesh.color4f[3] = mesh.color4f[7] = mesh.color4f[11] = mesh.color4f[15] = alpha;
529         DrawQ_Mesh (&mesh, flags);
530 #else
531         int size;
532         drawqueue_t *dq;
533         if (alpha < (1.0f / 255.0f) || !picname || !picname[0])
534                 return;
535         size = sizeof(*dq) + ((strlen(picname) + 1 + 3) & ~3);
536         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
537                 return;
538         red = bound(0, red, 1);
539         green = bound(0, green, 1);
540         blue = bound(0, blue, 1);
541         alpha = bound(0, alpha, 1);
542         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
543         dq->size = size;
544         dq->command = DRAWQUEUE_PIC;
545         dq->flags = flags;
546         dq->color = ((unsigned int) (red * 255.0f) << 24) | ((unsigned int) (green * 255.0f) << 16) | ((unsigned int) (blue * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
547         dq->x = x;
548         dq->y = y;
549         // if these are not zero, they override the pic's size
550         dq->scalex = width;
551         dq->scaley = height;
552         strcpy((char *)(dq + 1), picname);
553         r_refdef.drawqueuesize += dq->size;
554 #endif
555 }
556
557 void DrawQ_String(float x, float y, const char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags)
558 {
559         int size, len;
560         drawqueue_t *dq;
561         char *out;
562         if (alpha < (1.0f / 255.0f))
563                 return;
564         if (maxlen < 1)
565                 len = strlen(string);
566         else
567                 for (len = 0;len < maxlen && string[len];len++);
568         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
569         for (;len > 0 && string[len - 1] == ' ';len--);
570         if (len < 1)
571                 return;
572         if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * maxlen) || y < (-scaley))
573                 return;
574         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
575         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
576                 return;
577         red = bound(0, red, 1);
578         green = bound(0, green, 1);
579         blue = bound(0, blue, 1);
580         alpha = bound(0, alpha, 1);
581         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
582         dq->size = size;
583         dq->command = DRAWQUEUE_STRING;
584         dq->flags = flags;
585         dq->color = ((unsigned int) (red * 255.0f) << 24) | ((unsigned int) (green * 255.0f) << 16) | ((unsigned int) (blue * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
586         dq->x = x;
587         dq->y = y;
588         dq->scalex = scalex;
589         dq->scaley = scaley;
590         out = (char *)(dq + 1);
591         memcpy(out, string, len);
592         out[len] = 0;
593         r_refdef.drawqueuesize += dq->size;
594 }
595
596 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
597 {
598 #if 1
599         DrawQ_SuperPic(x,y,NULL,w,h,0,0,red,green,blue,alpha,1,0,red,green,blue,alpha,0,1,red,green,blue,alpha,1,1,red,green,blue,alpha,flags);
600 #elif 1
601         float floats[48];
602         drawqueuemesh_t mesh;
603         if (alpha < (1.0f / 255.0f))
604                 return;
605         mesh.texture = NULL;
606         mesh.numtriangles = 2;
607         mesh.numvertices = 4;
608         mesh.indices = picelements;
609         mesh.vertex3f = floats;
610         mesh.texcoord2f = floats + 16;
611         mesh.color4f = floats + 32;
612         memset(floats, 0, sizeof(floats));
613         mesh.vertex3f[0] = mesh.vertex3f[12] = x;
614         mesh.vertex3f[1] = mesh.vertex3f[5] = y;
615         mesh.vertex3f[4] = mesh.vertex3f[8] = x + w;
616         mesh.vertex3f[9] = mesh.vertex3f[13] = y + h;
617         mesh.color4f[0] = mesh.color4f[4] = mesh.color4f[8] = mesh.color4f[12] = red;
618         mesh.color4f[1] = mesh.color4f[5] = mesh.color4f[9] = mesh.color4f[13] = green;
619         mesh.color4f[2] = mesh.color4f[6] = mesh.color4f[10] = mesh.color4f[14] = blue;
620         mesh.color4f[3] = mesh.color4f[7] = mesh.color4f[11] = mesh.color4f[15] = alpha;
621         DrawQ_Mesh (&mesh, flags);
622 #else
623         int size;
624         drawqueue_t *dq;
625         if (alpha < (1.0f / 255.0f))
626                 return;
627         size = sizeof(*dq) + 4;
628         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
629                 return;
630         red = bound(0, red, 1);
631         green = bound(0, green, 1);
632         blue = bound(0, blue, 1);
633         alpha = bound(0, alpha, 1);
634         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
635         dq->size = size;
636         dq->command = DRAWQUEUE_PIC;
637         dq->flags = flags;
638         dq->color = ((unsigned int) (red * 255.0f) << 24) | ((unsigned int) (green * 255.0f) << 16) | ((unsigned int) (blue * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
639         dq->x = x;
640         dq->y = y;
641         dq->scalex = w;
642         dq->scaley = h;
643         // empty pic name
644         *((char *)(dq + 1)) = 0;
645         r_refdef.drawqueuesize += dq->size;
646 #endif
647 }
648
649 void DrawQ_SuperPic(float x, float y, char *picname, float width, float height, float s1, float t1, float r1, float g1, float b1, float a1, float s2, float t2, float r2, float g2, float b2, float a2, float s3, float t3, float r3, float g3, float b3, float a3, float s4, float t4, float r4, float g4, float b4, float a4, int flags)
650 {
651         float floats[36];
652         cachepic_t *pic;
653         drawqueuemesh_t mesh;
654         memset(&mesh, 0, sizeof(mesh));
655         if (picname && picname[0])
656         {
657                 pic = Draw_CachePic(picname);
658                 if (width == 0)
659                         width = pic->width;
660                 if (height == 0)
661                         height = pic->height;
662                 mesh.texture = pic->tex;
663         }
664         mesh.numtriangles = 2;
665         mesh.numvertices = 4;
666         mesh.element3i = picelements;
667         mesh.vertex3f = floats;
668         mesh.texcoord2f = floats + 12;
669         mesh.color4f = floats + 20;
670         memset(floats, 0, sizeof(floats));
671         mesh.vertex3f[0] = mesh.vertex3f[9] = x;
672         mesh.vertex3f[1] = mesh.vertex3f[4] = y;
673         mesh.vertex3f[3] = mesh.vertex3f[6] = x + width;
674         mesh.vertex3f[7] = mesh.vertex3f[10] = y + height;
675         mesh.texcoord2f[0] = s1;mesh.texcoord2f[1] = t1;mesh.color4f[ 0] = r1;mesh.color4f[ 1] = g1;mesh.color4f[ 2] = b1;mesh.color4f[ 3] = a1;
676         mesh.texcoord2f[2] = s2;mesh.texcoord2f[3] = t2;mesh.color4f[ 4] = r2;mesh.color4f[ 5] = g2;mesh.color4f[ 6] = b2;mesh.color4f[ 7] = a2;
677         mesh.texcoord2f[4] = s4;mesh.texcoord2f[5] = t4;mesh.color4f[ 8] = r4;mesh.color4f[ 9] = g4;mesh.color4f[10] = b4;mesh.color4f[11] = a4;
678         mesh.texcoord2f[6] = s3;mesh.texcoord2f[7] = t3;mesh.color4f[12] = r3;mesh.color4f[13] = g3;mesh.color4f[14] = b3;mesh.color4f[15] = a3;
679         DrawQ_Mesh (&mesh, flags);
680 }
681
682 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
683 {
684         int size;
685         void *p;
686         drawqueue_t *dq;
687         drawqueuemesh_t *m;
688         size = sizeof(*dq);
689         size += sizeof(drawqueuemesh_t);
690         size += sizeof(int[3]) * mesh->numtriangles;
691         size += sizeof(float[3]) * mesh->numvertices;
692         size += sizeof(float[2]) * mesh->numvertices;
693         size += sizeof(float[4]) * mesh->numvertices;
694         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
695                 return;
696         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
697         dq->size = size;
698         dq->command = DRAWQUEUE_MESH;
699         dq->flags = flags;
700         dq->color = 0;
701         dq->x = 0;
702         dq->y = 0;
703         dq->scalex = 0;
704         dq->scaley = 0;
705         p = (void *)(dq + 1);
706         m = p;(qbyte *)p += sizeof(drawqueuemesh_t);
707         m->numtriangles = mesh->numtriangles;
708         m->numvertices = mesh->numvertices;
709         m->texture = mesh->texture;
710         m->element3i  = p;memcpy(m->element3i , mesh->element3i , m->numtriangles * sizeof(int[3]));(qbyte *)p += m->numtriangles * sizeof(int[3]);
711         m->vertex3f   = p;memcpy(m->vertex3f  , mesh->vertex3f  , m->numvertices * sizeof(float[3]));(qbyte *)p += m->numvertices * sizeof(float[3]);
712         m->texcoord2f = p;memcpy(m->texcoord2f, mesh->texcoord2f, m->numvertices * sizeof(float[2]));(qbyte *)p += m->numvertices * sizeof(float[2]);
713         m->color4f    = p;memcpy(m->color4f   , mesh->color4f   , m->numvertices * sizeof(float[4]));(qbyte *)p += m->numvertices * sizeof(float[4]);
714         r_refdef.drawqueuesize += dq->size;
715 }
716
717 /*
718 ====================
719 CalcFov
720 ====================
721 */
722 float CalcFov (float fov_x, float width, float height)
723 {
724         // calculate vision size and alter by aspect, then convert back to angle
725         return atan (height / (width / tan(fov_x/360*M_PI))) * 360 / M_PI;
726 }
727
728 /*
729 =================
730 SCR_CalcRefdef
731
732 Must be called whenever vid changes
733 Internal use only
734 =================
735 */
736 static void SCR_CalcRefdef (void)
737 {
738         float size;
739         int contents;
740
741 //========================================
742
743 // bound viewsize
744         if (scr_viewsize.value < 30)
745                 Cvar_Set ("viewsize","30");
746         if (scr_viewsize.value > 120)
747                 Cvar_Set ("viewsize","120");
748
749 // bound field of view
750         if (scr_fov.value < 10)
751                 Cvar_Set ("fov","10");
752         if (scr_fov.value > 170)
753                 Cvar_Set ("fov","170");
754
755 // intermission is always full screen
756         if (cl.intermission)
757         {
758                 size = 1;
759                 sb_lines = 0;
760         }
761         else
762         {
763                 if (scr_viewsize.value >= 120)
764                         sb_lines = 0;           // no status bar at all
765                 else if (scr_viewsize.value >= 110)
766                         sb_lines = 24;          // no inventory
767                 else
768                         sb_lines = 24+16+8;
769                 size = scr_viewsize.value * (1.0 / 100.0);
770         }
771
772         if (size >= 1)
773         {
774                 r_refdef.width = vid.realwidth;
775                 r_refdef.height = vid.realheight;
776                 r_refdef.x = 0;
777                 r_refdef.y = 0;
778         }
779         else
780         {
781                 r_refdef.width = vid.realwidth * size;
782                 r_refdef.height = vid.realheight * size;
783                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
784                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
785         }
786
787         r_refdef.width = bound(0, r_refdef.width, vid.realwidth);
788         r_refdef.height = bound(0, r_refdef.height, vid.realheight);
789         r_refdef.x = bound(0, r_refdef.x, vid.realwidth - r_refdef.width) + vid.realx;
790         r_refdef.y = bound(0, r_refdef.y, vid.realheight - r_refdef.height) + vid.realy;
791
792         // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
793         r_refdef.fov_x = scr_fov.value * cl.viewzoom;
794         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
795
796         if (cl.worldmodel)
797         {
798                 Mod_CheckLoaded(cl.worldmodel);
799                 contents = Mod_PointContents(r_refdef.vieworg, cl.worldmodel);
800                 if (contents != CONTENTS_EMPTY && contents != CONTENTS_SOLID)
801                 {
802                         r_refdef.fov_x *= (sin(cl.time * 4.7) * 0.015 + 0.985);
803                         r_refdef.fov_y *= (sin(cl.time * 3.0) * 0.015 + 0.985);
804                 }
805         }
806 }
807
808 /*
809 ==================
810 SCR_ScreenShot_f
811 ==================
812 */
813 void SCR_ScreenShot_f (void)
814 {
815         static int i = 0;
816         char filename[16];
817         char checkname[MAX_OSPATH];
818 //
819 // find a file name to save it to
820 //
821         for (; i<=9999 ; i++)
822         {
823                 sprintf (filename, "dp%04i.tga", i);
824                 sprintf (checkname, "%s/%s", com_gamedir, filename);
825                 if (Sys_FileTime(checkname) == -1)
826                         break;  // file doesn't exist
827         }
828         if (i==10000)
829         {
830                 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
831                 return;
832         }
833
834         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight))
835                 Con_Printf ("Wrote %s\n", filename);
836         else
837                 Con_Printf ("unable to write %s\n", filename);
838 }
839
840 static int cl_avidemo_frame = 0;
841
842 void SCR_CaptureAVIDemo(void)
843 {
844         char filename[32];
845         sprintf(filename, "dpavi%06d.tga", cl_avidemo_frame);
846         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight))
847                 cl_avidemo_frame++;
848         else
849         {
850                 Cvar_SetValueQuick(&cl_avidemo, 0);
851                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
852                 cl_avidemo_frame = 0;
853         }
854 }
855
856 /*
857 ===============
858 R_Envmap_f
859
860 Grab six views for environment mapping tests
861 ===============
862 */
863 struct
864 {
865         float angles[3];
866         char *name;
867 }
868 envmapinfo[6] =
869 {
870         {{  0,   0, 0}, "ft"},
871         {{  0,  90, 0}, "rt"},
872         {{  0, 180, 0}, "bk"},
873         {{  0, 270, 0}, "lf"},
874         {{-90,  90, 0}, "up"},
875         {{ 90,  90, 0}, "dn"}
876 };
877
878 static void R_Envmap_f (void)
879 {
880         int j, size;
881         char filename[256], basename[256];
882
883         if (Cmd_Argc() != 3)
884         {
885                 Con_Printf ("envmap <basename> <size>: save out 6 cubic environment map images, usable with loadsky, note that size must one of 128, 256, 512, or 1024 and can't be bigger than your current resolution\n");
886                 return;
887         }
888
889         strcpy(basename, Cmd_Argv(1));
890         size = atoi(Cmd_Argv(2));
891         if (size != 128 && size != 256 && size != 512 && size != 1024)
892         {
893                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
894                 return;
895         }
896         if (size > vid.realwidth || size > vid.realheight)
897         {
898                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
899                 return;
900         }
901
902         envmap = true;
903
904         r_refdef.x = 0;
905         r_refdef.y = 0;
906         r_refdef.width = size;
907         r_refdef.height = size;
908
909         r_refdef.fov_x = 90;
910         r_refdef.fov_y = 90;
911
912         for (j = 0;j < 6;j++)
913         {
914                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
915                 VectorCopy(envmapinfo[j].angles, r_refdef.viewangles);
916                 R_ClearScreen();
917                 R_RenderView ();
918                 SCR_ScreenShot(filename, vid.realx, vid.realy, size, size);
919         }
920
921         envmap = false;
922 }
923
924 //=============================================================================
925
926 // LordHavoc: SHOWLMP stuff
927 #define SHOWLMP_MAXLABELS 256
928 typedef struct showlmp_s
929 {
930         qboolean        isactive;
931         float           x;
932         float           y;
933         char            label[32];
934         char            pic[128];
935 }
936 showlmp_t;
937
938 showlmp_t showlmp[SHOWLMP_MAXLABELS];
939
940 void SHOWLMP_decodehide(void)
941 {
942         int i;
943         qbyte *lmplabel;
944         lmplabel = MSG_ReadString();
945         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
946                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
947                 {
948                         showlmp[i].isactive = false;
949                         return;
950                 }
951 }
952
953 void SHOWLMP_decodeshow(void)
954 {
955         int i, k;
956         qbyte lmplabel[256], picname[256];
957         float x, y;
958         strcpy(lmplabel,MSG_ReadString());
959         strcpy(picname, MSG_ReadString());
960         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
961         {
962                 x = MSG_ReadByte();
963                 y = MSG_ReadByte();
964         }
965         else
966         {
967                 x = MSG_ReadShort();
968                 y = MSG_ReadShort();
969         }
970         k = -1;
971         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
972                 if (showlmp[i].isactive)
973                 {
974                         if (strcmp(showlmp[i].label, lmplabel) == 0)
975                         {
976                                 k = i;
977                                 break; // drop out to replace it
978                         }
979                 }
980                 else if (k < 0) // find first empty one to replace
981                         k = i;
982         if (k < 0)
983                 return; // none found to replace
984         // change existing one
985         showlmp[k].isactive = true;
986         strcpy(showlmp[k].label, lmplabel);
987         strcpy(showlmp[k].pic, picname);
988         showlmp[k].x = x;
989         showlmp[k].y = y;
990 }
991
992 void SHOWLMP_drawall(void)
993 {
994         int i;
995         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
996                 if (showlmp[i].isactive)
997                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
998 }
999
1000 void SHOWLMP_clear(void)
1001 {
1002         int i;
1003         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1004                 showlmp[i].isactive = false;
1005 }
1006
1007 void CL_SetupScreenSize(void)
1008 {
1009         static float old2dresolution = -1;
1010
1011         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1012
1013         VID_UpdateGamma(false);
1014
1015         if (scr_2dresolution.value != old2dresolution)
1016         {
1017                 Cvar_SetValue("scr_2dresolution", bound(0.0f, scr_2dresolution.value, 1.0f));
1018                 old2dresolution = scr_2dresolution.value;
1019         }
1020
1021         if (vid.realwidth > 320)
1022         {
1023                 vid.conwidth = (vid.realwidth - 320) * scr_2dresolution.value + 320;
1024                 vid.conwidth = bound(320, vid.conwidth, vid.realwidth);
1025         }
1026         else
1027                 vid.conwidth = 320;
1028
1029         if (vid.realheight > 240)
1030         {
1031                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1032                 vid.conheight = bound(240, vid.conheight, vid.realheight);
1033         }
1034         else
1035                 vid.conheight = 240;
1036
1037         SCR_SetUpToDrawConsole();
1038
1039         // determine size of refresh window
1040         SCR_CalcRefdef();
1041 }
1042
1043 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1044 void CL_UpdateScreen(void)
1045 {
1046         if (!scr_initialized || !con_initialized || vid_hidden)
1047                 return;                         // not initialized yet
1048
1049         if (cl_avidemo.integer)
1050                 SCR_CaptureAVIDemo();
1051         else
1052                 cl_avidemo_frame = 0;
1053
1054         if (cls.signon == SIGNONS)
1055                 R_TimeReport("other");
1056
1057         CL_SetupScreenSize();
1058
1059         DrawQ_Clear();
1060
1061         V_UpdateBlends();
1062         V_CalcRefdef ();
1063
1064         if (cls.signon == SIGNONS)
1065                 R_TimeReport("setup");
1066
1067         //FIXME: force menu if nothing else to look at?
1068         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1069
1070         if (scr_drawloading)
1071         {
1072                 scr_drawloading = false;
1073                 SCR_DrawLoading();
1074         }
1075         else
1076         {
1077                 if (cls.signon == SIGNONS)
1078                 {
1079                         SCR_DrawNet ();
1080                         SCR_DrawTurtle ();
1081                         SCR_DrawPause ();
1082                         Sbar_Draw();
1083                         SHOWLMP_drawall();
1084                         SCR_CheckDrawCenterString();
1085                 }
1086                 ui_draw();
1087                 CL_DrawVideo();
1088                 M_Draw();
1089                 if (cls.signon == SIGNONS)
1090                 {
1091                         R_TimeReport("2d");
1092                         R_TimeReport_End();
1093                         R_TimeReport_Start();
1094                 }
1095                 R_Shadow_EditLights_DrawSelectedLightProperties();
1096         }
1097         SCR_DrawConsole();
1098
1099         SCR_UpdateScreen();
1100 }
1101
1102 void CL_Screen_NewMap(void)
1103 {
1104         SHOWLMP_clear();
1105 }
1106