5 #include "cl_collision.h"
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_showbrand = {0, "showbrand","0"};
18 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
19 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640"};
20 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480"};
21 cvar_t vid_pixelaspect = {CVAR_SAVE, "vid_pixelaspect", "1"};
22 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","1"};
23 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9"};
24 cvar_t scr_screenshot_gamma = {CVAR_SAVE, "scr_screenshot_gamma","2.2"};
25 // scr_screenshot_name is defined in fs.c
26 cvar_t cl_capturevideo = {0, "cl_capturevideo", "0"};
27 cvar_t cl_capturevideo_sound = {0, "cl_capturevideo_sound", "0"};
28 cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30"};
29 cvar_t cl_capturevideo_rawrgb = {0, "cl_capturevideo_rawrgb", "0"};
30 cvar_t cl_capturevideo_rawyv12 = {0, "cl_capturevideo_rawyv12", "0"};
31 cvar_t r_textshadow = {0, "r_textshadow", "0"};
32 cvar_t r_letterbox = {0, "r_letterbox", "0"};
34 int jpeg_supported = false;
36 qboolean scr_initialized; // ready to draw
38 float scr_con_current;
40 extern int con_vislines;
42 void DrawCrosshair(int num);
43 static void SCR_ScreenShot_f (void);
44 static void R_Envmap_f (void);
47 void R_ClearScreen(void);
50 static vec4_t string_colors[] =
53 // LordHavoc: why on earth is cyan before magenta in Quake3?
54 // LordHavoc: note: Doom3 uses white for [0] and [7]
55 {0.0, 0.0, 0.0, 1.0}, // black
56 {1.0, 0.0, 0.0, 1.0}, // red
57 {0.0, 1.0, 0.0, 1.0}, // green
58 {1.0, 1.0, 0.0, 1.0}, // yellow
59 {0.0, 0.0, 1.0, 1.0}, // blue
60 {0.0, 1.0, 1.0, 1.0}, // cyan
61 {1.0, 0.0, 1.0, 1.0}, // magenta
62 {1.0, 1.0, 1.0, 1.0}, // white
63 // [515]'s BX_COLOREDTEXT extension
64 {1.0, 1.0, 1.0, 0.5}, // half transparent
65 {0.5, 0.5, 0.5, 1.0} // half brightness
66 // Black's color table
67 //{1.0, 1.0, 1.0, 1.0},
68 //{1.0, 0.0, 0.0, 1.0},
69 //{0.0, 1.0, 0.0, 1.0},
70 //{0.0, 0.0, 1.0, 1.0},
71 //{1.0, 1.0, 0.0, 1.0},
72 //{0.0, 1.0, 1.0, 1.0},
73 //{1.0, 0.0, 1.0, 1.0},
74 //{0.1, 0.1, 0.1, 1.0}
77 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
79 // color is read and changed in the end
80 void DrawQ_ColoredString( float x, float y, const char *text, int maxlen, float scalex, float scaley, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor )
85 const char *start, *current;
87 if( !outcolor || *outcolor == -1 ) {
88 colorindex = STRING_COLOR_DEFAULT;
90 colorindex = *outcolor;
92 color = string_colors[colorindex];
95 len = (int)strlen( text );
97 len = min( maxlen, (int) strlen( text ) );
99 start = current = text;
101 // check for color control char
102 if( *current == STRING_COLOR_TAG ) {
109 // display the tag char?
110 if( *current == STRING_COLOR_TAG ) {
111 // only display one of the two
116 } else if( '0' <= *current && *current <= '9' ) {
119 colorindex = colorindex * 10 + (*current - '0');
120 // only read as long as it makes a valid index
121 if( colorindex >= (int)STRING_COLORS_COUNT ) {
122 // undo the last operation
128 } while( len > 0 && '0' <= *current && *current <= '9' );
130 color = string_colors[colorindex];
131 // we jump over the color tag
135 // go on and read normal text in until the next control char
136 while( len > 0 && *current != STRING_COLOR_TAG ) {
141 if( start != current ) {
143 DrawQ_String( x, y, start, current - start, scalex, scaley, basered * color[0], basegreen * color[1], baseblue * color[2], basealpha * color[3], flags );
144 // update x to be at the new start position
145 x += (current - start) * scalex;
146 // set start accordingly
151 // return the last colorindex
153 *outcolor = colorindex;
158 ===============================================================================
162 ===============================================================================
165 char scr_centerstring[1024];
166 float scr_centertime_start; // for slow victory printing
167 float scr_centertime_off;
168 int scr_center_lines;
170 int scr_erase_center;
176 Called for important messages that should stay in the center of the screen
180 void SCR_CenterPrint(char *str)
182 strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
183 scr_centertime_off = scr_centertime.value;
184 scr_centertime_start = cl.time;
186 // count the number of lines for centering
187 scr_center_lines = 1;
197 void SCR_DrawCenterString (void)
205 // the finale prints the characters one at a time
207 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
211 scr_erase_center = 0;
212 start = scr_centerstring;
214 if (scr_center_lines <= 4)
215 y = vid_conheight.integer*0.35;
222 // scan the width of the line
223 for (l=0 ; l<vid_conwidth.integer/8 ; l++)
224 if (start[l] == '\n' || !start[l])
226 x = (vid_conwidth.integer - l*8)/2;
231 DrawQ_ColoredString(x, y, start, l, 8, 8, 1, 1, 1, 1, 0, &color);
239 while (*start && *start != '\n')
244 start++; // skip the \n
248 void SCR_CheckDrawCenterString (void)
250 if (scr_center_lines > scr_erase_lines)
251 scr_erase_lines = scr_center_lines;
253 scr_centertime_off -= host_frametime;
255 // don't draw if this is a normal stats-screen intermission,
256 // only if it is not an intermission, or a finale intermission
257 if (cl.intermission == 1)
259 if (scr_centertime_off <= 0 && !cl.intermission)
261 if (key_dest != key_game)
264 SCR_DrawCenterString ();
272 void SCR_DrawTurtle (void)
276 if (cls.state != ca_connected)
279 if (!scr_showturtle.integer)
282 if (host_frametime < 0.1)
292 DrawQ_Pic (0, 0, "gfx/turtle", 0, 0, 1, 1, 1, 1, 0);
300 void SCR_DrawNet (void)
302 if (cls.state != ca_connected)
304 if (realtime - cl.last_received_message < 0.3)
306 if (cls.demoplayback)
309 DrawQ_Pic (64, 0, "gfx/net", 0, 0, 1, 1, 1, 1, 0);
317 void SCR_DrawPause (void)
321 if (cls.state != ca_connected)
324 if (!scr_showpause.integer) // turn off for screenshots
330 pic = Draw_CachePic ("gfx/pause", true);
331 DrawQ_Pic ((vid_conwidth.integer - pic->width)/2, (vid_conheight.integer - pic->height)/2, "gfx/pause", 0, 0, 1, 1, 1, 1, 0);
339 void SCR_DrawBrand (void)
344 if (!scr_showbrand.value)
347 pic = Draw_CachePic ("gfx/brand", true);
349 switch ((int)scr_showbrand.value)
351 case 1: // bottom left
353 y = vid_conheight.integer - pic->height;
355 case 2: // bottom centre
356 x = (vid_conwidth.integer - pic->width) / 2;
357 y = vid_conheight.integer - pic->height;
359 case 3: // bottom right
360 x = vid_conwidth.integer - pic->width;
361 y = vid_conheight.integer - pic->height;
363 case 4: // centre right
364 x = vid_conwidth.integer - pic->width;
365 y = (vid_conheight.integer - pic->height) / 2;
368 x = vid_conwidth.integer - pic->width;
371 case 6: // top centre
372 x = (vid_conwidth.integer - pic->width) / 2;
379 case 8: // centre left
381 y = (vid_conheight.integer - pic->height) / 2;
387 DrawQ_Pic (x, y, "gfx/brand", 0, 0, 1, 1, 1, 1, 0);
390 //=============================================================================
395 SCR_SetUpToDrawConsole
398 void SCR_SetUpToDrawConsole (void)
400 // lines of console to display
405 if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
406 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
408 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
410 // decide on the height of the console
411 if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
412 conlines = vid_conheight.integer/2; // half screen
414 conlines = 0; // none visible
416 if (scr_conspeed.value)
418 if (scr_con_current > conlines)
420 scr_con_current -= scr_conspeed.value*host_realframetime;
421 if (scr_con_current < conlines)
422 scr_con_current = conlines;
425 else if (scr_con_current < conlines)
427 scr_con_current += scr_conspeed.value*host_realframetime;
428 if (scr_con_current > conlines)
429 scr_con_current = conlines;
433 scr_con_current = conlines;
441 void SCR_DrawConsole (void)
443 if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
446 Con_DrawConsole (vid_conheight.integer);
448 else if (scr_con_current)
449 Con_DrawConsole (scr_con_current);
453 if (key_dest == key_game || key_dest == key_message)
454 Con_DrawNotify (); // only draw notify in game
460 SCR_BeginLoadingPlaque
464 void SCR_BeginLoadingPlaque (void)
468 SCR_UpdateLoadingScreen();
471 //=============================================================================
473 char r_speeds_string[1024];
474 int speedstringcount, r_timereport_active;
475 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
477 void R_TimeReport(char *desc)
483 if (!r_timereport_active || r_showtrispass)
487 r_timereport_temp = r_timereport_current;
488 r_timereport_current = Sys_DoubleTime();
489 t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
491 dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %s", t, desc);
492 length = (int)strlen(tempbuf);
494 tempbuf[length++] = ' ';
496 if (speedstringcount + length > (vid_conwidth.integer / 8))
498 strlcat(r_speeds_string, "\n", sizeof(r_speeds_string));
499 speedstringcount = 0;
501 // skip the space at the beginning if it's the first on the line
502 if (speedstringcount == 0)
504 strlcat(r_speeds_string, tempbuf + 1, sizeof(r_speeds_string));
505 speedstringcount = length - 1;
509 strlcat(r_speeds_string, tempbuf, sizeof(r_speeds_string));
510 speedstringcount += length;
514 void R_TimeReport_Start(void)
516 r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
517 r_speeds_string[0] = 0;
518 if (r_timereport_active)
520 speedstringcount = 0;
521 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]);
522 sprintf(r_speeds_string + strlen(r_speeds_string), "%5i entities%6i surfaces%6i triangles%5i leafs%5i portals%6i particles\n", renderstats.entities, renderstats.entities_surfaces, renderstats.entities_triangles, renderstats.world_leafs, renderstats.world_portals, renderstats.particles);
523 sprintf(r_speeds_string + strlen(r_speeds_string), "%4i lights%4i clears%4i scissored%7i light%7i shadow%i7 dynamic\n", renderstats.lights, renderstats.lights_clears, renderstats.lights_scissored, renderstats.lights_lighttriangles, renderstats.lights_shadowtriangles, renderstats.lights_dynamicshadowtriangles);
524 if (renderstats.bloom)
525 sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles bloompixels%8i copied%8i drawn\n", renderstats.meshes, renderstats.meshes_elements / 3, renderstats.bloom_copypixels, renderstats.bloom_drawpixels);
527 sprintf(r_speeds_string + strlen(r_speeds_string), "rendered%6i meshes%8i triangles\n", renderstats.meshes, renderstats.meshes_elements / 3);
529 r_timereport_start = Sys_DoubleTime();
532 memset(&renderstats, 0, sizeof(renderstats));
535 void R_TimeReport_End(void)
537 r_timereport_current = r_timereport_start;
538 R_TimeReport("total");
540 if (r_timereport_active)
544 for (i = 0;r_speeds_string[i];i++)
545 if (r_speeds_string[i] == '\n')
547 y = vid_conheight.integer - sb_lines - lines * 8;
549 DrawQ_Fill(0, y, vid_conwidth.integer, lines * 8, 0, 0, 0, 0.5, 0);
550 while (r_speeds_string[i])
553 while (r_speeds_string[i] && r_speeds_string[i] != '\n')
556 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
557 if (r_speeds_string[i] == '\n')
571 void SCR_SizeUp_f (void)
573 Cvar_SetValue ("viewsize",scr_viewsize.value+10);
584 void SCR_SizeDown_f (void)
586 Cvar_SetValue ("viewsize",scr_viewsize.value-10);
589 void CL_Screen_Init(void)
591 Cvar_RegisterVariable (&scr_fov);
592 Cvar_RegisterVariable (&scr_viewsize);
593 Cvar_RegisterVariable (&scr_conspeed);
594 Cvar_RegisterVariable (&scr_conalpha);
595 Cvar_RegisterVariable (&scr_conbrightness);
596 Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
597 Cvar_RegisterVariable (&scr_showram);
598 Cvar_RegisterVariable (&scr_showturtle);
599 Cvar_RegisterVariable (&scr_showpause);
600 Cvar_RegisterVariable (&scr_showbrand);
601 Cvar_RegisterVariable (&scr_centertime);
602 Cvar_RegisterVariable (&scr_printspeed);
603 Cvar_RegisterVariable (&vid_conwidth);
604 Cvar_RegisterVariable (&vid_conheight);
605 Cvar_RegisterVariable (&vid_pixelaspect);
606 Cvar_RegisterVariable (&scr_screenshot_jpeg);
607 Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
608 Cvar_RegisterVariable (&scr_screenshot_gamma);
609 Cvar_RegisterVariable (&cl_capturevideo);
610 Cvar_RegisterVariable (&cl_capturevideo_sound);
611 Cvar_RegisterVariable (&cl_capturevideo_fps);
612 Cvar_RegisterVariable (&cl_capturevideo_rawrgb);
613 Cvar_RegisterVariable (&cl_capturevideo_rawyv12);
614 Cvar_RegisterVariable (&r_textshadow);
615 Cvar_RegisterVariable (&r_letterbox);
617 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
618 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
619 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
620 Cmd_AddCommand ("envmap", R_Envmap_f);
622 scr_initialized = true;
625 void DrawQ_Clear(void)
627 r_refdef.drawqueuesize = 0;
630 static int picelements[6] = {0, 1, 2, 0, 2, 3};
631 void DrawQ_Pic(float x, float y, const char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
633 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);
636 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)
641 if (alpha < (1.0f / 255.0f))
644 len = (int)strlen(string);
646 for (len = 0;len < maxlen && string[len];len++);
647 for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
648 for (;len > 0 && string[len - 1] == ' ';len--);
651 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || x < (-scalex * len) || y < (-scaley))
653 size = sizeof(*dq) + ((len + 1 + 3) & ~3);
654 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
656 red = bound(0, red, 1);
657 green = bound(0, green, 1);
658 blue = bound(0, blue, 1);
659 alpha = bound(0, alpha, 1);
660 dq = (drawqueue_t *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
662 dq->command = DRAWQUEUE_STRING;
664 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));
669 out = (char *)(dq + 1);
670 memcpy(out, string, len);
672 r_refdef.drawqueuesize += dq->size;
675 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)
677 if (r_textshadow.integer)
678 DrawQ_String_Real(x+scalex*0.25,y+scaley*0.25,string,maxlen,scalex,scaley,0,0,0,alpha*0.8,flags);
680 DrawQ_String_Real(x,y,string,maxlen,scalex,scaley,red,green,blue,alpha,flags);
685 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
687 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);
690 void DrawQ_SuperPic(float x, float y, const 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)
694 drawqueuemesh_t mesh;
695 memset(&mesh, 0, sizeof(mesh));
696 if (picname && picname[0])
698 pic = Draw_CachePic(picname, false);
702 height = pic->height;
703 mesh.texture = pic->tex;
705 mesh.num_triangles = 2;
706 mesh.num_vertices = 4;
707 mesh.data_element3i = picelements;
708 mesh.data_vertex3f = floats;
709 mesh.data_texcoord2f = floats + 12;
710 mesh.data_color4f = floats + 20;
711 memset(floats, 0, sizeof(floats));
712 mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
713 mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
714 mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
715 mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
716 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;
717 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;
718 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;
719 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;
720 DrawQ_Mesh (&mesh, flags);
723 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
730 size += sizeof(drawqueuemesh_t);
731 size += sizeof(int[3]) * mesh->num_triangles;
732 size += sizeof(float[3]) * mesh->num_vertices;
733 size += sizeof(float[2]) * mesh->num_vertices;
734 size += sizeof(float[4]) * mesh->num_vertices;
735 if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
737 dq = (drawqueue_t *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
739 dq->command = DRAWQUEUE_MESH;
746 p = (void *)(dq + 1);
747 m = (drawqueuemesh_t *)p;p = (qbyte*)p + sizeof(drawqueuemesh_t);
748 m->num_triangles = mesh->num_triangles;
749 m->num_vertices = mesh->num_vertices;
750 m->texture = mesh->texture;
751 m->data_element3i = (int *)p;memcpy(m->data_element3i , mesh->data_element3i , m->num_triangles * sizeof(int[3]));p = (qbyte*)p + m->num_triangles * sizeof(int[3]);
752 m->data_vertex3f = (float *)p;memcpy(m->data_vertex3f , mesh->data_vertex3f , m->num_vertices * sizeof(float[3]));p = (qbyte*)p + m->num_vertices * sizeof(float[3]);
753 m->data_texcoord2f = (float *)p;memcpy(m->data_texcoord2f, mesh->data_texcoord2f, m->num_vertices * sizeof(float[2]));p = (qbyte*)p + m->num_vertices * sizeof(float[2]);
754 m->data_color4f = (float *)p;memcpy(m->data_color4f , mesh->data_color4f , m->num_vertices * sizeof(float[4]));p = (qbyte*)p + m->num_vertices * sizeof(float[4]);
755 r_refdef.drawqueuesize += dq->size;
758 void DrawQ_SetClipArea(float x, float y, float width, float height)
761 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
763 Con_DPrint("DrawQueue full !\n");
766 dq = (drawqueue_t *) (r_refdef.drawqueue + r_refdef.drawqueuesize);
767 dq->size = sizeof(*dq);
768 dq->command = DRAWQUEUE_SETCLIP;
776 r_refdef.drawqueuesize += dq->size;
779 void DrawQ_ResetClipArea(void)
782 if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
784 Con_DPrint("DrawQueue full !\n");
787 dq = (drawqueue_t *) (r_refdef.drawqueue + r_refdef.drawqueuesize);
788 dq->size = sizeof(*dq);
789 dq->command = DRAWQUEUE_RESETCLIP;
797 r_refdef.drawqueuesize += dq->size;
805 void SCR_ScreenShot_f (void)
807 static int shotnumber;
808 static char oldname[MAX_QPATH];
809 char base[MAX_QPATH];
810 char filename[MAX_QPATH];
814 qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
816 sprintf (base, "screenshots/%s", scr_screenshot_name.string);
818 if (strcmp (oldname, scr_screenshot_name.string))
820 sprintf(oldname, "%s", scr_screenshot_name.string);
824 // find a file name to save it to
825 for (;shotnumber < 1000000;shotnumber++)
826 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
828 if (shotnumber >= 1000000)
830 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
834 sprintf(filename, "%s%06d.%s", base, shotnumber, jpeg ? "jpg" : "tga");
836 buffer1 = (qbyte *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
837 buffer2 = (qbyte *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
838 buffer3 = (qbyte *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
840 if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
841 Con_Printf("Wrote %s\n", filename);
843 Con_Printf("unable to write %s\n", filename);
852 typedef enum capturevideoformat_e
854 CAPTUREVIDEOFORMAT_TARGA,
855 CAPTUREVIDEOFORMAT_JPEG,
856 CAPTUREVIDEOFORMAT_RAWRGB,
857 CAPTUREVIDEOFORMAT_RAWYV12
859 capturevideoformat_t;
861 qboolean cl_capturevideo_active = false;
862 capturevideoformat_t cl_capturevideo_format;
863 static double cl_capturevideo_starttime = 0;
864 double cl_capturevideo_framerate = 0;
865 static int cl_capturevideo_soundrate = 0;
866 static int cl_capturevideo_frame = 0;
867 static qbyte *cl_capturevideo_buffer = NULL;
868 static qfile_t *cl_capturevideo_videofile = NULL;
869 qfile_t *cl_capturevideo_soundfile = NULL;
870 static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
871 static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
872 //static unsigned char cl_capturevideo_rgbgammatable[3][256];
874 void SCR_CaptureVideo_BeginVideo(void)
879 if (cl_capturevideo_active)
881 // soundrate is figured out on the first SoundFrame
882 cl_capturevideo_active = true;
883 cl_capturevideo_starttime = Sys_DoubleTime();
884 cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
885 cl_capturevideo_soundrate = 0;
886 cl_capturevideo_frame = 0;
887 cl_capturevideo_buffer = (qbyte *)Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
888 gamma = 1.0/scr_screenshot_gamma.value;
891 for (i = 0;i < 256;i++)
893 unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
894 cl_capturevideo_rgbgammatable[0][i] = j;
895 cl_capturevideo_rgbgammatable[1][i] = j;
896 cl_capturevideo_rgbgammatable[2][i] = j;
900 R = Y + 1.4075 * (Cr - 128);
901 G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
902 B = Y + 1.7790 * (Cb - 128);
903 Y = R * .299 + G * .587 + B * .114;
904 Cb = R * -.169 + G * -.332 + B * .500 + 128.;
905 Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
907 for (i = 0;i < 256;i++)
909 g = 255*pow(i/255.0, gamma);
910 // Y weights from RGB
911 cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
912 cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
913 cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
914 // Cb weights from RGB
915 cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
916 cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
917 cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
918 // Cr weights from RGB
919 cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
920 cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
921 cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
922 // range reduction of YCbCr to valid signal range
923 cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
924 cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
925 cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
928 if (cl_capturevideo_rawrgb.integer)
930 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
931 cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
933 else if (cl_capturevideo_rawyv12.integer)
935 cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
936 cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
938 else if (scr_screenshot_jpeg.integer)
940 cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
941 cl_capturevideo_videofile = NULL;
945 cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
946 cl_capturevideo_videofile = NULL;
949 if (cl_capturevideo_sound.integer)
951 cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
952 // wave header will be filled out when video ends
954 FS_Write (cl_capturevideo_soundfile, out, 44);
957 cl_capturevideo_soundfile = NULL;
960 void SCR_CaptureVideo_EndVideo(void)
964 if (!cl_capturevideo_active)
966 cl_capturevideo_active = false;
968 if (cl_capturevideo_videofile)
970 FS_Close(cl_capturevideo_videofile);
971 cl_capturevideo_videofile = NULL;
974 // finish the wave file
975 if (cl_capturevideo_soundfile)
977 i = (int)FS_Tell (cl_capturevideo_soundfile);
978 //"RIFF", (int) unknown (chunk size), "WAVE",
979 //"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
980 //"data", (int) unknown (chunk size)
981 memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
982 // the length of the whole RIFF chunk
985 out[5] = (n >> 8) & 0xFF;
986 out[6] = (n >> 16) & 0xFF;
987 out[7] = (n >> 24) & 0xFF;
989 n = cl_capturevideo_soundrate;
990 out[24] = (n) & 0xFF;
991 out[25] = (n >> 8) & 0xFF;
992 out[26] = (n >> 16) & 0xFF;
993 out[27] = (n >> 24) & 0xFF;
994 // bytes per second (rate * channels * bytes per channel)
995 n = cl_capturevideo_soundrate * 2 * 2;
996 out[28] = (n) & 0xFF;
997 out[29] = (n >> 8) & 0xFF;
998 out[30] = (n >> 16) & 0xFF;
999 out[31] = (n >> 24) & 0xFF;
1000 // the length of the data chunk
1002 out[40] = (n) & 0xFF;
1003 out[41] = (n >> 8) & 0xFF;
1004 out[42] = (n >> 16) & 0xFF;
1005 out[43] = (n >> 24) & 0xFF;
1006 FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
1007 FS_Write (cl_capturevideo_soundfile, out, 44);
1008 FS_Close (cl_capturevideo_soundfile);
1009 cl_capturevideo_soundfile = NULL;
1012 if (cl_capturevideo_buffer)
1014 Mem_Free (cl_capturevideo_buffer);
1015 cl_capturevideo_buffer = NULL;
1018 cl_capturevideo_starttime = 0;
1019 cl_capturevideo_framerate = 0;
1020 cl_capturevideo_frame = 0;
1023 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
1025 int x = 0, y = 0, width = vid.width, height = vid.height;
1026 unsigned char *b, *out;
1028 int outoffset = (width/2)*(height/2);
1029 //return SCR_ScreenShot(filename, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, cl_capturevideo_buffer + vid.width * vid.height * 6, 0, 0, vid.width, vid.height, false, false, false, jpeg, true);
1030 // speed is critical here, so do saving as directly as possible
1031 switch (cl_capturevideo_format)
1033 case CAPTUREVIDEOFORMAT_RAWYV12:
1034 // FIXME: width/height must be multiple of 2, enforce this?
1035 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1037 // process one line at a time, and CbCr every other line at 2 pixel intervals
1038 for (y = 0;y < height;y++)
1041 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++)
1042 *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]]];
1045 // 2x2 Cb and Cr planes
1047 // low quality, no averaging
1048 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++)
1051 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];
1053 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];
1056 // high quality, averaging
1057 int inpitch = width*3;
1058 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++)
1060 int blockr, blockg, blockb;
1061 blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
1062 blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
1063 blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
1065 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];
1067 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];
1072 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1073 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
1076 case CAPTUREVIDEOFORMAT_RAWRGB:
1077 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1079 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1080 if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
1083 case CAPTUREVIDEOFORMAT_JPEG:
1084 qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
1086 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1088 sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
1089 if (!JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer))
1093 case CAPTUREVIDEOFORMAT_TARGA:
1094 //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.width * vid.height * 3, );
1095 memset (cl_capturevideo_buffer, 0, 18);
1096 cl_capturevideo_buffer[2] = 2; // uncompressed type
1097 cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
1098 cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
1099 cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
1100 cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
1101 cl_capturevideo_buffer[16] = 24; // pixel size
1102 qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
1104 for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
1106 sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
1107 if (!FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18))
1116 void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
1118 if (!cl_capturevideo_soundfile)
1120 cl_capturevideo_soundrate = rate;
1121 if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < (fs_offset_t)(4 * length))
1123 Cvar_SetValueQuick(&cl_capturevideo, 0);
1124 Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1125 SCR_CaptureVideo_EndVideo();
1129 void SCR_CaptureVideo(void)
1132 if (cl_capturevideo.integer && r_render.integer)
1134 if (!cl_capturevideo_active)
1135 SCR_CaptureVideo_BeginVideo();
1136 if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
1138 Con_Printf("You can not change the video framerate while recording a video.\n");
1139 Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
1141 if (cl_capturevideo_soundfile)
1143 // preserve sound sync by duplicating frames when running slow
1144 newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
1147 newframenum = cl_capturevideo_frame + 1;
1148 // if falling behind more than one second, stop
1149 if (newframenum - cl_capturevideo_frame > (int)ceil(cl_capturevideo_framerate))
1151 Cvar_SetValueQuick(&cl_capturevideo, 0);
1152 Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cl_capturevideo_frame);
1153 SCR_CaptureVideo_EndVideo();
1157 if (!SCR_CaptureVideo_VideoFrame(newframenum))
1159 Cvar_SetValueQuick(&cl_capturevideo, 0);
1160 Con_Printf("video saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
1161 SCR_CaptureVideo_EndVideo();
1164 else if (cl_capturevideo_active)
1165 SCR_CaptureVideo_EndVideo();
1172 Grab six views for environment mapping tests
1179 qboolean flipx, flipy, flipdiagonaly;
1183 {{ 0, 0, 0}, "rt", false, false, false},
1184 {{ 0, 270, 0}, "ft", false, false, false},
1185 {{ 0, 180, 0}, "lf", false, false, false},
1186 {{ 0, 90, 0}, "bk", false, false, false},
1187 {{-90, 180, 0}, "up", true, true, false},
1188 {{ 90, 180, 0}, "dn", true, true, false},
1190 {{ 0, 0, 0}, "px", true, true, true},
1191 {{ 0, 90, 0}, "py", false, true, false},
1192 {{ 0, 180, 0}, "nx", false, false, true},
1193 {{ 0, 270, 0}, "ny", true, false, false},
1194 {{-90, 180, 0}, "pz", false, false, true},
1195 {{ 90, 180, 0}, "nz", false, false, true}
1198 static void R_Envmap_f (void)
1201 char filename[256], basename[256];
1206 if (Cmd_Argc() != 3)
1208 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");
1212 strlcpy (basename, Cmd_Argv(1), sizeof (basename));
1213 size = atoi(Cmd_Argv(2));
1214 if (size != 128 && size != 256 && size != 512 && size != 1024)
1216 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
1219 if (size > vid.width || size > vid.height)
1221 Con_Print("envmap: your resolution is not big enough to render that size\n");
1229 r_refdef.width = size;
1230 r_refdef.height = size;
1232 r_refdef.fov_x = 90;
1233 r_refdef.fov_y = 90;
1235 buffer1 = (qbyte *)Mem_Alloc(tempmempool, size * size * 3);
1236 buffer2 = (qbyte *)Mem_Alloc(tempmempool, size * size * 3);
1237 buffer3 = (qbyte *)Mem_Alloc(tempmempool, size * size * 3 + 18);
1239 for (j = 0;j < 12;j++)
1241 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
1242 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);
1247 SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, vid.height - (r_refdef.y + r_refdef.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false);
1257 //=============================================================================
1259 // LordHavoc: SHOWLMP stuff
1260 #define SHOWLMP_MAXLABELS 256
1261 typedef struct showlmp_s
1271 showlmp_t showlmp[SHOWLMP_MAXLABELS];
1273 void SHOWLMP_decodehide(void)
1277 lmplabel = MSG_ReadString();
1278 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1279 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
1281 showlmp[i].isactive = false;
1286 void SHOWLMP_decodeshow(void)
1289 char lmplabel[256], picname[256];
1291 strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
1292 strlcpy (picname, MSG_ReadString(), sizeof (picname));
1293 if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
1300 x = MSG_ReadShort();
1301 y = MSG_ReadShort();
1304 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1305 if (showlmp[i].isactive)
1307 if (strcmp(showlmp[i].label, lmplabel) == 0)
1310 break; // drop out to replace it
1313 else if (k < 0) // find first empty one to replace
1316 return; // none found to replace
1317 // change existing one
1318 showlmp[k].isactive = true;
1319 strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
1320 strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
1325 void SHOWLMP_drawall(void)
1328 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1329 if (showlmp[i].isactive)
1330 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
1333 void SHOWLMP_clear(void)
1336 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
1337 showlmp[i].isactive = false;
1340 void CL_SetupScreenSize(void)
1342 float conwidth, conheight;
1344 VID_UpdateGamma(false);
1346 conwidth = bound(320, vid_conwidth.value, 2048);
1347 conheight = bound(200, vid_conheight.value, 1536);
1348 if (vid_conwidth.value != conwidth)
1349 Cvar_SetValue("vid_conwidth", conwidth);
1350 if (vid_conheight.value != conheight)
1351 Cvar_SetValue("vid_conheight", conheight);
1353 vid_conwidth.integer = vid_conwidth.integer;
1354 vid_conheight.integer = vid_conheight.integer;
1356 SCR_SetUpToDrawConsole();
1359 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
1360 void CL_UpdateScreen(void)
1362 if (!scr_initialized || !con_initialized || vid_hidden)
1363 return; // not initialized yet
1365 // don't allow cheats in multiplayer
1366 if (!cl.islocalgame && cl.worldmodel)
1368 if (r_fullbright.integer != 0)
1369 Cvar_Set ("r_fullbright", "0");
1370 if (r_ambient.value != 0)
1371 Cvar_Set ("r_ambient", "0");
1375 if (scr_viewsize.value < 30)
1376 Cvar_Set ("viewsize","30");
1377 if (scr_viewsize.value > 120)
1378 Cvar_Set ("viewsize","120");
1380 // bound field of view
1381 if (scr_fov.value < 1)
1382 Cvar_Set ("fov","1");
1383 if (scr_fov.value > 170)
1384 Cvar_Set ("fov","170");
1386 // intermission is always full screen
1387 if (cl.intermission)
1391 if (scr_viewsize.value >= 120)
1392 sb_lines = 0; // no status bar at all
1393 else if (scr_viewsize.value >= 110)
1394 sb_lines = 24; // no inventory
1399 r_refdef.colormask[0] = 1;
1400 r_refdef.colormask[1] = 1;
1401 r_refdef.colormask[2] = 1;
1405 if (cls.signon == SIGNONS)
1406 R_TimeReport("other");
1408 CL_SetupScreenSize();
1412 if (cls.signon == SIGNONS)
1413 R_TimeReport("setup");
1415 //FIXME: force menu if nothing else to look at?
1416 //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1418 if (cls.signon == SIGNONS)
1423 if (!r_letterbox.value)
1426 SCR_CheckDrawCenterString();
1432 if (cls.signon == SIGNONS)
1436 R_TimeReport_Start();
1438 R_Shadow_EditLights_DrawSelectedLightProperties();
1447 void CL_Screen_NewMap(void)