]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
now aborts video capture if it falls behind by more than a second (as the sound is...
[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_conforcewhiledisconnected = {CVAR_SAVE, "scr_conforcewhiledisconnected", "1"};
13 cvar_t scr_centertime = {0, "scr_centertime","2"};
14 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
15 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
16 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
17 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
18 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640"};
19 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480"};
20 cvar_t vid_pixelaspect = {CVAR_SAVE, "vid_pixelaspect", "1"};
21 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
22 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9"};
23 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp"};
24 cvar_t cl_capturevideo = {0, "cl_capturevideo", "0"};
25 cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30"};
26 cvar_t cl_capturevideo_raw = {0, "cl_capturevideo_raw", "0"};
27 cvar_t r_textshadow = {0, "r_textshadow", "0"};
28 cvar_t r_letterbox = {0, "r_letterbox", "0"};
29
30 int jpeg_supported = false;
31
32 qboolean        scr_initialized;                // ready to draw
33
34 float           scr_con_current;
35 float           scr_conlines;           // lines of console to display
36
37 extern int      con_vislines;
38
39 qboolean        scr_drawloading = false;
40
41 void DrawCrosshair(int num);
42 static void SCR_ScreenShot_f (void);
43 static void R_Envmap_f (void);
44
45 // backend
46 void R_ClearScreen(void);
47
48 /*
49 ===============================================================================
50
51 CENTER PRINTING
52
53 ===============================================================================
54 */
55
56 char            scr_centerstring[1024];
57 float           scr_centertime_start;   // for slow victory printing
58 float           scr_centertime_off;
59 int                     scr_center_lines;
60 int                     scr_erase_lines;
61 int                     scr_erase_center;
62
63 /*
64 ==============
65 SCR_CenterPrint
66
67 Called for important messages that should stay in the center of the screen
68 for a few moments
69 ==============
70 */
71 void SCR_CenterPrint(char *str)
72 {
73         strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
74         scr_centertime_off = scr_centertime.value;
75         scr_centertime_start = cl.time;
76
77 // count the number of lines for centering
78         scr_center_lines = 1;
79         while (*str)
80         {
81                 if (*str == '\n')
82                         scr_center_lines++;
83                 str++;
84         }
85 }
86
87
88 void SCR_DrawCenterString (void)
89 {
90         char    *start;
91         int             l;
92         int             x, y;
93         int             remaining;
94
95 // the finale prints the characters one at a time
96         if (cl.intermission)
97                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
98         else
99                 remaining = 9999;
100
101         scr_erase_center = 0;
102         start = scr_centerstring;
103
104         if (scr_center_lines <= 4)
105                 y = vid.conheight*0.35;
106         else
107                 y = 48;
108
109         do
110         {
111         // scan the width of the line
112                 for (l=0 ; l<40 ; l++)
113                         if (start[l] == '\n' || !start[l])
114                                 break;
115                 x = (vid.conwidth - l*8)/2;
116                 if (l > 0)
117                 {
118                         if (remaining < l)
119                                 l = remaining;
120                         DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
121                         remaining -= l;
122                         if (remaining <= 0)
123                                 return;
124                 }
125
126                 y += 8;
127
128                 while (*start && *start != '\n')
129                         start++;
130
131                 if (!*start)
132                         break;
133                 start++;                // skip the \n
134         } while (1);
135 }
136
137 void SCR_CheckDrawCenterString (void)
138 {
139         if (scr_center_lines > scr_erase_lines)
140                 scr_erase_lines = scr_center_lines;
141
142         scr_centertime_off -= host_frametime;
143
144         // don't draw if this is a normal stats-screen intermission,
145         // only if it is not an intermission, or a finale intermission
146         if (cl.intermission == 1)
147                 return;
148         if (scr_centertime_off <= 0 && !cl.intermission)
149                 return;
150         if (key_dest != key_game)
151                 return;
152
153         SCR_DrawCenterString ();
154 }
155
156 /*
157 ==============
158 SCR_DrawTurtle
159 ==============
160 */
161 void SCR_DrawTurtle (void)
162 {
163         static int      count;
164
165         if (cls.state != ca_connected)
166                 return;
167
168         if (!scr_showturtle.integer)
169                 return;
170
171         if (host_frametime < 0.1)
172         {
173                 count = 0;
174                 return;
175         }
176
177         count++;
178         if (count < 3)
179                 return;
180
181         DrawQ_Pic (0, 0, "gfx/turtle.lmp", 0, 0, 1, 1, 1, 1, 0);
182 }
183
184 /*
185 ==============
186 SCR_DrawNet
187 ==============
188 */
189 void SCR_DrawNet (void)
190 {
191         if (cls.state != ca_connected)
192                 return;
193         if (realtime - cl.last_received_message < 0.3)
194                 return;
195         if (cls.demoplayback)
196                 return;
197
198         DrawQ_Pic (64, 0, "gfx/net.lmp", 0, 0, 1, 1, 1, 1, 0);
199 }
200
201 /*
202 ==============
203 DrawPause
204 ==============
205 */
206 void SCR_DrawPause (void)
207 {
208         cachepic_t      *pic;
209
210         if (cls.state != ca_connected)
211                 return;
212
213         if (!scr_showpause.integer)             // turn off for screenshots
214                 return;
215
216         if (!cl.paused)
217                 return;
218
219         pic = Draw_CachePic ("gfx/pause.lmp");
220         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
221 }
222
223
224
225 /*
226 ==============
227 SCR_DrawLoading
228 ==============
229 */
230 void SCR_DrawLoading (void)
231 {
232         cachepic_t      *pic;
233
234         pic = Draw_CachePic ("gfx/loading.lmp");
235         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/loading.lmp", 0, 0, 1, 1, 1, 1, 0);
236 }
237
238
239
240 //=============================================================================
241
242
243 /*
244 ==================
245 SCR_SetUpToDrawConsole
246 ==================
247 */
248 void SCR_SetUpToDrawConsole (void)
249 {
250         Con_CheckResize ();
251
252         if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
253                 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
254         else
255                 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
256
257 // decide on the height of the console
258         if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
259                 scr_conlines = vid.conheight; // full screen
260         else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
261                 scr_conlines = vid.conheight/2; // half screen
262         else
263                 scr_conlines = 0;                               // none visible
264
265         if (scr_conspeed.value)
266         {
267                 if (scr_conlines < scr_con_current)
268                 {
269                         scr_con_current -= scr_conspeed.value*host_realframetime;
270                         if (scr_conlines > scr_con_current)
271                                 scr_con_current = scr_conlines;
272
273                 }
274                 else if (scr_conlines > scr_con_current)
275                 {
276                         scr_con_current += scr_conspeed.value*host_realframetime;
277                         if (scr_conlines < scr_con_current)
278                                 scr_con_current = scr_conlines;
279                 }
280         }
281         else
282                 scr_con_current = scr_conlines;
283 }
284
285 /*
286 ==================
287 SCR_DrawConsole
288 ==================
289 */
290 void SCR_DrawConsole (void)
291 {
292         if (scr_con_current)
293                 Con_DrawConsole (scr_con_current);
294         else
295         {
296                 con_vislines = 0;
297                 if (key_dest == key_game || key_dest == key_message)
298                         Con_DrawNotify ();      // only draw notify in game
299         }
300 }
301
302 /*
303 ===============
304 SCR_BeginLoadingPlaque
305
306 ================
307 */
308 void SCR_BeginLoadingPlaque (void)
309 {
310         if (scr_drawloading)
311                 return;
312
313         S_StopAllSounds ();
314
315         scr_drawloading = true;
316         CL_UpdateScreen ();
317         scr_drawloading = true;
318         CL_UpdateScreen ();
319 }
320
321 //=============================================================================
322
323 char r_speeds_string[1024];
324 int speedstringcount, r_timereport_active;
325 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
326
327 void R_TimeReport(char *desc)
328 {
329         char tempbuf[256];
330         int length;
331         int t;
332
333         if (!r_timereport_active)
334                 return;
335
336         r_timereport_temp = r_timereport_current;
337         r_timereport_current = Sys_DoubleTime();
338         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
339
340         sprintf(tempbuf, "%8i %s", t, desc);
341         length = strlen(tempbuf);
342         while (length < 20)
343                 tempbuf[length++] = ' ';
344         tempbuf[length] = 0;
345         if (speedstringcount + length > (vid.conwidth / 8))
346         {
347                 strcat(r_speeds_string, "\n");
348                 speedstringcount = 0;
349         }
350         // skip the space at the beginning if it's the first on the line
351         if (speedstringcount == 0)
352         {
353                 strcat(r_speeds_string, tempbuf + 1);
354                 speedstringcount = length - 1;
355         }
356         else
357         {
358                 strcat(r_speeds_string, tempbuf);
359                 speedstringcount += length;
360         }
361 }
362
363 extern int c_rt_lights, c_rt_clears, c_rt_scissored;
364 extern int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
365 extern int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
366 void R_TimeReport_Start(void)
367 {
368         r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
369         r_speeds_string[0] = 0;
370         if (r_timereport_active)
371         {
372                 speedstringcount = 0;
373                 sprintf(r_speeds_string,
374                         "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
375                         "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
376                         "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
377                         "%6i modeltris%6i meshs%6i meshtris\n",
378                         r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2],
379                         c_faces, c_nodes, c_leafs, c_light_polys,
380                         c_models, c_bmodels, c_sprites, c_particles, c_dlights,
381                         c_alias_polys, c_meshs, c_meshelements / 3);
382
383                 sprintf(r_speeds_string + strlen(r_speeds_string),
384                         "realtime lighting:%4i lights%4i clears%4i scissored\n"
385                         "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n"
386                         "precomputed: %6i shadowmeshes%6i shadowtris\n",
387                         c_rt_lights, c_rt_clears, c_rt_scissored,
388                         c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris,
389                         c_rtcached_shadowmeshes, c_rtcached_shadowtris);
390
391                 c_alias_polys = 0;
392                 c_light_polys = 0;
393                 c_faces = 0;
394                 c_nodes = 0;
395                 c_leafs = 0;
396                 c_models = 0;
397                 c_bmodels = 0;
398                 c_sprites = 0;
399                 c_particles = 0;
400                 c_meshs = 0;
401                 c_meshelements = 0;
402
403                 r_timereport_start = Sys_DoubleTime();
404         }
405 }
406
407 void R_TimeReport_End(void)
408 {
409         r_timereport_current = r_timereport_start;
410         R_TimeReport("total");
411
412         if (r_timereport_active)
413         {
414                 int i, j, lines, y;
415                 lines = 1;
416                 for (i = 0;r_speeds_string[i];i++)
417                         if (r_speeds_string[i] == '\n')
418                                 lines++;
419                 y = vid.conheight - sb_lines - lines * 8;
420                 i = j = 0;
421                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
422                 while (r_speeds_string[i])
423                 {
424                         j = i;
425                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
426                                 i++;
427                         if (i - j > 0)
428                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
429                         if (r_speeds_string[i] == '\n')
430                                 i++;
431                         y += 8;
432                 }
433         }
434 }
435
436 /*
437 =================
438 SCR_SizeUp_f
439
440 Keybinding command
441 =================
442 */
443 void SCR_SizeUp_f (void)
444 {
445         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
446 }
447
448
449 /*
450 =================
451 SCR_SizeDown_f
452
453 Keybinding command
454 =================
455 */
456 void SCR_SizeDown_f (void)
457 {
458         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
459 }
460
461 void CL_Screen_Init(void)
462 {
463         Cvar_RegisterVariable (&scr_fov);
464         Cvar_RegisterVariable (&scr_viewsize);
465         Cvar_RegisterVariable (&scr_conspeed);
466         Cvar_RegisterVariable (&scr_conalpha);
467         Cvar_RegisterVariable (&scr_conbrightness);
468         Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
469         Cvar_RegisterVariable (&scr_showram);
470         Cvar_RegisterVariable (&scr_showturtle);
471         Cvar_RegisterVariable (&scr_showpause);
472         Cvar_RegisterVariable (&scr_centertime);
473         Cvar_RegisterVariable (&scr_printspeed);
474         Cvar_RegisterVariable (&vid_conwidth);
475         Cvar_RegisterVariable (&vid_conheight);
476         Cvar_RegisterVariable (&vid_pixelaspect);
477         Cvar_RegisterVariable (&scr_screenshot_jpeg);
478         Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
479         Cvar_RegisterVariable (&cl_capturevideo);
480         Cvar_RegisterVariable (&cl_capturevideo_fps);
481         Cvar_RegisterVariable (&cl_capturevideo_raw);
482         Cvar_RegisterVariable (&r_textshadow);
483         Cvar_RegisterVariable (&r_letterbox);
484
485         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
486         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
487         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
488         Cmd_AddCommand ("envmap", R_Envmap_f);
489
490         scr_initialized = true;
491 }
492
493 void DrawQ_Clear(void)
494 {
495         r_refdef.drawqueuesize = 0;
496 }
497
498 static int picelements[6] = {0, 1, 2, 0, 2, 3};
499 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
500 {
501         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);
502 }
503
504 void DrawQ_String_Real(float x, float y, const char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags)
505 {
506         int size, len;
507         drawqueue_t *dq;
508         char *out;
509         if (alpha < (1.0f / 255.0f))
510                 return;
511         if (maxlen < 1)
512                 len = strlen(string);
513         else
514                 for (len = 0;len < maxlen && string[len];len++);
515         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
516         for (;len > 0 && string[len - 1] == ' ';len--);
517         if (len < 1)
518                 return;
519         if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * maxlen) || y < (-scaley))
520                 return;
521         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
522         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
523                 return;
524         red = bound(0, red, 1);
525         green = bound(0, green, 1);
526         blue = bound(0, blue, 1);
527         alpha = bound(0, alpha, 1);
528         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
529         dq->size = size;
530         dq->command = DRAWQUEUE_STRING;
531         dq->flags = flags;
532         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));
533         dq->x = x;
534         dq->y = y;
535         dq->scalex = scalex;
536         dq->scaley = scaley;
537         out = (char *)(dq + 1);
538         memcpy(out, string, len);
539         out[len] = 0;
540         r_refdef.drawqueuesize += dq->size;
541 }
542
543 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)
544 {
545         if (r_textshadow.integer)
546                 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
547
548         DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags); 
549 }
550
551 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
552 {
553         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);
554 }
555
556 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)
557 {
558         float floats[36];
559         cachepic_t *pic;
560         drawqueuemesh_t mesh;
561         memset(&mesh, 0, sizeof(mesh));
562         if (picname && picname[0])
563         {
564                 pic = Draw_CachePic(picname);
565                 if (width == 0)
566                         width = pic->width;
567                 if (height == 0)
568                         height = pic->height;
569                 mesh.texture = pic->tex;
570         }
571         mesh.num_triangles = 2;
572         mesh.num_vertices = 4;
573         mesh.data_element3i = picelements;
574         mesh.data_vertex3f = floats;
575         mesh.data_texcoord2f = floats + 12;
576         mesh.data_color4f = floats + 20;
577         memset(floats, 0, sizeof(floats));
578         mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
579         mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
580         mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
581         mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
582         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;
583         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;
584         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;
585         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;
586         DrawQ_Mesh (&mesh, flags);
587 }
588
589 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
590 {
591         int size;
592         void *p;
593         drawqueue_t *dq;
594         drawqueuemesh_t *m;
595         size = sizeof(*dq);
596         size += sizeof(drawqueuemesh_t);
597         size += sizeof(int[3]) * mesh->num_triangles;
598         size += sizeof(float[3]) * mesh->num_vertices;
599         size += sizeof(float[2]) * mesh->num_vertices;
600         size += sizeof(float[4]) * mesh->num_vertices;
601         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
602                 return;
603         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
604         dq->size = size;
605         dq->command = DRAWQUEUE_MESH;
606         dq->flags = flags;
607         dq->color = 0;
608         dq->x = 0;
609         dq->y = 0;
610         dq->scalex = 0;
611         dq->scaley = 0;
612         p = (void *)(dq + 1);
613         m = p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
614         m->num_triangles = mesh->num_triangles;
615         m->num_vertices = mesh->num_vertices;
616         m->texture = mesh->texture;
617         m->data_element3i  = p;memcpy(m->data_element3i , mesh->data_element3i , m->num_triangles * sizeof(int[3]));p = (qbyte*)p + m->num_triangles * sizeof(int[3]);
618         m->data_vertex3f   = p;memcpy(m->data_vertex3f  , mesh->data_vertex3f  , m->num_vertices * sizeof(float[3]));p = (qbyte*)p + m->num_vertices * sizeof(float[3]);
619         m->data_texcoord2f = p;memcpy(m->data_texcoord2f, mesh->data_texcoord2f, m->num_vertices * sizeof(float[2]));p = (qbyte*)p + m->num_vertices * sizeof(float[2]);
620         m->data_color4f    = p;memcpy(m->data_color4f   , mesh->data_color4f   , m->num_vertices * sizeof(float[4]));p = (qbyte*)p + m->num_vertices * sizeof(float[4]);
621         r_refdef.drawqueuesize += dq->size;
622 }
623
624 void DrawQ_SetClipArea(float x, float y, float width, float height)
625 {
626         drawqueue_t * dq;
627         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
628         {
629                 Con_DPrint("DrawQueue full !\n");
630                 return;
631         }
632         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
633         dq->size = sizeof(*dq);
634         dq->command = DRAWQUEUE_SETCLIP;
635         dq->x = x;
636         dq->y = y;
637         dq->scalex = width;
638         dq->scaley = height;
639         dq->flags = 0;
640         dq->color = 0;
641
642         r_refdef.drawqueuesize += dq->size;
643 }
644
645 void DrawQ_ResetClipArea(void)
646 {
647         drawqueue_t *dq;
648         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
649         {
650                 Con_DPrint("DrawQueue full !\n");
651                 return;
652         }
653         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
654         dq->size = sizeof(*dq);
655         dq->command = DRAWQUEUE_RESETCLIP;
656         dq->x = 0;
657         dq->y = 0;
658         dq->scalex = 0;
659         dq->scaley = 0;
660         dq->flags = 0;
661         dq->color = 0;
662
663         r_refdef.drawqueuesize += dq->size;
664 }
665
666 /*
667 ==================
668 SCR_ScreenShot_f
669 ==================
670 */
671 void SCR_ScreenShot_f (void)
672 {
673         static int shotnumber;
674         static char oldname[MAX_QPATH];
675         char base[MAX_QPATH];
676         char filename[MAX_QPATH];
677         qbyte *buffer1;
678         qbyte *buffer2;
679         qbyte *buffer3;
680         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
681
682         sprintf (base, "screenshots/%s", scr_screenshot_name.string);
683
684         if (strcmp (oldname, scr_screenshot_name.string))
685         {
686                 sprintf(oldname, "%s", scr_screenshot_name.string);
687                 shotnumber = 0;
688         }
689
690         // find a file name to save it to
691         for (;shotnumber < 1000000;shotnumber++)
692                 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
693                         break;
694         if (shotnumber >= 1000000)
695         {
696                 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
697                 return;
698         }
699
700         sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
701
702         buffer1 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
703         buffer2 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
704         buffer3 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3 + 18);
705
706         if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
707                 Con_Printf("Wrote %s\n", filename);
708         else
709                 Con_Printf("unable to write %s\n", filename);
710
711         Mem_Free (buffer1);
712         Mem_Free (buffer2);
713         Mem_Free (buffer3);
714
715         shotnumber++;
716 }
717
718 typedef enum capturevideoformat_e
719 {
720         CAPTUREVIDEOFORMAT_TARGA,
721         CAPTUREVIDEOFORMAT_JPEG,
722         CAPTUREVIDEOFORMAT_RAW
723 }
724 capturevideoformat_t;
725
726 qboolean cl_capturevideo_active = false;
727 capturevideoformat_t cl_capturevideo_format;
728 static double cl_capturevideo_starttime = 0;
729 double cl_capturevideo_framerate = 0;
730 static int cl_capturevideo_soundrate = 0;
731 static int cl_capturevideo_frame = 0;
732 static qbyte *cl_capturevideo_buffer = NULL;
733 static qfile_t *cl_capturevideo_videofile = NULL;
734 static qfile_t *cl_capturevideo_soundfile = NULL;
735
736 void SCR_CaptureVideo_BeginVideo(void)
737 {
738         qbyte out[44];
739         if (cl_capturevideo_active)
740                 return;
741         // soundrate is figured out on the first SoundFrame
742         cl_capturevideo_active = true;
743         cl_capturevideo_starttime = Sys_DoubleTime();
744         cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
745         cl_capturevideo_soundrate = 0;
746         cl_capturevideo_frame = 0;
747         cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * (3+3+3) + 18);
748
749         if (cl_capturevideo_raw.integer)
750         {
751                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAW;
752                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false);
753         }
754         else if (scr_screenshot_jpeg.integer)
755         {
756                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
757                 cl_capturevideo_videofile = NULL;
758         }
759         else
760         {
761                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
762                 cl_capturevideo_videofile = NULL;
763         }
764
765         cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false);
766
767         // wave header will be filled out when video ends
768         memset(out, 0, 44);
769         FS_Write (cl_capturevideo_soundfile, out, 44);
770 }
771
772 void SCR_CaptureVideo_EndVideo(void)
773 {
774         int i, n;
775         qbyte out[44];
776         if (!cl_capturevideo_active)
777                 return;
778         cl_capturevideo_active = false;
779
780         if (cl_capturevideo_videofile)
781         {
782                 FS_Close(cl_capturevideo_videofile);
783                 cl_capturevideo_videofile = NULL;
784         }
785
786         // finish the wave file
787         if (cl_capturevideo_soundfile)
788         {
789                 i = FS_Tell (cl_capturevideo_soundfile);
790                 //"RIFF", (int) unknown (chunk size), "WAVE",
791                 //"fmt ", (int) 16 (chunk size), (short) format 1 (uncompressed PCM), (short) 2 channels, (int) unknown rate, (int) unknown bytes per second, (short) 4 bytes per sample (channels * bytes per channel), (short) 16 bits per channel
792                 //"data", (int) unknown (chunk size)
793                 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
794                 // the length of the whole RIFF chunk
795                 n = i - 8;
796                 out[4] = (n) & 0xFF;
797                 out[5] = (n >> 8) & 0xFF;
798                 out[6] = (n >> 16) & 0xFF;
799                 out[7] = (n >> 24) & 0xFF;
800                 // rate
801                 n = cl_capturevideo_soundrate;
802                 out[24] = (n) & 0xFF;
803                 out[25] = (n >> 8) & 0xFF;
804                 out[26] = (n >> 16) & 0xFF;
805                 out[27] = (n >> 24) & 0xFF;
806                 // bytes per second (rate * channels * bytes per channel)
807                 n = cl_capturevideo_soundrate * 2 * 2;
808                 out[28] = (n) & 0xFF;
809                 out[29] = (n >> 8) & 0xFF;
810                 out[30] = (n >> 16) & 0xFF;
811                 out[31] = (n >> 24) & 0xFF;
812                 // the length of the data chunk
813                 n = i - 44;
814                 out[40] = (n) & 0xFF;
815                 out[41] = (n >> 8) & 0xFF;
816                 out[42] = (n >> 16) & 0xFF;
817                 out[43] = (n >> 24) & 0xFF;
818                 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
819                 FS_Write (cl_capturevideo_soundfile, out, 44);
820                 FS_Close (cl_capturevideo_soundfile);
821                 cl_capturevideo_soundfile = NULL;
822         }
823
824         if (cl_capturevideo_buffer)
825         {
826                 Mem_Free (cl_capturevideo_buffer);
827                 cl_capturevideo_buffer = NULL;
828         }
829
830         cl_capturevideo_starttime = 0;
831         cl_capturevideo_framerate = 0;
832         cl_capturevideo_frame = 0;
833 }
834
835 qboolean SCR_CaptureVideo_VideoFrame(void)
836 {
837         int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
838         char filename[32];
839         //return SCR_ScreenShot(filename, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 6, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg);
840         // speed is critical here, so do saving as directly as possible
841         if (!r_render.integer)
842                 return false;
843         switch (cl_capturevideo_format)
844         {
845         case CAPTUREVIDEOFORMAT_RAW:
846                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
847                 CHECKGLERROR
848                 return FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3);
849         case CAPTUREVIDEOFORMAT_JPEG:
850                 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
851                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
852                 CHECKGLERROR
853                 return JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer);
854         case CAPTUREVIDEOFORMAT_TARGA:
855                 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
856                 memset (cl_capturevideo_buffer, 0, 18);
857                 cl_capturevideo_buffer[2] = 2;          // uncompressed type
858                 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
859                 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
860                 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
861                 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
862                 cl_capturevideo_buffer[16] = 24;        // pixel size
863                 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
864                 CHECKGLERROR
865                 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
866                 return FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18);
867         default:
868                 return false;
869         }
870 }
871
872 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
873 {
874         cl_capturevideo_soundrate = rate;
875         if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
876         {
877                 Cvar_SetValueQuick(&cl_capturevideo, 0);
878                 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
879                 SCR_CaptureVideo_EndVideo();
880         }
881 }
882
883 void SCR_CaptureVideo(void)
884 {
885         int newframenum, c;
886         if (cl_capturevideo.integer)
887         {
888                 if (!cl_capturevideo_active)
889                         SCR_CaptureVideo_BeginVideo();
890                 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
891                 {
892                         Con_Printf("You can not change the video framerate while recording a video.\n");
893                         Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
894                 }
895                 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
896                 c = (int)ceil(cl_capturevideo_framerate);
897                 while (cl_capturevideo_frame < newframenum)
898                 {
899                         if ((c--) && SCR_CaptureVideo_VideoFrame())
900                                 cl_capturevideo_frame++;
901                         else
902                         {
903                                 Cvar_SetValueQuick(&cl_capturevideo, 0);
904                                 if (c == 0)
905                                         Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
906                                 else
907                                         Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
908                                 SCR_CaptureVideo_EndVideo();
909                                 break;
910                         }
911                 }
912         }
913         else if (cl_capturevideo_active)
914                 SCR_CaptureVideo_EndVideo();
915 }
916
917 /*
918 ===============
919 R_Envmap_f
920
921 Grab six views for environment mapping tests
922 ===============
923 */
924 struct
925 {
926         float angles[3];
927         char *name;
928         qboolean flipx, flipy, flipdiagonaly;
929 }
930 envmapinfo[12] =
931 {
932         {{  0,   0, 0}, "rt",  true, false, false},
933         {{  0,  90, 0}, "ft",  true, false, false},
934         {{  0, 180, 0}, "lf",  true, false, false},
935         {{  0, 270, 0}, "bk",  true, false, false},
936         {{-90, 180, 0}, "up", false,  true, false},
937         {{ 90, 180, 0}, "dn", false,  true, false},
938
939         {{  0,   0, 0}, "px",  true,  true,  true},
940         {{  0,  90, 0}, "py", false,  true, false},
941         {{  0, 180, 0}, "nx", false, false,  true},
942         {{  0, 270, 0}, "ny",  true, false, false},
943         {{-90, 180, 0}, "pz", false, false,  true},
944         {{ 90, 180, 0}, "nz", false, false,  true}
945 };
946
947 static void R_Envmap_f (void)
948 {
949         int j, size;
950         char filename[256], basename[256];
951         qbyte *buffer1;
952         qbyte *buffer2;
953         qbyte *buffer3;
954
955         if (Cmd_Argc() != 3)
956         {
957                 Con_Print("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");
958                 return;
959         }
960
961         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
962         size = atoi(Cmd_Argv(2));
963         if (size != 128 && size != 256 && size != 512 && size != 1024)
964         {
965                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
966                 return;
967         }
968         if (size > vid.realwidth || size > vid.realheight)
969         {
970                 Con_Print("envmap: your resolution is not big enough to render that size\n");
971                 return;
972         }
973
974         envmap = true;
975
976         r_refdef.x = 0;
977         r_refdef.y = 0;
978         r_refdef.width = size;
979         r_refdef.height = size;
980
981         r_refdef.fov_x = 90;
982         r_refdef.fov_y = 90;
983
984         buffer1 = Mem_Alloc(tempmempool, size * size * 3);
985         buffer2 = Mem_Alloc(tempmempool, size * size * 3);
986         buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
987
988         for (j = 0;j < 12;j++)
989         {
990                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
991                 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);
992                 R_ClearScreen();
993                 R_Mesh_Start();
994                 R_RenderView();
995                 R_Mesh_Finish();
996                 SCR_ScreenShot(filename, buffer1, buffer2, buffer3, vid.realx, vid.realy + vid.realheight - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false);
997         }
998
999         Mem_Free (buffer1);
1000         Mem_Free (buffer2);
1001         Mem_Free (buffer3);
1002
1003         envmap = false;
1004 }
1005
1006 //=============================================================================
1007
1008 // LordHavoc: SHOWLMP stuff
1009 #define SHOWLMP_MAXLABELS 256
1010 typedef struct showlmp_s
1011 {
1012         qboolean        isactive;
1013         float           x;
1014         float           y;
1015         char            label[32];
1016         char            pic[128];
1017 }
1018 showlmp_t;
1019
1020 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1021
1022 void SHOWLMP_decodehide(void)
1023 {
1024         int i;
1025         qbyte *lmplabel;
1026         lmplabel = MSG_ReadString();
1027         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1028                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1029                 {
1030                         showlmp[i].isactive = false;
1031                         return;
1032                 }
1033 }
1034
1035 void SHOWLMP_decodeshow(void)
1036 {
1037         int i, k;
1038         qbyte lmplabel[256], picname[256];
1039         float x, y;
1040         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1041         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1042         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1043         {
1044                 x = MSG_ReadByte();
1045                 y = MSG_ReadByte();
1046         }
1047         else
1048         {
1049                 x = MSG_ReadShort();
1050                 y = MSG_ReadShort();
1051         }
1052         k = -1;
1053         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1054                 if (showlmp[i].isactive)
1055                 {
1056                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1057                         {
1058                                 k = i;
1059                                 break; // drop out to replace it
1060                         }
1061                 }
1062                 else if (k < 0) // find first empty one to replace
1063                         k = i;
1064         if (k < 0)
1065                 return; // none found to replace
1066         // change existing one
1067         showlmp[k].isactive = true;
1068         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1069         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1070         showlmp[k].x = x;
1071         showlmp[k].y = y;
1072 }
1073
1074 void SHOWLMP_drawall(void)
1075 {
1076         int i;
1077         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1078                 if (showlmp[i].isactive)
1079                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1080 }
1081
1082 void SHOWLMP_clear(void)
1083 {
1084         int i;
1085         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1086                 showlmp[i].isactive = false;
1087 }
1088
1089 void CL_SetupScreenSize(void)
1090 {
1091         float conwidth, conheight;
1092
1093         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1094
1095         VID_UpdateGamma(false);
1096
1097         conwidth = bound(320, vid_conwidth.value, 2048);
1098         conheight = bound(200, vid_conheight.value, 1536);
1099         if (vid_conwidth.value != conwidth)
1100                 Cvar_SetValue("vid_conwidth", conwidth);
1101         if (vid_conheight.value != conheight)
1102                 Cvar_SetValue("vid_conheight", conheight);
1103
1104         vid.conwidth = vid_conwidth.integer;
1105         vid.conheight = vid_conheight.integer;
1106
1107 /*      if (vid.realheight > 240)
1108         {
1109                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1110                 vid.conheight = bound(240, vid.conheight, vid.realheight);
1111         }
1112         else
1113                 vid.conheight = 240;*/
1114
1115         SCR_SetUpToDrawConsole();
1116 }
1117
1118 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1119 void CL_UpdateScreen(void)
1120 {
1121         if (!scr_initialized || !con_initialized || vid_hidden)
1122                 return;                         // not initialized yet
1123
1124         SCR_CaptureVideo();
1125
1126         if (cls.signon == SIGNONS)
1127                 R_TimeReport("other");
1128
1129         CL_SetupScreenSize();
1130
1131         DrawQ_Clear();
1132
1133         if (cls.signon == SIGNONS)
1134                 R_TimeReport("setup");
1135
1136         //FIXME: force menu if nothing else to look at?
1137         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1138
1139         if (scr_drawloading)
1140         {
1141                 scr_drawloading = false;
1142                 SCR_DrawLoading();
1143         }
1144         else
1145         {
1146                 if (cls.signon == SIGNONS)
1147                 {
1148                         SCR_DrawNet ();
1149                         SCR_DrawTurtle ();
1150                         SCR_DrawPause ();
1151                         if (!r_letterbox.value)
1152                                 Sbar_Draw();
1153                         SHOWLMP_drawall();
1154                         SCR_CheckDrawCenterString();
1155                 }
1156                 MR_Draw();
1157                 UI_Callback_Draw();
1158                 CL_DrawVideo();
1159                 //ui_draw();
1160                 if (cls.signon == SIGNONS)
1161                 {
1162                         R_TimeReport("2d");
1163                         R_TimeReport_End();
1164                         R_TimeReport_Start();
1165                 }
1166                 R_Shadow_EditLights_DrawSelectedLightProperties();
1167         }
1168         SCR_DrawConsole();
1169
1170         SCR_UpdateScreen();
1171 }
1172
1173 void CL_Screen_NewMap(void)
1174 {
1175         SHOWLMP_clear();
1176 }