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