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