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