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