added comment support to COM_ReadAndTokenizeLine
[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 vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640"};
18 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480"};
19 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
20 cvar_t cl_avidemo = {0, "cl_avidemo", "0"};
21
22 qboolean        scr_initialized;                // ready to draw
23
24 float           scr_con_current;
25 float           scr_conlines;           // lines of console to display
26
27 int                     clearconsole;
28 int                     clearnotify;
29
30 qboolean        scr_drawloading = false;
31
32 static qbyte menuplyr_pixels[4096];
33
34 void DrawCrosshair(int num);
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         strlcpy (scr_centerstring, str, sizeof (scr_centerstring));
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                 sprintf(r_speeds_string,
369                         "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
370                         "world:%6i faces%6i nodes%6i leafs%6i dlitwalls\n"
371                         "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
372                         "%6i modeltris%6i meshs%6i meshtris\n",
373                         r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], r_viewforward[0], r_viewforward[1], r_viewforward[2],
374                         c_faces, c_nodes, c_leafs, c_light_polys,
375                         c_models, c_bmodels, c_sprites, c_particles, c_dlights,
376                         c_alias_polys, c_meshs, c_meshelements / 3);
377
378                 sprintf(r_speeds_string + strlen(r_speeds_string),
379                         "realtime lighting:%4i lights%4i clears%4i scissored\n"
380                         "dynamic: %6i shadowmeshes%6i shadowtris%6i lightmeshes%6i lighttris\n"
381                         "precomputed: %6i shadowmeshes%6i shadowtris\n",
382                         c_rt_lights, c_rt_clears, c_rt_scissored,
383                         c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris,
384                         c_rtcached_shadowmeshes, c_rtcached_shadowtris);
385
386                 c_alias_polys = 0;
387                 c_light_polys = 0;
388                 c_faces = 0;
389                 c_nodes = 0;
390                 c_leafs = 0;
391                 c_models = 0;
392                 c_bmodels = 0;
393                 c_sprites = 0;
394                 c_particles = 0;
395                 c_meshs = 0;
396                 c_meshelements = 0;
397
398                 r_timereport_start = Sys_DoubleTime();
399         }
400 }
401
402 void R_TimeReport_End(void)
403 {
404         r_timereport_current = r_timereport_start;
405         R_TimeReport("total");
406
407         if (r_timereport_active)
408         {
409                 int i, j, lines, y;
410                 lines = 1;
411                 for (i = 0;r_speeds_string[i];i++)
412                         if (r_speeds_string[i] == '\n')
413                                 lines++;
414                 y = vid.conheight - sb_lines - lines * 8;
415                 i = j = 0;
416                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
417                 while (r_speeds_string[i])
418                 {
419                         j = i;
420                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
421                                 i++;
422                         if (i - j > 0)
423                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
424                         if (r_speeds_string[i] == '\n')
425                                 i++;
426                         y += 8;
427                 }
428         }
429 }
430
431 /*
432 =================
433 SCR_SizeUp_f
434
435 Keybinding command
436 =================
437 */
438 void SCR_SizeUp_f (void)
439 {
440         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
441 }
442
443
444 /*
445 =================
446 SCR_SizeDown_f
447
448 Keybinding command
449 =================
450 */
451 void SCR_SizeDown_f (void)
452 {
453         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
454 }
455
456 void CL_Screen_Init(void)
457 {
458         qpic_t *dat;
459
460         Cvar_RegisterVariable (&scr_fov);
461         Cvar_RegisterVariable (&scr_viewsize);
462         Cvar_RegisterVariable (&scr_conspeed);
463         Cvar_RegisterVariable (&scr_conalpha);
464         Cvar_RegisterVariable (&scr_conbrightness);
465         Cvar_RegisterVariable (&scr_showram);
466         Cvar_RegisterVariable (&scr_showturtle);
467         Cvar_RegisterVariable (&scr_showpause);
468         Cvar_RegisterVariable (&scr_centertime);
469         Cvar_RegisterVariable (&scr_printspeed);
470         Cvar_RegisterVariable (&vid_conwidth);
471         Cvar_RegisterVariable (&vid_conheight);
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.num_triangles = 2;
567         mesh.num_vertices = 4;
568         mesh.data_element3i = picelements;
569         mesh.data_vertex3f = floats;
570         mesh.data_texcoord2f = floats + 12;
571         mesh.data_color4f = floats + 20;
572         memset(floats, 0, sizeof(floats));
573         mesh.data_vertex3f[0] = mesh.data_vertex3f[9] = x;
574         mesh.data_vertex3f[1] = mesh.data_vertex3f[4] = y;
575         mesh.data_vertex3f[3] = mesh.data_vertex3f[6] = x + width;
576         mesh.data_vertex3f[7] = mesh.data_vertex3f[10] = y + height;
577         mesh.data_texcoord2f[0] = s1;mesh.data_texcoord2f[1] = t1;mesh.data_color4f[ 0] = r1;mesh.data_color4f[ 1] = g1;mesh.data_color4f[ 2] = b1;mesh.data_color4f[ 3] = a1;
578         mesh.data_texcoord2f[2] = s2;mesh.data_texcoord2f[3] = t2;mesh.data_color4f[ 4] = r2;mesh.data_color4f[ 5] = g2;mesh.data_color4f[ 6] = b2;mesh.data_color4f[ 7] = a2;
579         mesh.data_texcoord2f[4] = s4;mesh.data_texcoord2f[5] = t4;mesh.data_color4f[ 8] = r4;mesh.data_color4f[ 9] = g4;mesh.data_color4f[10] = b4;mesh.data_color4f[11] = a4;
580         mesh.data_texcoord2f[6] = s3;mesh.data_texcoord2f[7] = t3;mesh.data_color4f[12] = r3;mesh.data_color4f[13] = g3;mesh.data_color4f[14] = b3;mesh.data_color4f[15] = a3;
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->num_triangles;
593         size += sizeof(float[3]) * mesh->num_vertices;
594         size += sizeof(float[2]) * mesh->num_vertices;
595         size += sizeof(float[4]) * mesh->num_vertices;
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->num_triangles = mesh->num_triangles;
610         m->num_vertices = mesh->num_vertices;
611         m->texture = mesh->texture;
612         m->data_element3i  = p;memcpy(m->data_element3i , mesh->data_element3i , m->num_triangles * sizeof(int[3]));(qbyte *)p += m->num_triangles * sizeof(int[3]);
613         m->data_vertex3f   = p;memcpy(m->data_vertex3f  , mesh->data_vertex3f  , m->num_vertices * sizeof(float[3]));(qbyte *)p += m->num_vertices * sizeof(float[3]);
614         m->data_texcoord2f = p;memcpy(m->data_texcoord2f, mesh->data_texcoord2f, m->num_vertices * sizeof(float[2]));(qbyte *)p += m->num_vertices * sizeof(float[2]);
615         m->data_color4f    = p;memcpy(m->data_color4f   , mesh->data_color4f   , m->num_vertices * sizeof(float[4]));(qbyte *)p += m->num_vertices * sizeof(float[4]);
616         r_refdef.drawqueuesize += dq->size;
617 }
618
619 void DrawQ_SetClipArea(float x, float y, float width, float height)
620 {
621         drawqueue_t * dq;
622         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
623         {
624                 Con_DPrintf("DrawQueue full !\n");
625                 return;
626         }
627         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
628         dq->size = sizeof(*dq);
629         dq->command = DRAWQUEUE_SETCLIP;
630         dq->x = x;
631         dq->y = y;
632         dq->scalex = width;
633         dq->scaley = height;
634         dq->flags = 0;
635         dq->color = 0;
636
637         r_refdef.drawqueuesize += dq->size;
638 }
639
640 void DrawQ_ResetClipArea(void)
641 {
642         drawqueue_t *dq;
643         if(r_refdef.drawqueuesize + (int)sizeof(*dq) > r_refdef.maxdrawqueuesize)
644         {
645                 Con_DPrintf("DrawQueue full !\n");
646                 return;
647         }
648         dq = (void*) (r_refdef.drawqueue + r_refdef.drawqueuesize);
649         dq->size = sizeof(*dq);
650         dq->command = DRAWQUEUE_RESETCLIP;
651         dq->x = 0;
652         dq->y = 0;
653         dq->scalex = 0;
654         dq->scaley = 0;
655         dq->flags = 0;
656         dq->color = 0;
657
658         r_refdef.drawqueuesize += dq->size;
659 }
660
661 /*
662 ====================
663 CalcFov
664 ====================
665 */
666 float CalcFov (float fov_x, float width, float height)
667 {
668         // calculate vision size and alter by aspect, then convert back to angle
669         return atan (height / (width / tan(fov_x/360*M_PI))) * 360 / M_PI;
670 }
671
672 /*
673 =================
674 SCR_CalcRefdef
675
676 Must be called whenever vid changes
677 Internal use only
678 =================
679 */
680 static void SCR_CalcRefdef (void)
681 {
682         float size;
683         int contents;
684
685 //========================================
686
687 // bound viewsize
688         if (scr_viewsize.value < 30)
689                 Cvar_Set ("viewsize","30");
690         if (scr_viewsize.value > 120)
691                 Cvar_Set ("viewsize","120");
692
693 // bound field of view
694         if (scr_fov.value < 10)
695                 Cvar_Set ("fov","10");
696         if (scr_fov.value > 170)
697                 Cvar_Set ("fov","170");
698
699 // intermission is always full screen
700         if (cl.intermission)
701         {
702                 size = 1;
703                 sb_lines = 0;
704         }
705         else
706         {
707                 if (scr_viewsize.value >= 120)
708                         sb_lines = 0;           // no status bar at all
709                 else if (scr_viewsize.value >= 110)
710                         sb_lines = 24;          // no inventory
711                 else
712                         sb_lines = 24+16+8;
713                 size = scr_viewsize.value * (1.0 / 100.0);
714         }
715
716         if (size >= 1)
717         {
718                 r_refdef.width = vid.realwidth;
719                 r_refdef.height = vid.realheight;
720                 r_refdef.x = 0;
721                 r_refdef.y = 0;
722         }
723         else
724         {
725                 r_refdef.width = vid.realwidth * size;
726                 r_refdef.height = vid.realheight * size;
727                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
728                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
729         }
730
731         r_refdef.width = bound(0, r_refdef.width, vid.realwidth);
732         r_refdef.height = bound(0, r_refdef.height, vid.realheight);
733         r_refdef.x = bound(0, r_refdef.x, vid.realwidth - r_refdef.width) + vid.realx;
734         r_refdef.y = bound(0, r_refdef.y, vid.realheight - r_refdef.height) + vid.realy;
735
736         // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
737         r_refdef.fov_x = scr_fov.value * cl.viewzoom;
738         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
739
740         if (cl.worldmodel)
741         {
742                 Mod_CheckLoaded(cl.worldmodel);
743                 contents = CL_PointSuperContents(r_vieworigin);
744                 if (contents & SUPERCONTENTS_LIQUIDSMASK)
745                 {
746                         r_refdef.fov_x *= (sin(cl.time * 4.7) * 0.015 + 0.985);
747                         r_refdef.fov_y *= (sin(cl.time * 3.0) * 0.015 + 0.985);
748                 }
749         }
750 }
751
752 /*
753 ==================
754 SCR_ScreenShot_f
755 ==================
756 */
757 void SCR_ScreenShot_f (void)
758 {
759         static int i = 0;
760         char filename[16];
761         char checkname[MAX_OSPATH];
762         const char* extens;
763         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
764
765         if (jpeg)
766                 extens = "jpg";
767         else
768                 extens = "tga";
769
770         // find a file name to save it to
771         for (; i<=9999 ; i++)
772         {
773                 sprintf (filename, "dp%04i.%s", i, extens);
774                 sprintf (checkname, "%s/%s", fs_gamedir, filename);
775                 if (!FS_SysFileExists(checkname))
776                         break;
777         }
778         if (i==10000)
779         {
780                 Con_Printf ("SCR_ScreenShot_f: Couldn't create the image file\n");
781                 return;
782         }
783
784         if (SCR_ScreenShot (filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
785                 Con_Printf ("Wrote %s\n", filename);
786         else
787                 Con_Printf ("unable to write %s\n", filename);
788 }
789
790 static int cl_avidemo_frame = 0;
791
792 void SCR_CaptureAVIDemo(void)
793 {
794         char filename[32];
795         qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
796
797         if (jpeg)
798                 sprintf(filename, "dpavi%06d.jpg", cl_avidemo_frame);
799         else
800                 sprintf(filename, "dpavi%06d.tga", cl_avidemo_frame);
801
802         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
803                 cl_avidemo_frame++;
804         else
805         {
806                 Cvar_SetValueQuick(&cl_avidemo, 0);
807                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
808                 cl_avidemo_frame = 0;
809         }
810 }
811
812 /*
813 ===============
814 R_Envmap_f
815
816 Grab six views for environment mapping tests
817 ===============
818 */
819 struct
820 {
821         float angles[3];
822         char *name;
823 }
824 envmapinfo[6] =
825 {
826         {{  0,   0, 0}, "ft"},
827         {{  0,  90, 0}, "rt"},
828         {{  0, 180, 0}, "bk"},
829         {{  0, 270, 0}, "lf"},
830         {{-90,  90, 0}, "up"},
831         {{ 90,  90, 0}, "dn"}
832 };
833
834 static void R_Envmap_f (void)
835 {
836         int j, size;
837         char filename[256], basename[256];
838
839         if (Cmd_Argc() != 3)
840         {
841                 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");
842                 return;
843         }
844
845         strlcpy (basename, Cmd_Argv(1), sizeof (basename));
846         size = atoi(Cmd_Argv(2));
847         if (size != 128 && size != 256 && size != 512 && size != 1024)
848         {
849                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
850                 return;
851         }
852         if (size > vid.realwidth || size > vid.realheight)
853         {
854                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
855                 return;
856         }
857
858         envmap = true;
859
860         r_refdef.x = 0;
861         r_refdef.y = 0;
862         r_refdef.width = size;
863         r_refdef.height = size;
864
865         r_refdef.fov_x = 90;
866         r_refdef.fov_y = 90;
867
868         for (j = 0;j < 6;j++)
869         {
870                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
871                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], envmapinfo[j].angles[0], envmapinfo[j].angles[1], envmapinfo[j].angles[2], 1);
872                 R_ClearScreen();
873                 R_RenderView();
874                 SCR_ScreenShot(filename, vid.realx, vid.realy + vid.realheight - (r_refdef.y + r_refdef.height), size, size, false);
875         }
876
877         envmap = false;
878 }
879
880 //=============================================================================
881
882 // LordHavoc: SHOWLMP stuff
883 #define SHOWLMP_MAXLABELS 256
884 typedef struct showlmp_s
885 {
886         qboolean        isactive;
887         float           x;
888         float           y;
889         char            label[32];
890         char            pic[128];
891 }
892 showlmp_t;
893
894 showlmp_t showlmp[SHOWLMP_MAXLABELS];
895
896 void SHOWLMP_decodehide(void)
897 {
898         int i;
899         qbyte *lmplabel;
900         lmplabel = MSG_ReadString();
901         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
902                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
903                 {
904                         showlmp[i].isactive = false;
905                         return;
906                 }
907 }
908
909 void SHOWLMP_decodeshow(void)
910 {
911         int i, k;
912         qbyte lmplabel[256], picname[256];
913         float x, y;
914         strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
915         strlcpy (picname, MSG_ReadString(), sizeof (picname));
916         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
917         {
918                 x = MSG_ReadByte();
919                 y = MSG_ReadByte();
920         }
921         else
922         {
923                 x = MSG_ReadShort();
924                 y = MSG_ReadShort();
925         }
926         k = -1;
927         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
928                 if (showlmp[i].isactive)
929                 {
930                         if (strcmp(showlmp[i].label, lmplabel) == 0)
931                         {
932                                 k = i;
933                                 break; // drop out to replace it
934                         }
935                 }
936                 else if (k < 0) // find first empty one to replace
937                         k = i;
938         if (k < 0)
939                 return; // none found to replace
940         // change existing one
941         showlmp[k].isactive = true;
942         strlcpy (showlmp[k].label, lmplabel, sizeof (showlmp[k].label));
943         strlcpy (showlmp[k].pic, picname, sizeof (showlmp[k].pic));
944         showlmp[k].x = x;
945         showlmp[k].y = y;
946 }
947
948 void SHOWLMP_drawall(void)
949 {
950         int i;
951         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
952                 if (showlmp[i].isactive)
953                         DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
954 }
955
956 void SHOWLMP_clear(void)
957 {
958         int i;
959         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
960                 showlmp[i].isactive = false;
961 }
962
963 void CL_SetupScreenSize(void)
964 {
965         float conwidth, conheight;
966
967         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
968
969         VID_UpdateGamma(false);
970
971         conwidth = bound(320, vid_conwidth.value, 2048);
972         conheight = bound(200, vid_conheight.value, 1536);
973         if (vid_conwidth.value != conwidth)
974                 Cvar_SetValue("vid_conwidth", conwidth);
975         if (vid_conheight.value != conheight)
976                 Cvar_SetValue("vid_conheight", conheight);
977
978         vid.conwidth = vid_conwidth.integer;
979         vid.conheight = vid_conheight.integer;
980
981 /*      if (vid.realheight > 240)
982         {
983                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
984                 vid.conheight = bound(240, vid.conheight, vid.realheight);
985         }
986         else
987                 vid.conheight = 240;*/
988
989         SCR_SetUpToDrawConsole();
990
991         // determine size of refresh window
992         SCR_CalcRefdef();
993 }
994
995 extern void R_Shadow_EditLights_DrawSelectedLightProperties(void);
996 void CL_UpdateScreen(void)
997 {
998         if (!scr_initialized || !con_initialized || vid_hidden)
999                 return;                         // not initialized yet
1000
1001         if (cl_avidemo.integer)
1002                 SCR_CaptureAVIDemo();
1003         else
1004                 cl_avidemo_frame = 0;
1005
1006         if (cls.signon == SIGNONS)
1007                 R_TimeReport("other");
1008
1009         CL_SetupScreenSize();
1010
1011         DrawQ_Clear();
1012
1013         if (cls.signon == SIGNONS)
1014                 R_TimeReport("setup");
1015
1016         //FIXME: force menu if nothing else to look at?
1017         //if (key_dest == key_game && cls.signon != SIGNONS && cls.state == ca_disconnected)
1018
1019         if (scr_drawloading)
1020         {
1021                 scr_drawloading = false;
1022                 SCR_DrawLoading();
1023         }
1024         else
1025         {
1026                 if (cls.signon == SIGNONS)
1027                 {
1028                         SCR_DrawNet ();
1029                         SCR_DrawTurtle ();
1030                         SCR_DrawPause ();
1031                         Sbar_Draw();
1032                         SHOWLMP_drawall();
1033                         SCR_CheckDrawCenterString();
1034                 }
1035                 MR_Draw();
1036                 CL_DrawVideo();
1037                 ui_draw();
1038                 if (cls.signon == SIGNONS)
1039                 {
1040                         R_TimeReport("2d");
1041                         R_TimeReport_End();
1042                         R_TimeReport_Start();
1043                 }
1044                 R_Shadow_EditLights_DrawSelectedLightProperties();
1045         }
1046         SCR_DrawConsole();
1047
1048         SCR_UpdateScreen();
1049 }
1050
1051 void CL_Screen_NewMap(void)
1052 {
1053         SHOWLMP_clear();
1054 }
1055