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