]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_screen.c
greatly improved video capture speed by generating the files much more directly ...
[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;
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                 while (cl_capturevideo_frame < newframenum)
897                 {
898                         if (SCR_CaptureVideo_VideoFrame())
899                                 cl_capturevideo_frame++;
900                         else
901                         {
902                                 Cvar_SetValueQuick(&cl_capturevideo, 0);
903                                 Con_Printf("video saving failed on frame %i, out of disk space? stopping avi demo capture.\n", cl_capturevideo_frame);
904                                 SCR_CaptureVideo_EndVideo();
905                                 break;
906                         }
907                 }
908         }
909         else if (cl_capturevideo_active)
910                 SCR_CaptureVideo_EndVideo();
911 }
912
913 /*
914 ===============
915 R_Envmap_f
916
917 Grab six views for environment mapping tests
918 ===============
919 */
920 struct
921 {
922         float angles[3];
923         char *name;
924         qboolean flipx, flipy, flipdiagonaly;
925 }
926 envmapinfo[12] =
927 {
928         {{  0,   0, 0}, "rt",  true, false, false},
929         {{  0,  90, 0}, "ft",  true, false, false},
930         {{  0, 180, 0}, "lf",  true, false, false},
931         {{  0, 270, 0}, "bk",  true, false, false},
932         {{-90, 180, 0}, "up", false,  true, false},
933         {{ 90, 180, 0}, "dn", false,  true, false},
934
935         {{  0,   0, 0}, "px",  true,  true,  true},
936         {{  0,  90, 0}, "py", false,  true, false},
937         {{  0, 180, 0}, "nx", false, false,  true},
938         {{  0, 270, 0}, "ny",  true, false, false},
939         {{-90, 180, 0}, "pz", false, false,  true},
940         {{ 90, 180, 0}, "nz", false, false,  true}
941 };
942
943 static void R_Envmap_f (void)
944 {
945         int j, size;
946         char filename[256], basename[256];
947         qbyte *buffer1;
948         qbyte *buffer2;
949         qbyte *buffer3;
950
951         if (Cmd_Argc() != 3)
952         {
953                 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");
954                 return;
955         }
956
957         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
958         size = atoi(Cmd_Argv(2));
959         if (size != 128 && size != 256 && size != 512 && size != 1024)
960         {
961                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
962                 return;
963         }
964         if (size > vid.realwidth || size > vid.realheight)
965         {
966                 Con_Print("envmap: your resolution is not big enough to render that size\n");
967                 return;
968         }
969
970         envmap = true;
971
972         r_refdef.x = 0;
973         r_refdef.y = 0;
974         r_refdef.width = size;
975         r_refdef.height = size;
976
977         r_refdef.fov_x = 90;
978         r_refdef.fov_y = 90;
979
980         buffer1 = Mem_Alloc(tempmempool, size * size * 3);
981         buffer2 = Mem_Alloc(tempmempool, size * size * 3);
982         buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
983
984         for (j = 0;j < 12;j++)
985         {
986                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
987                 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);
988                 R_ClearScreen();
989                 R_Mesh_Start();
990                 R_RenderView();
991                 R_Mesh_Finish();
992                 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);
993         }
994
995         Mem_Free (buffer1);
996         Mem_Free (buffer2);
997         Mem_Free (buffer3);
998
999         envmap = false;
1000 }
1001
1002 //=============================================================================
1003
1004 // LordHavoc: SHOWLMP stuff
1005 #define SHOWLMP_MAXLABELS 256
1006 typedef struct showlmp_s
1007 {
1008         qboolean        isactive;
1009         float           x;
1010         float           y;
1011         char            label[32];
1012         char            pic[128];
1013 }
1014 showlmp_t;
1015
1016 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1017
1018 void SHOWLMP_decodehide(void)
1019 {
1020         int i;
1021         qbyte *lmplabel;
1022         lmplabel = MSG_ReadString();
1023         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1024                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1025                 {
1026                         showlmp[i].isactive = false;
1027                         return;
1028                 }
1029 }
1030
1031 void SHOWLMP_decodeshow(void)
1032 {
1033         int i, k;
1034         qbyte lmplabel[256], picname[256];
1035         float x, y;
1036         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1037         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1038         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1039         {
1040                 x = MSG_ReadByte();
1041                 y = MSG_ReadByte();
1042         }
1043         else
1044         {
1045                 x = MSG_ReadShort();
1046                 y = MSG_ReadShort();
1047         }
1048         k = -1;
1049         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1050                 if (showlmp[i].isactive)
1051                 {
1052                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1053                         {
1054                                 k = i;
1055                                 break; // drop out to replace it
1056                         }
1057                 }
1058                 else if (k < 0) // find first empty one to replace
1059                         k = i;
1060         if (k < 0)
1061                 return; // none found to replace
1062         // change existing one
1063         showlmp[k].isactive = true;
1064         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1065         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1066         showlmp[k].x = x;
1067         showlmp[k].y = y;
1068 }
1069
1070 void SHOWLMP_drawall(void)
1071 {
1072         int i;
1073         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1074                 if (showlmp[i].isactive)
1075                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1076 }
1077
1078 void SHOWLMP_clear(void)
1079 {
1080         int i;
1081         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1082                 showlmp[i].isactive = false;
1083 }
1084
1085 void CL_SetupScreenSize(void)
1086 {
1087         float conwidth, conheight;
1088
1089         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1090
1091         VID_UpdateGamma(false);
1092
1093         conwidth = bound(320, vid_conwidth.value, 2048);
1094         conheight = bound(200, vid_conheight.value, 1536);
1095         if (vid_conwidth.value != conwidth)
1096                 Cvar_SetValue("vid_conwidth", conwidth);
1097         if (vid_conheight.value != conheight)
1098                 Cvar_SetValue("vid_conheight", conheight);
1099
1100         vid.conwidth = vid_conwidth.integer;
1101         vid.conheight = vid_conheight.integer;
1102
1103 /*      if (vid.realheight > 240)
1104         {
1105                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1106                 vid.conheight = bound(240, vid.conheight, vid.realheight);
1107         }
1108         else
1109                 vid.conheight = 240;*/
1110
1111         SCR_SetUpToDrawConsole();
1112 }
1113
1114 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1115 void CL_UpdateScreen(void)
1116 {
1117         if (!scr_initialized || !con_initialized || vid_hidden)
1118                 return;                         // not initialized yet
1119
1120         SCR_CaptureVideo();
1121
1122         if (cls.signon == SIGNONS)
1123                 R_TimeReport("other");
1124
1125         CL_SetupScreenSize();
1126
1127         DrawQ_Clear();
1128
1129         if (cls.signon == SIGNONS)
1130                 R_TimeReport("setup");
1131
1132         //FIXME: force menu if nothing else to look at?
1133         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1134
1135         if (scr_drawloading)
1136         {
1137                 scr_drawloading = false;
1138                 SCR_DrawLoading();
1139         }
1140         else
1141         {
1142                 if (cls.signon == SIGNONS)
1143                 {
1144                         SCR_DrawNet ();
1145                         SCR_DrawTurtle ();
1146                         SCR_DrawPause ();
1147                         if (!r_letterbox.value)
1148                                 Sbar_Draw();
1149                         SHOWLMP_drawall();
1150                         SCR_CheckDrawCenterString();
1151                 }
1152                 MR_Draw();
1153                 UI_Callback_Draw();
1154                 CL_DrawVideo();
1155                 //ui_draw();
1156                 if (cls.signon == SIGNONS)
1157                 {
1158                         R_TimeReport("2d");
1159                         R_TimeReport_End();
1160                         R_TimeReport_Start();
1161                 }
1162                 R_Shadow_EditLights_DrawSelectedLightProperties();
1163         }
1164         SCR_DrawConsole();
1165
1166         SCR_UpdateScreen();
1167 }
1168
1169 void CL_Screen_NewMap(void)
1170 {
1171         SHOWLMP_clear();
1172 }