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