b9a10ed9effdbe992bd2af541e40ce373ad6516e
[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         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
753
754         if (jpeg)
755                 sprintf(filename, "dpavi%06d.jpg", cl_avidemo_frame);
756         else
757                 sprintf(filename, "dpavi%06d.tga", cl_avidemo_frame);
758
759         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
760                 cl_avidemo_frame++;
761         else
762         {
763                 Cvar_SetValueQuick(&cl_avidemo, 0);
764                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
765                 cl_avidemo_frame = 0;
766         }
767 }
768
769 /*
770 ===============
771 R_Envmap_f
772
773 Grab six views for environment mapping tests
774 ===============
775 */
776 struct
777 {
778         float angles[3];
779         char *name;
780 }
781 envmapinfo[6] =
782 {
783         {{  0,   0, 0}, "ft"},
784         {{  0,  90, 0}, "rt"},
785         {{  0, 180, 0}, "bk"},
786         {{  0, 270, 0}, "lf"},
787         {{-90,  90, 0}, "up"},
788         {{ 90,  90, 0}, "dn"}
789 };
790
791 static void R_Envmap_f (void)
792 {
793         int j, size;
794         char filename[256], basename[256];
795
796         if (Cmd_Argc() != 3)
797         {
798                 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");
799                 return;
800         }
801
802         strcpy(basename, Cmd_Argv(1));
803         size = atoi(Cmd_Argv(2));
804         if (size != 128 && size != 256 && size != 512 && size != 1024)
805         {
806                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
807                 return;
808         }
809         if (size > vid.realwidth || size > vid.realheight)
810         {
811                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
812                 return;
813         }
814
815         envmap = true;
816
817         r_refdef.x = 0;
818         r_refdef.y = 0;
819         r_refdef.width = size;
820         r_refdef.height = size;
821
822         r_refdef.fov_x = 90;
823         r_refdef.fov_y = 90;
824
825         for (j = 0;j < 6;j++)
826         {
827                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
828                 VectorCopy(envmapinfo[j].angles, r_refdef.viewangles);
829                 R_ClearScreen();
830                 R_RenderView ();
831                 SCR_ScreenShot(filename, vid.realx, vid.realy, size, size, false);
832         }
833
834         envmap = false;
835 }
836
837 //=============================================================================
838
839 // LordHavoc: SHOWLMP stuff
840 #define SHOWLMP_MAXLABELS 256
841 typedef struct showlmp_s
842 {
843         qboolean        isactive;
844         float           x;
845         float           y;
846         char            label[32];
847         char            pic[128];
848 }
849 showlmp_t;
850
851 showlmp_t showlmp[SHOWLMP_MAXLABELS];
852
853 void SHOWLMP_decodehide(void)
854 {
855         int i;
856         qbyte *lmplabel;
857         lmplabel = MSG_ReadString();
858         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
859                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
860                 {
861                         showlmp[i].isactive = false;
862                         return;
863                 }
864 }
865
866 void SHOWLMP_decodeshow(void)
867 {
868         int i, k;
869         qbyte lmplabel[256], picname[256];
870         float x, y;
871         strcpy(lmplabel,MSG_ReadString());
872         strcpy(picname, MSG_ReadString());
873         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
874         {
875                 x = MSG_ReadByte();
876                 y = MSG_ReadByte();
877         }
878         else
879         {
880                 x = MSG_ReadShort();
881                 y = MSG_ReadShort();
882         }
883         k = -1;
884         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
885                 if (showlmp[i].isactive)
886                 {
887                         if (strcmp(showlmp[i].label, lmplabel) == 0)
888                         {
889                                 k = i;
890                                 break; // drop out to replace it
891                         }
892                 }
893                 else if (k < 0) // find first empty one to replace
894                         k = i;
895         if (k < 0)
896                 return; // none found to replace
897         // change existing one
898         showlmp[k].isactive = true;
899         strcpy(showlmp[k].label, lmplabel);
900         strcpy(showlmp[k].pic, picname);
901         showlmp[k].x = x;
902         showlmp[k].y = y;
903 }
904
905 void SHOWLMP_drawall(void)
906 {
907         int i;
908         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
909                 if (showlmp[i].isactive)
910                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
911 }
912
913 void SHOWLMP_clear(void)
914 {
915         int i;
916         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
917                 showlmp[i].isactive = false;
918 }
919
920 void CL_SetupScreenSize(void)
921 {
922         static float old2dresolution = -1;
923
924         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
925
926         VID_UpdateGamma(false);
927
928         if (scr_2dresolution.value != old2dresolution)
929         {
930                 Cvar_SetValue("scr_2dresolution", bound(0.0f, scr_2dresolution.value, 1.0f));
931                 old2dresolution = scr_2dresolution.value;
932         }
933
934         if (vid.realwidth > 320)
935         {
936                 vid.conwidth = (vid.realwidth - 320) * scr_2dresolution.value + 320;
937                 vid.conwidth = bound(320, vid.conwidth, vid.realwidth);
938         }
939         else
940                 vid.conwidth = 320;
941
942         if (vid.realheight > 240)
943         {
944                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
945                 vid.conheight = bound(240, vid.conheight, vid.realheight);
946         }
947         else
948                 vid.conheight = 240;
949
950         SCR_SetUpToDrawConsole();
951
952         // determine size of refresh window
953         SCR_CalcRefdef();
954 }
955
956 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
957 void CL_UpdateScreen(void)
958 {
959         if (!scr_initialized || !con_initialized || vid_hidden)
960                 return;                         // not initialized yet
961
962         if (cl_avidemo.integer)
963                 SCR_CaptureAVIDemo();
964         else
965                 cl_avidemo_frame = 0;
966
967         if (cls.signon == SIGNONS)
968                 R_TimeReport("other");
969
970         CL_SetupScreenSize();
971
972         DrawQ_Clear();
973
974         if (!intimerefresh)
975                 V_CalcRefdef();
976
977         if (cls.signon == SIGNONS)
978                 R_TimeReport("setup");
979
980         //FIXME: force menu if nothing else to look at?
981         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
982
983         if (scr_drawloading)
984         {
985                 scr_drawloading = false;
986                 SCR_DrawLoading();
987         }
988         else
989         {
990                 if (cls.signon == SIGNONS)
991                 {
992                         SCR_DrawNet ();
993                         SCR_DrawTurtle ();
994                         SCR_DrawPause ();
995                         Sbar_Draw();
996                         SHOWLMP_drawall();
997                         SCR_CheckDrawCenterString();
998                 }
999                 ui_draw();
1000                 CL_DrawVideo();
1001                 M_Draw();
1002                 if (cls.signon == SIGNONS)
1003                 {
1004                         R_TimeReport("2d");
1005                         R_TimeReport_End();
1006                         R_TimeReport_Start();
1007                 }
1008                 R_Shadow_EditLights_DrawSelectedLightProperties();
1009         }
1010         SCR_DrawConsole();
1011
1012         SCR_UpdateScreen();
1013 }
1014
1015 void CL_Screen_NewMap(void)
1016 {
1017         SHOWLMP_clear();
1018 }
1019