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