]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_textures.c
Make the view slowly go back after hitting the ground. We need to do the same for...
[xonotic/darkplaces.git] / gl_textures.c
index 25074722fc49283cd7acde7dccd37b5021cd0dbc..f7245a9892cbb859d09a79f8809378f4a5c1e27f 100644 (file)
@@ -3,10 +3,16 @@
 #include "image.h"
 #include "jpeg.h"
 #include "image_png.h"
+#include "intoverflow.h"
 
 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
+cvar_t gl_picmip_world = {CVAR_SAVE, "gl_picmip_world", "0", "extra picmip level for world textures (may be negative, which will then reduce gl_picmip for these)"};
+cvar_t r_picmipworld = {CVAR_SAVE, "r_picmipworld", "1", "whether gl_picmip shall apply to world textures too (setting this to 0 is a shorthand for gl_picmip_world -9999999)"};
+cvar_t gl_picmip_sprites = {CVAR_SAVE, "gl_picmip_sprites", "0", "extra picmip level for sprite textures (may be negative, which will then reduce gl_picmip for these)"};
+cvar_t r_picmipsprites = {CVAR_SAVE, "r_picmipsprites", "1", "make gl_picmip affect sprites too (saves some graphics memory in sprite heavy games) (setting this to 0 is a shorthand for gl_picmip_sprites -9999999)"};
+cvar_t gl_picmip_other = {CVAR_SAVE, "gl_picmip_other", "0", "extra picmip level for other textures (may be negative, which will then reduce gl_picmip for these)"};
 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
@@ -19,21 +25,20 @@ cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression
 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
-cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "0", "use alternate path for dynamic lightmap updates"};
+cvar_t gl_texturecompression_reflectmask = {CVAR_SAVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"};
+cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
 
+qboolean       gl_filter_force = false;
 int            gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
 int            gl_filter_mag = GL_LINEAR;
 
 
 static mempool_t *texturemempool;
+static memexpandablearray_t texturearray;
 
 // note: this must not conflict with TEXF_ flags in r_textures.h
-// cleared when a texture is uploaded
-#define GLTEXF_UPLOAD          0x00010000
 // bitmask for mismatch checking
 #define GLTEXF_IMPORTANTBITS (0)
-// set when image is uploaded and freed
-#define GLTEXF_DESTROYED       0x00040000
 // dynamic texture (treat texnum == 0 differently)
 #define GLTEXF_DYNAMIC         0x00080000
 
@@ -43,26 +48,32 @@ typedef struct textypeinfo_s
        int inputbytesperpixel;
        int internalbytesperpixel;
        float glinternalbytesperpixel;
-       int glformat;
        int glinternalformat;
+       int glformat;
        int gltype;
 }
 textypeinfo_t;
 
-static textypeinfo_t textype_palette                = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA   , 3, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_palette_alpha          = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA   , 4, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_palette_compress       = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_BGRA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_rgba                   = {TEXTYPE_RGBA   , 4, 4, 4.0f, GL_RGBA   , 3, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_rgba_alpha             = {TEXTYPE_RGBA   , 4, 4, 4.0f, GL_RGBA   , 4, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_rgba_compress          = {TEXTYPE_RGBA   , 4, 4, 0.5f, GL_RGBA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_rgba_alpha_compress    = {TEXTYPE_RGBA   , 4, 4, 1.0f, GL_RGBA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_bgra                   = {TEXTYPE_BGRA   , 4, 4, 4.0f, GL_BGRA   , 3, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_bgra_alpha             = {TEXTYPE_BGRA   , 4, 4, 4.0f, GL_BGRA   , 4, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_bgra_compress          = {TEXTYPE_BGRA   , 4, 4, 0.5f, GL_BGRA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_bgra_alpha_compress    = {TEXTYPE_BGRA   , 4, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_shadowmap16            = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
-static textypeinfo_t textype_shadowmap24            = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
+
+static textypeinfo_t textype_palette                = {TEXTYPE_PALETTE    , 1, 4, 4.0f, 3                               , GL_BGRA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_palette_alpha          = {TEXTYPE_PALETTE    , 1, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba                   = {TEXTYPE_RGBA       , 4, 4, 4.0f, 3                               , GL_RGBA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_alpha             = {TEXTYPE_RGBA       , 4, 4, 4.0f, 4                               , GL_RGBA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_compress          = {TEXTYPE_RGBA       , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_alpha_compress    = {TEXTYPE_RGBA       , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra                   = {TEXTYPE_BGRA       , 4, 4, 4.0f, 3                               , GL_BGRA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_alpha             = {TEXTYPE_BGRA       , 4, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_compress          = {TEXTYPE_BGRA       , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_alpha_compress    = {TEXTYPE_BGRA       , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA           , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_shadowmap16            = {TEXTYPE_SHADOWMAP  , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB        , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_shadowmap24            = {TEXTYPE_SHADOWMAP  , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB        , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT  };
+static textypeinfo_t textype_alpha                  = {TEXTYPE_ALPHA      , 1, 4, 4.0f, GL_ALPHA                        , GL_ALPHA          , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_dxt1                   = {TEXTYPE_DXT1       , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0                 , 0                };
+static textypeinfo_t textype_dxt1a                  = {TEXTYPE_DXT1A      , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0                 , 0                };
+static textypeinfo_t textype_dxt3                   = {TEXTYPE_DXT3       , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0                 , 0                };
+static textypeinfo_t textype_dxt5                   = {TEXTYPE_DXT5       , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0                 , 0                };
+static textypeinfo_t textype_colorbuffer            = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
+
 
 typedef enum gltexturetype_e
 {
@@ -75,7 +86,6 @@ typedef enum gltexturetype_e
 gltexturetype_t;
 
 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
-static int gltexturetypebindingenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB};
 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
 static int cubemapside[6] =
 {
@@ -89,18 +99,18 @@ static int cubemapside[6] =
 
 typedef struct gltexture_s
 {
-       // this field is exposed to the R_GetTexture macro, for speed reasons
-       // (must be identical in rtexture_t)
+       // this portion of the struct is exposed to the R_GetTexture macro for
+       // speed reasons, must be identical in rtexture_t!
        int texnum; // GL texture slot number
+       qboolean dirty; // indicates that R_RealGetTexture should be called
+       int gltexturetypeenum; // used by R_Mesh_TexBind
 
        // dynamic texture stuff [11/22/2007 Black]
-       // used to hold the texture number of dirty textures   
-       int dirtytexnum;
        updatecallback_t updatecallback;
        void *updatacallback_data;
        // --- [11/22/2007 Black]
 
-       // stores backup copy of texture for deferred texture updates (r_nopartialtextureupdates cvar)
+       // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
        unsigned char *bufferpixels;
        qboolean buffermodified;
 
@@ -120,6 +130,8 @@ typedef struct gltexture_s
        // flags supplied to the LoadTexture function
        // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
        int flags;
+       // picmip level
+       int miplevel;
        // pointer to one of the textype_ structs
        textypeinfo_t *textype;
        // one of the GLTEXTURETYPE_ values
@@ -157,79 +169,40 @@ static gltexturepool_t *gltexturepoolchain = NULL;
 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
 static int resizebuffersize = 0;
 static const unsigned char *texturebuffer;
-static int texturebuffersize = 0;
 
 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
 {
-       if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
-       {
-               if (flags & TEXF_ALPHA)
-               {
-                       switch(textype)
-                       {
-                       case TEXTYPE_PALETTE:
-                               return &textype_palette_alpha_compress;
-                       case TEXTYPE_RGBA:
-                               return &textype_rgba_alpha_compress;
-                       case TEXTYPE_BGRA:
-                               return &textype_bgra_alpha_compress;
-                       default:
-                               Host_Error("R_GetTexTypeInfo: unknown texture format");
-                               return NULL;
-                       }
-               }
-               else
-               {
-                       switch(textype)
-                       {
-                       case TEXTYPE_PALETTE:
-                               return &textype_palette_compress;
-                       case TEXTYPE_RGBA:
-                               return &textype_rgba_compress;
-                       case TEXTYPE_BGRA:
-                               return &textype_bgra_compress;
-                       default:
-                               Host_Error("R_GetTexTypeInfo: unknown texture format");
-                               return NULL;
-                       }
-               }
-       }
-       else
+       switch(textype)
        {
-               if (flags & TEXF_ALPHA)
-               {
-                       switch(textype)
-                       {
-                       case TEXTYPE_PALETTE:
-                               return &textype_palette_alpha;
-                       case TEXTYPE_RGBA:
-                               return &textype_rgba_alpha;
-                       case TEXTYPE_BGRA:
-                               return &textype_bgra_alpha;
-                       default:
-                               Host_Error("R_GetTexTypeInfo: unknown texture format");
-                               return NULL;
-                       }
-               }
-               else
-               {
-                       switch(textype)
-                       {
-                       case TEXTYPE_PALETTE:
-                               return &textype_palette;
-                       case TEXTYPE_RGBA:
-                               return &textype_rgba;
-                       case TEXTYPE_BGRA:
-                               return &textype_bgra;
-                       case TEXTYPE_SHADOWMAP:
-                               return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
-                       default:
-                               Host_Error("R_GetTexTypeInfo: unknown texture format");
-                               return NULL;
-                       }
-               }
+       case TEXTYPE_DXT1:
+               return &textype_dxt1;
+       case TEXTYPE_DXT1A:
+               return &textype_dxt1a;
+       case TEXTYPE_DXT3:
+               return &textype_dxt3;
+       case TEXTYPE_DXT5:
+               return &textype_dxt5;
+       case TEXTYPE_PALETTE:
+               return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
+       case TEXTYPE_RGBA:
+               if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
+                       return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
+               return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
+       case TEXTYPE_BGRA:
+               if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
+                       return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
+               return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
+       case TEXTYPE_ALPHA:
+               return &textype_alpha;
+       case TEXTYPE_SHADOWMAP:
+               return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
+       case TEXTYPE_COLORBUFFER:
+               return &textype_colorbuffer;
+       default:
+               Host_Error("R_GetTexTypeInfo: unknown texture format");
+               break;
        }
-       return NULL; // this line only to hush compiler warnings
+       return NULL;
 }
 
 // dynamic texture code [11/22/2007 Black]
@@ -240,10 +213,10 @@ void R_MarkDirtyTexture(rtexture_t *rt) {
        }
 
        // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
-       if( !glt->dirtytexnum && glt->flags & GLTEXF_DYNAMIC ) {
-               glt->dirtytexnum = glt->texnum;
+       if (glt->flags & GLTEXF_DYNAMIC)
+       {
                // mark it as dirty, so R_RealGetTexture gets called
-               glt->texnum = 0;
+               glt->dirty = true;
        }
 }
 
@@ -256,14 +229,10 @@ void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void
        glt->flags |= GLTEXF_DYNAMIC;
        glt->updatecallback = updatecallback;
        glt->updatacallback_data = data;
-       glt->dirtytexnum = 0;
 }
 
 static void R_UpdateDynamicTexture(gltexture_t *glt) {
-       glt->texnum = glt->dirtytexnum;
-       // reset dirtytexnum again (not dirty anymore)
-       glt->dirtytexnum = 0;
-       // TODO: now assert that t->texnum != 0 ?
+       glt->dirty = false;
        if( glt->updatecallback ) {
                glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
        }
@@ -290,7 +259,7 @@ void R_FreeTexture(rtexture_t *rt)
        else
                Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
 
-       if (!(glt->flags & GLTEXF_UPLOAD))
+       if (glt->texnum)
        {
                CHECKGLERROR
                qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
@@ -298,7 +267,7 @@ void R_FreeTexture(rtexture_t *rt)
 
        if (glt->inputtexels)
                Mem_Free(glt->inputtexels);
-       Mem_Free(glt);
+       Mem_ExpandableArray_FreeRecord(&texturearray, glt);
 }
 
 rtexturepool_t *R_AllocTexturePool(void)
@@ -363,6 +332,7 @@ static void GL_TextureMode_f (void)
 
        if (Cmd_Argc() == 1)
        {
+               Con_Printf("Texture mode is %sforced\n", gl_filter_force ? "" : "not ");
                for (i = 0;i < 6;i++)
                {
                        if (gl_filter_min == modes[i].minification)
@@ -375,7 +345,7 @@ static void GL_TextureMode_f (void)
                return;
        }
 
-       for (i = 0;i < 6;i++)
+       for (i = 0;i < (int)(sizeof(modes)/sizeof(*modes));i++)
                if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
                        break;
        if (i == 6)
@@ -386,6 +356,7 @@ static void GL_TextureMode_f (void)
 
        gl_filter_min = modes[i].minification;
        gl_filter_mag = modes[i].magnification;
+       gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
 
        // change all the existing mipmap texture objects
        // FIXME: force renderer(/client/something?) restart instead?
@@ -396,9 +367,9 @@ static void GL_TextureMode_f (void)
                for (glt = pool->gltchain;glt;glt = glt->chain)
                {
                        // only update already uploaded images
-                       if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
+                       if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
                        {
-                               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
+                               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
                                qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
                                if (glt->flags & TEXF_MIPMAP)
                                {
@@ -415,7 +386,7 @@ static void GL_TextureMode_f (void)
        }
 }
 
-static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
+static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
 {
        int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
 
@@ -427,7 +398,7 @@ static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, in
                if (flags & TEXF_PICMIP)
                {
                        maxsize = bound(1, gl_max_size.integer, maxsize);
-                       picmip = gl_picmip.integer;
+                       picmip = miplevel;
                }
                break;
        case GLTEXTURETYPE_3D:
@@ -478,7 +449,7 @@ static int R_CalcTexelDataSize (gltexture_t *glt)
 {
        int width2, height2, depth2, size;
 
-       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
+       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
 
        size = width2 * height2 * depth2;
 
@@ -520,7 +491,7 @@ void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean print
                for (glt = pool->gltchain;glt;glt = glt->chain)
                {
                        glsize = R_CalcTexelDataSize(glt);
-                       isloaded = !(glt->flags & GLTEXF_UPLOAD);
+                       isloaded = glt->texnum != 0;
                        pooltotal++;
                        pooltotalt += glsize;
                        pooltotalp += glt->inputdatasize;
@@ -559,12 +530,13 @@ static void r_textures_start(void)
        qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
 
        texturemempool = Mem_AllocPool("texture management", 0, NULL);
+       Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
 
        // Disable JPEG screenshots if the DLL isn't loaded
        if (! JPEG_OpenLibrary ())
                Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
-       // TODO: support png screenshots?
-       PNG_OpenLibrary ();
+       if (! PNG_OpenLibrary ())
+               Cvar_SetValueQuick (&scr_screenshot_png, 0);
 }
 
 static void r_textures_shutdown(void)
@@ -580,10 +552,10 @@ static void r_textures_shutdown(void)
        }
 
        resizebuffersize = 0;
-       texturebuffersize = 0;
        resizebuffer = NULL;
        colorconvertbuffer = NULL;
        texturebuffer = NULL;
+       Mem_ExpandableArray_FreeArray(&texturearray);
        Mem_FreePool(&texturemempool);
 }
 
@@ -593,10 +565,15 @@ static void r_textures_newmap(void)
 
 void R_Textures_Init (void)
 {
-       Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
+       Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc); an additional argument 'force' forces the texture mode even in cases where it may not be appropriate");
        Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
        Cvar_RegisterVariable (&gl_max_size);
        Cvar_RegisterVariable (&gl_picmip);
+       Cvar_RegisterVariable (&gl_picmip_world);
+       Cvar_RegisterVariable (&r_picmipworld);
+       Cvar_RegisterVariable (&gl_picmip_sprites);
+       Cvar_RegisterVariable (&r_picmipsprites);
+       Cvar_RegisterVariable (&gl_picmip_other);
        Cvar_RegisterVariable (&gl_max_lightmapsize);
        Cvar_RegisterVariable (&r_lerpimages);
        Cvar_RegisterVariable (&gl_texture_anisotropy);
@@ -610,9 +587,10 @@ void R_Textures_Init (void)
        Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
        Cvar_RegisterVariable (&gl_texturecompression_sky);
        Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
+       Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
        Cvar_RegisterVariable (&gl_nopartialtextureupdates);
 
-       R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
+       R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap, NULL, NULL);
 }
 
 void R_Textures_Frame (void)
@@ -652,9 +630,9 @@ void R_Textures_Frame (void)
                        for (glt = pool->gltchain;glt;glt = glt->chain)
                        {
                                // only update already uploaded images
-                               if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
+                               if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
                                {
-                                       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
+                                       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
 
                                        qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
                                        qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
@@ -704,7 +682,7 @@ static void GL_SetupTextureParameters(int flags, textype_t textype, int texturet
        }
 
        CHECKGLERROR
-       if (flags & TEXF_FORCENEAREST)
+       if (!gl_filter_force && flags & TEXF_FORCENEAREST)
        {
                if (flags & TEXF_MIPMAP)
                {
@@ -716,7 +694,7 @@ static void GL_SetupTextureParameters(int flags, textype_t textype, int texturet
                }
                qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
        }
-       else if (flags & TEXF_FORCELINEAR)
+       else if (!gl_filter_force && flags & TEXF_FORCELINEAR)
        {
                if (flags & TEXF_MIPMAP)
                {
@@ -779,7 +757,7 @@ static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int
 
        // we need to restore the texture binding after finishing the upload
        GL_ActiveTexture(0);
-       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
+       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
        qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
 
        // these are rounded up versions of the size to do better resampling
@@ -811,7 +789,9 @@ static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int
                prevbuffer = colorconvertbuffer;
        }
 
-       if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
+       // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
+
+       if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
        {
                // update a portion of the image
                switch(glt->texturetype)
@@ -832,9 +812,6 @@ static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int
                if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
                        Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
 
-               // upload the image for the first time
-               glt->flags &= ~GLTEXF_UPLOAD;
-
                // cubemaps contain multiple images and thus get processed a bit differently
                if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
                {
@@ -926,41 +903,12 @@ static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int
        qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
 }
 
-int R_RealGetTexture(rtexture_t *rt)
-{
-       if (rt)
-       {
-               gltexture_t *glt;
-               glt = (gltexture_t *)rt;
-               if (glt->flags & GLTEXF_DYNAMIC)
-                       R_UpdateDynamicTexture(glt);
-               if (glt->flags & GLTEXF_UPLOAD)
-               {
-                       CHECKGLERROR
-                       qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
-                       R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
-                       if (glt->inputtexels)
-                       {
-                               Mem_Free(glt->inputtexels);
-                               glt->inputtexels = NULL;
-                               glt->flags |= GLTEXF_DESTROYED;
-                       }
-                       else if (glt->flags & GLTEXF_DESTROYED)
-                               Con_Printf("R_GetTexture: Texture %s already uploaded and destroyed.  Can not upload original image again.  Uploaded blank texture.\n", glt->identifier);
-               }
-
-               return glt->texnum;
-       }
-       else
-               return 0;
-}
-
-static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
+static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, int miplevel, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
 {
        int i, size;
        gltexture_t *glt;
        gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
-       textypeinfo_t *texinfo;
+       textypeinfo_t *texinfo, *texinfo2;
 
        if (cls.state == ca_dedicated)
                return NULL;
@@ -1029,11 +977,30 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
                break;
        case TEXTYPE_SHADOWMAP:
                break;
+       case TEXTYPE_DXT1:
+               break;
+       case TEXTYPE_DXT1A:
+       case TEXTYPE_DXT3:
+       case TEXTYPE_DXT5:
+               flags |= TEXF_ALPHA;
+               break;
+       case TEXTYPE_ALPHA:
+               flags |= TEXF_ALPHA;
+               break;
+       case TEXTYPE_COLORBUFFER:
+               flags |= TEXF_ALPHA;
+               break;
        default:
                Host_Error("R_LoadTexture: unknown texture type");
        }
 
-       glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
+       texinfo2 = R_GetTexTypeInfo(textype, flags);
+       if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
+               texinfo = texinfo2;
+       else
+               Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
+
+       glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
        if (identifier)
                strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
        glt->pool = pool;
@@ -1042,7 +1009,8 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        glt->inputwidth = width;
        glt->inputheight = height;
        glt->inputdepth = depth;
-       glt->flags = flags | GLTEXF_UPLOAD;
+       glt->flags = flags;
+       glt->miplevel = (miplevel < 0) ? R_PicmipForFlags(flags) : miplevel; // note: if miplevel is -1, we know the texture is in original size and we can picmip it normally
        glt->textype = texinfo;
        glt->texturetype = texturetype;
        glt->inputdatasize = size;
@@ -1053,12 +1021,13 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        glt->bytesperpixel = texinfo->internalbytesperpixel;
        glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
        glt->texnum = 0;
+       glt->dirty = false;
+       glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
        // init the dynamic texture attributes, too [11/22/2007 Black]
-       glt->dirtytexnum = 0;
        glt->updatecallback = NULL;
        glt->updatacallback_data = NULL;
 
-       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
+       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
 
        // upload the texture
        // data may be NULL (blank texture for dynamic rendering)
@@ -1074,24 +1043,24 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        return (rtexture_t *)glt;
 }
 
-rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
 {
-       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
+       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, miplevel, textype, GLTEXTURETYPE_2D, data, palette);
 }
 
-rtexture_t *R_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
 {
-       return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
+       return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, miplevel, textype, GLTEXTURETYPE_3D, data, palette);
 }
 
-rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
 {
-       return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
+       return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, miplevel, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
 }
 
-rtexture_t *R_LoadTextureRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTextureRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
 {
-       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
+       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, miplevel, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
 }
 
 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
@@ -1108,17 +1077,405 @@ static int R_ShadowMapTextureFlags(int precision, qboolean filter)
 
 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
 {
-       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
+       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
 }
 
 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
 {
-       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
+       return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
 }
 
 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
 {
-    return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
+    return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), -1, TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
+}
+
+int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
+{
+       gltexture_t *glt = (gltexture_t *)rt;
+       unsigned char *dds;
+       int oldbindtexnum;
+       int bytesperpixel = 0;
+       int bytesperblock = 0;
+       int dds_flags;
+       int dds_format_flags;
+       int dds_caps1;
+       int dds_caps2;
+       int ret;
+       int mip;
+       int mipmaps;
+       int mipinfo[16][4];
+       int ddssize = 128;
+       GLint internalformat;
+       const char *ddsfourcc;
+       if (!rt)
+               return -1; // NULL pointer
+       if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
+               return -2; // broken driver - crashes on reading internal format
+       if (!qglGetTexLevelParameteriv)
+               return -2;
+       GL_ActiveTexture(0);
+       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+       qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+       qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
+       switch(internalformat)
+       {
+       default: ddsfourcc = NULL;bytesperpixel = 4;break;
+       case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+       case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
+       case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
+       case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
+       }
+       if (!bytesperblock && skipuncompressed)
+               return -3; // skipped
+       memset(mipinfo, 0, sizeof(mipinfo));
+       mipinfo[0][0] = glt->tilewidth;
+       mipinfo[0][1] = glt->tileheight;
+       mipmaps = 1;
+       if (glt->flags & TEXF_MIPMAP)
+       {
+               for (mip = 1;mip < 16;mip++)
+               {
+                       mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
+                       mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
+                       if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
+                       {
+                               mip++;
+                               break;
+                       }
+               }
+               mipmaps = mip;
+       }
+       for (mip = 0;mip < mipmaps;mip++)
+       {
+               mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
+               mipinfo[mip][3] = ddssize;
+               ddssize += mipinfo[mip][2];
+       }
+       dds = Mem_Alloc(tempmempool, ddssize);
+       if (!dds)
+               return -4;
+       dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
+       dds_caps2 = 0;
+       if (bytesperblock)
+       {
+               dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
+               dds_format_flags = 0x4; // DDPF_FOURCC
+       }
+       else
+       {
+               dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
+               dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
+       }
+       if (mipmaps)
+       {
+               dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
+               dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
+       }
+       memcpy(dds, "DDS ", 4);
+       StoreLittleLong(dds+4, ddssize);
+       StoreLittleLong(dds+8, dds_flags);
+       StoreLittleLong(dds+12, mipinfo[0][1]); // height
+       StoreLittleLong(dds+16, mipinfo[0][0]); // width
+       StoreLittleLong(dds+24, 1); // depth
+       StoreLittleLong(dds+28, mipmaps); // mipmaps
+       StoreLittleLong(dds+76, 32); // format size
+       StoreLittleLong(dds+80, dds_format_flags);
+       StoreLittleLong(dds+108, dds_caps1);
+       StoreLittleLong(dds+112, dds_caps2);
+       if (bytesperblock)
+       {
+               StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
+               memcpy(dds+84, ddsfourcc, 4);
+               for (mip = 0;mip < mipmaps;mip++)
+               {
+                       qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
+               }
+       }
+       else
+       {
+               StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
+               StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
+               dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
+               for (mip = 0;mip < mipmaps;mip++)
+               {
+                       qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
+               }
+       }
+       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+       ret = FS_WriteFile(filename, dds, ddssize);
+       Mem_Free(dds);
+       return ret ? ddssize : -5;
+}
+
+rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor, int miplevel) // DDS textures are opaque, so miplevel isn't a pointer but just seen as a hint
+{
+       int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
+       //int dds_flags;
+       textype_t textype;
+       int bytesperblock, bytesperpixel;
+       int mipcomplete;
+       gltexture_t *glt;
+       gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
+       textypeinfo_t *texinfo;
+       int mip, mipwidth, mipheight, mipsize;
+       unsigned int c;
+       GLint oldbindtexnum;
+       const unsigned char *mippixels, *ddspixels;
+       unsigned char *dds;
+       fs_offset_t ddsfilesize;
+       unsigned int ddssize;
+
+       if (cls.state == ca_dedicated)
+               return NULL;
+
+       dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
+       ddssize = ddsfilesize;
+
+       if (!dds)
+       {
+               Log_Printf("ddstexturefailures.log", "%s\n", filename);
+               return NULL; // not found
+       }
+
+       if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
+       {
+               Mem_Free(dds);
+               Con_Printf("^1%s: not a DDS image\n", filename);
+               return NULL;
+       }
+
+       //dds_flags = BuffLittleLong(dds+8);
+       dds_format_flags = BuffLittleLong(dds+80);
+       dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
+       dds_width = BuffLittleLong(dds+16);
+       dds_height = BuffLittleLong(dds+12);
+       ddspixels = dds + 128;
+
+       //flags &= ~TEXF_ALPHA; // disabled, as we DISABLE TEXF_ALPHA in the alpha detection, not enable it!
+       if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
+       {
+               // very sloppy BGRA 32bit identification
+               textype = TEXTYPE_BGRA;
+               bytesperblock = 0;
+               bytesperpixel = 4;
+               size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
+               if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+               {
+                       Mem_Free(dds);
+                       Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
+                       return NULL;
+               }
+               // check alpha
+               for (i = 3;i < size;i += 4)
+                       if (ddspixels[i] < 255)
+                               break;
+               if (i >= size)
+                       flags &= ~TEXF_ALPHA;
+       }
+       else if (!memcmp(dds+84, "DXT1", 4))
+       {
+               // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
+               // LordHavoc: it is my belief that this does not infringe on the
+               // patent because it is not decoding pixels...
+               textype = TEXTYPE_DXT1;
+               bytesperblock = 8;
+               bytesperpixel = 0;
+               //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
+               size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+               if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+               {
+                       Mem_Free(dds);
+                       Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
+                       return NULL;
+               }
+               for (i = 0;i < size;i += bytesperblock)
+                       if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
+                               break;
+               if (i < size)
+                       textype = TEXTYPE_DXT1A;
+               else
+                       flags &= ~TEXF_ALPHA;
+       }
+       else if (!memcmp(dds+84, "DXT3", 4))
+       {
+               textype = TEXTYPE_DXT3;
+               bytesperblock = 16;
+               bytesperpixel = 0;
+               size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+               if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+               {
+                       Mem_Free(dds);
+                       Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
+                       return NULL;
+               }
+       }
+       else if (!memcmp(dds+84, "DXT5", 4))
+       {
+               textype = TEXTYPE_DXT5;
+               bytesperblock = 16;
+               bytesperpixel = 0;
+               size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+               if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+               {
+                       Mem_Free(dds);
+                       Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
+                       return NULL;
+               }
+       }
+       else
+       {
+               Mem_Free(dds);
+               Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
+               return NULL;
+       }
+
+       // return whether this texture is transparent
+       if (hasalphaflag)
+               *hasalphaflag = (flags & TEXF_ALPHA) != 0;
+
+       // calculate average color if requested
+       if (avgcolor)
+       {
+               float f;
+               Vector4Clear(avgcolor);
+               if (bytesperblock)
+               {
+                       for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
+                       {
+                               c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
+                               avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
+                               avgcolor[1] += ((c >>  5) & 0x3F) + ((c >> 21) & 0x3F);
+                               avgcolor[2] += ((c      ) & 0x1F) + ((c >> 16) & 0x1F);
+                       }
+                       f = (float)bytesperblock / size;
+                       avgcolor[0] *= (0.5f / 31.0f) * f;
+                       avgcolor[1] *= (0.5f / 63.0f) * f;
+                       avgcolor[2] *= (0.5f / 31.0f) * f;
+                       avgcolor[3] = 1; // too hard to calculate
+               }
+               else
+               {
+                       for (i = 0;i < size;i += 4)
+                       {
+                               avgcolor[0] += ddspixels[i+2];
+                               avgcolor[1] += ddspixels[i+1];
+                               avgcolor[2] += ddspixels[i];
+                               avgcolor[3] += ddspixels[i+3];
+                       }
+                       f = (1.0f / 255.0f) * bytesperpixel / size;
+                       avgcolor[0] *= f;
+                       avgcolor[1] *= f;
+                       avgcolor[2] *= f;
+                       avgcolor[3] *= f;
+               }
+       }
+
+       // this is where we apply gl_picmip
+       mippixels = ddspixels;
+       mipwidth = dds_width;
+       mipheight = dds_height;
+       while(miplevel >= 1 && dds_miplevels >= 1)
+       {
+               if (mipwidth <= 1 && mipheight <= 1)
+                       break;
+               mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+               mippixels += mipsize; // just skip
+               --dds_miplevels;
+               --miplevel;
+               if (mipwidth > 1)
+                       mipwidth >>= 1;
+               if (mipheight > 1)
+                       mipheight >>= 1;
+       }
+
+       // when not requesting mipmaps, do not load them
+       if(!(flags & TEXF_MIPMAP))
+               dds_miplevels = 0;
+
+       if (dds_miplevels >= 1)
+               flags |= TEXF_MIPMAP;
+       else
+               flags &= ~TEXF_MIPMAP;
+
+       // if S3TC is not supported, there's very little we can do about it
+       if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
+       {
+               Mem_Free(dds);
+               Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
+               return NULL;
+       }
+
+       texinfo = R_GetTexTypeInfo(textype, flags);
+
+       glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
+       strlcpy (glt->identifier, filename, sizeof(glt->identifier));
+       glt->pool = pool;
+       glt->chain = pool->gltchain;
+       pool->gltchain = glt;
+       glt->inputwidth = mipwidth;
+       glt->inputheight = mipheight;
+       glt->inputdepth = 1;
+       glt->flags = flags;
+       glt->textype = texinfo;
+       glt->texturetype = GLTEXTURETYPE_2D;
+       glt->inputdatasize = ddssize;
+       glt->glinternalformat = texinfo->glinternalformat;
+       glt->glformat = texinfo->glformat;
+       glt->gltype = texinfo->gltype;
+       glt->bytesperpixel = texinfo->internalbytesperpixel;
+       glt->sides = 1;
+       glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
+       glt->tilewidth = mipwidth;
+       glt->tileheight = mipheight;
+       glt->tiledepth = 1;
+
+       // texture uploading can take a while, so make sure we're sending keepalives
+       CL_KeepaliveMessage(false);
+
+       // upload the texture
+       // we need to restore the texture binding after finishing the upload
+       CHECKGLERROR
+       GL_ActiveTexture(0);
+       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+       qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+       qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+       mipcomplete = false;
+
+       for (mip = 0;mip <= dds_miplevels;mip++) // <= to include the not-counted "largest" miplevel
+       {
+               mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+               if (mippixels + mipsize > dds + ddssize)
+                       break;
+               if (bytesperblock)
+               {
+                       qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
+               }
+               else
+               {
+                       qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
+               }
+               mippixels += mipsize;
+               if (mipwidth <= 1 && mipheight <= 1)
+               {
+                       mipcomplete = true;
+                       break;
+               }
+               if (mipwidth > 1)
+                       mipwidth >>= 1;
+               if (mipheight > 1)
+                       mipheight >>= 1;
+       }
+       if (dds_miplevels >= 1 && !mipcomplete)
+       {
+               // need to set GL_TEXTURE_MAX_LEVEL
+               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
+       }
+       GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
+       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+
+       Mem_Free(dds);
+       return (rtexture_t *)glt;
 }
 
 int R_TextureWidth(rtexture_t *rt)
@@ -1167,30 +1524,34 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in
                        height = glt->tileheight - y;
                if (width < 1 || height < 1)
                        return;
+               glt->dirty = true;
                glt->buffermodified = true;
                output += y*outputskip + x*bpp;
                for (j = 0;j < height;j++, output += outputskip, input += inputskip)
                        memcpy(output, input, width*bpp);
-               if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
-                       R_FlushTexture(rt);
        }
        else
                R_Upload(glt, data, x, y, 0, width, height, 1);
 }
 
-void R_FlushTexture(rtexture_t *rt)
+int R_RealGetTexture(rtexture_t *rt)
 {
-       gltexture_t *glt;
-       if (rt == NULL)
-               Host_Error("R_FlushTexture: no texture supplied");
-
-       // update part of the texture
-       glt = (gltexture_t *)rt;
-
-       if (!glt->buffermodified || !glt->bufferpixels)
-               return;
-       glt->buffermodified = false;
-       R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
+       if (rt)
+       {
+               gltexture_t *glt;
+               glt = (gltexture_t *)rt;
+               if (glt->flags & GLTEXF_DYNAMIC)
+                       R_UpdateDynamicTexture(glt);
+               if (glt->buffermodified && glt->bufferpixels)
+               {
+                       glt->buffermodified = false;
+                       R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
+               }
+               glt->dirty = false;
+               return glt->texnum;
+       }
+       else
+               return 0;
 }
 
 void R_ClearTexture (rtexture_t *rt)
@@ -1199,3 +1560,29 @@ void R_ClearTexture (rtexture_t *rt)
 
        R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
 }
+
+int R_PicmipForFlags(int flags)
+{
+       int miplevel = 0;
+       if(flags & TEXF_PICMIP)
+       {
+               miplevel += gl_picmip.integer;
+               if (flags & TEXF_ISWORLD)
+               {
+                       if (r_picmipworld.integer)
+                               miplevel += gl_picmip_world.integer;
+                       else
+                               miplevel = 0;
+               }
+               else if (flags & TEXF_ISSPRITE)
+               {
+                       if (r_picmipsprites.integer)
+                               miplevel += gl_picmip_sprites.integer;
+                       else
+                               miplevel = 0;
+               }
+               else
+                       miplevel += gl_picmip_other.integer;
+       }
+       return miplevel;
+}