00a2630a9133bffac28fb0fc392c80ff5db0b22a
[xonotic/darkplaces.git] / cl_screen.c
1
2 #include "quakedef.h"
3 #include "cl_video.h"
4 #include "jpeg.h"
5 #include "cl_collision.h"
6
7 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
8 cvar_t scr_fov = {CVAR_SAVE, "fov","90"};       // 1 - 170
9 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
10 cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1"};
11 cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "0.2"};
12 cvar_t scr_conforcewhiledisconnected = {CVAR_SAVE, "scr_conforcewhiledisconnected", "1"};
13 cvar_t scr_centertime = {0, "scr_centertime","2"};
14 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
15 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
16 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
17 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
18 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640"};
19 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480"};
20 cvar_t 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;
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         /*
747         for (i = 0;i < 256;i++)
748         {
749                 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
750                 cl_capturevideo_rgbgammatable[0][i] = j;
751                 cl_capturevideo_rgbgammatable[1][i] = j;
752                 cl_capturevideo_rgbgammatable[2][i] = j;
753         }
754         */
755 /*
756 R = Y + 1.4075 * (Cr - 128);
757 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
758 B = Y + 1.7790 * (Cb - 128);
759 Y = R *  .299 + G *  .587 + B *  .114;
760 Cb = R * -.169 + G * -.332 + B *  .500 + 128.;
761 Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
762 */
763         for (i = 0;i < 256;i++)
764         {
765                 g = 255*pow(i/255.0, gamma);
766                 // Y weights from RGB
767                 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g *  0.299);
768                 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g *  0.587);
769                 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g *  0.114);
770                 // Cb weights from RGB
771                 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
772                 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
773                 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g *  0.500);
774                 // Cr weights from RGB
775                 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g *  0.500);
776                 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
777                 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
778                 // range reduction of YCbCr to valid signal range
779                 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
780                 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
781                 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
782         }
783
784         if (cl_capturevideo_rawrgb.integer)
785         {
786                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
787                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false);
788         }
789         else if (cl_capturevideo_rawyv12.integer)
790         {
791                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
792                 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false);
793         }
794         else if (scr_screenshot_jpeg.integer)
795         {
796                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
797                 cl_capturevideo_videofile = NULL;
798         }
799         else
800         {
801                 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
802                 cl_capturevideo_videofile = NULL;
803         }
804
805         cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false);
806
807         // wave header will be filled out when video ends
808         memset(out, 0, 44);
809         FS_Write (cl_capturevideo_soundfile, out, 44);
810 }
811
812 void SCR_CaptureVideo_EndVideo(void)
813 {
814         int i, n;
815         qbyte out[44];
816         if (!cl_capturevideo_active)
817                 return;
818         cl_capturevideo_active = false;
819
820         if (cl_capturevideo_videofile)
821         {
822                 FS_Close(cl_capturevideo_videofile);
823                 cl_capturevideo_videofile = NULL;
824         }
825
826         // finish the wave file
827         if (cl_capturevideo_soundfile)
828         {
829                 i = FS_Tell (cl_capturevideo_soundfile);
830                 //"RIFF", (int) unknown (chunk size), "WAVE",
831                 //"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
832                 //"data", (int) unknown (chunk size)
833                 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
834                 // the length of the whole RIFF chunk
835                 n = i - 8;
836                 out[4] = (n) & 0xFF;
837                 out[5] = (n >> 8) & 0xFF;
838                 out[6] = (n >> 16) & 0xFF;
839                 out[7] = (n >> 24) & 0xFF;
840                 // rate
841                 n = cl_capturevideo_soundrate;
842                 out[24] = (n) & 0xFF;
843                 out[25] = (n >> 8) & 0xFF;
844                 out[26] = (n >> 16) & 0xFF;
845                 out[27] = (n >> 24) & 0xFF;
846                 // bytes per second (rate * channels * bytes per channel)
847                 n = cl_capturevideo_soundrate * 2 * 2;
848                 out[28] = (n) & 0xFF;
849                 out[29] = (n >> 8) & 0xFF;
850                 out[30] = (n >> 16) & 0xFF;
851                 out[31] = (n >> 24) & 0xFF;
852                 // the length of the data chunk
853                 n = i - 44;
854                 out[40] = (n) & 0xFF;
855                 out[41] = (n >> 8) & 0xFF;
856                 out[42] = (n >> 16) & 0xFF;
857                 out[43] = (n >> 24) & 0xFF;
858                 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
859                 FS_Write (cl_capturevideo_soundfile, out, 44);
860                 FS_Close (cl_capturevideo_soundfile);
861                 cl_capturevideo_soundfile = NULL;
862         }
863
864         if (cl_capturevideo_buffer)
865         {
866                 Mem_Free (cl_capturevideo_buffer);
867                 cl_capturevideo_buffer = NULL;
868         }
869
870         cl_capturevideo_starttime = 0;
871         cl_capturevideo_framerate = 0;
872         cl_capturevideo_frame = 0;
873 }
874
875 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
876 {
877         int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
878         unsigned char *b, *out;
879         char filename[32];
880         int outoffset = (width/2)*(height/2);
881         //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);
882         // speed is critical here, so do saving as directly as possible
883         switch (cl_capturevideo_format)
884         {
885         case CAPTUREVIDEOFORMAT_RAWYV12:
886                 // FIXME: width/height must be multiple of 2, enforce this?
887                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
888                 CHECKGLERROR
889                 // process one line at a time, and CbCr every other line at 2 pixel intervals
890                 for (y = 0;y < height;y++)
891                 {
892                         // 1x1 Y
893                         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++)
894                                 *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]]];
895                         if ((y & 1) == 0)
896                         {
897                                 // 2x2 Cb and Cr planes
898 #if 1
899                                 // low quality, no averaging
900                                 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++)
901                                 {
902                                         // Cr
903                                         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];
904                                         // Cb
905                                         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];
906                                 }
907 #else
908                                 // high quality, averaging
909                                 int inpitch = width*3;
910                                 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++)
911                                 {
912                                         int blockr, blockg, blockb;
913                                         blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
914                                         blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
915                                         blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
916                                         // Cr
917                                         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];
918                                         // Cb
919                                         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];
920                                 }
921 #endif
922                         }
923                 }
924                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
925                         if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
926                                 return false;
927                 return true;
928         case CAPTUREVIDEOFORMAT_RAWRGB:
929                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
930                 CHECKGLERROR
931                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
932                         if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
933                                 return false;
934                 return true;
935         case CAPTUREVIDEOFORMAT_JPEG:
936                 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
937                 CHECKGLERROR
938                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
939                 {
940                         sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
941                         if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
942                                 return false;
943                 }
944                 return true;
945         case CAPTUREVIDEOFORMAT_TARGA:
946                 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
947                 memset (cl_capturevideo_buffer, 0, 18);
948                 cl_capturevideo_buffer[2] = 2;          // uncompressed type
949                 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
950                 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
951                 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
952                 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
953                 cl_capturevideo_buffer[16] = 24;        // pixel size
954                 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
955                 CHECKGLERROR
956                 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
957                 {
958                         sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
959                         if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
960                                 return false;
961                 }
962                 return true;
963         default:
964                 return false;
965         }
966 }
967
968 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
969 {
970         cl_capturevideo_soundrate = rate;
971         if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
972         {
973                 Cvar_SetValueQuick(&cl_capturevideo, 0);
974                 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
975                 SCR_CaptureVideo_EndVideo();
976         }
977 }
978
979 void SCR_CaptureVideo(void)
980 {
981         int newframenum;
982         if (cl_capturevideo.integer && r_render.integer)
983         {
984                 if (!cl_capturevideo_active)
985                         SCR_CaptureVideo_BeginVideo();
986                 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
987                 {
988                         Con_Printf("You can not change the video framerate while recording a video.\n");
989                         Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
990                 }
991                 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
992                 // if falling behind more than one second, stop
993                 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
994                 {
995                         Cvar_SetValueQuick(&cl_capturevideo, 0);
996                         Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
997                         SCR_CaptureVideo_EndVideo();
998                         return;
999                 }
1000                 // write frames
1001                 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1002                 {
1003                         Cvar_SetValueQuick(&cl_capturevideo, 0);
1004                         Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1005                         SCR_CaptureVideo_EndVideo();
1006                 }
1007         }
1008         else if (cl_capturevideo_active)
1009                 SCR_CaptureVideo_EndVideo();
1010 }
1011
1012 /*
1013 ===============
1014 R_Envmap_f
1015
1016 Grab six views for environment mapping tests
1017 ===============
1018 */
1019 struct
1020 {
1021         float angles[3];
1022         char *name;
1023         qboolean flipx, flipy, flipdiagonaly;
1024 }
1025 envmapinfo[12] =
1026 {
1027         {{  0,   0, 0}, "rt",  true, false, false},
1028         {{  0,  90, 0}, "ft",  true, false, false},
1029         {{  0, 180, 0}, "lf",  true, false, false},
1030         {{  0, 270, 0}, "bk",  true, false, false},
1031         {{-90, 180, 0}, "up", false,  true, false},
1032         {{ 90, 180, 0}, "dn", false,  true, false},
1033
1034         {{  0,   0, 0}, "px",  true,  true,  true},
1035         {{  0,  90, 0}, "py", false,  true, false},
1036         {{  0, 180, 0}, "nx", false, false,  true},
1037         {{  0, 270, 0}, "ny",  true, false, false},
1038         {{-90, 180, 0}, "pz", false, false,  true},
1039         {{ 90, 180, 0}, "nz", false, false,  true}
1040 };
1041
1042 static void R_Envmap_f (void)
1043 {
1044         int j, size;
1045         char filename[256], basename[256];
1046         qbyte *buffer1;
1047         qbyte *buffer2;
1048         qbyte *buffer3;
1049
1050         if (Cmd_Argc() != 3)
1051         {
1052                 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");
1053                 return;
1054         }
1055
1056         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1057         size = atoi(Cmd_Argv(2));
1058         if (size != 128 && size != 256 && size != 512 && size != 1024)
1059         {
1060                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1061                 return;
1062         }
1063         if (size > vid.realwidth || size > vid.realheight)
1064         {
1065                 Con_Print("envmap: your resolution is not big enough to render that size\n");
1066                 return;
1067         }
1068
1069         envmap = true;
1070
1071         r_refdef.x = 0;
1072         r_refdef.y = 0;
1073         r_refdef.width = size;
1074         r_refdef.height = size;
1075
1076         r_refdef.fov_x = 90;
1077         r_refdef.fov_y = 90;
1078
1079         buffer1 = Mem_Alloc(tempmempool, size * size * 3);
1080         buffer2 = Mem_Alloc(tempmempool, size * size * 3);
1081         buffer3 = Mem_Alloc(tempmempool, size * size * 3 + 18);
1082
1083         for (j = 0;j < 12;j++)
1084         {
1085                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1086                 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);
1087                 R_ClearScreen();
1088                 R_Mesh_Start();
1089                 R_RenderView();
1090                 R_Mesh_Finish();
1091                 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);
1092         }
1093
1094         Mem_Free (buffer1);
1095         Mem_Free (buffer2);
1096         Mem_Free (buffer3);
1097
1098         envmap = false;
1099 }
1100
1101 //=============================================================================
1102
1103 // LordHavoc: SHOWLMP stuff
1104 #define SHOWLMP_MAXLABELS 256
1105 typedef struct showlmp_s
1106 {
1107         qboolean        isactive;
1108         float           x;
1109         float           y;
1110         char            label[32];
1111         char            pic[128];
1112 }
1113 showlmp_t;
1114
1115 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1116
1117 void SHOWLMP_decodehide(void)
1118 {
1119         int i;
1120         qbyte *lmplabel;
1121         lmplabel = MSG_ReadString();
1122         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1123                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1124                 {
1125                         showlmp[i].isactive = false;
1126                         return;
1127                 }
1128 }
1129
1130 void SHOWLMP_decodeshow(void)
1131 {
1132         int i, k;
1133         qbyte lmplabel[256], picname[256];
1134         float x, y;
1135         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1136         strlcpy (picname, MSG_ReadString(), sizeof (picname));
1137         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1138         {
1139                 x = MSG_ReadByte();
1140                 y = MSG_ReadByte();
1141         }
1142         else
1143         {
1144                 x = MSG_ReadShort();
1145                 y = MSG_ReadShort();
1146         }
1147         k = -1;
1148         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1149                 if (showlmp[i].isactive)
1150                 {
1151                         if (strcmp(showlmp[i].label, lmplabel) == 0)
1152                         {
1153                                 k = i;
1154                                 break; // drop out to replace it
1155                         }
1156                 }
1157                 else if (k < 0) // find first empty one to replace
1158                         k = i;
1159         if (k < 0)
1160                 return; // none found to replace
1161         // change existing one
1162         showlmp[k].isactive = true;
1163         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1164         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1165         showlmp[k].x = x;
1166         showlmp[k].y = y;
1167 }
1168
1169 void SHOWLMP_drawall(void)
1170 {
1171         int i;
1172         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1173                 if (showlmp[i].isactive)
1174                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1175 }
1176
1177 void SHOWLMP_clear(void)
1178 {
1179         int i;
1180         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1181                 showlmp[i].isactive = false;
1182 }
1183
1184 void CL_SetupScreenSize(void)
1185 {
1186         float conwidth, conheight;
1187
1188         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
1189
1190         VID_UpdateGamma(false);
1191
1192         conwidth = bound(320, vid_conwidth.value, 2048);
1193         conheight = bound(200, vid_conheight.value, 1536);
1194         if (vid_conwidth.value != conwidth)
1195                 Cvar_SetValue("vid_conwidth", conwidth);
1196         if (vid_conheight.value != conheight)
1197                 Cvar_SetValue("vid_conheight", conheight);
1198
1199         vid.conwidth = vid_conwidth.integer;
1200         vid.conheight = vid_conheight.integer;
1201
1202 /*      if (vid.realheight > 240)
1203         {
1204                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
1205                 vid.conheight = bound(240, vid.conheight, vid.realheight);
1206         }
1207         else
1208                 vid.conheight = 240;*/
1209
1210         SCR_SetUpToDrawConsole();
1211 }
1212
1213 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1214 void CL_UpdateScreen(void)
1215 {
1216         if (!scr_initialized || !con_initialized || vid_hidden)
1217                 return;                         // not initialized yet
1218
1219         // don't allow cheats in multiplayer
1220         if (!cl.islocalgame && cl.worldmodel)
1221         {
1222                 if (r_fullbright.integer != 0)
1223                         Cvar_Set ("r_fullbright", "0");
1224                 if (r_ambient.value != 0)
1225                         Cvar_Set ("r_ambient", "0");
1226         }
1227
1228         // bound viewsize
1229         if (scr_viewsize.value < 30)
1230                 Cvar_Set ("viewsize","30");
1231         if (scr_viewsize.value > 120)
1232                 Cvar_Set ("viewsize","120");
1233
1234         // bound field of view
1235         if (scr_fov.value < 1)
1236                 Cvar_Set ("fov","1");
1237         if (scr_fov.value > 170)
1238                 Cvar_Set ("fov","170");
1239
1240         // intermission is always full screen
1241         if (cl.intermission)
1242                 sb_lines = 0;
1243         else
1244         {
1245                 if (scr_viewsize.value >= 120)
1246                         sb_lines = 0;           // no status bar at all
1247                 else if (scr_viewsize.value >= 110)
1248                         sb_lines = 24;          // no inventory
1249                 else
1250                         sb_lines = 24+16+8;
1251         }
1252
1253         r_refdef.colormask[0] = 1;
1254         r_refdef.colormask[1] = 1;
1255         r_refdef.colormask[2] = 1;
1256
1257         SCR_CaptureVideo();
1258
1259         if (cls.signon == SIGNONS)
1260                 R_TimeReport("other");
1261
1262         CL_SetupScreenSize();
1263
1264         DrawQ_Clear();
1265
1266         if (cls.signon == SIGNONS)
1267                 R_TimeReport("setup");
1268
1269         //FIXME: force menu if nothing else to look at?
1270         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1271
1272         if (cls.signon == SIGNONS)
1273         {
1274                 SCR_DrawNet ();
1275                 SCR_DrawTurtle ();
1276                 SCR_DrawPause ();
1277                 if (!r_letterbox.value)
1278                         Sbar_Draw();
1279                 SHOWLMP_drawall();
1280                 SCR_CheckDrawCenterString();
1281         }
1282         MR_Draw();
1283         UI_Callback_Draw();
1284         CL_DrawVideo();
1285         //ui_draw();
1286         if (cls.signon == SIGNONS)
1287         {
1288                 R_TimeReport("2d");
1289                 R_TimeReport_End();
1290                 R_TimeReport_Start();
1291         }
1292         R_Shadow_EditLights_DrawSelectedLightProperties();
1293
1294         SCR_DrawConsole();
1295
1296         SCR_UpdateScreen();
1297 }
1298
1299 void CL_Screen_NewMap(void)
1300 {
1301         SHOWLMP_clear();
1302 }