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