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