]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_screen.c
buffer csprogs downloads and load csprogs from the buffer instead of a file, if available
[xonotic/darkplaces.git] / cl_screen.c
index ebff6f5315d9360968211b6017e6d5f31a47eaa0..dfc556187ece9948d970244709d5f7f938e2275f 100644 (file)
@@ -38,9 +38,12 @@ cvar_t scr_loadingscreen_background = {0, "scr_loadingscreen_background","0", "s
 cvar_t scr_loadingscreen_scale = {0, "scr_loadingscreen_scale","1", "scale factor of the background"};
 cvar_t scr_loadingscreen_scale_base = {0, "scr_loadingscreen_scale_base","0", "0 = console pixels, 1 = video pixels"};
 cvar_t scr_loadingscreen_scale_limit = {0, "scr_loadingscreen_scale_limit","0", "0 = no limit, 1 = until first edge hits screen edge, 2 = until last edge hits screen edge, 3 = until width hits screen width, 4 = until height hits screen height"};
+cvar_t scr_loadingscreen_picture = {CVAR_SAVE, "scr_loadingscreen_picture", "gfx/loading", "picture shown during loading"};
 cvar_t scr_loadingscreen_count = {0, "scr_loadingscreen_count","1", "number of loading screen files to use randomly (named loading.tga, loading2.tga, loading3.tga, ...)"};
+cvar_t scr_loadingscreen_firstforstartup = {0, "scr_loadingscreen_firstforstartup","0", "remove loading.tga from random scr_loadingscreen_count selection and only display it on client startup, 0 = normal, 1 = firstforstartup"};
 cvar_t scr_loadingscreen_barcolor = {0, "scr_loadingscreen_barcolor", "0 0 1", "rgb color of loadingscreen progress bar"};
 cvar_t scr_loadingscreen_barheight = {0, "scr_loadingscreen_barheight", "8", "the height of the loadingscreen progress bar"};
+cvar_t scr_loadingscreen_maxfps = {0, "scr_loadingscreen_maxfps", "10", "restrict maximal FPS for loading screen so it will not update very often (this will make lesser loading times on a maps loading large number of models)"};
 cvar_t scr_infobar_height = {0, "scr_infobar_height", "8", "the height of the infobar items"};
 cvar_t vid_conwidth = {CVAR_SAVE, "vid_conwidth", "640", "virtual width of 2D graphics system"};
 cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480", "virtual height of 2D graphics system"};
@@ -72,10 +75,6 @@ cvar_t r_stereo_redblue = {0, "r_stereo_redblue", "0", "red/blue anaglyph stereo
 cvar_t r_stereo_redcyan = {0, "r_stereo_redcyan", "0", "red/cyan anaglyph stereo glasses, the kind given away at drive-in movies like Creature From The Black Lagoon In 3D"};
 cvar_t r_stereo_redgreen = {0, "r_stereo_redgreen", "0", "red/green anaglyph stereo glasses (for those who don't mind yellow)"};
 cvar_t r_stereo_angle = {0, "r_stereo_angle", "0", "separation angle of eyes (makes the views look different directions, as an example, 90 gives a 90 degree separation where the views are 45 degrees left and 45 degrees right)"};
-cvar_t scr_zoomwindow = {CVAR_SAVE, "scr_zoomwindow", "0", "displays a zoomed in overlay window"};
-cvar_t scr_zoomwindow_viewsizex = {CVAR_SAVE, "scr_zoomwindow_viewsizex", "20", "horizontal viewsize of zoom window"};
-cvar_t scr_zoomwindow_viewsizey = {CVAR_SAVE, "scr_zoomwindow_viewsizey", "20", "vertical viewsize of zoom window"};
-cvar_t scr_zoomwindow_fov = {CVAR_SAVE, "scr_zoomwindow_fov", "20", "fov of zoom window"};
 cvar_t scr_stipple = {0, "scr_stipple", "0", "interlacing-like stippling of the display"};
 cvar_t scr_refresh = {0, "scr_refresh", "1", "allows you to completely shut off rendering for benchmarking purposes"};
 cvar_t scr_screenshot_name_in_mapdir = {CVAR_SAVE, "scr_screenshot_name_in_mapdir", "0", "if set to 1, screenshots are placed in a subdirectory named like the map they are from"};
@@ -147,7 +146,7 @@ void SCR_CenterPrint(const char *str)
 }
 
 
-void SCR_DrawCenterString (void)
+static void SCR_DrawCenterString (void)
 {
        char    *start;
        int             x, y;
@@ -204,7 +203,7 @@ void SCR_DrawCenterString (void)
        } while (1);
 }
 
-void SCR_CheckDrawCenterString (void)
+static void SCR_CheckDrawCenterString (void)
 {
        if (scr_center_lines > scr_erase_lines)
                scr_erase_lines = scr_center_lines;
@@ -224,7 +223,7 @@ void SCR_CheckDrawCenterString (void)
        SCR_DrawCenterString ();
 }
 
-void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, int graphheight, float graphscale, const char *label, float textsize, int packetcounter, netgraphitem_t *netgraph)
+static void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, int graphheight, float graphscale, const char *label, float textsize, int packetcounter, netgraphitem_t *netgraph)
 {
        netgraphitem_t *graph;
        int j, x, y, numlines;
@@ -239,9 +238,6 @@ void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, int gra
        float *c;
        DrawQ_Fill(graphx, graphy, graphwidth, graphheight + textsize * 2, 0, 0, 0, 0.5, 0);
        // draw the bar graph itself
-       // advance the packet counter because it is the latest packet column being
-       // built up and should come last
-       packetcounter = (packetcounter + 1) % NETGRAPH_PACKETS;
        memset(g, 0, sizeof(g));
        for (j = 0;j < NETGRAPH_PACKETS;j++)
        {
@@ -312,11 +308,12 @@ void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, int gra
 SCR_DrawNetGraph
 ==============
 */
-void SCR_DrawNetGraph (void)
+static void SCR_DrawNetGraph (void)
 {
        int i, separator1, separator2, graphwidth, graphheight, netgraph_x, netgraph_y, textsize, index, netgraphsperrow;
        float graphscale;
        netconn_t *c;
+       char vabuf[1024];
 
        if (cls.state != ca_connected)
                return;
@@ -352,7 +349,7 @@ void SCR_DrawNetGraph (void)
                                continue;
                        netgraph_x = (vid_conwidth.integer + separator2) - (1 + (index % netgraphsperrow)) * (graphwidth * 2 + separator1 + separator2);
                        netgraph_y = (vid_conheight.integer - 48 + separator2) - (1 + (index / netgraphsperrow)) * (graphheight + textsize + separator2);
-                       SCR_DrawNetGraph_DrawGraph(netgraph_x                          , netgraph_y, graphwidth, graphheight, graphscale, va("%s", svs.clients[i].name), textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
+                       SCR_DrawNetGraph_DrawGraph(netgraph_x                          , netgraph_y, graphwidth, graphheight, graphscale, va(vabuf, sizeof(vabuf), "%s", svs.clients[i].name), textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
                        SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, ""                           , textsize, c->incoming_packetcounter, c->incoming_netgraph);
                        index++;
                }
@@ -364,7 +361,7 @@ void SCR_DrawNetGraph (void)
 SCR_DrawTurtle
 ==============
 */
-void SCR_DrawTurtle (void)
+static void SCR_DrawTurtle (void)
 {
        static int      count;
 
@@ -392,7 +389,7 @@ void SCR_DrawTurtle (void)
 SCR_DrawNet
 ==============
 */
-void SCR_DrawNet (void)
+static void SCR_DrawNet (void)
 {
        if (cls.state != ca_connected)
                return;
@@ -409,7 +406,7 @@ void SCR_DrawNet (void)
 DrawPause
 ==============
 */
-void SCR_DrawPause (void)
+static void SCR_DrawPause (void)
 {
        cachepic_t      *pic;
 
@@ -431,7 +428,7 @@ void SCR_DrawPause (void)
 SCR_DrawBrand
 ==============
 */
-void SCR_DrawBrand (void)
+static void SCR_DrawBrand (void)
 {
        cachepic_t      *pic;
        float           x, y;
@@ -553,9 +550,10 @@ static int SCR_DrawCurlDownload(int offset)
        float size = scr_infobar_height.value;
        Curl_downloadinfo_t *downinfo;
        char temp[256];
+       char addinfobuf[128];
        const char *addinfo;
 
-       downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo);
+       downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo, addinfobuf, sizeof(addinfobuf));
        if(!downinfo)
                return 0;
 
@@ -610,6 +608,7 @@ static int SCR_InfobarHeight(void)
        Curl_downloadinfo_t *downinfo;
        const char *addinfo;
        int nDownloads;
+       char addinfobuf[128];
 
        if (cl.time > cl.oldtime)
                scr_infobartime_off -= cl.time - cl.oldtime;
@@ -618,7 +617,7 @@ static int SCR_InfobarHeight(void)
        if(cls.qw_downloadname[0])
                offset += 1;
 
-       downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo);
+       downinfo = Curl_GetDownloadInfo(&nDownloads, &addinfo, addinfobuf, sizeof(addinfobuf));
        if(downinfo)
        {
                offset += (nDownloads + (addinfo ? 1 : 0));
@@ -634,7 +633,7 @@ static int SCR_InfobarHeight(void)
 SCR_InfoBar_f
 ==============
 */
-void SCR_InfoBar_f(void)
+static void SCR_InfoBar_f(void)
 {
        if(Cmd_Argc() == 3)
        {
@@ -653,7 +652,7 @@ void SCR_InfoBar_f(void)
 SCR_SetUpToDrawConsole
 ==================
 */
-void SCR_SetUpToDrawConsole (void)
+static void SCR_SetUpToDrawConsole (void)
 {
        // lines of console to display
        float conlines;
@@ -710,13 +709,13 @@ SCR_BeginLoadingPlaque
 
 ================
 */
-void SCR_BeginLoadingPlaque (void)
+void SCR_BeginLoadingPlaque (qboolean startup)
 {
        // save console log up to this point to log_file if it was set by configs
        Log_Start();
 
        Host_StartVideo();
-       SCR_UpdateLoadingScreen(false);
+       SCR_UpdateLoadingScreen(false, startup);
 }
 
 //=============================================================================
@@ -740,7 +739,7 @@ void R_TimeReport(const char *desc)
                GL_Finish();
        CHECKGLERROR
        r_timereport_temp = r_timereport_current;
-       r_timereport_current = Sys_DoubleTime();
+       r_timereport_current = Sys_DirtyTime();
        t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0 + 0.5);
 
        length = dpsnprintf(tempbuf, sizeof(tempbuf), "%8i %s", t, desc);
@@ -760,7 +759,7 @@ void R_TimeReport(const char *desc)
        speedstringcount += length;
 }
 
-void R_TimeReport_BeginFrame(void)
+static void R_TimeReport_BeginFrame(void)
 {
        speedstringcount = 0;
        r_speeds_timestring[0] = 0;
@@ -770,7 +769,7 @@ void R_TimeReport_BeginFrame(void)
        if (r_speeds.integer >= 2)
        {
                r_timereport_active = true;
-               r_timereport_start = r_timereport_current = Sys_DoubleTime();
+               r_timereport_start = r_timereport_current = Sys_DirtyTime();
        }
 }
 
@@ -784,7 +783,7 @@ static int R_CountLeafTriangles(const dp_model_t *model, const mleaf_t *leaf)
 
 extern cvar_t r_viewscale;
 extern float viewscalefpsadjusted;
-void R_TimeReport_EndFrame(void)
+static void R_TimeReport_EndFrame(void)
 {
        int i, j, lines, y;
        cl_locnode_t *loc;
@@ -799,7 +798,7 @@ void R_TimeReport_EndFrame(void)
                loc = CL_Locs_FindNearest(cl.movement_origin);
                viewleaf = (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf) ? r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, r_refdef.view.origin) : NULL;
                dpsnprintf(string, sizeof(string),
-"%6.0fus rendertime %3.0f%% viewscale %s%s %.3f cl.time\n"
+"%6.0fus rendertime %3.0f%% viewscale %s%s %.3f cl.time%2.4f brightness\n"
 "%3i renders org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n"
 "%5i viewleaf%5i cluster%3i area%4i brushes%4i surfaces(%7i triangles)\n"
 "%7i surfaces%7i triangles %5i entities (%7i surfaces%7i triangles)\n"
@@ -811,7 +810,7 @@ void R_TimeReport_EndFrame(void)
 "%6i draws%8i vertices%8i triangles bloompixels%8i copied%8i drawn\n"
 "updated%5i indexbuffers%8i bytes%5i vertexbuffers%8i bytes\n"
 "%s"
-, r_refdef.lastdrawscreentime * 1000000.0, r_viewscale.value * sqrt(viewscalefpsadjusted) * 100.0f, loc ? "Location: " : "", loc ? loc->name : "", cl.time
+, r_refdef.lastdrawscreentime * 1000000.0, r_viewscale.value * sqrt(viewscalefpsadjusted) * 100.0f, loc ? "Location: " : "", loc ? loc->name : "", cl.time, r_refdef.view.colorscale
 , r_refdef.stats.renders, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], r_refdef.view.forward[0], r_refdef.view.forward[1], r_refdef.view.forward[2]
 , viewleaf ? (int)(viewleaf - r_refdef.scene.worldmodel->brush.data_leafs) : -1, viewleaf ? viewleaf->clusterindex : -1, viewleaf ? viewleaf->areaindex : -1, viewleaf ? viewleaf->numleafbrushes : 0, viewleaf ? viewleaf->numleafsurfaces : 0, viewleaf ? R_CountLeafTriangles(r_refdef.scene.worldmodel, viewleaf) : 0
 , r_refdef.stats.world_surfaces, r_refdef.stats.world_triangles, r_refdef.stats.entities, r_refdef.stats.entities_surfaces, r_refdef.stats.entities_triangles
@@ -833,7 +832,7 @@ void R_TimeReport_EndFrame(void)
                if (r_speeds.integer >= 2)
                {
                        r_timereport_active = true;
-                       r_timereport_start = r_timereport_current = Sys_DoubleTime();
+                       r_timereport_start = r_timereport_current = Sys_DirtyTime();
                }
        }
 
@@ -871,7 +870,7 @@ SCR_SizeUp_f
 Keybinding command
 =================
 */
-void SCR_SizeUp_f (void)
+static void SCR_SizeUp_f (void)
 {
        Cvar_SetValue ("viewsize",scr_viewsize.value+10);
 }
@@ -884,7 +883,7 @@ SCR_SizeDown_f
 Keybinding command
 =================
 */
-void SCR_SizeDown_f (void)
+static void SCR_SizeDown_f (void)
 {
        Cvar_SetValue ("viewsize",scr_viewsize.value-10);
 }
@@ -916,9 +915,12 @@ void CL_Screen_Init(void)
        Cvar_RegisterVariable (&scr_loadingscreen_scale);
        Cvar_RegisterVariable (&scr_loadingscreen_scale_base);
        Cvar_RegisterVariable (&scr_loadingscreen_scale_limit);
+       Cvar_RegisterVariable (&scr_loadingscreen_picture);
        Cvar_RegisterVariable (&scr_loadingscreen_count);
+       Cvar_RegisterVariable (&scr_loadingscreen_firstforstartup);
        Cvar_RegisterVariable (&scr_loadingscreen_barcolor);
        Cvar_RegisterVariable (&scr_loadingscreen_barheight);
+       Cvar_RegisterVariable (&scr_loadingscreen_maxfps);
        Cvar_RegisterVariable (&scr_infobar_height);
        Cvar_RegisterVariable (&scr_showram);
        Cvar_RegisterVariable (&scr_showturtle);
@@ -956,10 +958,6 @@ void CL_Screen_Init(void)
        Cvar_RegisterVariable(&r_stereo_redcyan);
        Cvar_RegisterVariable(&r_stereo_redgreen);
        Cvar_RegisterVariable(&r_stereo_angle);
-       Cvar_RegisterVariable(&scr_zoomwindow);
-       Cvar_RegisterVariable(&scr_zoomwindow_viewsizex);
-       Cvar_RegisterVariable(&scr_zoomwindow_viewsizey);
-       Cvar_RegisterVariable(&scr_zoomwindow_fov);
        Cvar_RegisterVariable(&scr_stipple);
        Cvar_RegisterVariable(&scr_refresh);
        Cvar_RegisterVariable(&shownetgraph);
@@ -998,6 +996,7 @@ void SCR_ScreenShot_f (void)
        unsigned char *buffer2;
        qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
        qboolean png = (scr_screenshot_png.integer != 0) && !jpeg;
+       char vabuf[1024];
 
        if (Cmd_Argc() == 2)
        {
@@ -1037,9 +1036,9 @@ void SCR_ScreenShot_f (void)
 
                // find a file name to save it to
                for (shotnumber100 = 0;shotnumber100 < 100;shotnumber100++)
-                       if (!FS_SysFileExists(va("%s/screenshots/%s-%02d.tga", fs_gamedir, prefix_name, shotnumber100))
-                        && !FS_SysFileExists(va("%s/screenshots/%s-%02d.jpg", fs_gamedir, prefix_name, shotnumber100))
-                        && !FS_SysFileExists(va("%s/screenshots/%s-%02d.png", fs_gamedir, prefix_name, shotnumber100)))
+                       if (!FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s-%02d.tga", fs_gamedir, prefix_name, shotnumber100))
+                        && !FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s-%02d.jpg", fs_gamedir, prefix_name, shotnumber100))
+                        && !FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s-%02d.png", fs_gamedir, prefix_name, shotnumber100)))
                                break;
                if (shotnumber100 >= 100)
                {
@@ -1068,9 +1067,9 @@ void SCR_ScreenShot_f (void)
 
                // find a file name to save it to
                for (;shotnumber < 1000000;shotnumber++)
-                       if (!FS_SysFileExists(va("%s/screenshots/%s%06d.tga", fs_gamedir, prefix_name, shotnumber))
-                        && !FS_SysFileExists(va("%s/screenshots/%s%06d.jpg", fs_gamedir, prefix_name, shotnumber))
-                        && !FS_SysFileExists(va("%s/screenshots/%s%06d.png", fs_gamedir, prefix_name, shotnumber)))
+                       if (!FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s%06d.tga", fs_gamedir, prefix_name, shotnumber))
+                        && !FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s%06d.jpg", fs_gamedir, prefix_name, shotnumber))
+                        && !FS_SysFileExists(va(vabuf, sizeof(vabuf), "%s/screenshots/%s%06d.png", fs_gamedir, prefix_name, shotnumber)))
                                break;
                if (shotnumber >= 1000000)
                {
@@ -1105,7 +1104,7 @@ void SCR_ScreenShot_f (void)
        Mem_Free (buffer2);
 }
 
-void SCR_CaptureVideo_BeginVideo(void)
+static void SCR_CaptureVideo_BeginVideo(void)
 {
        double r, g, b;
        unsigned int i;
@@ -1140,7 +1139,7 @@ void SCR_CaptureVideo_BeginVideo(void)
        cls.capturevideo.soundchannels = S_GetSoundChannels();
        cls.capturevideo.startrealtime = realtime;
        cls.capturevideo.frame = cls.capturevideo.lastfpsframe = 0;
-       cls.capturevideo.starttime = cls.capturevideo.lastfpstime = Sys_DoubleTime();
+       cls.capturevideo.starttime = cls.capturevideo.lastfpstime = realtime;
        cls.capturevideo.soundsampleframe = 0;
        cls.capturevideo.realtime = cl_capturevideo_realtime.integer != 0;
        cls.capturevideo.screenbuffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 4);
@@ -1298,7 +1297,7 @@ static void SCR_ScaleDownBGRA(unsigned char *in, int inw, int inh, unsigned char
        }
 }
 
-void SCR_CaptureVideo_VideoFrame(int newframestepframenum)
+static void SCR_CaptureVideo_VideoFrame(int newframestepframenum)
 {
        int x = 0, y = 0;
        int width = cls.capturevideo.width, height = cls.capturevideo.height;
@@ -1319,7 +1318,7 @@ void SCR_CaptureVideo_VideoFrame(int newframestepframenum)
        if(cl_capturevideo_printfps.integer)
        {
                char buf[80];
-               double t = Sys_DoubleTime();
+               double t = realtime;
                if(t > cls.capturevideo.lastfpstime + 1)
                {
                        double fps1 = (cls.capturevideo.frame - cls.capturevideo.lastfpsframe) / (t - cls.capturevideo.lastfpstime + 0.0000001);
@@ -1338,7 +1337,7 @@ void SCR_CaptureVideo_SoundFrame(const portable_sampleframe_t *paintbuffer, size
        cls.capturevideo.soundframe(paintbuffer, length);
 }
 
-void SCR_CaptureVideo(void)
+static void SCR_CaptureVideo(void)
 {
        int newframenum;
        if (cl_capturevideo.integer)
@@ -1453,6 +1452,8 @@ static void R_Envmap_f (void)
 
        r_refdef.view.frustum_x = 1; // tan(45 * M_PI / 180.0);
        r_refdef.view.frustum_y = 1; // tan(45 * M_PI / 180.0);
+       r_refdef.view.ortho_x = 90; // abused as angle by VM_CL_R_SetView
+       r_refdef.view.ortho_y = 90; // abused as angle by VM_CL_R_SetView
 
        buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 4);
        buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
@@ -1481,7 +1482,7 @@ void SHOWLMP_decodehide(void)
 {
        int i;
        char *lmplabel;
-       lmplabel = MSG_ReadString();
+       lmplabel = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
        for (i = 0;i < cl.num_showlmps;i++)
                if (cl.showlmps[i].isactive && strcmp(cl.showlmps[i].label, lmplabel) == 0)
                {
@@ -1495,17 +1496,17 @@ void SHOWLMP_decodeshow(void)
        int k;
        char lmplabel[256], picname[256];
        float x, y;
-       strlcpy (lmplabel,MSG_ReadString(), sizeof (lmplabel));
-       strlcpy (picname, MSG_ReadString(), sizeof (picname));
+       strlcpy (lmplabel,MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (lmplabel));
+       strlcpy (picname, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (picname));
        if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
        {
-               x = MSG_ReadByte();
-               y = MSG_ReadByte();
+               x = MSG_ReadByte(&cl_message);
+               y = MSG_ReadByte(&cl_message);
        }
        else
        {
-               x = MSG_ReadShort();
-               y = MSG_ReadShort();
+               x = MSG_ReadShort(&cl_message);
+               y = MSG_ReadShort(&cl_message);
        }
        if (!cl.showlmps || cl.num_showlmps >= cl.max_showlmps)
        {
@@ -1643,7 +1644,6 @@ static void SCR_DrawTouchscreenOverlay(void)
        }
 }
 
-extern void R_UpdateFog(void);
 void R_ClearScreen(qboolean fogcolor)
 {
        float clearcolor[4];
@@ -1662,14 +1662,9 @@ void R_ClearScreen(qboolean fogcolor)
        GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (vid.stencil ? GL_STENCIL_BUFFER_BIT : 0), clearcolor, 1.0f, 128);
 }
 
-qboolean CL_VM_UpdateView (void);
-void SCR_DrawConsole (void);
-void R_Shadow_EditLights_DrawSelectedLightProperties(void);
-
 int r_stereo_side;
 
-extern void Sbar_ShowFPS(void);
-void SCR_DrawScreen (void)
+static void SCR_DrawScreen (void)
 {
        Draw_Frame();
 
@@ -1743,31 +1738,11 @@ void SCR_DrawScreen (void)
 
                r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
                r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
+               r_refdef.view.ortho_x = atan(r_refdef.view.frustum_x) * (360.0 / M_PI); // abused as angle by VM_CL_R_SetView
+               r_refdef.view.ortho_y = atan(r_refdef.view.frustum_y) * (360.0 / M_PI); // abused as angle by VM_CL_R_SetView
 
                if(!CL_VM_UpdateView())
                        R_RenderView();
-
-               if (scr_zoomwindow.integer)
-               {
-                       float sizex = bound(10, scr_zoomwindow_viewsizex.value, 100) / 100.0;
-                       float sizey = bound(10, scr_zoomwindow_viewsizey.value, 100) / 100.0;
-                       r_refdef.view.width = (int)(vid.width * sizex);
-                       r_refdef.view.height = (int)(vid.height * sizey);
-                       r_refdef.view.depth = 1;
-                       r_refdef.view.x = (int)((vid.width - r_refdef.view.width)/2);
-                       r_refdef.view.y = 0;
-                       r_refdef.view.z = 0;
-
-                       r_refdef.view.useperspective = true;
-                       r_refdef.view.frustum_y = tan(scr_zoomwindow_fov.value * M_PI / 360.0) * (3.0/4.0) * cl.viewzoom;
-                       r_refdef.view.frustum_x = r_refdef.view.frustum_y * vid_pixelheight.value * (float)r_refdef.view.width / (float)r_refdef.view.height;
-
-                       r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
-                       r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
-
-                       if(!CL_VM_UpdateView())
-                               R_RenderView();
-               }
        }
 
        if (!r_stereo_sidebyside.integer && !r_stereo_horizontal.integer && !r_stereo_vertical.integer)
@@ -1918,7 +1893,7 @@ static void SCR_SetLoadingScreenTexture(void)
 void SCR_UpdateLoadingScreenIfShown(void)
 {
        if(loadingscreendone)
-               SCR_UpdateLoadingScreen(loadingscreencleared);
+               SCR_UpdateLoadingScreen(loadingscreencleared, false);
 }
 
 void SCR_PushLoadingScreen (qboolean redraw, const char *msg, float len_in_parent)
@@ -2040,7 +2015,7 @@ static void SCR_DrawLoadingStack(void)
                sscanf(scr_loadingscreen_barcolor.string, "%f %f %f", &colors[12], &colors[13], &colors[14]);  colors[15] = 1;
 
                R_Mesh_PrepareVertices_Generic_Arrays(4, verts, colors, NULL);
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true, true);
+               R_SetupShader_Generic_NoTexture(true, true);
                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
 
                // make sure everything is cleared, including the progress indicator
@@ -2057,13 +2032,14 @@ static void SCR_DrawLoadingScreen_SharedSetup (qboolean clear)
 {
        r_viewport_t viewport;
        float x, y, w, h, sw, sh, f;
+       char vabuf[1024];
        // release mouse grab while loading
        if (!vid.fullscreen)
                VID_SetMouse(false, false, false);
 //     CHECKGLERROR
        r_refdef.draw2dstage = true;
        R_Viewport_InitOrtho(&viewport, &identitymatrix, 0, 0, vid.width, vid.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
-       R_Mesh_ResetRenderTargets();
+       R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
        R_SetViewport(&viewport);
        GL_ColorMask(1,1,1,1);
        // when starting up a new video mode, make sure the screen is cleared to black
@@ -2073,7 +2049,7 @@ static void SCR_DrawLoadingScreen_SharedSetup (qboolean clear)
        R_Mesh_Start();
        R_EntityMatrix(&identitymatrix);
        // draw the loading plaque
-       loadingscreenpic = Draw_CachePic_Flags (loadingscreenpic_number ? va("gfx/loading%d", loadingscreenpic_number+1) : "gfx/loading", loadingscreenpic_number ? CACHEPICFLAG_NOTPERSISTENT : 0);
+       loadingscreenpic = Draw_CachePic_Flags (loadingscreenpic_number ? va(vabuf, sizeof(vabuf), "%s%d", scr_loadingscreen_picture.string, loadingscreenpic_number+1) : scr_loadingscreen_picture.string, loadingscreenpic_number ? CACHEPICFLAG_NOTPERSISTENT : 0);
 
        w = loadingscreenpic->width;
        h = loadingscreenpic->height;
@@ -2139,11 +2115,11 @@ static void SCR_DrawLoadingScreen (qboolean clear)
        if(loadingscreentexture)
        {
                R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreentexture_vertex3f, NULL, loadingscreentexture_texcoord2f);
-               R_SetupShader_Generic(loadingscreentexture, NULL, GL_MODULATE, 1, true, true);
+               R_SetupShader_Generic(loadingscreentexture, NULL, GL_MODULATE, 1, true, true, true);
                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
        }
        R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreenpic_vertex3f, NULL, loadingscreenpic_texcoord2f);
-       R_SetupShader_Generic(Draw_GetPicTexture(loadingscreenpic), NULL, GL_MODULATE, 1, true, true);
+       R_SetupShader_Generic(Draw_GetPicTexture(loadingscreenpic), NULL, GL_MODULATE, 1, true, true, false);
        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
        SCR_DrawLoadingStack();
 }
@@ -2155,7 +2131,9 @@ static void SCR_DrawLoadingScreen_SharedFinish (qboolean clear)
        VID_Finish();
 }
 
-void SCR_UpdateLoadingScreen (qboolean clear)
+static double loadingscreen_lastupdate;
+
+void SCR_UpdateLoadingScreen (qboolean clear, qboolean startup)
 {
        keydest_t       old_key_dest;
        int                     old_key_consoleactive;
@@ -2164,6 +2142,15 @@ void SCR_UpdateLoadingScreen (qboolean clear)
        if (vid_hidden || cls.state == ca_dedicated)
                return;
 
+       // limit update rate
+       if (scr_loadingscreen_maxfps.value)
+       {
+               double t = Sys_DirtyTime();
+               if ((t - loadingscreen_lastupdate) < 1.0f/scr_loadingscreen_maxfps.value)
+                       return;
+               loadingscreen_lastupdate = t;
+       }
+
        if(!scr_loadingscreen_background.integer)
                clear = true;
        
@@ -2171,7 +2158,17 @@ void SCR_UpdateLoadingScreen (qboolean clear)
                clear |= loadingscreencleared;
 
        if(!loadingscreendone)
-               loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer > 1 ? scr_loadingscreen_count.integer : 1);
+       {
+               if(startup && scr_loadingscreen_firstforstartup.integer)
+                       loadingscreenpic_number = 0;
+               else if(scr_loadingscreen_firstforstartup.integer)
+                       if(scr_loadingscreen_count.integer > 1)
+                               loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer - 1) + 1;
+                       else
+                               loadingscreenpic_number = 0;
+               else
+                       loadingscreenpic_number = rand() % (scr_loadingscreen_count.integer > 1 ? scr_loadingscreen_count.integer : 1);
+       }
 
        if(clear)
                SCR_ClearLoadingScreenTexture();
@@ -2232,21 +2229,90 @@ extern cvar_t cl_minfps;
 extern cvar_t cl_minfps_fade;
 extern cvar_t cl_minfps_qualitymax;
 extern cvar_t cl_minfps_qualitymin;
-extern cvar_t cl_minfps_qualitypower;
-extern cvar_t cl_minfps_qualityscale;
-extern cvar_t r_viewscale_fpsscaling;
-static double cl_updatescreen_rendertime = 0;
+extern cvar_t cl_minfps_qualitymultiply;
+extern cvar_t cl_minfps_qualityhysteresis;
+extern cvar_t cl_minfps_qualitystepmax;
+extern cvar_t cl_minfps_force;
 static double cl_updatescreen_quality = 1;
-extern void Sbar_ShowFPS_Update(void);
 void CL_UpdateScreen(void)
 {
        vec3_t vieworigin;
-       double rendertime1;
-       double drawscreenstart;
+       static double drawscreenstart = 0.0;
+       double drawscreendelta;
        float conwidth, conheight;
-       float f;
        r_viewport_t viewport;
 
+       if(drawscreenstart)
+       {
+               drawscreendelta = Sys_DirtyTime() - drawscreenstart;
+               if (cl_minfps.value > 0 && (cl_minfps_force.integer || !(cls.timedemo || (cls.capturevideo.active && !cls.capturevideo.realtime))) && drawscreendelta >= 0 && drawscreendelta < 60)
+               {
+                       // quality adjustment according to render time
+                       double actualframetime;
+                       double targetframetime;
+                       double adjust;
+                       double f;
+                       double h;
+
+                       // fade lastdrawscreentime
+                       r_refdef.lastdrawscreentime += (drawscreendelta - r_refdef.lastdrawscreentime) * cl_minfps_fade.value;
+
+                       // find actual and target frame times
+                       actualframetime = r_refdef.lastdrawscreentime;
+                       targetframetime = (1.0 / cl_minfps.value);
+
+                       // we scale hysteresis by quality
+                       h = cl_updatescreen_quality * cl_minfps_qualityhysteresis.value;
+
+                       // calculate adjustment assuming linearity
+                       f = cl_updatescreen_quality / actualframetime * cl_minfps_qualitymultiply.value;
+                       adjust = (targetframetime - actualframetime) * f;
+
+                       // one sided hysteresis
+                       if(adjust > 0)
+                               adjust = max(0, adjust - h);
+
+                       // adjust > 0 if:
+                       //   (targetframetime - actualframetime) * f > h
+                       //   ((1.0 / cl_minfps.value) - actualframetime) * (cl_updatescreen_quality / actualframetime * cl_minfps_qualitymultiply.value) > (cl_updatescreen_quality * cl_minfps_qualityhysteresis.value)
+                       //   ((1.0 / cl_minfps.value) - actualframetime) * (cl_minfps_qualitymultiply.value / actualframetime) > cl_minfps_qualityhysteresis.value
+                       //   (1.0 / cl_minfps.value) * (cl_minfps_qualitymultiply.value / actualframetime) - cl_minfps_qualitymultiply.value > cl_minfps_qualityhysteresis.value
+                       //   (1.0 / cl_minfps.value) * (cl_minfps_qualitymultiply.value / actualframetime) > cl_minfps_qualityhysteresis.value + cl_minfps_qualitymultiply.value
+                       //   (1.0 / cl_minfps.value) / actualframetime > (cl_minfps_qualityhysteresis.value + cl_minfps_qualitymultiply.value) / cl_minfps_qualitymultiply.value
+                       //   (1.0 / cl_minfps.value) / actualframetime > 1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value
+                       //   cl_minfps.value * actualframetime < 1.0 / (1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value)
+                       //   actualframetime < 1.0 / cl_minfps.value / (1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value)
+                       //   actualfps > cl_minfps.value * (1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value)
+
+                       // adjust < 0 if:
+                       //   (targetframetime - actualframetime) * f < 0
+                       //   ((1.0 / cl_minfps.value) - actualframetime) * (cl_updatescreen_quality / actualframetime * cl_minfps_qualitymultiply.value) < 0
+                       //   ((1.0 / cl_minfps.value) - actualframetime) < 0
+                       //   -actualframetime) < -(1.0 / cl_minfps.value)
+                       //   actualfps < cl_minfps.value
+
+                       /*
+                       Con_Printf("adjust UP if fps > %f, adjust DOWN if fps < %f\n",
+                                       cl_minfps.value * (1.0 + cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value),
+                                       cl_minfps.value);
+                       */
+
+                       // don't adjust too much at once
+                       adjust = bound(-cl_minfps_qualitystepmax.value, adjust, cl_minfps_qualitystepmax.value);
+
+                       // adjust!
+                       cl_updatescreen_quality += adjust;
+                       cl_updatescreen_quality = bound(max(0.01, cl_minfps_qualitymin.value), cl_updatescreen_quality, cl_minfps_qualitymax.value);
+               }
+               else
+               {
+                       cl_updatescreen_quality = 1;
+                       r_refdef.lastdrawscreentime = 0;
+               }
+       }
+
+       drawscreenstart = Sys_DirtyTime();
+
        Sbar_ShowFPS_Update();
 
        if (!scr_initialized || !con_initialized || !scr_refresh.integer)
@@ -2273,8 +2339,6 @@ void CL_UpdateScreen(void)
                return;
        }
 
-       rendertime1 = Sys_DoubleTime();
-
        conwidth = bound(160, vid_conwidth.value, 32768);
        conheight = bound(90, vid_conheight.value, 24576);
        if (vid_conwidth.value != conwidth)
@@ -2334,7 +2398,7 @@ void CL_UpdateScreen(void)
 #endif
 
        R_Viewport_InitOrtho(&viewport, &identitymatrix, 0, 0, vid.width, vid.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
-       R_Mesh_ResetRenderTargets();
+       R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
        R_SetViewport(&viewport);
        GL_ScissorTest(false);
        GL_ColorMask(1,1,1,1);
@@ -2343,8 +2407,9 @@ void CL_UpdateScreen(void)
        R_ClearScreen(false);
        r_refdef.view.clear = false;
        r_refdef.view.isoverlay = false;
-       f = pow((float)cl_updatescreen_quality, cl_minfps_qualitypower.value) * cl_minfps_qualityscale.value;
-       r_refdef.view.quality = bound(cl_minfps_qualitymin.value, f, cl_minfps_qualitymax.value);
+
+       // calculate r_refdef.view.quality
+       r_refdef.view.quality = cl_updatescreen_quality;
 
 #ifndef USE_GLES2
        if (qglPolygonStipple)
@@ -2375,9 +2440,6 @@ void CL_UpdateScreen(void)
        }
 #endif
 
-       if (r_viewscale_fpsscaling.integer)
-               GL_Finish();
-       drawscreenstart = Sys_DoubleTime();
 #ifndef USE_GLES2
        if (R_Stereo_Active())
        {
@@ -2396,6 +2458,7 @@ void CL_UpdateScreen(void)
                SCR_DrawScreen();
 
                r_stereo_side = 1;
+               r_refdef.view.clear = true;
 
                if (r_stereo_redblue.integer || r_stereo_redgreen.integer || r_stereo_redcyan.integer)
                {
@@ -2412,22 +2475,12 @@ void CL_UpdateScreen(void)
        else
 #endif
                SCR_DrawScreen();
-       if (r_viewscale_fpsscaling.integer)
-               GL_Finish();
-       r_refdef.lastdrawscreentime = Sys_DoubleTime() - drawscreenstart;
 
        SCR_CaptureVideo();
 
        if (qglFlush)
                qglFlush(); // FIXME: should we really be using qglFlush here?
 
-       // quality adjustment according to render time
-       cl_updatescreen_rendertime += ((Sys_DoubleTime() - rendertime1) - cl_updatescreen_rendertime) * bound(0, cl_minfps_fade.value, 1);
-       if (cl_minfps.value > 0 && cl_updatescreen_rendertime > 0 && !cls.timedemo && (!cls.capturevideo.active || !cls.capturevideo.realtime))
-               cl_updatescreen_quality = 1 / (cl_updatescreen_rendertime * cl_minfps.value);
-       else
-               cl_updatescreen_quality = 1;
-
        if (!vid_activewindow)
                VID_SetMouse(false, false, false);
        else if (key_consoleactive)