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