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