]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
more cleanups of R_RenderScene (now r_view_ variables exist which are copied from...
[divverent/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"};       // 1 - 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 SCR_ScreenShot_f
651 ==================
652 */
653 void SCR_ScreenShot_f (void)
654 {
655         static int shotnumber = 0;
656         const char *base;
657         char filename[64];
658         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
659
660         base = "screenshots/dp";
661         if (gamemode == GAME_FNIGGIUM)
662                 base = "screenshots/fniggium";
663         
664         // find a file name to save it to
665         for (;shotnumber < 1000000;shotnumber++)
666                 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
667                         break;
668         if (shotnumber >= 1000000)
669         {
670                 Con_Printf("SCR_ScreenShot_f: Couldn't create the image file\n");
671                 return;
672         }
673
674         if (jpeg)
675                 sprintf(filename, "%s%06d.jpg", base, shotnumber);
676         else
677                 sprintf(filename, "%s%06d.tga", base, shotnumber);
678
679         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
680                 Con_Printf("Wrote %s\n", filename);
681         else
682                 Con_Printf("unable to write %s\n", filename);
683         shotnumber++;
684 }
685
686 static int cl_avidemo_frame = 0;
687
688 void SCR_CaptureAVIDemo(void)
689 {
690         char filename[32];
691         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
692
693         if (jpeg)
694                 sprintf(filename, "video/dp%06d.jpg", cl_avidemo_frame);
695         else
696                 sprintf(filename, "video/dp%06d.tga", cl_avidemo_frame);
697
698         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
699                 cl_avidemo_frame++;
700         else
701         {
702                 Cvar_SetValueQuick(&cl_avidemo, 0);
703                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
704                 cl_avidemo_frame = 0;
705         }
706 }
707
708 /*
709 ===============
710 R_Envmap_f
711
712 Grab six views for environment mapping tests
713 ===============
714 */
715 struct
716 {
717         float angles[3];
718         char *name;
719 }
720 envmapinfo[6] =
721 {
722         {{  0,   0, 0}, "ft"},
723         {{  0,  90, 0}, "rt"},
724         {{  0, 180, 0}, "bk"},
725         {{  0, 270, 0}, "lf"},
726         {{-90,  90, 0}, "up"},
727         {{ 90,  90, 0}, "dn"}
728 };
729
730 static void R_Envmap_f (void)
731 {
732         int j, size;
733         char filename[256], basename[256];
734
735         if (Cmd_Argc() != 3)
736         {
737                 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");
738                 return;
739         }
740
741         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
742         size = atoi(Cmd_Argv(2));
743         if (size != 128 && size != 256 && size != 512 && size != 1024)
744         {
745                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
746                 return;
747         }
748         if (size > vid.realwidth || size > vid.realheight)
749         {
750                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
751                 return;
752         }
753
754         envmap = true;
755
756         r_refdef.x = 0;
757         r_refdef.y = 0;
758         r_refdef.width = size;
759         r_refdef.height = size;
760
761         r_refdef.fov_x = 90;
762         r_refdef.fov_y = 90;
763
764         for (j = 0;j < 6;j++)
765         {
766                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
767                 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);
768                 R_ClearScreen();
769                 R_RenderView();
770                 SCR_ScreenShot(filename, vid.realx, vid.realy + vid.realheight - (r_refdef.y + r_refdef.height), size, size, false);
771         }
772
773         envmap = false;
774 }
775
776 //=============================================================================
777
778 // LordHavoc: SHOWLMP stuff
779 #define SHOWLMP_MAXLABELS 256
780 typedef struct showlmp_s
781 {
782         qboolean        isactive;
783         float           x;
784         float           y;
785         char            label[32];
786         char            pic[128];
787 }
788 showlmp_t;
789
790 showlmp_t showlmp[SHOWLMP_MAXLABELS];
791
792 void SHOWLMP_decodehide(void)
793 {
794         int i;
795         qbyte *lmplabel;
796         lmplabel = MSG_ReadString();
797         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
798                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
799                 {
800                         showlmp[i].isactive = false;
801                         return;
802                 }
803 }
804
805 void SHOWLMP_decodeshow(void)
806 {
807         int i, k;
808         qbyte lmplabel[256], picname[256];
809         float x, y;
810         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
811         strlcpy (picname, MSG_ReadString(), sizeof (picname));
812         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
813         {
814                 x = MSG_ReadByte();
815                 y = MSG_ReadByte();
816         }
817         else
818         {
819                 x = MSG_ReadShort();
820                 y = MSG_ReadShort();
821         }
822         k = -1;
823         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
824                 if (showlmp[i].isactive)
825                 {
826                         if (strcmp(showlmp[i].label, lmplabel) == 0)
827                         {
828                                 k = i;
829                                 break; // drop out to replace it
830                         }
831                 }
832                 else if (k < 0) // find first empty one to replace
833                         k = i;
834         if (k < 0)
835                 return; // none found to replace
836         // change existing one
837         showlmp[k].isactive = true;
838         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
839         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
840         showlmp[k].x = x;
841         showlmp[k].y = y;
842 }
843
844 void SHOWLMP_drawall(void)
845 {
846         int i;
847         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
848                 if (showlmp[i].isactive)
849                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
850 }
851
852 void SHOWLMP_clear(void)
853 {
854         int i;
855         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
856                 showlmp[i].isactive = false;
857 }
858
859 void CL_SetupScreenSize(void)
860 {
861         float conwidth, conheight;
862
863         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
864
865         VID_UpdateGamma(false);
866
867         conwidth = bound(320, vid_conwidth.value, 2048);
868         conheight = bound(200, vid_conheight.value, 1536);
869         if (vid_conwidth.value != conwidth)
870                 Cvar_SetValue("vid_conwidth", conwidth);
871         if (vid_conheight.value != conheight)
872                 Cvar_SetValue("vid_conheight", conheight);
873
874         vid.conwidth = vid_conwidth.integer;
875         vid.conheight = vid_conheight.integer;
876
877 /*      if (vid.realheight > 240)
878         {
879                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
880                 vid.conheight = bound(240, vid.conheight, vid.realheight);
881         }
882         else
883                 vid.conheight = 240;*/
884
885         SCR_SetUpToDrawConsole();
886 }
887
888 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
889 void CL_UpdateScreen(void)
890 {
891         if (!scr_initialized || !con_initialized || vid_hidden)
892                 return;                         // not initialized yet
893
894         if (cl_avidemo.integer)
895                 SCR_CaptureAVIDemo();
896         else
897                 cl_avidemo_frame = 0;
898
899         if (cls.signon == SIGNONS)
900                 R_TimeReport("other");
901
902         CL_SetupScreenSize();
903
904         DrawQ_Clear();
905
906         if (cls.signon == SIGNONS)
907                 R_TimeReport("setup");
908
909         //FIXME: force menu if nothing else to look at?
910         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
911
912         if (scr_drawloading)
913         {
914                 scr_drawloading = false;
915                 SCR_DrawLoading();
916         }
917         else
918         {
919                 if (cls.signon == SIGNONS)
920                 {
921                         SCR_DrawNet ();
922                         SCR_DrawTurtle ();
923                         SCR_DrawPause ();
924                         Sbar_Draw();
925                         SHOWLMP_drawall();
926                         SCR_CheckDrawCenterString();
927                 }
928                 MR_Draw();
929                 UI_Callback_Draw();
930                 CL_DrawVideo();
931                 //ui_draw();
932                 if (cls.signon == SIGNONS)
933                 {
934                         R_TimeReport("2d");
935                         R_TimeReport_End();
936                         R_TimeReport_Start();
937                 }
938                 R_Shadow_EditLights_DrawSelectedLightProperties();
939         }
940         SCR_DrawConsole();
941
942         SCR_UpdateScreen();
943 }
944
945 void CL_Screen_NewMap(void)
946 {
947         SHOWLMP_clear();
948 }
949