]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_screen.c
now supports GL_EXT_stencil_two_side extension (found on NV30/R300 class cards),...
[xonotic/darkplaces.git] / cl_screen.c
1
2 #include "quakedef.h"
3 #include "cl_video.h"
4 #include "jpeg.h"
5 #include "cl_collision.h"
6
7 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
8 cvar_t scr_fov = {CVAR_SAVE, "fov","90"};       // 1 - 170
9 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
10 cvar_t scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1"};
11 cvar_t scr_conbrightness = {CVAR_SAVE, "scr_conbrightness", "0.2"};
12 cvar_t scr_conforcewhiledisconnected = {CVAR_SAVE, "scr_conforcewhiledisconnected", "1"};
13 cvar_t scr_centertime = {0, "scr_centertime","2"};
14 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
15 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
16 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
17 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
18 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640"};
19 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480"};
20 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
21 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9"};
22 cvar_t cl_avidemo = {0, "cl_avidemo", "0"};
23
24 int jpeg_supported = false;
25
26 qboolean        scr_initialized;                // ready to draw
27
28 float           scr_con_current;
29 float           scr_conlines;           // lines of console to display
30
31 int                     clearconsole;
32 int                     clearnotify;
33
34 qboolean        scr_drawloading = false;
35
36 void DrawCrosshair(int num);
37 static void SCR_ScreenShot_f (void);
38 static void R_Envmap_f (void);
39
40 // backend
41 void R_ClearScreen(void);
42
43 /*
44 ===============================================================================
45
46 CENTER PRINTING
47
48 ===============================================================================
49 */
50
51 char            scr_centerstring[1024];
52 float           scr_centertime_start;   // for slow victory printing
53 float           scr_centertime_off;
54 int                     scr_center_lines;
55 int                     scr_erase_lines;
56 int                     scr_erase_center;
57
58 /*
59 ==============
60 SCR_CenterPrint
61
62 Called for important messages that should stay in the center of the screen
63 for a few moments
64 ==============
65 */
66 void SCR_CenterPrint(char *str)
67 {
68         strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
69         scr_centertime_off = scr_centertime.value;
70         scr_centertime_start = cl.time;
71
72 // count the number of lines for centering
73         scr_center_lines = 1;
74         while (*str)
75         {
76                 if (*str == '\n')
77                         scr_center_lines++;
78                 str++;
79         }
80 }
81
82
83 void SCR_DrawCenterString (void)
84 {
85         char    *start;
86         int             l;
87         int             x, y;
88         int             remaining;
89
90 // the finale prints the characters one at a time
91         if (cl.intermission)
92                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
93         else
94                 remaining = 9999;
95
96         scr_erase_center = 0;
97         start = scr_centerstring;
98
99         if (scr_center_lines <= 4)
100                 y = vid.conheight*0.35;
101         else
102                 y = 48;
103
104         do
105         {
106         // scan the width of the line
107                 for (l=0 ; l<40 ; l++)
108                         if (start[l] == '\n' || !start[l])
109                                 break;
110                 x = (vid.conwidth - l*8)/2;
111                 if (l > 0)
112                 {
113                         if (remaining < l)
114                                 l = remaining;
115                         DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
116                         remaining -= l;
117                         if (remaining <= 0)
118                                 return;
119                 }
120
121                 y += 8;
122
123                 while (*start && *start != '\n')
124                         start++;
125
126                 if (!*start)
127                         break;
128                 start++;                // skip the \n
129         } while (1);
130 }
131
132 void SCR_CheckDrawCenterString (void)
133 {
134         if (scr_center_lines > scr_erase_lines)
135                 scr_erase_lines = scr_center_lines;
136
137         scr_centertime_off -= host_frametime;
138
139         // don't draw if this is a normal stats-screen intermission,
140         // only if it is not an intermission, or a finale intermission
141         if (cl.intermission == 1)
142                 return;
143         if (scr_centertime_off <= 0 && !cl.intermission)
144                 return;
145         if (key_dest != key_game)
146                 return;
147
148         SCR_DrawCenterString ();
149 }
150
151 /*
152 ==============
153 SCR_DrawTurtle
154 ==============
155 */
156 void SCR_DrawTurtle (void)
157 {
158         static int      count;
159
160         if (cls.state != ca_connected)
161                 return;
162
163         if (!scr_showturtle.integer)
164                 return;
165
166         if (host_frametime < 0.1)
167         {
168                 count = 0;
169                 return;
170         }
171
172         count++;
173         if (count < 3)
174                 return;
175
176         DrawQ_Pic (0, 0, "gfx/turtle.lmp", 0, 0, 1, 1, 1, 1, 0);
177 }
178
179 /*
180 ==============
181 SCR_DrawNet
182 ==============
183 */
184 void SCR_DrawNet (void)
185 {
186         if (cls.state != ca_connected)
187                 return;
188         if (realtime - cl.last_received_message < 0.3)
189                 return;
190         if (cls.demoplayback)
191                 return;
192
193         DrawQ_Pic (64, 0, "gfx/net.lmp", 0, 0, 1, 1, 1, 1, 0);
194 }
195
196 /*
197 ==============
198 DrawPause
199 ==============
200 */
201 void SCR_DrawPause (void)
202 {
203         cachepic_t      *pic;
204
205         if (cls.state != ca_connected)
206                 return;
207
208         if (!scr_showpause.integer)             // turn off for screenshots
209                 return;
210
211         if (!cl.paused)
212                 return;
213
214         pic = Draw_CachePic ("gfx/pause.lmp");
215         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
216 }
217
218
219
220 /*
221 ==============
222 SCR_DrawLoading
223 ==============
224 */
225 void SCR_DrawLoading (void)
226 {
227         cachepic_t      *pic;
228
229         pic = Draw_CachePic ("gfx/loading.lmp");
230         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/loading.lmp", 0, 0, 1, 1, 1, 1, 0);
231 }
232
233
234
235 //=============================================================================
236
237
238 /*
239 ==================
240 SCR_SetUpToDrawConsole
241 ==================
242 */
243 void SCR_SetUpToDrawConsole (void)
244 {
245         Con_CheckResize ();
246
247         if (key_dest == key_game && cls.signon != SIGNONS && scr_conforcewhiledisconnected.integer)
248                 key_consoleactive |= KEY_CONSOLEACTIVE_FORCED;
249         else
250                 key_consoleactive &= ~KEY_CONSOLEACTIVE_FORCED;
251
252 // decide on the height of the console
253         if (key_consoleactive & KEY_CONSOLEACTIVE_FORCED)
254                 scr_conlines = vid.conheight; // full screen
255         else if (key_consoleactive & KEY_CONSOLEACTIVE_USER)
256                 scr_conlines = vid.conheight/2; // half screen
257         else
258                 scr_conlines = 0;                               // none visible
259
260         if (scr_conspeed.value)
261         {
262                 if (scr_conlines < scr_con_current)
263                 {
264                         scr_con_current -= scr_conspeed.value*host_realframetime;
265                         if (scr_conlines > scr_con_current)
266                                 scr_con_current = scr_conlines;
267
268                 }
269                 else if (scr_conlines > scr_con_current)
270                 {
271                         scr_con_current += scr_conspeed.value*host_realframetime;
272                         if (scr_conlines < scr_con_current)
273                                 scr_con_current = scr_conlines;
274                 }
275         }
276         else
277                 scr_con_current = scr_conlines;
278 }
279
280 /*
281 ==================
282 SCR_DrawConsole
283 ==================
284 */
285 void SCR_DrawConsole (void)
286 {
287         if (scr_con_current)
288         {
289                 Con_DrawConsole (scr_con_current);
290                 clearconsole = 0;
291         }
292         else
293         {
294                 if (key_dest == key_game || key_dest == key_message)
295                         Con_DrawNotify ();      // only draw notify in game
296         }
297 }
298
299 /*
300 ===============
301 SCR_BeginLoadingPlaque
302
303 ================
304 */
305 void SCR_BeginLoadingPlaque (void)
306 {
307         if (scr_drawloading)
308                 return;
309
310         S_StopAllSounds (true);
311
312         scr_drawloading = true;
313         CL_UpdateScreen ();
314         scr_drawloading = true;
315         CL_UpdateScreen ();
316 }
317
318 //=============================================================================
319
320 char r_speeds_string[1024];
321 int speedstringcount, r_timereport_active;
322 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
323
324 void R_TimeReport(char *desc)
325 {
326         char tempbuf[256];
327         int length;
328         int t;
329
330         if (!r_timereport_active)
331                 return;
332
333         r_timereport_temp = r_timereport_current;
334         r_timereport_current = Sys_DoubleTime();
335         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
336
337         sprintf(tempbuf, "%8i %s", t, desc);
338         length = strlen(tempbuf);
339         while (length < 20)
340                 tempbuf[length++] = ' ';
341         tempbuf[length] = 0;
342         if (speedstringcount + length > (vid.conwidth / 8))
343         {
344                 strcat(r_speeds_string, "\n");
345                 speedstringcount = 0;
346         }
347         // skip the space at the beginning if it's the first on the line
348         if (speedstringcount == 0)
349         {
350                 strcat(r_speeds_string, tempbuf + 1);
351                 speedstringcount = length - 1;
352         }
353         else
354         {
355                 strcat(r_speeds_string, tempbuf);
356                 speedstringcount += length;
357         }
358 }
359
360 extern int c_rt_lights, c_rt_clears, c_rt_scissored;
361 extern int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
362 extern int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
363 void R_TimeReport_Start(void)
364 {
365         r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
366         r_speeds_string[0] = 0;
367         if (r_timereport_active)
368         {
369                 speedstringcount = 0;
370                 sprintf(r_speeds_string,
371                         "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
372                         "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
373                         "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
374                         "%6i modeltris%6i meshs%6i meshtris\n",
375                         r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2],
376                         c_faces, c_nodes, c_leafs, c_light_polys,
377                         c_models, c_bmodels, c_sprites, c_particles, c_dlights,
378                         c_alias_polys, c_meshs, c_meshelements / 3);
379
380                 sprintf(r_speeds_string + strlen(r_speeds_string),
381                         "realtime lighting:%4i lights%4i clears%4i scissored\n"
382                         "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n"
383                         "precomputed: %6i shadowmeshes%6i shadowtris\n",
384                         c_rt_lights, c_rt_clears, c_rt_scissored,
385                         c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris,
386                         c_rtcached_shadowmeshes, c_rtcached_shadowtris);
387
388                 c_alias_polys = 0;
389                 c_light_polys = 0;
390                 c_faces = 0;
391                 c_nodes = 0;
392                 c_leafs = 0;
393                 c_models = 0;
394                 c_bmodels = 0;
395                 c_sprites = 0;
396                 c_particles = 0;
397                 c_meshs = 0;
398                 c_meshelements = 0;
399
400                 r_timereport_start = Sys_DoubleTime();
401         }
402 }
403
404 void R_TimeReport_End(void)
405 {
406         r_timereport_current = r_timereport_start;
407         R_TimeReport("total");
408
409         if (r_timereport_active)
410         {
411                 int i, j, lines, y;
412                 lines = 1;
413                 for (i = 0;r_speeds_string[i];i++)
414                         if (r_speeds_string[i] == '\n')
415                                 lines++;
416                 y = vid.conheight - sb_lines - lines * 8;
417                 i = j = 0;
418                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
419                 while (r_speeds_string[i])
420                 {
421                         j = i;
422                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
423                                 i++;
424                         if (i - j > 0)
425                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
426                         if (r_speeds_string[i] == '\n')
427                                 i++;
428                         y += 8;
429                 }
430         }
431 }
432
433 /*
434 =================
435 SCR_SizeUp_f
436
437 Keybinding command
438 =================
439 */
440 void SCR_SizeUp_f (void)
441 {
442         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
443 }
444
445
446 /*
447 =================
448 SCR_SizeDown_f
449
450 Keybinding command
451 =================
452 */
453 void SCR_SizeDown_f (void)
454 {
455         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
456 }
457
458 void CL_Screen_Init(void)
459 {
460         Cvar_RegisterVariable (&scr_fov);
461         Cvar_RegisterVariable (&scr_viewsize);
462         Cvar_RegisterVariable (&scr_conspeed);
463         Cvar_RegisterVariable (&scr_conalpha);
464         Cvar_RegisterVariable (&scr_conbrightness);
465         Cvar_RegisterVariable (&scr_conforcewhiledisconnected);
466         Cvar_RegisterVariable (&scr_showram);
467         Cvar_RegisterVariable (&scr_showturtle);
468         Cvar_RegisterVariable (&scr_showpause);
469         Cvar_RegisterVariable (&scr_centertime);
470         Cvar_RegisterVariable (&scr_printspeed);
471         Cvar_RegisterVariable (&vid_conwidth);
472         Cvar_RegisterVariable (&vid_conheight);
473         Cvar_RegisterVariable (&scr_screenshot_jpeg);
474         Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
475         Cvar_RegisterVariable (&cl_avidemo);
476
477         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
478         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
479         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
480         Cmd_AddCommand ("envmap", R_Envmap_f);
481
482         // different default in GAME_FNIGGIUM
483         if (gamemode == GAME_FNIGGIUM)
484                 Cvar_SetQuick(&scr_conforcewhiledisconnected, 0);
485         
486         scr_initialized = true;
487 }
488
489 void DrawQ_Clear(void)
490 {
491         r_refdef.drawqueuesize = 0;
492 }
493
494 static int picelements[6] = {0, 1, 2, 0, 2, 3};
495 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
496 {
497         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);
498 }
499
500 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)
501 {
502         int size, len;
503         drawqueue_t *dq;
504         char *out;
505         if (alpha < (1.0f / 255.0f))
506                 return;
507         if (maxlen < 1)
508                 len = strlen(string);
509         else
510                 for (len = 0;len < maxlen && string[len];len++);
511         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
512         for (;len > 0 && string[len - 1] == ' ';len--);
513         if (len < 1)
514                 return;
515         if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * maxlen) || y < (-scaley))
516                 return;
517         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
518         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
519                 return;
520         red = bound(0, red, 1);
521         green = bound(0, green, 1);
522         blue = bound(0, blue, 1);
523         alpha = bound(0, alpha, 1);
524         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
525         dq->size = size;
526         dq->command = DRAWQUEUE_STRING;
527         dq->flags = flags;
528         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));
529         dq->x = x;
530         dq->y = y;
531         dq->scalex = scalex;
532         dq->scaley = scaley;
533         out = (char *)(dq + 1);
534         memcpy(out, string, len);
535         out[len] = 0;
536         r_refdef.drawqueuesize += dq->size;
537 }
538
539 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
540 {
541         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);
542 }
543
544 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)
545 {
546         float floats[36];
547         cachepic_t *pic;
548         drawqueuemesh_t mesh;
549         memset(&mesh, 0, sizeof(mesh));
550         if (picname && picname[0])
551         {
552                 pic = Draw_CachePic(picname);
553                 if (width == 0)
554                         width = pic->width;
555                 if (height == 0)
556                         height = pic->height;
557                 mesh.texture = pic->tex;
558         }
559         mesh.num_triangles = 2;
560         mesh.num_vertices = 4;
561         mesh.data_element3i = picelements;
562         mesh.data_vertex3f = floats;
563         mesh.data_texcoord2f = floats + 12;
564         mesh.data_color4f = floats + 20;
565         memset(floats, 0, sizeof(floats));
566         mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
567         mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
568         mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
569         mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
570         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;
571         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;
572         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;
573         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;
574         DrawQ_Mesh (&mesh, flags);
575 }
576
577 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
578 {
579         int size;
580         void *p;
581         drawqueue_t *dq;
582         drawqueuemesh_t *m;
583         size = sizeof(*dq);
584         size += sizeof(drawqueuemesh_t);
585         size += sizeof(int[3]) * mesh->num_triangles;
586         size += sizeof(float[3]) * mesh->num_vertices;
587         size += sizeof(float[2]) * mesh->num_vertices;
588         size += sizeof(float[4]) * mesh->num_vertices;
589         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
590                 return;
591         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
592         dq->size = size;
593         dq->command = DRAWQUEUE_MESH;
594         dq->flags = flags;
595         dq->color = 0;
596         dq->x = 0;
597         dq->y = 0;
598         dq->scalex = 0;
599         dq->scaley = 0;
600         p = (void *)(dq + 1);
601         m = p;(qbyte *)p += sizeof(drawqueuemesh_t);
602         m->num_triangles = mesh->num_triangles;
603         m->num_vertices = mesh->num_vertices;
604         m->texture = mesh->texture;
605         m->data_element3i  = p;memcpy(m->data_element3i , mesh->data_element3i , m->num_triangles * sizeof(int[3]));(qbyte *)p += m->num_triangles * sizeof(int[3]);
606         m->data_vertex3f   = p;memcpy(m->data_vertex3f  , mesh->data_vertex3f  , m->num_vertices * sizeof(float[3]));(qbyte *)p += m->num_vertices * sizeof(float[3]);
607         m->data_texcoord2f = p;memcpy(m->data_texcoord2f, mesh->data_texcoord2f, m->num_vertices * sizeof(float[2]));(qbyte *)p += m->num_vertices * sizeof(float[2]);
608         m->data_color4f    = p;memcpy(m->data_color4f   , mesh->data_color4f   , m->num_vertices * sizeof(float[4]));(qbyte *)p += m->num_vertices * sizeof(float[4]);
609         r_refdef.drawqueuesize += dq->size;
610 }
611
612 void DrawQ_SetClipArea(float x, float y, float width, float height)
613 {
614         drawqueue_t * dq;
615         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
616         {
617                 Con_DPrint("DrawQueue full !\n");
618                 return;
619         }
620         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
621         dq->size = sizeof(*dq);
622         dq->command = DRAWQUEUE_SETCLIP;
623         dq->x = x;
624         dq->y = y;
625         dq->scalex = width;
626         dq->scaley = height;
627         dq->flags = 0;
628         dq->color = 0;
629
630         r_refdef.drawqueuesize += dq->size;
631 }
632
633 void DrawQ_ResetClipArea(void)
634 {
635         drawqueue_t *dq;
636         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
637         {
638                 Con_DPrint("DrawQueue full !\n");
639                 return;
640         }
641         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
642         dq->size = sizeof(*dq);
643         dq->command = DRAWQUEUE_RESETCLIP;
644         dq->x = 0;
645         dq->y = 0;
646         dq->scalex = 0;
647         dq->scaley = 0;
648         dq->flags = 0;
649         dq->color = 0;
650
651         r_refdef.drawqueuesize += dq->size;
652 }
653
654 /*
655 ==================
656 SCR_ScreenShot_f
657 ==================
658 */
659 void SCR_ScreenShot_f (void)
660 {
661         static int shotnumber = 0;
662         const char *base;
663         char filename[64];
664         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
665
666         base = "screenshots/dp";
667         if (gamemode == GAME_FNIGGIUM)
668                 base = "screenshots/fniggium";
669         
670         // find a file name to save it to
671         for (;shotnumber < 1000000;shotnumber++)
672                 if (!FS_SysFileExists(va("%s/%s%06d.tga", fs_gamedir, base, shotnumber)) && !FS_SysFileExists(va("%s/%s%06d.jpg", fs_gamedir, base, shotnumber)))
673                         break;
674         if (shotnumber >= 1000000)
675         {
676                 Con_Print("SCR_ScreenShot_f: Couldn't create the image file\n");
677                 return;
678         }
679
680         if (jpeg)
681                 sprintf(filename, "%s%06d.jpg", base, shotnumber);
682         else
683                 sprintf(filename, "%s%06d.tga", base, shotnumber);
684
685         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
686                 Con_Printf("Wrote %s\n", filename);
687         else
688                 Con_Printf("unable to write %s\n", filename);
689         shotnumber++;
690 }
691
692 static int cl_avidemo_frame = 0;
693
694 void SCR_CaptureAVIDemo(void)
695 {
696         char filename[32];
697         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
698
699         if (jpeg)
700                 sprintf(filename, "video/dp%06d.jpg", cl_avidemo_frame);
701         else
702                 sprintf(filename, "video/dp%06d.tga", cl_avidemo_frame);
703
704         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
705                 cl_avidemo_frame++;
706         else
707         {
708                 Cvar_SetValueQuick(&cl_avidemo, 0);
709                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
710                 cl_avidemo_frame = 0;
711         }
712 }
713
714 /*
715 ===============
716 R_Envmap_f
717
718 Grab six views for environment mapping tests
719 ===============
720 */
721 struct
722 {
723         float angles[3];
724         char *name;
725 }
726 envmapinfo[6] =
727 {
728         {{  0,   0, 0}, "ft"},
729         {{  0,  90, 0}, "rt"},
730         {{  0, 180, 0}, "bk"},
731         {{  0, 270, 0}, "lf"},
732         {{-90,  90, 0}, "up"},
733         {{ 90,  90, 0}, "dn"}
734 };
735
736 static void R_Envmap_f (void)
737 {
738         int j, size;
739         char filename[256], basename[256];
740
741         if (Cmd_Argc() != 3)
742         {
743                 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");
744                 return;
745         }
746
747         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
748         size = atoi(Cmd_Argv(2));
749         if (size != 128 && size != 256 && size != 512 && size != 1024)
750         {
751                 Con_Print("envmap: size must be one of 128, 256, 512, or 1024\n");
752                 return;
753         }
754         if (size > vid.realwidth || size > vid.realheight)
755         {
756                 Con_Print("envmap: your resolution is not big enough to render that size\n");
757                 return;
758         }
759
760         envmap = true;
761
762         r_refdef.x = 0;
763         r_refdef.y = 0;
764         r_refdef.width = size;
765         r_refdef.height = size;
766
767         r_refdef.fov_x = 90;
768         r_refdef.fov_y = 90;
769
770         for (j = 0;j < 6;j++)
771         {
772                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
773                 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);
774                 R_ClearScreen();
775                 R_RenderView();
776                 SCR_ScreenShot(filename, vid.realx, vid.realy + vid.realheight - (r_refdef.y + r_refdef.height), size, size, false);
777         }
778
779         envmap = false;
780 }
781
782 //=============================================================================
783
784 // LordHavoc: SHOWLMP stuff
785 #define SHOWLMP_MAXLABELS 256
786 typedef struct showlmp_s
787 {
788         qboolean        isactive;
789         float           x;
790         float           y;
791         char            label[32];
792         char            pic[128];
793 }
794 showlmp_t;
795
796 showlmp_t showlmp[SHOWLMP_MAXLABELS];
797
798 void SHOWLMP_decodehide(void)
799 {
800         int i;
801         qbyte *lmplabel;
802         lmplabel = MSG_ReadString();
803         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
804                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
805                 {
806                         showlmp[i].isactive = false;
807                         return;
808                 }
809 }
810
811 void SHOWLMP_decodeshow(void)
812 {
813         int i, k;
814         qbyte lmplabel[256], picname[256];
815         float x, y;
816         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
817         strlcpy (picname, MSG_ReadString(), sizeof (picname));
818         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
819         {
820                 x = MSG_ReadByte();
821                 y = MSG_ReadByte();
822         }
823         else
824         {
825                 x = MSG_ReadShort();
826                 y = MSG_ReadShort();
827         }
828         k = -1;
829         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
830                 if (showlmp[i].isactive)
831                 {
832                         if (strcmp(showlmp[i].label, lmplabel) == 0)
833                         {
834                                 k = i;
835                                 break; // drop out to replace it
836                         }
837                 }
838                 else if (k < 0) // find first empty one to replace
839                         k = i;
840         if (k < 0)
841                 return; // none found to replace
842         // change existing one
843         showlmp[k].isactive = true;
844         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
845         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
846         showlmp[k].x = x;
847         showlmp[k].y = y;
848 }
849
850 void SHOWLMP_drawall(void)
851 {
852         int i;
853         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
854                 if (showlmp[i].isactive)
855                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
856 }
857
858 void SHOWLMP_clear(void)
859 {
860         int i;
861         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
862                 showlmp[i].isactive = false;
863 }
864
865 void CL_SetupScreenSize(void)
866 {
867         float conwidth, conheight;
868
869         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
870
871         VID_UpdateGamma(false);
872
873         conwidth = bound(320, vid_conwidth.value, 2048);
874         conheight = bound(200, vid_conheight.value, 1536);
875         if (vid_conwidth.value != conwidth)
876                 Cvar_SetValue("vid_conwidth", conwidth);
877         if (vid_conheight.value != conheight)
878                 Cvar_SetValue("vid_conheight", conheight);
879
880         vid.conwidth = vid_conwidth.integer;
881         vid.conheight = vid_conheight.integer;
882
883 /*      if (vid.realheight > 240)
884         {
885                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
886                 vid.conheight = bound(240, vid.conheight, vid.realheight);
887         }
888         else
889                 vid.conheight = 240;*/
890
891         SCR_SetUpToDrawConsole();
892 }
893
894 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
895 void CL_UpdateScreen(void)
896 {
897         if (!scr_initialized || !con_initialized || vid_hidden)
898                 return;                         // not initialized yet
899
900         if (cl_avidemo.integer)
901                 SCR_CaptureAVIDemo();
902         else
903                 cl_avidemo_frame = 0;
904
905         if (cls.signon == SIGNONS)
906                 R_TimeReport("other");
907
908         CL_SetupScreenSize();
909
910         DrawQ_Clear();
911
912         if (cls.signon == SIGNONS)
913                 R_TimeReport("setup");
914
915         //FIXME: force menu if nothing else to look at?
916         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
917
918         if (scr_drawloading)
919         {
920                 scr_drawloading = false;
921                 SCR_DrawLoading();
922         }
923         else
924         {
925                 if (cls.signon == SIGNONS)
926                 {
927                         SCR_DrawNet ();
928                         SCR_DrawTurtle ();
929                         SCR_DrawPause ();
930                         Sbar_Draw();
931                         SHOWLMP_drawall();
932                         SCR_CheckDrawCenterString();
933                 }
934                 MR_Draw();
935                 UI_Callback_Draw();
936                 CL_DrawVideo();
937                 //ui_draw();
938                 if (cls.signon == SIGNONS)
939                 {
940                         R_TimeReport("2d");
941                         R_TimeReport_End();
942                         R_TimeReport_Start();
943                 }
944                 R_Shadow_EditLights_DrawSelectedLightProperties();
945         }
946         SCR_DrawConsole();
947
948         SCR_UpdateScreen();
949 }
950
951 void CL_Screen_NewMap(void)
952 {
953         SHOWLMP_clear();
954 }
955