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