split r_shadow_realtime into r_shadow_realtime_world (which requires stencil) and...
[xonotic/darkplaces.git] / cl_screen.c
1
2 #include "quakedef.h"
3 #include "cl_video.h"
4 #include "jpeg.h"
5
6 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
7 cvar_t scr_fov = {CVAR_SAVE, "fov","90"};       // 10 - 170
8 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
9 cvar_t scr_centertime = {0, "scr_centertime","2"};
10 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
11 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
12 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
13 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
14 cvar_t scr_2dresolution = {CVAR_SAVE, "scr_2dresolution", "1"};
15 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
16 cvar_t cl_avidemo = {0, "cl_avidemo", "0"};
17
18 qboolean        scr_initialized;                // ready to draw
19
20 float           scr_con_current;
21 float           scr_conlines;           // lines of console to display
22
23 int                     clearconsole;
24 int                     clearnotify;
25
26 qboolean        scr_drawloading = false;
27
28 static qbyte menuplyr_pixels[4096];
29
30 void DrawCrosshair(int num);
31 void V_CalcRefdef (void);
32 static void SCR_ScreenShot_f (void);
33 static void R_Envmap_f (void);
34
35 // backend
36 void R_ClearScreen(void);
37
38 /*
39 ===============================================================================
40
41 CENTER PRINTING
42
43 ===============================================================================
44 */
45
46 char            scr_centerstring[1024];
47 float           scr_centertime_start;   // for slow victory printing
48 float           scr_centertime_off;
49 int                     scr_center_lines;
50 int                     scr_erase_lines;
51 int                     scr_erase_center;
52
53 /*
54 ==============
55 SCR_CenterPrint
56
57 Called for important messages that should stay in the center of the screen
58 for a few moments
59 ==============
60 */
61 void SCR_CenterPrint (char *str)
62 {
63         strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
64         scr_centertime_off = scr_centertime.value;
65         scr_centertime_start = cl.time;
66
67 // count the number of lines for centering
68         scr_center_lines = 1;
69         while (*str)
70         {
71                 if (*str == '\n')
72                         scr_center_lines++;
73                 str++;
74         }
75 }
76
77
78 void SCR_DrawCenterString (void)
79 {
80         char    *start;
81         int             l;
82         int             x, y;
83         int             remaining;
84
85 // the finale prints the characters one at a time
86         if (cl.intermission)
87                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
88         else
89                 remaining = 9999;
90
91         scr_erase_center = 0;
92         start = scr_centerstring;
93
94         if (scr_center_lines <= 4)
95                 y = vid.conheight*0.35;
96         else
97                 y = 48;
98
99         do
100         {
101         // scan the width of the line
102                 for (l=0 ; l<40 ; l++)
103                         if (start[l] == '\n' || !start[l])
104                                 break;
105                 x = (vid.conwidth - l*8)/2;
106                 if (l > 0)
107                 {
108                         if (remaining < l)
109                                 l = remaining;
110                         DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
111                         remaining -= l;
112                         if (remaining <= 0)
113                                 return;
114                 }
115
116                 y += 8;
117
118                 while (*start && *start != '\n')
119                         start++;
120
121                 if (!*start)
122                         break;
123                 start++;                // skip the \n
124         } while (1);
125 }
126
127 void SCR_CheckDrawCenterString (void)
128 {
129         if (scr_center_lines > scr_erase_lines)
130                 scr_erase_lines = scr_center_lines;
131
132         scr_centertime_off -= host_frametime;
133
134         // don't draw if this is a normal stats-screen intermission,
135         // only if it is not an intermission, or a finale intermission
136         if (cl.intermission == 1)
137                 return;
138         if (scr_centertime_off <= 0 && !cl.intermission)
139                 return;
140         if (key_dest != key_game)
141                 return;
142
143         SCR_DrawCenterString ();
144 }
145
146 /*
147 ==============
148 SCR_DrawTurtle
149 ==============
150 */
151 void SCR_DrawTurtle (void)
152 {
153         static int      count;
154
155         if (cls.state != ca_connected)
156                 return;
157
158         if (!scr_showturtle.integer)
159                 return;
160
161         if (host_frametime < 0.1)
162         {
163                 count = 0;
164                 return;
165         }
166
167         count++;
168         if (count < 3)
169                 return;
170
171         DrawQ_Pic (0, 0, "turtle", 0, 0, 1, 1, 1, 1, 0);
172 }
173
174 /*
175 ==============
176 SCR_DrawNet
177 ==============
178 */
179 void SCR_DrawNet (void)
180 {
181         if (cls.state != ca_connected)
182                 return;
183         if (realtime - cl.last_received_message < 0.3)
184                 return;
185         if (cls.demoplayback)
186                 return;
187
188         DrawQ_Pic (64, 0, "net", 0, 0, 1, 1, 1, 1, 0);
189 }
190
191 /*
192 ==============
193 DrawPause
194 ==============
195 */
196 void SCR_DrawPause (void)
197 {
198         cachepic_t      *pic;
199
200         if (cls.state != ca_connected)
201                 return;
202
203         if (!scr_showpause.integer)             // turn off for screenshots
204                 return;
205
206         if (!cl.paused)
207                 return;
208
209         pic = Draw_CachePic ("gfx/pause.lmp");
210         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
211 }
212
213
214
215 /*
216 ==============
217 SCR_DrawLoading
218 ==============
219 */
220 void SCR_DrawLoading (void)
221 {
222         cachepic_t      *pic;
223
224         pic = Draw_CachePic ("gfx/loading.lmp");
225         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/loading.lmp", 0, 0, 1, 1, 1, 1, 0);
226 }
227
228
229
230 //=============================================================================
231
232
233 /*
234 ==================
235 SCR_SetUpToDrawConsole
236 ==================
237 */
238 void SCR_SetUpToDrawConsole (void)
239 {
240         Con_CheckResize ();
241
242         if (key_dest == key_game && cls.signon != SIGNONS)
243                 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
244         else
245                 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
246
247 // decide on the height of the console
248         if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
249                 scr_conlines = vid.conheight; // full screen
250         else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
251                 scr_conlines = vid.conheight/2; // half screen
252         else
253                 scr_conlines = 0;                               // none visible
254
255         if (scr_conspeed.value)
256         {
257                 if (scr_conlines < scr_con_current)
258                 {
259                         scr_con_current -= scr_conspeed.value*host_realframetime;
260                         if (scr_conlines > scr_con_current)
261                                 scr_con_current = scr_conlines;
262
263                 }
264                 else if (scr_conlines > scr_con_current)
265                 {
266                         scr_con_current += scr_conspeed.value*host_realframetime;
267                         if (scr_conlines < scr_con_current)
268                                 scr_con_current = scr_conlines;
269                 }
270         }
271         else
272                 scr_con_current = scr_conlines;
273 }
274
275 /*
276 ==================
277 SCR_DrawConsole
278 ==================
279 */
280 void SCR_DrawConsole (void)
281 {
282         if (scr_con_current)
283         {
284                 Con_DrawConsole (scr_con_current);
285                 clearconsole = 0;
286         }
287         else
288         {
289                 if (key_dest == key_game || key_dest == key_message)
290                         Con_DrawNotify ();      // only draw notify in game
291         }
292 }
293
294 /*
295 ===============
296 SCR_BeginLoadingPlaque
297
298 ================
299 */
300 void SCR_BeginLoadingPlaque (void)
301 {
302         if (scr_drawloading)
303                 return;
304
305         S_StopAllSounds (true);
306
307         scr_drawloading = true;
308         CL_UpdateScreen ();
309         scr_drawloading = true;
310         CL_UpdateScreen ();
311 }
312
313 //=============================================================================
314
315 char r_speeds_string[1024];
316 int speedstringcount, r_timereport_active;
317 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
318
319 void R_TimeReport(char *desc)
320 {
321         char tempbuf[256];
322         int length;
323         int t;
324
325         if (!r_timereport_active)
326                 return;
327
328         r_timereport_temp = r_timereport_current;
329         r_timereport_current = Sys_DoubleTime();
330         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
331
332         sprintf(tempbuf, "%8i %s", t, desc);
333         length = strlen(tempbuf);
334         while (length < 20)
335                 tempbuf[length++] = ' ';
336         tempbuf[length] = 0;
337         if (speedstringcount + length > (vid.conwidth / 8))
338         {
339                 strcat(r_speeds_string, "\n");
340                 speedstringcount = 0;
341         }
342         // skip the space at the beginning if it's the first on the line
343         if (speedstringcount == 0)
344         {
345                 strcat(r_speeds_string, tempbuf + 1);
346                 speedstringcount = length - 1;
347         }
348         else
349         {
350                 strcat(r_speeds_string, tempbuf);
351                 speedstringcount += length;
352         }
353 }
354
355 extern int c_rt_lights, c_rt_clears, c_rt_scissored;
356 extern int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
357 extern int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
358 void R_TimeReport_Start(void)
359 {
360         r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
361         r_speeds_string[0] = 0;
362         if (r_timereport_active)
363         {
364                 speedstringcount = 0;
365                 AngleVectors (r_refdef.viewangles, vpn, NULL, NULL);
366                 sprintf(r_speeds_string,
367                         "org:'%+8.2f %+8.2f %+8.2f' ang:'%+4.0f %+4.0f %+4.0f' dir:'%+2.3f %+2.3f %+2.3f'\n"
368                         "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
369                         "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
370                         "%6i modeltris%6i meshs%6i meshtris\n",
371                         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],
372                         c_faces, c_nodes, c_leafs, c_light_polys,
373                         c_models, c_bmodels, c_sprites, c_particles, c_dlights,
374                         c_alias_polys, c_meshs, c_meshelements / 3);
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                 c_alias_polys = 0;
385                 c_light_polys = 0;
386                 c_faces = 0;
387                 c_nodes = 0;
388                 c_leafs = 0;
389                 c_models = 0;
390                 c_bmodels = 0;
391                 c_sprites = 0;
392                 c_particles = 0;
393                 c_meshs = 0;
394                 c_meshelements = 0;
395
396                 r_timereport_start = Sys_DoubleTime();
397         }
398 }
399
400 void R_TimeReport_End(void)
401 {
402         r_timereport_current = r_timereport_start;
403         R_TimeReport("total");
404
405         if (r_timereport_active)
406         {
407                 int i, j, lines, y;
408                 lines = 1;
409                 for (i = 0;r_speeds_string[i];i++)
410                         if (r_speeds_string[i] == '\n')
411                                 lines++;
412                 y = vid.conheight - sb_lines - lines * 8;
413                 i = j = 0;
414                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
415                 while (r_speeds_string[i])
416                 {
417                         j = i;
418                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
419                                 i++;
420                         if (i - j > 0)
421                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
422                         if (r_speeds_string[i] == '\n')
423                                 i++;
424                         y += 8;
425                 }
426         }
427 }
428
429 /*
430 =================
431 SCR_SizeUp_f
432
433 Keybinding command
434 =================
435 */
436 void SCR_SizeUp_f (void)
437 {
438         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
439 }
440
441
442 /*
443 =================
444 SCR_SizeDown_f
445
446 Keybinding command
447 =================
448 */
449 void SCR_SizeDown_f (void)
450 {
451         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
452 }
453
454 void CL_Screen_Init(void)
455 {
456         qpic_t *dat;
457
458         Cvar_RegisterVariable (&scr_fov);
459         Cvar_RegisterVariable (&scr_viewsize);
460         Cvar_RegisterVariable (&scr_conspeed);
461         Cvar_RegisterVariable (&scr_showram);
462         Cvar_RegisterVariable (&scr_showturtle);
463         Cvar_RegisterVariable (&scr_showpause);
464         Cvar_RegisterVariable (&scr_centertime);
465         Cvar_RegisterVariable (&scr_printspeed);
466         Cvar_RegisterVariable (&scr_2dresolution);
467         Cvar_RegisterVariable (&scr_screenshot_jpeg);
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 *)FS_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         const char* extens;
819         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
820
821         if (jpeg)
822                 extens = "jpg";
823         else
824                 extens = "tga";
825
826         // find a file name to save it to
827         for (; i<=9999 ; i++)
828         {
829                 sprintf (filename, "dp%04i.%s", i, extens);
830                 sprintf (checkname, "%s/%s", fs_gamedir, filename);
831                 if (!FS_SysFileExists(checkname))
832                         break;
833         }
834         if (i==10000)
835         {
836                 Con_Printf ("SCR_ScreenShot_f: Couldn't create the image file\n");
837                 return;
838         }
839
840         if (SCR_ScreenShot (filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
841                 Con_Printf ("Wrote %s\n", filename);
842         else
843                 Con_Printf ("unable to write %s\n", filename);
844 }
845
846 static int cl_avidemo_frame = 0;
847
848 void SCR_CaptureAVIDemo(void)
849 {
850         char filename[32];
851         sprintf(filename, "dpavi%06d.tga", cl_avidemo_frame);
852         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, false))
853                 cl_avidemo_frame++;
854         else
855         {
856                 Cvar_SetValueQuick(&cl_avidemo, 0);
857                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
858                 cl_avidemo_frame = 0;
859         }
860 }
861
862 /*
863 ===============
864 R_Envmap_f
865
866 Grab six views for environment mapping tests
867 ===============
868 */
869 struct
870 {
871         float angles[3];
872         char *name;
873 }
874 envmapinfo[6] =
875 {
876         {{  0,   0, 0}, "ft"},
877         {{  0,  90, 0}, "rt"},
878         {{  0, 180, 0}, "bk"},
879         {{  0, 270, 0}, "lf"},
880         {{-90,  90, 0}, "up"},
881         {{ 90,  90, 0}, "dn"}
882 };
883
884 static void R_Envmap_f (void)
885 {
886         int j, size;
887         char filename[256], basename[256];
888
889         if (Cmd_Argc() != 3)
890         {
891                 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");
892                 return;
893         }
894
895         strcpy(basename, Cmd_Argv(1));
896         size = atoi(Cmd_Argv(2));
897         if (size != 128 && size != 256 && size != 512 && size != 1024)
898         {
899                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
900                 return;
901         }
902         if (size > vid.realwidth || size > vid.realheight)
903         {
904                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
905                 return;
906         }
907
908         envmap = true;
909
910         r_refdef.x = 0;
911         r_refdef.y = 0;
912         r_refdef.width = size;
913         r_refdef.height = size;
914
915         r_refdef.fov_x = 90;
916         r_refdef.fov_y = 90;
917
918         for (j = 0;j < 6;j++)
919         {
920                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
921                 VectorCopy(envmapinfo[j].angles, r_refdef.viewangles);
922                 R_ClearScreen();
923                 R_RenderView ();
924                 SCR_ScreenShot(filename, vid.realx, vid.realy, size, size, false);
925         }
926
927         envmap = false;
928 }
929
930 //=============================================================================
931
932 // LordHavoc: SHOWLMP stuff
933 #define SHOWLMP_MAXLABELS 256
934 typedef struct showlmp_s
935 {
936         qboolean        isactive;
937         float           x;
938         float           y;
939         char            label[32];
940         char            pic[128];
941 }
942 showlmp_t;
943
944 showlmp_t showlmp[SHOWLMP_MAXLABELS];
945
946 void SHOWLMP_decodehide(void)
947 {
948         int i;
949         qbyte *lmplabel;
950         lmplabel = MSG_ReadString();
951         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
952                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
953                 {
954                         showlmp[i].isactive = false;
955                         return;
956                 }
957 }
958
959 void SHOWLMP_decodeshow(void)
960 {
961         int i, k;
962         qbyte lmplabel[256], picname[256];
963         float x, y;
964         strcpy(lmplabel,MSG_ReadString());
965         strcpy(picname, MSG_ReadString());
966         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
967         {
968                 x = MSG_ReadByte();
969                 y = MSG_ReadByte();
970         }
971         else
972         {
973                 x = MSG_ReadShort();
974                 y = MSG_ReadShort();
975         }
976         k = -1;
977         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
978                 if (showlmp[i].isactive)
979                 {
980                         if (strcmp(showlmp[i].label, lmplabel) == 0)
981                         {
982                                 k = i;
983                                 break; // drop out to replace it
984                         }
985                 }
986                 else if (k < 0) // find first empty one to replace
987                         k = i;
988         if (k < 0)
989                 return; // none found to replace
990         // change existing one
991         showlmp[k].isactive = true;
992         strcpy(showlmp[k].label, lmplabel);
993         strcpy(showlmp[k].pic, picname);
994         showlmp[k].x = x;
995         showlmp[k].y = y;
996 }
997
998 void SHOWLMP_drawall(void)
999 {
1000         int i;
1001         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1002                 if (showlmp[i].isactive)
1003                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1004 }
1005
1006 void SHOWLMP_clear(void)
1007 {
1008         int i;
1009         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1010                 showlmp[i].isactive = false;
1011 }
1012
1013 void CL_SetupScreenSize(void)
1014 {
1015         static float old2dresolution = -1;
1016
1017         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1018
1019         VID_UpdateGamma(false);
1020
1021         if (scr_2dresolution.value != old2dresolution)
1022         {
1023                 Cvar_SetValue("scr_2dresolution", bound(0.0f, scr_2dresolution.value, 1.0f));
1024                 old2dresolution = scr_2dresolution.value;
1025         }
1026
1027         if (vid.realwidth > 320)
1028         {
1029                 vid.conwidth = (vid.realwidth - 320) * scr_2dresolution.value + 320;
1030                 vid.conwidth = bound(320, vid.conwidth, vid.realwidth);
1031         }
1032         else
1033                 vid.conwidth = 320;
1034
1035         if (vid.realheight > 240)
1036         {
1037                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1038                 vid.conheight = bound(240, vid.conheight, vid.realheight);
1039         }
1040         else
1041                 vid.conheight = 240;
1042
1043         SCR_SetUpToDrawConsole();
1044
1045         // determine size of refresh window
1046         SCR_CalcRefdef();
1047 }
1048
1049 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1050 void CL_UpdateScreen(void)
1051 {
1052         if (!scr_initialized || !con_initialized || vid_hidden)
1053                 return;                         // not initialized yet
1054
1055         if (cl_avidemo.integer)
1056                 SCR_CaptureAVIDemo();
1057         else
1058                 cl_avidemo_frame = 0;
1059
1060         if (cls.signon == SIGNONS)
1061                 R_TimeReport("other");
1062
1063         CL_SetupScreenSize();
1064
1065         DrawQ_Clear();
1066
1067         V_UpdateBlends();
1068         V_CalcRefdef ();
1069
1070         if (cls.signon == SIGNONS)
1071                 R_TimeReport("setup");
1072
1073         //FIXME: force menu if nothing else to look at?
1074         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1075
1076         if (scr_drawloading)
1077         {
1078                 scr_drawloading = false;
1079                 SCR_DrawLoading();
1080         }
1081         else
1082         {
1083                 if (cls.signon == SIGNONS)
1084                 {
1085                         SCR_DrawNet ();
1086                         SCR_DrawTurtle ();
1087                         SCR_DrawPause ();
1088                         Sbar_Draw();
1089                         SHOWLMP_drawall();
1090                         SCR_CheckDrawCenterString();
1091                 }
1092                 ui_draw();
1093                 CL_DrawVideo();
1094                 M_Draw();
1095                 if (cls.signon == SIGNONS)
1096                 {
1097                         R_TimeReport("2d");
1098                         R_TimeReport_End();
1099                         R_TimeReport_Start();
1100                 }
1101                 R_Shadow_EditLights_DrawSelectedLightProperties();
1102         }
1103         SCR_DrawConsole();
1104
1105         SCR_UpdateScreen();
1106 }
1107
1108 void CL_Screen_NewMap(void)
1109 {
1110         SHOWLMP_clear();
1111 }
1112