2D art can now be drawn using vertex pointers
[divverent/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 void R_TimeReport_Start(void)
359 {
360         r_timereport_active = r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected;
361         r_speeds_string[0] = 0;
362         if (r_timereport_active)
363         {
364                 speedstringcount = 0;
365                 AngleVectors (r_refdef.viewangles, vpn, NULL, NULL);
366                 sprintf(r_speeds_string,
367                         "org:'%+8.2f %+8.2f %+8.2f' ang:'%+4.0f %+4.0f %+4.0f' dir:'%+2.3f %+2.3f %+2.3f'\n"
368                         "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
369                         "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
370                         "%6i modeltris%6i meshs%6i meshtris\n",
371                         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],
372                         c_faces, c_nodes, c_leafs, c_light_polys,
373                         c_models, c_bmodels, c_sprites, c_particles, c_dlights,
374                         c_alias_polys, c_meshs, c_meshelements / 3);
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                 c_alias_polys = 0;
385                 c_light_polys = 0;
386                 c_faces = 0;
387                 c_nodes = 0;
388                 c_leafs = 0;
389                 c_models = 0;
390                 c_bmodels = 0;
391                 c_sprites = 0;
392                 c_particles = 0;
393                 c_meshs = 0;
394                 c_meshelements = 0;
395
396                 r_timereport_start = Sys_DoubleTime();
397         }
398 }
399
400 void R_TimeReport_End(void)
401 {
402         r_timereport_current = r_timereport_start;
403         R_TimeReport("total");
404
405         if (r_timereport_active)
406         {
407                 int i, j, lines, y;
408                 lines = 1;
409                 for (i = 0;r_speeds_string[i];i++)
410                         if (r_speeds_string[i] == '\n')
411                                 lines++;
412                 y = vid.conheight - sb_lines - lines * 8;
413                 i = j = 0;
414                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
415                 while (r_speeds_string[i])
416                 {
417                         j = i;
418                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
419                                 i++;
420                         if (i - j > 0)
421                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
422                         if (r_speeds_string[i] == '\n')
423                                 i++;
424                         y += 8;
425                 }
426         }
427 }
428
429 /*
430 =================
431 SCR_SizeUp_f
432
433 Keybinding command
434 =================
435 */
436 void SCR_SizeUp_f (void)
437 {
438         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
439 }
440
441
442 /*
443 =================
444 SCR_SizeDown_f
445
446 Keybinding command
447 =================
448 */
449 void SCR_SizeDown_f (void)
450 {
451         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
452 }
453
454 void CL_Screen_Init(void)
455 {
456         qpic_t *dat;
457
458         Cvar_RegisterVariable (&scr_fov);
459         Cvar_RegisterVariable (&scr_viewsize);
460         Cvar_RegisterVariable (&scr_conspeed);
461         Cvar_RegisterVariable (&scr_showram);
462         Cvar_RegisterVariable (&scr_showturtle);
463         Cvar_RegisterVariable (&scr_showpause);
464         Cvar_RegisterVariable (&scr_centertime);
465         Cvar_RegisterVariable (&scr_printspeed);
466         Cvar_RegisterVariable (&scr_2dresolution);
467         Cvar_RegisterVariable (&scr_screenshot_jpeg);
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 *)FS_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 static int picelements[6] = {0, 1, 2, 0, 2, 3};
497 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
498 {
499         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);
500 }
501
502 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)
503 {
504         int size, len;
505         drawqueue_t *dq;
506         char *out;
507         if (alpha < (1.0f / 255.0f))
508                 return;
509         if (maxlen < 1)
510                 len = strlen(string);
511         else
512                 for (len = 0;len < maxlen && string[len];len++);
513         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
514         for (;len > 0 && string[len - 1] == ' ';len--);
515         if (len < 1)
516                 return;
517         if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * maxlen) || y < (-scaley))
518                 return;
519         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
520         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
521                 return;
522         red = bound(0, red, 1);
523         green = bound(0, green, 1);
524         blue = bound(0, blue, 1);
525         alpha = bound(0, alpha, 1);
526         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
527         dq->size = size;
528         dq->command = DRAWQUEUE_STRING;
529         dq->flags = flags;
530         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));
531         dq->x = x;
532         dq->y = y;
533         dq->scalex = scalex;
534         dq->scaley = scaley;
535         out = (char *)(dq + 1);
536         memcpy(out, string, len);
537         out[len] = 0;
538         r_refdef.drawqueuesize += dq->size;
539 }
540
541 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
542 {
543         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);
544 }
545
546 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)
547 {
548         float floats[36];
549         cachepic_t *pic;
550         drawqueuemesh_t mesh;
551         memset(&mesh, 0, sizeof(mesh));
552         if (picname && picname[0])
553         {
554                 pic = Draw_CachePic(picname);
555                 if (width == 0)
556                         width = pic->width;
557                 if (height == 0)
558                         height = pic->height;
559                 mesh.texture = pic->tex;
560         }
561         mesh.numtriangles = 2;
562         mesh.numvertices = 4;
563         mesh.element3i = picelements;
564         mesh.vertex3f = floats;
565         mesh.texcoord2f = floats + 12;
566         mesh.color4f = floats + 20;
567         memset(floats, 0, sizeof(floats));
568         mesh.vertex3f[0] = mesh.vertex3f[9] = x;
569         mesh.vertex3f[1] = mesh.vertex3f[4] = y;
570         mesh.vertex3f[3] = mesh.vertex3f[6] = x + width;
571         mesh.vertex3f[7] = mesh.vertex3f[10] = y + height;
572         mesh.texcoord2f[0] = s1;mesh.texcoord2f[1] = t1;mesh.color4f[ 0] = r1;mesh.color4f[ 1] = g1;mesh.color4f[ 2] = b1;mesh.color4f[ 3] = a1;
573         mesh.texcoord2f[2] = s2;mesh.texcoord2f[3] = t2;mesh.color4f[ 4] = r2;mesh.color4f[ 5] = g2;mesh.color4f[ 6] = b2;mesh.color4f[ 7] = a2;
574         mesh.texcoord2f[4] = s4;mesh.texcoord2f[5] = t4;mesh.color4f[ 8] = r4;mesh.color4f[ 9] = g4;mesh.color4f[10] = b4;mesh.color4f[11] = a4;
575         mesh.texcoord2f[6] = s3;mesh.texcoord2f[7] = t3;mesh.color4f[12] = r3;mesh.color4f[13] = g3;mesh.color4f[14] = b3;mesh.color4f[15] = a3;
576         DrawQ_Mesh (&mesh, flags);
577 }
578
579 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
580 {
581         int size;
582         void *p;
583         drawqueue_t *dq;
584         drawqueuemesh_t *m;
585         size = sizeof(*dq);
586         size += sizeof(drawqueuemesh_t);
587         size += sizeof(int[3]) * mesh->numtriangles;
588         size += sizeof(float[3]) * mesh->numvertices;
589         size += sizeof(float[2]) * mesh->numvertices;
590         size += sizeof(float[4]) * mesh->numvertices;
591         if (r_refdef.drawqueuesize + size > r_refdef.maxdrawqueuesize)
592                 return;
593         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
594         dq->size = size;
595         dq->command = DRAWQUEUE_MESH;
596         dq->flags = flags;
597         dq->color = 0;
598         dq->x = 0;
599         dq->y = 0;
600         dq->scalex = 0;
601         dq->scaley = 0;
602         p = (void *)(dq + 1);
603         m = p;(qbyte *)p += sizeof(drawqueuemesh_t);
604         m->numtriangles = mesh->numtriangles;
605         m->numvertices = mesh->numvertices;
606         m->texture = mesh->texture;
607         m->element3i  = p;memcpy(m->element3i , mesh->element3i , m->numtriangles * sizeof(int[3]));(qbyte *)p += m->numtriangles * sizeof(int[3]);
608         m->vertex3f   = p;memcpy(m->vertex3f  , mesh->vertex3f  , m->numvertices * sizeof(float[3]));(qbyte *)p += m->numvertices * sizeof(float[3]);
609         m->texcoord2f = p;memcpy(m->texcoord2f, mesh->texcoord2f, m->numvertices * sizeof(float[2]));(qbyte *)p += m->numvertices * sizeof(float[2]);
610         m->color4f    = p;memcpy(m->color4f   , mesh->color4f   , m->numvertices * sizeof(float[4]));(qbyte *)p += m->numvertices * sizeof(float[4]);
611         r_refdef.drawqueuesize += dq->size;
612 }
613
614 /*
615 ====================
616 CalcFov
617 ====================
618 */
619 float CalcFov (float fov_x, float width, float height)
620 {
621         // calculate vision size and alter by aspect, then convert back to angle
622         return atan (height / (width / tan(fov_x/360*M_PI))) * 360 / M_PI;
623 }
624
625 /*
626 =================
627 SCR_CalcRefdef
628
629 Must be called whenever vid changes
630 Internal use only
631 =================
632 */
633 static void SCR_CalcRefdef (void)
634 {
635         float size;
636         int contents;
637
638 //========================================
639
640 // bound viewsize
641         if (scr_viewsize.value < 30)
642                 Cvar_Set ("viewsize","30");
643         if (scr_viewsize.value > 120)
644                 Cvar_Set ("viewsize","120");
645
646 // bound field of view
647         if (scr_fov.value < 10)
648                 Cvar_Set ("fov","10");
649         if (scr_fov.value > 170)
650                 Cvar_Set ("fov","170");
651
652 // intermission is always full screen
653         if (cl.intermission)
654         {
655                 size = 1;
656                 sb_lines = 0;
657         }
658         else
659         {
660                 if (scr_viewsize.value >= 120)
661                         sb_lines = 0;           // no status bar at all
662                 else if (scr_viewsize.value >= 110)
663                         sb_lines = 24;          // no inventory
664                 else
665                         sb_lines = 24+16+8;
666                 size = scr_viewsize.value * (1.0 / 100.0);
667         }
668
669         if (size >= 1)
670         {
671                 r_refdef.width = vid.realwidth;
672                 r_refdef.height = vid.realheight;
673                 r_refdef.x = 0;
674                 r_refdef.y = 0;
675         }
676         else
677         {
678                 r_refdef.width = vid.realwidth * size;
679                 r_refdef.height = vid.realheight * size;
680                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
681                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
682         }
683
684         r_refdef.width = bound(0, r_refdef.width, vid.realwidth);
685         r_refdef.height = bound(0, r_refdef.height, vid.realheight);
686         r_refdef.x = bound(0, r_refdef.x, vid.realwidth - r_refdef.width) + vid.realx;
687         r_refdef.y = bound(0, r_refdef.y, vid.realheight - r_refdef.height) + vid.realy;
688
689         // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
690         r_refdef.fov_x = scr_fov.value * cl.viewzoom;
691         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
692
693         if (cl.worldmodel)
694         {
695                 Mod_CheckLoaded(cl.worldmodel);
696                 contents = Mod_PointContents(r_refdef.vieworg, cl.worldmodel);
697                 if (contents != CONTENTS_EMPTY && contents != CONTENTS_SOLID)
698                 {
699                         r_refdef.fov_x *= (sin(cl.time * 4.7) * 0.015 + 0.985);
700                         r_refdef.fov_y *= (sin(cl.time * 3.0) * 0.015 + 0.985);
701                 }
702         }
703 }
704
705 /*
706 ==================
707 SCR_ScreenShot_f
708 ==================
709 */
710 void SCR_ScreenShot_f (void)
711 {
712         static int i = 0;
713         char filename[16];
714         char checkname[MAX_OSPATH];
715         const char* extens;
716         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
717
718         if (jpeg)
719                 extens = "jpg";
720         else
721                 extens = "tga";
722
723         // find a file name to save it to
724         for (; i<=9999 ; i++)
725         {
726                 sprintf (filename, "dp%04i.%s", i, extens);
727                 sprintf (checkname, "%s/%s", fs_gamedir, filename);
728                 if (!FS_SysFileExists(checkname))
729                         break;
730         }
731         if (i==10000)
732         {
733                 Con_Printf ("SCR_ScreenShot_f: Couldn't create the image file\n");
734                 return;
735         }
736
737         if (SCR_ScreenShot (filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
738                 Con_Printf ("Wrote %s\n", filename);
739         else
740                 Con_Printf ("unable to write %s\n", filename);
741 }
742
743 static int cl_avidemo_frame = 0;
744
745 void SCR_CaptureAVIDemo(void)
746 {
747         char filename[32];
748         sprintf(filename, "dpavi%06d.tga", cl_avidemo_frame);
749         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, false))
750                 cl_avidemo_frame++;
751         else
752         {
753                 Cvar_SetValueQuick(&cl_avidemo, 0);
754                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
755                 cl_avidemo_frame = 0;
756         }
757 }
758
759 /*
760 ===============
761 R_Envmap_f
762
763 Grab six views for environment mapping tests
764 ===============
765 */
766 struct
767 {
768         float angles[3];
769         char *name;
770 }
771 envmapinfo[6] =
772 {
773         {{  0,   0, 0}, "ft"},
774         {{  0,  90, 0}, "rt"},
775         {{  0, 180, 0}, "bk"},
776         {{  0, 270, 0}, "lf"},
777         {{-90,  90, 0}, "up"},
778         {{ 90,  90, 0}, "dn"}
779 };
780
781 static void R_Envmap_f (void)
782 {
783         int j, size;
784         char filename[256], basename[256];
785
786         if (Cmd_Argc() != 3)
787         {
788                 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");
789                 return;
790         }
791
792         strcpy(basename, Cmd_Argv(1));
793         size = atoi(Cmd_Argv(2));
794         if (size != 128 && size != 256 && size != 512 && size != 1024)
795         {
796                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
797                 return;
798         }
799         if (size > vid.realwidth || size > vid.realheight)
800         {
801                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
802                 return;
803         }
804
805         envmap = true;
806
807         r_refdef.x = 0;
808         r_refdef.y = 0;
809         r_refdef.width = size;
810         r_refdef.height = size;
811
812         r_refdef.fov_x = 90;
813         r_refdef.fov_y = 90;
814
815         for (j = 0;j < 6;j++)
816         {
817                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
818                 VectorCopy(envmapinfo[j].angles, r_refdef.viewangles);
819                 R_ClearScreen();
820                 R_RenderView ();
821                 SCR_ScreenShot(filename, vid.realx, vid.realy, size, size, false);
822         }
823
824         envmap = false;
825 }
826
827 //=============================================================================
828
829 // LordHavoc: SHOWLMP stuff
830 #define SHOWLMP_MAXLABELS 256
831 typedef struct showlmp_s
832 {
833         qboolean        isactive;
834         float           x;
835         float           y;
836         char            label[32];
837         char            pic[128];
838 }
839 showlmp_t;
840
841 showlmp_t showlmp[SHOWLMP_MAXLABELS];
842
843 void SHOWLMP_decodehide(void)
844 {
845         int i;
846         qbyte *lmplabel;
847         lmplabel = MSG_ReadString();
848         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
849                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
850                 {
851                         showlmp[i].isactive = false;
852                         return;
853                 }
854 }
855
856 void SHOWLMP_decodeshow(void)
857 {
858         int i, k;
859         qbyte lmplabel[256], picname[256];
860         float x, y;
861         strcpy(lmplabel,MSG_ReadString());
862         strcpy(picname, MSG_ReadString());
863         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
864         {
865                 x = MSG_ReadByte();
866                 y = MSG_ReadByte();
867         }
868         else
869         {
870                 x = MSG_ReadShort();
871                 y = MSG_ReadShort();
872         }
873         k = -1;
874         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
875                 if (showlmp[i].isactive)
876                 {
877                         if (strcmp(showlmp[i].label, lmplabel) == 0)
878                         {
879                                 k = i;
880                                 break; // drop out to replace it
881                         }
882                 }
883                 else if (k < 0) // find first empty one to replace
884                         k = i;
885         if (k < 0)
886                 return; // none found to replace
887         // change existing one
888         showlmp[k].isactive = true;
889         strcpy(showlmp[k].label, lmplabel);
890         strcpy(showlmp[k].pic, picname);
891         showlmp[k].x = x;
892         showlmp[k].y = y;
893 }
894
895 void SHOWLMP_drawall(void)
896 {
897         int i;
898         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
899                 if (showlmp[i].isactive)
900                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
901 }
902
903 void SHOWLMP_clear(void)
904 {
905         int i;
906         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
907                 showlmp[i].isactive = false;
908 }
909
910 void CL_SetupScreenSize(void)
911 {
912         static float old2dresolution = -1;
913
914         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
915
916         VID_UpdateGamma(false);
917
918         if (scr_2dresolution.value != old2dresolution)
919         {
920                 Cvar_SetValue("scr_2dresolution", bound(0.0f, scr_2dresolution.value, 1.0f));
921                 old2dresolution = scr_2dresolution.value;
922         }
923
924         if (vid.realwidth > 320)
925         {
926                 vid.conwidth = (vid.realwidth - 320) * scr_2dresolution.value + 320;
927                 vid.conwidth = bound(320, vid.conwidth, vid.realwidth);
928         }
929         else
930                 vid.conwidth = 320;
931
932         if (vid.realheight > 240)
933         {
934                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
935                 vid.conheight = bound(240, vid.conheight, vid.realheight);
936         }
937         else
938                 vid.conheight = 240;
939
940         SCR_SetUpToDrawConsole();
941
942         // determine size of refresh window
943         SCR_CalcRefdef();
944 }
945
946 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
947 void CL_UpdateScreen(void)
948 {
949         if (!scr_initialized || !con_initialized || vid_hidden)
950                 return;                         // not initialized yet
951
952         if (cl_avidemo.integer)
953                 SCR_CaptureAVIDemo();
954         else
955                 cl_avidemo_frame = 0;
956
957         if (cls.signon == SIGNONS)
958                 R_TimeReport("other");
959
960         CL_SetupScreenSize();
961
962         DrawQ_Clear();
963
964         V_UpdateBlends();
965         V_CalcRefdef ();
966
967         if (cls.signon == SIGNONS)
968                 R_TimeReport("setup");
969
970         //FIXME: force menu if nothing else to look at?
971         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
972
973         if (scr_drawloading)
974         {
975                 scr_drawloading = false;
976                 SCR_DrawLoading();
977         }
978         else
979         {
980                 if (cls.signon == SIGNONS)
981                 {
982                         SCR_DrawNet ();
983                         SCR_DrawTurtle ();
984                         SCR_DrawPause ();
985                         Sbar_Draw();
986                         SHOWLMP_drawall();
987                         SCR_CheckDrawCenterString();
988                 }
989                 ui_draw();
990                 CL_DrawVideo();
991                 M_Draw();
992                 if (cls.signon == SIGNONS)
993                 {
994                         R_TimeReport("2d");
995                         R_TimeReport_End();
996                         R_TimeReport_Start();
997                 }
998                 R_Shadow_EditLights_DrawSelectedLightProperties();
999         }
1000         SCR_DrawConsole();
1001
1002         SCR_UpdateScreen();
1003 }
1004
1005 void CL_Screen_NewMap(void)
1006 {
1007         SHOWLMP_clear();
1008 }
1009