]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_screen.c
added scr_screenshot_jpeg_quality cvar (and added it to the 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 (&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
482 void DrawQ_Clear(void)
483 {
484         r_refdef.drawqueuesize = 0;
485 }
486
487 static int picelements[6] = {0, 1, 2, 0, 2, 3};
488 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
489 {
490         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);
491 }
492
493 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)
494 {
495         int size, len;
496         drawqueue_t *dq;
497         char *out;
498         if (alpha < (1.0f / 255.0f))
499                 return;
500         if (maxlen < 1)
501                 len = strlen(string);
502         else
503                 for (len = 0;len < maxlen && string[len];len++);
504         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
505         for (;len > 0 && string[len - 1] == ' ';len--);
506         if (len < 1)
507                 return;
508         if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * maxlen) || y < (-scaley))
509                 return;
510         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
511         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
512                 return;
513         red = bound(0, red, 1);
514         green = bound(0, green, 1);
515         blue = bound(0, blue, 1);
516         alpha = bound(0, alpha, 1);
517         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
518         dq->size = size;
519         dq->command = DRAWQUEUE_STRING;
520         dq->flags = flags;
521         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));
522         dq->x = x;
523         dq->y = y;
524         dq->scalex = scalex;
525         dq->scaley = scaley;
526         out = (char *)(dq + 1);
527         memcpy(out, string, len);
528         out[len] = 0;
529         r_refdef.drawqueuesize += dq->size;
530 }
531
532 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
533 {
534         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);
535 }
536
537 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)
538 {
539         float floats[36];
540         cachepic_t *pic;
541         drawqueuemesh_t mesh;
542         memset(&mesh, 0, sizeof(mesh));
543         if (picname && picname[0])
544         {
545                 pic = Draw_CachePic(picname);
546                 if (width == 0)
547                         width = pic->width;
548                 if (height == 0)
549                         height = pic->height;
550                 mesh.texture = pic->tex;
551         }
552         mesh.num_triangles = 2;
553         mesh.num_vertices = 4;
554         mesh.data_element3i = picelements;
555         mesh.data_vertex3f = floats;
556         mesh.data_texcoord2f = floats + 12;
557         mesh.data_color4f = floats + 20;
558         memset(floats, 0, sizeof(floats));
559         mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
560         mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
561         mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
562         mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
563         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;
564         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;
565         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;
566         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;
567         DrawQ_Mesh (&mesh, flags);
568 }
569
570 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
571 {
572         int size;
573         void *p;
574         drawqueue_t *dq;
575         drawqueuemesh_t *m;
576         size = sizeof(*dq);
577         size += sizeof(drawqueuemesh_t);
578         size += sizeof(int[3]) * mesh->num_triangles;
579         size += sizeof(float[3]) * mesh->num_vertices;
580         size += sizeof(float[2]) * mesh->num_vertices;
581         size += sizeof(float[4]) * mesh->num_vertices;
582         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
583                 return;
584         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
585         dq->size = size;
586         dq->command = DRAWQUEUE_MESH;
587         dq->flags = flags;
588         dq->color = 0;
589         dq->x = 0;
590         dq->y = 0;
591         dq->scalex = 0;
592         dq->scaley = 0;
593         p = (void *)(dq + 1);
594         m = p;(qbyte *)p += sizeof(drawqueuemesh_t);
595         m->num_triangles = mesh->num_triangles;
596         m->num_vertices = mesh->num_vertices;
597         m->texture = mesh->texture;
598         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]);
599         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]);
600         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]);
601         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]);
602         r_refdef.drawqueuesize += dq->size;
603 }
604
605 void DrawQ_SetClipArea(float x, float y, float width, float height)
606 {
607         drawqueue_t * dq;
608         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
609         {
610                 Con_DPrintf("DrawQueue full !\n");
611                 return;
612         }
613         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
614         dq->size = sizeof(*dq);
615         dq->command = DRAWQUEUE_SETCLIP;
616         dq->x = x;
617         dq->y = y;
618         dq->scalex = width;
619         dq->scaley = height;
620         dq->flags = 0;
621         dq->color = 0;
622
623         r_refdef.drawqueuesize += dq->size;
624 }
625
626 void DrawQ_ResetClipArea(void)
627 {
628         drawqueue_t *dq;
629         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
630         {
631                 Con_DPrintf("DrawQueue full !\n");
632                 return;
633         }
634         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
635         dq->size = sizeof(*dq);
636         dq->command = DRAWQUEUE_RESETCLIP;
637         dq->x = 0;
638         dq->y = 0;
639         dq->scalex = 0;
640         dq->scaley = 0;
641         dq->flags = 0;
642         dq->color = 0;
643
644         r_refdef.drawqueuesize += dq->size;
645 }
646
647 /*
648 ====================
649 CalcFov
650 ====================
651 */
652 float CalcFov (float fov_x, float width, float height)
653 {
654         // calculate vision size and alter by aspect, then convert back to angle
655         return atan (height / (width / tan(fov_x/360*M_PI))) * 360 / M_PI;
656 }
657
658 /*
659 =================
660 SCR_CalcRefdef
661
662 Must be called whenever vid changes
663 Internal use only
664 =================
665 */
666 static void SCR_CalcRefdef (void)
667 {
668         float size;
669         int contents;
670
671 //========================================
672
673 // bound viewsize
674         if (scr_viewsize.value < 30)
675                 Cvar_Set ("viewsize","30");
676         if (scr_viewsize.value > 120)
677                 Cvar_Set ("viewsize","120");
678
679 // bound field of view
680         if (scr_fov.value < 10)
681                 Cvar_Set ("fov","10");
682         if (scr_fov.value > 170)
683                 Cvar_Set ("fov","170");
684
685 // intermission is always full screen
686         if (cl.intermission)
687         {
688                 size = 1;
689                 sb_lines = 0;
690         }
691         else
692         {
693                 if (scr_viewsize.value >= 120)
694                         sb_lines = 0;           // no status bar at all
695                 else if (scr_viewsize.value >= 110)
696                         sb_lines = 24;          // no inventory
697                 else
698                         sb_lines = 24+16+8;
699                 size = scr_viewsize.value * (1.0 / 100.0);
700         }
701
702         if (size >= 1)
703         {
704                 r_refdef.width = vid.realwidth;
705                 r_refdef.height = vid.realheight;
706                 r_refdef.x = 0;
707                 r_refdef.y = 0;
708         }
709         else
710         {
711                 r_refdef.width = vid.realwidth * size;
712                 r_refdef.height = vid.realheight * size;
713                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
714                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
715         }
716
717         r_refdef.width = bound(0, r_refdef.width, vid.realwidth);
718         r_refdef.height = bound(0, r_refdef.height, vid.realheight);
719         r_refdef.x = bound(0, r_refdef.x, vid.realwidth - r_refdef.width) + vid.realx;
720         r_refdef.y = bound(0, r_refdef.y, vid.realheight - r_refdef.height) + vid.realy;
721
722         // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
723         r_refdef.fov_x = scr_fov.value * cl.viewzoom;
724         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
725
726         if (cl.worldmodel)
727         {
728                 Mod_CheckLoaded(cl.worldmodel);
729                 contents = CL_PointSuperContents(r_vieworigin);
730                 if (contents & SUPERCONTENTS_LIQUIDSMASK)
731                 {
732                         r_refdef.fov_x *= (sin(cl.time * 4.7) * 0.015 + 0.985);
733                         r_refdef.fov_y *= (sin(cl.time * 3.0) * 0.015 + 0.985);
734                 }
735         }
736 }
737
738 /*
739 ==================
740 SCR_ScreenShot_f
741 ==================
742 */
743 void SCR_ScreenShot_f (void)
744 {
745         static int i = 0;
746         char filename[16];
747         char checkname[MAX_OSPATH];
748         const char* extens;
749         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
750
751         if (jpeg)
752                 extens = "jpg";
753         else
754                 extens = "tga";
755
756         // find a file name to save it to
757         for (; i<=9999 ; i++)
758         {
759                 sprintf (filename, "dp%04i.%s", i, extens);
760                 sprintf (checkname, "%s/%s", fs_gamedir, filename);
761                 if (!FS_SysFileExists(checkname))
762                         break;
763         }
764         if (i==10000)
765         {
766                 Con_Printf ("SCR_ScreenShot_f: Couldn't create the image file\n");
767                 return;
768         }
769
770         if (SCR_ScreenShot (filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
771                 Con_Printf ("Wrote %s\n", filename);
772         else
773                 Con_Printf ("unable to write %s\n", filename);
774 }
775
776 static int cl_avidemo_frame = 0;
777
778 void SCR_CaptureAVIDemo(void)
779 {
780         char filename[32];
781         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
782
783         if (jpeg)
784                 sprintf(filename, "dpavi%06d.jpg", cl_avidemo_frame);
785         else
786                 sprintf(filename, "dpavi%06d.tga", cl_avidemo_frame);
787
788         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
789                 cl_avidemo_frame++;
790         else
791         {
792                 Cvar_SetValueQuick(&cl_avidemo, 0);
793                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
794                 cl_avidemo_frame = 0;
795         }
796 }
797
798 /*
799 ===============
800 R_Envmap_f
801
802 Grab six views for environment mapping tests
803 ===============
804 */
805 struct
806 {
807         float angles[3];
808         char *name;
809 }
810 envmapinfo[6] =
811 {
812         {{  0,   0, 0}, "ft"},
813         {{  0,  90, 0}, "rt"},
814         {{  0, 180, 0}, "bk"},
815         {{  0, 270, 0}, "lf"},
816         {{-90,  90, 0}, "up"},
817         {{ 90,  90, 0}, "dn"}
818 };
819
820 static void R_Envmap_f (void)
821 {
822         int j, size;
823         char filename[256], basename[256];
824
825         if (Cmd_Argc() != 3)
826         {
827                 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");
828                 return;
829         }
830
831         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
832         size = atoi(Cmd_Argv(2));
833         if (size != 128 && size != 256 && size != 512 && size != 1024)
834         {
835                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
836                 return;
837         }
838         if (size > vid.realwidth || size > vid.realheight)
839         {
840                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
841                 return;
842         }
843
844         envmap = true;
845
846         r_refdef.x = 0;
847         r_refdef.y = 0;
848         r_refdef.width = size;
849         r_refdef.height = size;
850
851         r_refdef.fov_x = 90;
852         r_refdef.fov_y = 90;
853
854         for (j = 0;j < 6;j++)
855         {
856                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
857                 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);
858                 R_ClearScreen();
859                 R_RenderView();
860                 SCR_ScreenShot(filename, vid.realx, vid.realy + vid.realheight - (r_refdef.y + r_refdef.height), size, size, false);
861         }
862
863         envmap = false;
864 }
865
866 //=============================================================================
867
868 // LordHavoc: SHOWLMP stuff
869 #define SHOWLMP_MAXLABELS 256
870 typedef struct showlmp_s
871 {
872         qboolean        isactive;
873         float           x;
874         float           y;
875         char            label[32];
876         char            pic[128];
877 }
878 showlmp_t;
879
880 showlmp_t showlmp[SHOWLMP_MAXLABELS];
881
882 void SHOWLMP_decodehide(void)
883 {
884         int i;
885         qbyte *lmplabel;
886         lmplabel = MSG_ReadString();
887         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
888                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
889                 {
890                         showlmp[i].isactive = false;
891                         return;
892                 }
893 }
894
895 void SHOWLMP_decodeshow(void)
896 {
897         int i, k;
898         qbyte lmplabel[256], picname[256];
899         float x, y;
900         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
901         strlcpy (picname, MSG_ReadString(), sizeof (picname));
902         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
903         {
904                 x = MSG_ReadByte();
905                 y = MSG_ReadByte();
906         }
907         else
908         {
909                 x = MSG_ReadShort();
910                 y = MSG_ReadShort();
911         }
912         k = -1;
913         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
914                 if (showlmp[i].isactive)
915                 {
916                         if (strcmp(showlmp[i].label, lmplabel) == 0)
917                         {
918                                 k = i;
919                                 break; // drop out to replace it
920                         }
921                 }
922                 else if (k < 0) // find first empty one to replace
923                         k = i;
924         if (k < 0)
925                 return; // none found to replace
926         // change existing one
927         showlmp[k].isactive = true;
928         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
929         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
930         showlmp[k].x = x;
931         showlmp[k].y = y;
932 }
933
934 void SHOWLMP_drawall(void)
935 {
936         int i;
937         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
938                 if (showlmp[i].isactive)
939                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
940 }
941
942 void SHOWLMP_clear(void)
943 {
944         int i;
945         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
946                 showlmp[i].isactive = false;
947 }
948
949 void CL_SetupScreenSize(void)
950 {
951         float conwidth, conheight;
952
953         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
954
955         VID_UpdateGamma(false);
956
957         conwidth = bound(320, vid_conwidth.value, 2048);
958         conheight = bound(200, vid_conheight.value, 1536);
959         if (vid_conwidth.value != conwidth)
960                 Cvar_SetValue("vid_conwidth", conwidth);
961         if (vid_conheight.value != conheight)
962                 Cvar_SetValue("vid_conheight", conheight);
963
964         vid.conwidth = vid_conwidth.integer;
965         vid.conheight = vid_conheight.integer;
966
967 /*      if (vid.realheight > 240)
968         {
969                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
970                 vid.conheight = bound(240, vid.conheight, vid.realheight);
971         }
972         else
973                 vid.conheight = 240;*/
974
975         SCR_SetUpToDrawConsole();
976
977         // determine size of refresh window
978         SCR_CalcRefdef();
979 }
980
981 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
982 void CL_UpdateScreen(void)
983 {
984         if (!scr_initialized || !con_initialized || vid_hidden)
985                 return;                         // not initialized yet
986
987         if (cl_avidemo.integer)
988                 SCR_CaptureAVIDemo();
989         else
990                 cl_avidemo_frame = 0;
991
992         if (cls.signon == SIGNONS)
993                 R_TimeReport("other");
994
995         CL_SetupScreenSize();
996
997         DrawQ_Clear();
998
999         if (cls.signon == SIGNONS)
1000                 R_TimeReport("setup");
1001
1002         //FIXME: force menu if nothing else to look at?
1003         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1004
1005         if (scr_drawloading)
1006         {
1007                 scr_drawloading = false;
1008                 SCR_DrawLoading();
1009         }
1010         else
1011         {
1012                 if (cls.signon == SIGNONS)
1013                 {
1014                         SCR_DrawNet ();
1015                         SCR_DrawTurtle ();
1016                         SCR_DrawPause ();
1017                         Sbar_Draw();
1018                         SHOWLMP_drawall();
1019                         SCR_CheckDrawCenterString();
1020                 }
1021                 MR_Draw();
1022                 CL_DrawVideo();
1023                 ui_draw();
1024                 if (cls.signon == SIGNONS)
1025                 {
1026                         R_TimeReport("2d");
1027                         R_TimeReport_End();
1028                         R_TimeReport_Start();
1029                 }
1030                 R_Shadow_EditLights_DrawSelectedLightProperties();
1031         }
1032         SCR_DrawConsole();
1033
1034         SCR_UpdateScreen();
1035 }
1036
1037 void CL_Screen_NewMap(void)
1038 {
1039         SHOWLMP_clear();
1040 }
1041