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