Disabled vsync when doing a timedemo.
[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_gamma = {CVAR_SAVE, "scr_screenshot_gamma","2.2"};
24 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp"};
25 cvar_t cl_capturevideo = {0, "cl_capturevideo", "0"};
26 cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30"};
27 cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0"};
28 cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0"};
29 cvar_t r_textshadow = {0, "r_textshadow", "0"};
30 cvar_t r_letterbox = {0, "r_letterbox", "0"};
31
32 int jpeg_supported = false;
33
34 qboolean        scr_initialized;                // ready to draw
35
36 float           scr_con_current;
37 float           scr_conlines;           // lines of console to display
38
39 extern int      con_vislines;
40
41 void DrawCrosshair(int num);
42 static void SCR_ScreenShot_f (void);
43 static void R_Envmap_f (void);
44
45 // backend
46 void R_ClearScreen(void);
47
48 /*
49 ===============================================================================
50
51 CENTER PRINTING
52
53 ===============================================================================
54 */
55
56 char            scr_centerstring[1024];
57 float           scr_centertime_start;   // for slow victory printing
58 float           scr_centertime_off;
59 int                     scr_center_lines;
60 int                     scr_erase_lines;
61 int                     scr_erase_center;
62
63 /*
64 ==============
65 SCR_CenterPrint
66
67 Called for important messages that should stay in the center of the screen
68 for a few moments
69 ==============
70 */
71 void SCR_CenterPrint(char *str)
72 {
73         strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
74         scr_centertime_off = scr_centertime.value;
75         scr_centertime_start = cl.time;
76
77 // count the number of lines for centering
78         scr_center_lines = 1;
79         while (*str)
80         {
81                 if (*str == '\n')
82                         scr_center_lines++;
83                 str++;
84         }
85 }
86
87
88 void SCR_DrawCenterString (void)
89 {
90         char    *start;
91         int             l;
92         int             x, y;
93         int             remaining;
94
95 // the finale prints the characters one at a time
96         if (cl.intermission)
97                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
98         else
99                 remaining = 9999;
100
101         scr_erase_center = 0;
102         start = scr_centerstring;
103
104         if (scr_center_lines <= 4)
105                 y = vid.conheight*0.35;
106         else
107                 y = 48;
108
109         do
110         {
111         // scan the width of the line
112                 for (l=0 ; l<vid.conwidth/8 ; l++)
113                         if (start[l] == '\n' || !start[l])
114                                 break;
115                 x = (vid.conwidth - l*8)/2;
116                 if (l > 0)
117                 {
118                         if (remaining < l)
119                                 l = remaining;
120                         DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
121                         remaining -= l;
122                         if (remaining <= 0)
123                                 return;
124                 }
125
126                 y += 8;
127
128                 while (*start && *start != '\n')
129                         start++;
130
131                 if (!*start)
132                         break;
133                 start++;                // skip the \n
134         } while (1);
135 }
136
137 void SCR_CheckDrawCenterString (void)
138 {
139         if (scr_center_lines > scr_erase_lines)
140                 scr_erase_lines = scr_center_lines;
141
142         scr_centertime_off -= host_frametime;
143
144         // don't draw if this is a normal stats-screen intermission,
145         // only if it is not an intermission, or a finale intermission
146         if (cl.intermission == 1)
147                 return;
148         if (scr_centertime_off <= 0 && !cl.intermission)
149                 return;
150         if (key_dest != key_game)
151                 return;
152
153         SCR_DrawCenterString ();
154 }
155
156 /*
157 ==============
158 SCR_DrawTurtle
159 ==============
160 */
161 void SCR_DrawTurtle (void)
162 {
163         static int      count;
164
165         if (cls.state != ca_connected)
166                 return;
167
168         if (!scr_showturtle.integer)
169                 return;
170
171         if (host_frametime < 0.1)
172         {
173                 count = 0;
174                 return;
175         }
176
177         count++;
178         if (count < 3)
179                 return;
180
181         DrawQ_Pic (0, 0, "gfx/turtle.lmp", 0, 0, 1, 1, 1, 1, 0);
182 }
183
184 /*
185 ==============
186 SCR_DrawNet
187 ==============
188 */
189 void SCR_DrawNet (void)
190 {
191         if (cls.state != ca_connected)
192                 return;
193         if (realtime - cl.last_received_message < 0.3)
194                 return;
195         if (cls.demoplayback)
196                 return;
197
198         DrawQ_Pic (64, 0, "gfx/net.lmp", 0, 0, 1, 1, 1, 1, 0);
199 }
200
201 /*
202 ==============
203 DrawPause
204 ==============
205 */
206 void SCR_DrawPause (void)
207 {
208         cachepic_t      *pic;
209
210         if (cls.state != ca_connected)
211                 return;
212
213         if (!scr_showpause.integer)             // turn off for screenshots
214                 return;
215
216         if (!cl.paused)
217                 return;
218
219         pic = Draw_CachePic ("gfx/pause.lmp");
220         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
221 }
222
223
224
225
226
227 //=============================================================================
228
229
230 /*
231 ==================
232 SCR_SetUpToDrawConsole
233 ==================
234 */
235 void SCR_SetUpToDrawConsole (void)
236 {
237         Con_CheckResize ();
238
239         if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
240                 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
241         else
242                 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
243
244 // decide on the height of the console
245         if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
246                 scr_conlines = vid.conheight; // full screen
247         else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
248                 scr_conlines = vid.conheight/2; // half screen
249         else
250                 scr_conlines = 0;                               // none visible
251
252         if (scr_conspeed.value)
253         {
254                 if (scr_conlines < scr_con_current)
255                 {
256                         scr_con_current -= scr_conspeed.value*host_realframetime;
257                         if (scr_conlines > scr_con_current)
258                                 scr_con_current = scr_conlines;
259
260                 }
261                 else if (scr_conlines > scr_con_current)
262                 {
263                         scr_con_current += scr_conspeed.value*host_realframetime;
264                         if (scr_conlines < scr_con_current)
265                                 scr_con_current = scr_conlines;
266                 }
267         }
268         else
269                 scr_con_current = scr_conlines;
270 }
271
272 /*
273 ==================
274 SCR_DrawConsole
275 ==================
276 */
277 void SCR_DrawConsole (void)
278 {
279         if (scr_con_current)
280                 Con_DrawConsole (scr_con_current);
281         else
282         {
283                 con_vislines = 0;
284                 if (key_dest == key_game || key_dest == key_message)
285                         Con_DrawNotify ();      // only draw notify in game
286         }
287 }
288
289 /*
290 ===============
291 SCR_BeginLoadingPlaque
292
293 ================
294 */
295 void SCR_BeginLoadingPlaque (void)
296 {
297         S_StopAllSounds ();
298         SCR_UpdateLoadingScreen();
299 }
300
301 //=============================================================================
302
303 char r_speeds_string[1024];
304 int speedstringcount, r_timereport_active;
305 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
306
307 void R_TimeReport(char *desc)
308 {
309         char tempbuf[256];
310         int length;
311         int t;
312
313         if (!r_timereport_active)
314                 return;
315
316         r_timereport_temp = r_timereport_current;
317         r_timereport_current = Sys_DoubleTime();
318         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
319
320         sprintf(tempbuf, "%8i %s", t, desc);
321         length = strlen(tempbuf);
322         while (length < 20)
323                 tempbuf[length++] = ' ';
324         tempbuf[length] = 0;
325         if (speedstringcount + length > (vid.conwidth / 8))
326         {
327                 strcat(r_speeds_string, "\n");
328                 speedstringcount = 0;
329         }
330         // skip the space at the beginning if it's the first on the line
331         if (speedstringcount == 0)
332         {
333                 strcat(r_speeds_string, tempbuf + 1);
334                 speedstringcount = length - 1;
335         }
336         else
337         {
338                 strcat(r_speeds_string, tempbuf);
339                 speedstringcount += length;
340         }
341 }
342
343 void R_TimeReport_Start(void)
344 {
345         r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
346         r_speeds_string[0] = 0;
347         if (r_timereport_active)
348         {
349                 speedstringcount = 0;
350                 sprintf(r_speeds_string + strlen(r_speeds_string), "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n", r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2]);
351                 sprintf(r_speeds_string + strlen(r_speeds_string), "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n", c_faces, c_nodes, c_leafs, c_light_polys);
352                 sprintf(r_speeds_string + strlen(r_speeds_string), "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n", c_models, c_bmodels, c_sprites, c_particles, c_dlights);
353                 sprintf(r_speeds_string + strlen(r_speeds_string), "%6i modeltris%6i meshs%6i meshtris\n", c_alias_polys, c_meshs, c_meshelements / 3);
354                 sprintf(r_speeds_string + strlen(r_speeds_string), "bloom %s: %i copies (%i pixels) %i draws (%i pixels)\n", c_bloom ? "active" : "inactive", c_bloomcopies, c_bloomcopypixels, c_bloomdraws, c_bloomdrawpixels);
355                 sprintf(r_speeds_string + strlen(r_speeds_string), "realtime lighting:%4i lights%4i clears%4i scissored\n", c_rt_lights, c_rt_clears, c_rt_scissored);
356                 sprintf(r_speeds_string + strlen(r_speeds_string), "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n", c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris);
357                 sprintf(r_speeds_string + strlen(r_speeds_string), "precomputed: %6i shadowmeshes%6i shadowtris\n", c_rtcached_shadowmeshes, c_rtcached_shadowtris);
358
359                 c_alias_polys = 0;
360                 c_light_polys = 0;
361                 c_faces = 0;
362                 c_nodes = 0;
363                 c_leafs = 0;
364                 c_models = 0;
365                 c_bmodels = 0;
366                 c_sprites = 0;
367                 c_particles = 0;
368                 c_dlights = 0;
369                 c_meshs = 0;
370                 c_meshelements = 0;
371                 c_rt_lights = 0;
372                 c_rt_clears = 0;
373                 c_rt_scissored = 0;
374                 c_rt_shadowmeshes = 0;
375                 c_rt_shadowtris = 0;
376                 c_rt_lightmeshes = 0;
377                 c_rt_lighttris = 0;
378                 c_rtcached_shadowmeshes = 0;
379                 c_rtcached_shadowtris = 0;
380                 c_bloom = 0;
381                 c_bloomcopies = 0;
382                 c_bloomcopypixels = 0;
383                 c_bloomdraws = 0;
384                 c_bloomdrawpixels = 0;
385
386                 r_timereport_start = Sys_DoubleTime();
387         }
388 }
389
390 void R_TimeReport_End(void)
391 {
392         r_timereport_current = r_timereport_start;
393         R_TimeReport("total");
394
395         if (r_timereport_active)
396         {
397                 int i, j, lines, y;
398                 lines = 1;
399                 for (i = 0;r_speeds_string[i];i++)
400                         if (r_speeds_string[i] == '\n')
401                                 lines++;
402                 y = vid.conheight - sb_lines - lines * 8;
403                 i = j = 0;
404                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
405                 while (r_speeds_string[i])
406                 {
407                         j = i;
408                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
409                                 i++;
410                         if (i - j > 0)
411                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
412                         if (r_speeds_string[i] == '\n')
413                                 i++;
414                         y += 8;
415                 }
416         }
417 }
418
419 /*
420 =================
421 SCR_SizeUp_f
422
423 Keybinding command
424 =================
425 */
426 void SCR_SizeUp_f (void)
427 {
428         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
429 }
430
431
432 /*
433 =================
434 SCR_SizeDown_f
435
436 Keybinding command
437 =================
438 */
439 void SCR_SizeDown_f (void)
440 {
441         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
442 }
443
444 void CL_Screen_Init(void)
445 {
446         Cvar_RegisterVariable (&scr_fov);
447         Cvar_RegisterVariable (&scr_viewsize);
448         Cvar_RegisterVariable (&scr_conspeed);
449         Cvar_RegisterVariable (&scr_conalpha);
450         Cvar_RegisterVariable (&scr_conbrightness);
451         Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
452         Cvar_RegisterVariable (&scr_showram);
453         Cvar_RegisterVariable (&scr_showturtle);
454         Cvar_RegisterVariable (&scr_showpause);
455         Cvar_RegisterVariable (&scr_centertime);
456         Cvar_RegisterVariable (&scr_printspeed);
457         Cvar_RegisterVariable (&vid_conwidth);
458         Cvar_RegisterVariable (&vid_conheight);
459         Cvar_RegisterVariable (&vid_pixelaspect);
460         Cvar_RegisterVariable (&scr_screenshot_jpeg);
461         Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
462         Cvar_RegisterVariable (&scr_screenshot_gamma);
463         Cvar_RegisterVariable (&cl_capturevideo);
464         Cvar_RegisterVariable (&cl_capturevideo_fps);
465         Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
466         Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
467         Cvar_RegisterVariable (&r_textshadow);
468         Cvar_RegisterVariable (&r_letterbox);
469
470         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
471         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
472         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
473         Cmd_AddCommand ("envmap", R_Envmap_f);
474
475         scr_initialized = true;
476 }
477
478 void DrawQ_Clear(void)
479 {
480         r_refdef.drawqueuesize = 0;
481 }
482
483 static int picelements[6] = {0, 1, 2, 0, 2, 3};
484 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
485 {
486         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);
487 }
488
489 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)
490 {
491         int size, len;
492         drawqueue_t *dq;
493         char *out;
494         if (alpha < (1.0f / 255.0f))
495                 return;
496         if (maxlen < 1)
497                 len = strlen(string);
498         else
499                 for (len = 0;len < maxlen && string[len];len++);
500         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
501         for (;len > 0 && string[len - 1] == ' ';len--);
502         if (len < 1)
503                 return;
504         if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * len) || y < (-scaley))
505                 return;
506         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
507         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
508                 return;
509         red = bound(0, red, 1);
510         green = bound(0, green, 1);
511         blue = bound(0, blue, 1);
512         alpha = bound(0, alpha, 1);
513         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
514         dq->size = size;
515         dq->command = DRAWQUEUE_STRING;
516         dq->flags = flags;
517         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));
518         dq->x = x;
519         dq->y = y;
520         dq->scalex = scalex;
521         dq->scaley = scaley;
522         out = (char *)(dq + 1);
523         memcpy(out, string, len);
524         out[len] = 0;
525         r_refdef.drawqueuesize += dq->size;
526 }
527
528 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)
529 {
530         if (r_textshadow.integer)
531                 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
532
533         DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags); 
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         sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
686
687         buffer1 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
688         buffer2 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
689         buffer3 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3 + 18);
690
691         if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
692                 Con_Printf("Wrote %s\n", filename);
693         else
694                 Con_Printf("unable to write %s\n", filename);
695
696         Mem_Free (buffer1);
697         Mem_Free (buffer2);
698         Mem_Free (buffer3);
699
700         shotnumber++;
701 }
702
703 typedef enum capturevideoformat_e
704 {
705         CAPTUREVIDEOFORMAT_TARGA,
706         CAPTUREVIDEOFORMAT_JPEG,
707         CAPTUREVIDEOFORMAT_RAWRGB,
708         CAPTUREVIDEOFORMAT_RAWYV12
709 }
710 capturevideoformat_t;
711
712 qboolean cl_capturevideo_active = false;
713 capturevideoformat_t cl_capturevideo_format;
714 static double cl_capturevideo_starttime = 0;
715 double cl_capturevideo_framerate = 0;
716 static int cl_capturevideo_soundrate = 0;
717 static int cl_capturevideo_frame = 0;
718 static qbyte *cl_capturevideo_buffer = NULL;
719 static qfile_t *cl_capturevideo_videofile = NULL;
720 static qfile_t *cl_capturevideo_soundfile = NULL;
721 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
722 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
723 static unsigned char cl_capturevideo_rgbgammatable[3][256];
724
725 void SCR_CaptureVideo_BeginVideo(void)
726 {
727         double gamma, g;
728         unsigned int i, j;
729         qbyte out[44];
730         if (cl_capturevideo_active)
731                 return;
732         // soundrate is figured out on the first SoundFrame
733         cl_capturevideo_active = true;
734         cl_capturevideo_starttime = Sys_DoubleTime();
735         cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
736         cl_capturevideo_soundrate = 0;
737         cl_capturevideo_frame = 0;
738         cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * (3+3+3) + 18);
739         gamma = 1.0/scr_screenshot_gamma.value;
740
741         for (i = 0;i < 256;i++)
742         {
743                 j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
744                 cl_capturevideo_rgbgammatable[0][i] = j;
745                 cl_capturevideo_rgbgammatable[1][i] = j;
746                 cl_capturevideo_rgbgammatable[2][i] = j;
747         }
748 /*
749 R = Y + 1.4075 * (Cr - 128);
750 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
751 B = Y + 1.7790 * (Cb - 128);
752 Y = R *  .299 + G *  .587 + B *  .114;
753 Cb = R * -.169 + G * -.332 + B *  .500 + 128.;
754 Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
755 */
756         for (i = 0;i < 256;i++)
757         {
758                 g = i;//255*pow(i/255.0, gamma);
759                 // Y weights from RGB
760                 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g *  0.299);
761                 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g *  0.587);
762                 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g *  0.114);
763                 // Cb weights from RGB
764                 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
765                 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
766                 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g *  0.500);
767                 // Cr weights from RGB
768                 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g *  0.500);
769                 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
770                 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
771                 // range reduction of YCbCr to valid signal range
772                 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
773                 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
774                 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
775         }
776
777         if (cl_capturevideo_rawrgb.integer)
778         {
779                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
780                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false);
781         }
782         else if (cl_capturevideo_rawyv12.integer)
783         {
784                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
785                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false);
786         }
787         else if (scr_screenshot_jpeg.integer)
788         {
789                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
790                 cl_capturevideo_videofile = NULL;
791         }
792         else
793         {
794                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
795                 cl_capturevideo_videofile = NULL;
796         }
797
798         cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false);
799
800         // wave header will be filled out when video ends
801         memset(out, 0, 44);
802         FS_Write (cl_capturevideo_soundfile, out, 44);
803 }
804
805 void SCR_CaptureVideo_EndVideo(void)
806 {
807         int i, n;
808         qbyte out[44];
809         if (!cl_capturevideo_active)
810                 return;
811         cl_capturevideo_active = false;
812
813         if (cl_capturevideo_videofile)
814         {
815                 FS_Close(cl_capturevideo_videofile);
816                 cl_capturevideo_videofile = NULL;
817         }
818
819         // finish the wave file
820         if (cl_capturevideo_soundfile)
821         {
822                 i = FS_Tell (cl_capturevideo_soundfile);
823                 //"RIFF", (int) unknown (chunk size), "WAVE",
824                 //"fmt ", (int) 16 (chunk size), (short) format 1 (uncompressed PCM), (short) 2 channels, (int) unknown rate, (int) unknown bytes per second, (short) 4 bytes per sample (channels * bytes per channel), (short) 16 bits per channel
825                 //"data", (int) unknown (chunk size)
826                 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
827                 // the length of the whole RIFF chunk
828                 n = i - 8;
829                 out[4] = (n) & 0xFF;
830                 out[5] = (n >> 8) & 0xFF;
831                 out[6] = (n >> 16) & 0xFF;
832                 out[7] = (n >> 24) & 0xFF;
833                 // rate
834                 n = cl_capturevideo_soundrate;
835                 out[24] = (n) & 0xFF;
836                 out[25] = (n >> 8) & 0xFF;
837                 out[26] = (n >> 16) & 0xFF;
838                 out[27] = (n >> 24) & 0xFF;
839                 // bytes per second (rate * channels * bytes per channel)
840                 n = cl_capturevideo_soundrate * 2 * 2;
841                 out[28] = (n) & 0xFF;
842                 out[29] = (n >> 8) & 0xFF;
843                 out[30] = (n >> 16) & 0xFF;
844                 out[31] = (n >> 24) & 0xFF;
845                 // the length of the data chunk
846                 n = i - 44;
847                 out[40] = (n) & 0xFF;
848                 out[41] = (n >> 8) & 0xFF;
849                 out[42] = (n >> 16) & 0xFF;
850                 out[43] = (n >> 24) & 0xFF;
851                 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
852                 FS_Write (cl_capturevideo_soundfile, out, 44);
853                 FS_Close (cl_capturevideo_soundfile);
854                 cl_capturevideo_soundfile = NULL;
855         }
856
857         if (cl_capturevideo_buffer)
858         {
859                 Mem_Free (cl_capturevideo_buffer);
860                 cl_capturevideo_buffer = NULL;
861         }
862
863         cl_capturevideo_starttime = 0;
864         cl_capturevideo_framerate = 0;
865         cl_capturevideo_frame = 0;
866 }
867
868 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
869 {
870         int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
871         unsigned char *b, *out;
872         char filename[32];
873         int outoffset = (width/2)*(height/2);
874         //return SCR_ScreenShot(filename, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 6, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg);
875         // speed is critical here, so do saving as directly as possible
876         switch (cl_capturevideo_format)
877         {
878         case CAPTUREVIDEOFORMAT_RAWYV12:
879                 // FIXME: width/height must be multiple of 2, enforce this?
880                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
881                 CHECKGLERROR
882                 // process one line at a time, and CbCr every other line at 2 pixel intervals
883                 for (y = 0;y < height;y++)
884                 {
885                         // 1x1 Y
886                         for (b = cl_capturevideo_buffer + (height-1-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + y*width, x = 0;x < width;x++, b += 3, out++)
887                                 *out = cl_capturevideo_yuvnormalizetable[0][cl_capturevideo_rgbtoyuvscaletable[0][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[0][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[0][2][b[2]]];
888                         if ((y & 1) == 0)
889                         {
890                                 // 2x2 Cb and Cr planes
891 #if 1
892                                 // low quality, no averaging
893                                 for (b = cl_capturevideo_buffer + (height-2-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
894                                 {
895                                         // Cr
896                                         out[0        ] = cl_capturevideo_yuvnormalizetable[2][cl_capturevideo_rgbtoyuvscaletable[2][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[2][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[2][2][b[2]] + 128];
897                                         // Cb
898                                         out[outoffset] = cl_capturevideo_yuvnormalizetable[1][cl_capturevideo_rgbtoyuvscaletable[1][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[1][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[1][2][b[2]] + 128];
899                                 }
900 #else
901                                 // high quality, averaging
902                                 int inpitch = width*3;
903                                 for (b = cl_capturevideo_buffer + (height-2-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
904                                 {
905                                         int blockr, blockg, blockb;
906                                         blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
907                                         blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
908                                         blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
909                                         // Cr
910                                         out[0        ] = cl_capturevideo_yuvnormalizetable[2][cl_capturevideo_rgbtoyuvscaletable[2][0][blockr] + cl_capturevideo_rgbtoyuvscaletable[2][1][blockg] + cl_capturevideo_rgbtoyuvscaletable[2][2][blockb] + 128];
911                                         // Cb
912                                         out[outoffset] = cl_capturevideo_yuvnormalizetable[1][cl_capturevideo_rgbtoyuvscaletable[1][0][blockr] + cl_capturevideo_rgbtoyuvscaletable[1][1][blockg] + cl_capturevideo_rgbtoyuvscaletable[1][2][blockb] + 128];
913                                 }
914 #endif
915                         }
916                 }
917                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
918                         if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
919                                 return false;
920                 return true;
921         case CAPTUREVIDEOFORMAT_RAWRGB:
922                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
923                 CHECKGLERROR
924                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
925                         if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
926                                 return false;
927                 return true;
928         case CAPTUREVIDEOFORMAT_JPEG:
929                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
930                 CHECKGLERROR
931                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
932                 {
933                         sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
934                         if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
935                                 return false;
936                 }
937                 return true;
938         case CAPTUREVIDEOFORMAT_TARGA:
939                 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
940                 memset (cl_capturevideo_buffer, 0, 18);
941                 cl_capturevideo_buffer[2] = 2;          // uncompressed type
942                 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
943                 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
944                 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
945                 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
946                 cl_capturevideo_buffer[16] = 24;        // pixel size
947                 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
948                 CHECKGLERROR
949                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
950                 {
951                         sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
952                         if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
953                                 return false;
954                 }
955                 return true;
956         default:
957                 return false;
958         }
959 }
960
961 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
962 {
963         cl_capturevideo_soundrate = rate;
964         if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
965         {
966                 Cvar_SetValueQuick(&cl_capturevideo, 0);
967                 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
968                 SCR_CaptureVideo_EndVideo();
969         }
970 }
971
972 void SCR_CaptureVideo(void)
973 {
974         int newframenum;
975         if (cl_capturevideo.integer && r_render.integer)
976         {
977                 if (!cl_capturevideo_active)
978                         SCR_CaptureVideo_BeginVideo();
979                 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
980                 {
981                         Con_Printf("You can not change the video framerate while recording a video.\n");
982                         Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
983                 }
984                 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
985                 // if falling behind more than one second, stop
986                 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
987                 {
988                         Cvar_SetValueQuick(&cl_capturevideo, 0);
989                         Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
990                         SCR_CaptureVideo_EndVideo();
991                         return;
992                 }
993                 // write frames
994                 if (!SCR_CaptureVideo_VideoFrame(newframenum))
995                 {
996                         Cvar_SetValueQuick(&cl_capturevideo, 0);
997                         Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
998                         SCR_CaptureVideo_EndVideo();
999                 }
1000         }
1001         else if (cl_capturevideo_active)
1002                 SCR_CaptureVideo_EndVideo();
1003 }
1004
1005 /*
1006 ===============
1007 R_Envmap_f
1008
1009 Grab six views for environment mapping tests
1010 ===============
1011 */
1012 struct
1013 {
1014         float angles[3];
1015         char *name;
1016         qboolean flipx, flipy, flipdiagonaly;
1017 }
1018 envmapinfo[12] =
1019 {
1020         {{  0,   0, 0}, "rt",  true, false, false},
1021         {{  0,  90, 0}, "ft",  true, false, false},
1022         {{  0, 180, 0}, "lf",  true, false, false},
1023         {{  0, 270, 0}, "bk",  true, false, false},
1024         {{-90, 180, 0}, "up", false,  true, false},
1025         {{ 90, 180, 0}, "dn", false,  true, false},
1026
1027         {{  0,   0, 0}, "px",  true,  true,  true},
1028         {{  0,  90, 0}, "py", false,  true, false},
1029         {{  0, 180, 0}, "nx", false, false,  true},
1030         {{  0, 270, 0}, "ny",  true, false, false},
1031         {{-90, 180, 0}, "pz", false, false,  true},
1032         {{ 90, 180, 0}, "nz", false, false,  true}
1033 };
1034
1035 static void R_Envmap_f (void)
1036 {
1037         int j, size;
1038         char filename[256], basename[256];
1039         qbyte *buffer1;
1040         qbyte *buffer2;
1041         qbyte *buffer3;
1042
1043         if (Cmd_Argc() != 3)
1044         {
1045                 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");
1046                 return;
1047         }
1048
1049         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1050         size = atoi(Cmd_Argv(2));
1051         if (size != 128 && size != 256 && size != 512 && size != 1024)
1052         {
1053                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1054                 return;
1055         }
1056         if (size > vid.realwidth || size > vid.realheight)
1057         {
1058                 Con_Print("envmap: your resolution is not big enough to render that size\n");
1059                 return;
1060         }
1061
1062         envmap = true;
1063
1064         r_refdef.x = 0;
1065         r_refdef.y = 0;
1066         r_refdef.width = size;
1067         r_refdef.height = size;
1068
1069         r_refdef.fov_x = 90;
1070         r_refdef.fov_y = 90;
1071
1072         buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1073         buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1074         buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1075
1076         for (j = 0;j < 12;j++)
1077         {
1078                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1079                 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);
1080                 R_ClearScreen();
1081                 R_Mesh_Start();
1082                 R_RenderView();
1083                 R_Mesh_Finish();
1084                 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);
1085         }
1086
1087         Mem_Free (buffer1);
1088         Mem_Free (buffer2);
1089         Mem_Free (buffer3);
1090
1091         envmap = false;
1092 }
1093
1094 //=============================================================================
1095
1096 // LordHavoc: SHOWLMP stuff
1097 #define SHOWLMP_MAXLABELS 256
1098 typedef struct showlmp_s
1099 {
1100         qboolean        isactive;
1101         float           x;
1102         float           y;
1103         char            label[32];
1104         char            pic[128];
1105 }
1106 showlmp_t;
1107
1108 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1109
1110 void SHOWLMP_decodehide(void)
1111 {
1112         int i;
1113         qbyte *lmplabel;
1114         lmplabel = MSG_ReadString();
1115         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1116                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1117                 {
1118                         showlmp[i].isactive = false;
1119                         return;
1120                 }
1121 }
1122
1123 void SHOWLMP_decodeshow(void)
1124 {
1125         int i, k;
1126         qbyte lmplabel[256], picname[256];
1127         float x, y;
1128         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1129         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1130         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1131         {
1132                 x = MSG_ReadByte();
1133                 y = MSG_ReadByte();
1134         }
1135         else
1136         {
1137                 x = MSG_ReadShort();
1138                 y = MSG_ReadShort();
1139         }
1140         k = -1;
1141         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1142                 if (showlmp[i].isactive)
1143                 {
1144                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1145                         {
1146                                 k = i;
1147                                 break; // drop out to replace it
1148                         }
1149                 }
1150                 else if (k < 0) // find first empty one to replace
1151                         k = i;
1152         if (k < 0)
1153                 return; // none found to replace
1154         // change existing one
1155         showlmp[k].isactive = true;
1156         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1157         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1158         showlmp[k].x = x;
1159         showlmp[k].y = y;
1160 }
1161
1162 void SHOWLMP_drawall(void)
1163 {
1164         int i;
1165         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1166                 if (showlmp[i].isactive)
1167                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1168 }
1169
1170 void SHOWLMP_clear(void)
1171 {
1172         int i;
1173         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1174                 showlmp[i].isactive = false;
1175 }
1176
1177 void CL_SetupScreenSize(void)
1178 {
1179         float conwidth, conheight;
1180
1181         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1182
1183         VID_UpdateGamma(false);
1184
1185         conwidth = bound(320, vid_conwidth.value, 2048);
1186         conheight = bound(200, vid_conheight.value, 1536);
1187         if (vid_conwidth.value != conwidth)
1188                 Cvar_SetValue("vid_conwidth", conwidth);
1189         if (vid_conheight.value != conheight)
1190                 Cvar_SetValue("vid_conheight", conheight);
1191
1192         vid.conwidth = vid_conwidth.integer;
1193         vid.conheight = vid_conheight.integer;
1194
1195 /*      if (vid.realheight > 240)
1196         {
1197                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1198                 vid.conheight = bound(240, vid.conheight, vid.realheight);
1199         }
1200         else
1201                 vid.conheight = 240;*/
1202
1203         SCR_SetUpToDrawConsole();
1204 }
1205
1206 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1207 void CL_UpdateScreen(void)
1208 {
1209         if (!scr_initialized || !con_initialized || vid_hidden)
1210                 return;                         // not initialized yet
1211
1212         // don't allow cheats in multiplayer
1213         if (!cl.islocalgame && cl.worldmodel)
1214         {
1215                 if (r_fullbright.integer != 0)
1216                         Cvar_Set ("r_fullbright", "0");
1217                 if (r_ambient.value != 0)
1218                         Cvar_Set ("r_ambient", "0");
1219         }
1220
1221         // bound viewsize
1222         if (scr_viewsize.value < 30)
1223                 Cvar_Set ("viewsize","30");
1224         if (scr_viewsize.value > 120)
1225                 Cvar_Set ("viewsize","120");
1226
1227         // bound field of view
1228         if (scr_fov.value < 1)
1229                 Cvar_Set ("fov","1");
1230         if (scr_fov.value > 170)
1231                 Cvar_Set ("fov","170");
1232
1233         // intermission is always full screen
1234         if (cl.intermission)
1235                 sb_lines = 0;
1236         else
1237         {
1238                 if (scr_viewsize.value >= 120)
1239                         sb_lines = 0;           // no status bar at all
1240                 else if (scr_viewsize.value >= 110)
1241                         sb_lines = 24;          // no inventory
1242                 else
1243                         sb_lines = 24+16+8;
1244         }
1245
1246         r_refdef.colormask[0] = 1;
1247         r_refdef.colormask[1] = 1;
1248         r_refdef.colormask[2] = 1;
1249
1250         SCR_CaptureVideo();
1251
1252         if (cls.signon == SIGNONS)
1253                 R_TimeReport("other");
1254
1255         CL_SetupScreenSize();
1256
1257         DrawQ_Clear();
1258
1259         if (cls.signon == SIGNONS)
1260                 R_TimeReport("setup");
1261
1262         //FIXME: force menu if nothing else to look at?
1263         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1264
1265         if (cls.signon == SIGNONS)
1266         {
1267                 SCR_DrawNet ();
1268                 SCR_DrawTurtle ();
1269                 SCR_DrawPause ();
1270                 if (!r_letterbox.value)
1271                         Sbar_Draw();
1272                 SHOWLMP_drawall();
1273                 SCR_CheckDrawCenterString();
1274         }
1275         MR_Draw();
1276         UI_Callback_Draw();
1277         CL_DrawVideo();
1278         //ui_draw();
1279         if (cls.signon == SIGNONS)
1280         {
1281                 R_TimeReport("2d");
1282                 R_TimeReport_End();
1283                 R_TimeReport_Start();
1284         }
1285         R_Shadow_EditLights_DrawSelectedLightProperties();
1286
1287         SCR_DrawConsole();
1288
1289         SCR_UpdateScreen();
1290 }
1291
1292 void CL_Screen_NewMap(void)
1293 {
1294         SHOWLMP_clear();
1295 }