]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_textures.c
fix a mingw warning
[xonotic/darkplaces.git] / gl_textures.c
index 7c1d44c3abe1e771dbe16dbad93f94c4b98343ed..d5685c3285ee3d1f06d8c9e932aa843eba797cf7 100644 (file)
-#include "quakedef.h"
-
-cvar_t r_max_size = {CVAR_SAVE, "r_max_size", "2048"};
-cvar_t r_max_scrapsize = {CVAR_SAVE, "r_max_scrapsize", "256"};
-cvar_t r_picmip = {CVAR_SAVE, "r_picmip", "0"};
-cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1"};
-cvar_t r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1"};
 
-int            gl_filter_min = GL_LINEAR_MIPMAP_LINEAR; //NEAREST;
+#include "quakedef.h"
+#ifdef SUPPORTD3D
+#include <d3d9.h>
+extern LPDIRECT3DDEVICE9 vid_d3d9dev;
+#endif
+#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"};
+cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
+cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
+cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
+cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
+cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
+cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
+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_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;
 
+#ifdef SUPPORTD3D
+int d3d_filter_flatmin = D3DTEXF_LINEAR;
+int d3d_filter_flatmag = D3DTEXF_LINEAR;
+int d3d_filter_flatmix = D3DTEXF_POINT;
+int d3d_filter_mipmin = D3DTEXF_LINEAR;
+int d3d_filter_mipmag = D3DTEXF_LINEAR;
+int d3d_filter_mipmix = D3DTEXF_LINEAR;
+int d3d_filter_nomip = false;
+#endif
+
 
 static mempool_t *texturemempool;
-static mempool_t *texturedatamempool;
-static mempool_t *textureprocessingmempool;
+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
-// texture generated by code, also causes permanent GLTEXF_UPLOAD effect
-#define GLTEXF_PROCEDURAL 0x00020000
 // bitmask for mismatch checking
-#define GLTEXF_IMPORTANTBITS (GLTEXF_PROCEDURAL)
-// set when image is uploaded and freed
-#define GLTEXF_DESTROYED 0x00040000
+#define GLTEXF_IMPORTANTBITS (0)
+// dynamic texture (treat texnum == 0 differently)
+#define GLTEXF_DYNAMIC         0x00080000
 
-// size of images which hold fragment textures, ignores picmip and max_size
-//#define BLOCK_SIZE 256
-static int block_size;
-
-// really this number only governs gltexnuminuse
-#define MAX_GLTEXTURES 65536
-
-// since there is only one set of GL texture numbers, we have to track them
-// globally, everything else is per texture pool
-static qbyte *gltexnuminuse;
-
-typedef struct
+typedef struct textypeinfo_s
 {
-       int textype;
+       textype_t textype;
        int inputbytesperpixel;
        int internalbytesperpixel;
-       int glformat;
+       float glinternalbytesperpixel;
        int glinternalformat;
-       int align;
+       int glformat;
+       int gltype;
 }
 textypeinfo_t;
 
-static textypeinfo_t textype_qpalette       = {TEXTYPE_QPALETTE, 1, 4, GL_RGBA, 3, 1};
-static textypeinfo_t textype_rgb            = {TEXTYPE_RGB     , 3, 3, GL_RGB , 3, 3};
-static textypeinfo_t textype_rgba           = {TEXTYPE_RGBA    , 4, 4, GL_RGBA, 3, 1};
-static textypeinfo_t textype_qpalette_alpha = {TEXTYPE_QPALETTE, 1, 4, GL_RGBA, 4, 1};
-static textypeinfo_t textype_rgba_alpha     = {TEXTYPE_RGBA    , 4, 4, GL_RGBA, 4, 1};
-
-// a tiling texture (most common type)
-#define GLIMAGETYPE_TILE 0
-// a fragments texture (contains one or more fragment textures)
-#define GLIMAGETYPE_FRAGMENTS 1
 
-// a gltextureimage can have one (or more if fragments) gltextures inside
-typedef struct gltextureimage_s
+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
 {
-       struct gltextureimage_s *imagechain;
-       int texturecount;
-       int type; // one of the GLIMAGETYPE_ values
-       int texnum; // GL texture slot number
-       int width, height;
-       int bytesperpixel; // bytes per pixel
-       int glformat; // GL_RGB or GL_RGBA
-       int glinternalformat; // 3 or 4
-       int flags;
-       short *blockallocation; // fragment allocation
+       GLTEXTURETYPE_2D,
+       GLTEXTURETYPE_3D,
+       GLTEXTURETYPE_CUBEMAP,
+       GLTEXTURETYPE_RECTANGLE,
+       GLTEXTURETYPE_TOTAL
 }
-gltextureimage_t;
+gltexturetype_t;
+
+static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
+static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
+static int cubemapside[6] =
+{
+       GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
+       GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
+       GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
+       GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
+       GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
+       GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
+};
 
 typedef struct gltexture_s
 {
+       // 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
+       // d3d stuff the backend needs
+       void *d3dtexture;
+#ifdef SUPPORTD3D
+       int d3dformat;
+       int d3dusage;
+       int d3dpool;
+       int d3daddressu;
+       int d3daddressv;
+       int d3daddressw;
+       int d3dmagfilter;
+       int d3dminfilter;
+       int d3dmipfilter;
+       int d3dmaxmiplevelfilter;
+       int d3dmipmaplodbias;
+       int d3dmaxmiplevel;
+#endif
+
+       // dynamic texture stuff [11/22/2007 Black]
+       updatecallback_t updatecallback;
+       void *updatacallback_data;
+       // --- [11/22/2007 Black]
+
+       // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
+       unsigned char *bufferpixels;
+       qboolean buffermodified;
+
        // pointer to texturepool (check this to see if the texture is allocated)
        struct gltexturepool_s *pool;
        // pointer to next texture in texturepool chain
        struct gltexture_s *chain;
-       // pointer into gltextureimage array
-       gltextureimage_t *image;
        // name of the texture (this might be removed someday), no duplicates
-       char *identifier;
-       // location in the image, and size
-       int x, y, width, height;
-       // copy of the original texture supplied to the upload function, for re-uploading or deferred uploads (non-precached)
-       qbyte *inputtexels;
-       // to identify cache mismatchs (this might be removed someday)
-       int crc;
-       // flags supplied to the LoadTexture/ProceduralTexture functions
+       char identifier[MAX_QPATH + 32];
+       // original data size in *inputtexels
+       int inputwidth, inputheight, inputdepth;
+       // copy of the original texture(s) supplied to the upload function, for
+       // delayed uploads (non-precached)
+       unsigned char *inputtexels;
+       // original data size in *inputtexels
+       int inputdatasize;
+       // flags supplied to the LoadTexture function
        // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
        int flags;
-       // procedural texture generation function, called once per frame if the texture is used
-       int (*generate)(qbyte *buffer, int width, int height, void *parameterdata, int parameterdatasize);
-       // data provided to generate, persistent from call to call
-       qbyte *proceduraldata;
-       // size of data
-       int proceduraldatasize;
-       // used only to avoid updating the texture more than once per frame
-       int proceduralframecount;
+       // picmip level
+       int miplevel;
        // pointer to one of the textype_ structs
        textypeinfo_t *textype;
+       // one of the GLTEXTURETYPE_ values
+       int texturetype;
+       // palette if the texture is TEXTYPE_PALETTE
+       const unsigned int *palette;
+       // actual stored texture size after gl_picmip and gl_max_size are applied
+       // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
+       int tilewidth, tileheight, tiledepth;
+       // 1 or 6 depending on texturetype
+       int sides;
+       // how many mipmap levels in this texture
+       int miplevels;
+       // bytes per pixel
+       int bytesperpixel;
+       // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
+       int glformat;
+       // 3 or 4
+       int glinternalformat;
+       // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
+       int gltype;
 }
 gltexture_t;
 
@@ -109,8 +190,7 @@ gltexture_t;
 
 typedef struct gltexturepool_s
 {
-       int sentinel;
-       struct gltextureimage_s *imagechain;
+       unsigned int sentinel;
        struct gltexture_s *gltchain;
        struct gltexturepool_s *next;
 }
@@ -118,147 +198,108 @@ gltexturepool_t;
 
 static gltexturepool_t *gltexturepoolchain = NULL;
 
-static qbyte *resizebuffer = NULL, *colorconvertbuffer;
+static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
 static int resizebuffersize = 0;
-static qbyte *texturebuffer;
-static int texturebuffersize = 0;
-
-static int realmaxsize = 0;
+static const unsigned char *texturebuffer;
 
-static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
+static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
 {
-       if (flags & TEXF_ALPHA)
+       switch(textype)
        {
-               switch(textype)
-               {
-               case TEXTYPE_QPALETTE:
-                       return &textype_qpalette_alpha;
-               case TEXTYPE_RGB:
-                       Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed\n");
-                       return NULL;
-               case TEXTYPE_RGBA:
-                       return &textype_rgba_alpha;
-               default:
-                       Host_Error("R_GetTexTypeInfo: unknown texture format\n");
-                       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;
        }
-       else
+       return NULL;
+}
+
+// dynamic texture code [11/22/2007 Black]
+void R_MarkDirtyTexture(rtexture_t *rt) {
+       gltexture_t *glt = (gltexture_t*) rt;
+       if( !glt ) {
+               return;
+       }
+
+       // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
+       if (glt->flags & GLTEXF_DYNAMIC)
        {
-               switch(textype)
-               {
-               case TEXTYPE_QPALETTE:
-                       return &textype_qpalette;
-               case TEXTYPE_RGB:
-                       return &textype_rgb;
-               case TEXTYPE_RGBA:
-                       return &textype_rgba;
-               default:
-                       Host_Error("R_GetTexTypeInfo: unknown texture format\n");
-                       return NULL;
-               }
+               // mark it as dirty, so R_RealGetTexture gets called
+               glt->dirty = true;
        }
 }
 
-static void R_UploadTexture(gltexture_t *t);
+void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
+       gltexture_t *glt = (gltexture_t*) rt;
+       if( !glt ) {
+               return;
+       }
+
+       glt->flags |= GLTEXF_DYNAMIC;
+       glt->updatecallback = updatecallback;
+       glt->updatacallback_data = data;
+}
 
-static void R_PrecacheTexture(gltexture_t *glt)
-{
-       int precache;
-       precache = false;
-       if (glt->flags & TEXF_ALWAYSPRECACHE)
-               precache = true;
-       else if (r_precachetextures.integer >= 2)
-               precache = true;
-       else if (r_precachetextures.integer >= 1)
-               if (glt->flags & TEXF_PRECACHE)
-                       precache = true;
-
-       if (precache)
-               R_UploadTexture(glt);
+static void R_UpdateDynamicTexture(gltexture_t *glt) {
+       glt->dirty = false;
+       if( glt->updatecallback ) {
+               glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
+       }
 }
 
-int R_GetTexture(rtexture_t *rt)
+void R_PurgeTexture(rtexture_t *rt)
 {
-       gltexture_t *glt;
-       if (!rt)
-               return 0;
-       glt = (gltexture_t *)rt;
-       if (glt->flags & (GLTEXF_UPLOAD | GLTEXF_PROCEDURAL))
-       {
-               if (glt->flags & GLTEXF_PROCEDURAL)
-               {
-                       if (glt->proceduralframecount != r_framecount)
-                       {
-                               glt->proceduralframecount = r_framecount;
-                               R_UploadTexture(glt);
-                       }
-               }
-               else
-                       R_UploadTexture(glt);
+       if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
+               R_FreeTexture(rt);
        }
-       return glt->image->texnum;
 }
 
-static void R_FreeTexture(gltexture_t *glt)
+void R_FreeTexture(rtexture_t *rt)
 {
-       gltexture_t **gltpointer;
-       gltextureimage_t *image, **gltimagepointer;
-       GLuint texnum;
+       gltexture_t *glt, **gltpointer;
 
+       glt = (gltexture_t *)rt;
        if (glt == NULL)
-               Host_Error("R_FreeTexture: texture == NULL\n");
+               Host_Error("R_FreeTexture: texture == NULL");
 
        for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
        if (*gltpointer == glt)
                *gltpointer = glt->chain;
        else
-               Host_Error("R_FreeTexture: texture \"%s\" not linked in pool\n", glt->identifier);
-
-       // note: if freeing a fragment texture, this will not make the claimed
-       // space available for new textures unless all other fragments in the
-       // image are also freed
-       image = glt->image;
-       image->texturecount--;
-       if (image->texturecount < 1)
-       {
-               for (gltimagepointer = &glt->pool->imagechain;*gltimagepointer && *gltimagepointer != image;gltimagepointer = &(*gltimagepointer)->imagechain);
-               if (*gltimagepointer == image)
-                       *gltimagepointer = image->imagechain;
-               else
-                       Host_Error("R_FreeTexture: image not linked in pool\n");
-               if (image->texnum)
-               {
-                       texnum = image->texnum;
-                       gltexnuminuse[image->texnum] = 0;
-                       glDeleteTextures(1, &texnum);
-               }
-               if (image->blockallocation)
-                       Mem_Free(image->blockallocation);
-               Mem_Free(image);
+               Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
+
+       if (glt->texnum)
+       {
+               CHECKGLERROR
+               qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
        }
 
-       if (glt->identifier)
-               Mem_Free(glt->identifier);
        if (glt->inputtexels)
                Mem_Free(glt->inputtexels);
-       if (glt->proceduraldata)
-               Mem_Free(glt->proceduraldata);
-       Mem_Free(glt);
-}
-
-static gltexture_t *R_FindTexture (gltexturepool_t *pool, char *identifier)
-{
-       gltexture_t     *glt;
-
-       if (!identifier)
-               return NULL;
-
-       for (glt = pool->gltchain;glt;glt = glt->chain)
-               if (glt->identifier && !strcmp (identifier, glt->identifier))
-                       return glt;
-
-       return NULL;
+       Mem_ExpandableArray_FreeRecord(&texturearray, glt);
 }
 
 rtexturepool_t *R_AllocTexturePool(void)
@@ -266,10 +307,9 @@ rtexturepool_t *R_AllocTexturePool(void)
        gltexturepool_t *pool;
        if (texturemempool == NULL)
                return NULL;
-       pool = Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
+       pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
        if (pool == NULL)
                return NULL;
-       //memset(pool, 0, sizeof(gltexturepool_t));
        pool->next = gltexturepoolchain;
        gltexturepoolchain = pool;
        pool->sentinel = TEXTUREPOOL_SENTINEL;
@@ -286,28 +326,26 @@ void R_FreeTexturePool(rtexturepool_t **rtexturepool)
        pool = (gltexturepool_t *)(*rtexturepool);
        *rtexturepool = NULL;
        if (pool->sentinel != TEXTUREPOOL_SENTINEL)
-               Host_Error("R_FreeTexturePool: pool already freed\n");
+               Host_Error("R_FreeTexturePool: pool already freed");
        for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
        if (*poolpointer == pool)
                *poolpointer = pool->next;
        else
-               Host_Error("R_FreeTexturePool: pool not linked\n");
+               Host_Error("R_FreeTexturePool: pool not linked");
        while (pool->gltchain)
-               R_FreeTexture(pool->gltchain);
-       if (pool->imagechain)
-               Sys_Error("R_FreeTexturePool: not all images freed\n");
+               R_FreeTexture((rtexture_t *)pool->gltchain);
        Mem_Free(pool);
 }
 
 
-typedef struct
+typedef struct glmode_s
 {
        char *name;
        int minification, magnification;
 }
 glmode_t;
 
-static glmode_t modes[] =
+static glmode_t modes[6] =
 {
        {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
        {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
@@ -317,792 +355,1852 @@ static glmode_t modes[] =
        {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
 };
 
-extern int gl_backend_rebindtextures;
+#ifdef SUPPORTD3D
+typedef struct d3dmode_s
+{
+       char *name;
+       int m1, m2;
+}
+d3dmode_t;
+
+static d3dmode_t d3dmodes[6] =
+{
+       {"GL_NEAREST", D3DTEXF_POINT, D3DTEXF_POINT},
+       {"GL_LINEAR", D3DTEXF_LINEAR, D3DTEXF_POINT},
+       {"GL_NEAREST_MIPMAP_NEAREST", D3DTEXF_POINT, D3DTEXF_POINT},
+       {"GL_LINEAR_MIPMAP_NEAREST", D3DTEXF_LINEAR, D3DTEXF_POINT},
+       {"GL_NEAREST_MIPMAP_LINEAR", D3DTEXF_POINT, D3DTEXF_LINEAR},
+       {"GL_LINEAR_MIPMAP_LINEAR", D3DTEXF_LINEAR, D3DTEXF_LINEAR}
+};
+#endif
 
 static void GL_TextureMode_f (void)
 {
        int i;
-       gltextureimage_t *image;
+       GLint oldbindtexnum;
+       gltexture_t *glt;
        gltexturepool_t *pool;
 
        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)
                        {
-                               Con_Printf ("%s\n", modes[i].name);
+                               Con_Printf("%s\n", modes[i].name);
                                return;
                        }
                }
-               Con_Print("current filter is unknown???\n");
+               Con_Print("current filter is unknown???\n");
                return;
        }
 
-       for (i = 0;i < 6;i++)
-               if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
+       for (i = 0;i < (int)(sizeof(modes)/sizeof(*modes));i++)
+               if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
                        break;
        if (i == 6)
        {
-               Con_Print("bad filter name\n");
+               Con_Print("bad filter name\n");
                return;
        }
 
        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?
-       for (pool = gltexturepoolchain;pool;pool = pool->next)
+       switch(vid.renderpath)
        {
-               for (image = pool->imagechain;image;image = image->imagechain)
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               // change all the existing mipmap texture objects
+               // FIXME: force renderer(/client/something?) restart instead?
+               CHECKGLERROR
+               GL_ActiveTexture(0);
+               for (pool = gltexturepoolchain;pool;pool = pool->next)
                {
-                       // only update already uploaded images
-                       if (!(image->flags & GLTEXF_UPLOAD))
+                       for (glt = pool->gltchain;glt;glt = glt->chain)
                        {
-                               glBindTexture(GL_TEXTURE_2D, image->texnum);
-                               if (image->flags & TEXF_MIPMAP)
-                                       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
-                               else
-                                       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
-                               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
+                               // only update already uploaded images
+                               if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
+                               {
+                                       oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+                                       qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+                                       if (glt->flags & TEXF_MIPMAP)
+                                       {
+                                               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
+                                       }
+                                       else
+                                       {
+                                               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
+                                       }
+                                       qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
+                                       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+                               }
+                       }
+               }
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               d3d_filter_flatmin = d3dmodes[i].m1;
+               d3d_filter_flatmag = d3dmodes[i].m1;
+               d3d_filter_flatmix = D3DTEXF_POINT;
+               d3d_filter_mipmin = d3dmodes[i].m1;
+               d3d_filter_mipmag = d3dmodes[i].m1;
+               d3d_filter_mipmix = d3dmodes[i].m2;
+               d3d_filter_nomip = i < 2;
+               if (gl_texture_anisotropy.integer > 1 && i == 5)
+                       d3d_filter_mipmin = d3d_filter_mipmag = D3DTEXF_ANISOTROPIC;
+               for (pool = gltexturepoolchain;pool;pool = pool->next)
+               {
+                       for (glt = pool->gltchain;glt;glt = glt->chain)
+                       {
+                               // only update already uploaded images
+                               if (glt->d3dtexture && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
+                               {
+                                       if (glt->flags & TEXF_MIPMAP)
+                                       {
+                                               glt->d3dminfilter = d3d_filter_mipmin;
+                                               glt->d3dmagfilter = d3d_filter_mipmag;
+                                               glt->d3dmipfilter = d3d_filter_mipmix;
+                                               glt->d3dmaxmiplevelfilter = 0;
+                                       }
+                                       else
+                                       {
+                                               glt->d3dminfilter = d3d_filter_flatmin;
+                                               glt->d3dmagfilter = d3d_filter_flatmag;
+                                               glt->d3dmipfilter = d3d_filter_flatmix;
+                                               glt->d3dmaxmiplevelfilter = 0;
+                                       }
+                               }
                        }
                }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
-       gl_backend_rebindtextures = true;
 }
 
-static int R_CalcTexelDataSize (gltexture_t *glt)
+static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth, int *outmiplevels)
 {
-       int width2, height2, size;
-       if (glt->flags & TEXF_FRAGMENT)
-               size = glt->width * glt->height;
-       else
+       int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1, miplevels = 1;
+
+       switch (texturetype)
        {
-               if (r_max_size.integer > realmaxsize)
-                       Cvar_SetValue("r_max_size", realmaxsize);
-               // calculate final size
-               for (width2 = 1;width2 < glt->width;width2 <<= 1);
-               for (height2 = 1;height2 < glt->height;height2 <<= 1);
-               for (width2 >>= r_picmip.integer;width2 > r_max_size.integer;width2 >>= 1);
-               for (height2 >>= r_picmip.integer;height2 > r_max_size.integer;height2 >>= 1);
-               if (width2 < 1) width2 = 1;
-               if (height2 < 1) height2 = 1;
-
-               size = 0;
-               if (glt->flags & TEXF_MIPMAP)
+       default:
+       case GLTEXTURETYPE_2D:
+               maxsize = vid.maxtexturesize_2d;
+               if (flags & TEXF_PICMIP)
                {
-                       while (width2 > 1 || height2 > 1)
-                       {
-                               size += width2 * height2;
-                               if (width2 > 1)
-                                       width2 >>= 1;
-                               if (height2 > 1)
-                                       height2 >>= 1;
-                       }
-                       size++; // count the last 1x1 mipmap
+                       maxsize = bound(1, gl_max_size.integer, maxsize);
+                       picmip = miplevel;
+               }
+               break;
+       case GLTEXTURETYPE_3D:
+               maxsize = vid.maxtexturesize_3d;
+               break;
+       case GLTEXTURETYPE_CUBEMAP:
+               maxsize = vid.maxtexturesize_cubemap;
+               break;
+       }
+
+       if (outwidth)
+       {
+               if (vid.support.arb_texture_non_power_of_two)
+                       width2 = min(inwidth >> picmip, maxsize);
+               else
+               {
+                       for (width2 = 1;width2 < inwidth;width2 <<= 1);
+                       for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
+               }
+               *outwidth = max(1, width2);
+       }
+       if (outheight)
+       {
+               if (vid.support.arb_texture_non_power_of_two)
+                       height2 = min(inheight >> picmip, maxsize);
+               else
+               {
+                       for (height2 = 1;height2 < inheight;height2 <<= 1);
+                       for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
                }
+               *outheight = max(1, height2);
+       }
+       if (outdepth)
+       {
+               if (vid.support.arb_texture_non_power_of_two)
+                       depth2 = min(indepth >> picmip, maxsize);
                else
-                       size = width2*height2;
+               {
+                       for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
+                       for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
+               }
+               *outdepth = max(1, depth2);
        }
-       size *= glt->textype->internalbytesperpixel;
 
-       return size;
+       miplevels = 1;
+       if (flags & TEXF_MIPMAP)
+       {
+               int extent = max(width2, max(height2, depth2));
+               while(extent >>= 1)
+                       miplevels++;
+       }
+       if (outmiplevels)
+               *outmiplevels = miplevels;
 }
 
-void R_TextureStats_PrintTotal(void)
+
+static int R_CalcTexelDataSize (gltexture_t *glt)
 {
-       int glsize, inputsize, total = 0, totalt = 0, totalp = 0, loaded = 0, loadedt = 0, loadedp = 0;
-       gltexture_t *glt;
-       gltexturepool_t *pool;
-       for (pool = gltexturepoolchain;pool;pool = pool->next)
+       int width2, height2, depth2, size;
+
+       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2, NULL);
+
+       size = width2 * height2 * depth2;
+
+       if (glt->flags & TEXF_MIPMAP)
        {
-               for (glt = pool->gltchain;glt;glt = glt->chain)
+               while (width2 > 1 || height2 > 1 || depth2 > 1)
                {
-                       glsize = R_CalcTexelDataSize(glt);
-                       inputsize = glt->width * glt->height * glt->textype->inputbytesperpixel;
-
-                       total++;
-                       totalt += glsize;
-                       totalp += inputsize;
-                       if (!(glt->flags & GLTEXF_UPLOAD))
-                       {
-                               loaded++;
-                               loadedt += glsize;
-                               loadedp += inputsize;
-                       }
+                       if (width2 > 1)
+                               width2 >>= 1;
+                       if (height2 > 1)
+                               height2 >>= 1;
+                       if (depth2 > 1)
+                               depth2 >>= 1;
+                       size += width2 * height2 * depth2;
                }
        }
-       Con_Printf("total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", total, totalt / 1048576.0, totalp / 1048576.0, loaded, loadedt / 1048576.0, loadedp / 1048576.0, total - loaded, (totalt - loadedt) / 1048576.0, (totalp - loadedp) / 1048576.0);
+
+       return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
 }
 
-static void R_TextureStats_f(void)
+void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
 {
-       int loaded;
+       int glsize;
+       int isloaded;
+       int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
+       int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
        gltexture_t *glt;
        gltexturepool_t *pool;
-       Con_Printf("glsize input crc  loaded mip alpha name\n");
+       if (printeach)
+               Con_Print("glsize input loaded mip alpha name\n");
        for (pool = gltexturepoolchain;pool;pool = pool->next)
        {
+               pooltotal = 0;
+               pooltotalt = 0;
+               pooltotalp = 0;
+               poolloaded = 0;
+               poolloadedt = 0;
+               poolloadedp = 0;
                for (glt = pool->gltchain;glt;glt = glt->chain)
                {
-                       loaded = !(glt->flags & GLTEXF_UPLOAD);
-                       if (glt->flags & GLTEXF_PROCEDURAL)
-                               Con_Printf("%c%4i%c %4i  PROC %s %s %s %s\n"  , loaded ? '[' : ' ', (R_CalcTexelDataSize(glt) + 1023) / 1024, loaded ? ']' : ' ',                               (glt->width * glt->height * glt->textype->inputbytesperpixel + 1023) / 1024,                                         loaded ? "loaded" : "      ", (glt->flags & TEXF_MIPMAP) ? "mip" : "   ", (glt->flags & TEXF_ALPHA) ? "alpha" : "     ", glt->identifier ? glt->identifier : "<unnamed>");
-                       else
-                               Con_Printf("%c%4i%c%c%4i%c %04X %s %s %s %s\n", loaded ? '[' : ' ', (R_CalcTexelDataSize(glt) + 1023) / 1024, loaded ? ']' : ' ', glt->inputtexels ? '[' : ' ', (glt->width * glt->height * glt->textype->inputbytesperpixel + 1023) / 1024, glt->inputtexels ? ']' : ' ', glt->crc, loaded ? "loaded" : "      ", (glt->flags & TEXF_MIPMAP) ? "mip" : "   ", (glt->flags & TEXF_ALPHA) ? "alpha" : "     ", glt->identifier ? glt->identifier : "<unnamed>");
+                       glsize = R_CalcTexelDataSize(glt);
+                       isloaded = glt->texnum != 0;
+                       pooltotal++;
+                       pooltotalt += glsize;
+                       pooltotalp += glt->inputdatasize;
+                       if (isloaded)
+                       {
+                               poolloaded++;
+                               poolloadedt += glsize;
+                               poolloadedp += glt->inputdatasize;
+                       }
+                       if (printeach)
+                               Con_Printf("%c%4i%c%c%4i%c %s %s %s %s\n", isloaded ? '[' : ' ', (glsize + 1023) / 1024, isloaded ? ']' : ' ', glt->inputtexels ? '[' : ' ', (glt->inputdatasize + 1023) / 1024, glt->inputtexels ? ']' : ' ', isloaded ? "loaded" : "      ", (glt->flags & TEXF_MIPMAP) ? "mip" : "   ", (glt->flags & TEXF_ALPHA) ? "alpha" : "     ", glt->identifier);
                }
-               Con_Printf("pool %10p\n", pool);
+               if (printpool)
+                       Con_Printf("texturepool %10p total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", (void *)pool, pooltotal, pooltotalt / 1048576.0, pooltotalp / 1048576.0, poolloaded, poolloadedt / 1048576.0, poolloadedp / 1048576.0, pooltotal - poolloaded, (pooltotalt - poolloadedt) / 1048576.0, (pooltotalp - poolloadedp) / 1048576.0);
+               sumtotal += pooltotal;
+               sumtotalt += pooltotalt;
+               sumtotalp += pooltotalp;
+               sumloaded += poolloaded;
+               sumloadedt += poolloadedt;
+               sumloadedp += poolloadedp;
        }
-       R_TextureStats_PrintTotal();
+       if (printtotal)
+               Con_Printf("textures total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", sumtotal, sumtotalt / 1048576.0, sumtotalp / 1048576.0, sumloaded, sumloadedt / 1048576.0, sumloadedp / 1048576.0, sumtotal - sumloaded, (sumtotalt - sumloadedt) / 1048576.0, (sumtotalp - sumloadedp) / 1048576.0);
 }
 
-char engineversion[40];
+static void R_TextureStats_f(void)
+{
+       R_TextureStats_Print(true, true, true);
+}
 
 static void r_textures_start(void)
 {
-       // deal with size limits of various drivers (3dfx in particular)
-       glGetIntegerv(GL_MAX_TEXTURE_SIZE, &realmaxsize);
-       CHECKGLERROR
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               // LordHavoc: allow any alignment
+               CHECKGLERROR
+               qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
+               qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       }
 
-       // use the largest scrap texture size we can (not sure if this is really a good idea)
-       for (block_size = 1;block_size < realmaxsize && block_size < r_max_scrapsize.integer;block_size <<= 1);
+       texturemempool = Mem_AllocPool("texture management", 0, NULL);
+       Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
 
-       texturemempool = Mem_AllocPool("Texture Info");
-       texturedatamempool = Mem_AllocPool("Texture Storage (not yet uploaded)");
-       textureprocessingmempool = Mem_AllocPool("Texture Processing Buffers");
-       gltexnuminuse = Mem_Alloc(texturemempool, MAX_GLTEXTURES);
-       //memset(gltexnuminuse, 0, MAX_GLTEXTURES);
+       // Disable JPEG screenshots if the DLL isn't loaded
+       if (! JPEG_OpenLibrary ())
+               Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
+       if (! PNG_OpenLibrary ())
+               Cvar_SetValueQuick (&scr_screenshot_png, 0);
 }
 
 static void r_textures_shutdown(void)
 {
        rtexturepool_t *temp;
+
+       JPEG_CloseLibrary ();
+
        while(gltexturepoolchain)
        {
                temp = (rtexturepool_t *) gltexturepoolchain;
                R_FreeTexturePool(&temp);
        }
 
-       /*
-       if (resizebuffer) Mem_Free(resizebuffer);resizebuffer = NULL;
-       if (colorconvertbuffer) Mem_Free(colorconvertbuffer);colorconvertbuffer = NULL;
-       if (texturebuffer) Mem_Free(texturebuffer);texturebuffer = NULL;
-       if (gltexnuminuse) Mem_Free(gltexnuminuse);gltexnuminuse = NULL;
-       */
        resizebuffersize = 0;
-       texturebuffersize = 0;
        resizebuffer = NULL;
        colorconvertbuffer = NULL;
        texturebuffer = NULL;
-       gltexnuminuse = NULL;
+       Mem_ExpandableArray_FreeArray(&texturearray);
        Mem_FreePool(&texturemempool);
-       Mem_FreePool(&texturedatamempool);
-       Mem_FreePool(&textureprocessingmempool);
 }
 
 static void r_textures_newmap(void)
 {
 }
 
-void R_Textures_Init (void)
+static void r_textures_devicelost(void)
 {
-       Cmd_AddCommand ("gl_texturemode", &GL_TextureMode_f);
-       Cmd_AddCommand("r_texturestats", R_TextureStats_f);
-       Cvar_RegisterVariable (&r_max_scrapsize);
-       Cvar_RegisterVariable (&r_max_size);
-       Cvar_RegisterVariable (&r_picmip);
-       Cvar_RegisterVariable (&r_lerpimages);
-       Cvar_RegisterVariable (&r_precachetextures);
-       gltexnuminuse = NULL;
+       int i, endindex;
+       gltexture_t *glt;
+       endindex = Mem_ExpandableArray_IndexRange(&texturearray);
+       for (i = 0;i < endindex;i++)
+       {
+               glt = (gltexture_t *) Mem_ExpandableArray_RecordAtIndex(&texturearray, i);
+               if (!glt || !(glt->flags & TEXF_RENDERTARGET))
+                       continue;
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       break;
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       if (glt->tiledepth > 1)
+                               IDirect3DVolumeTexture9_Release((IDirect3DVolumeTexture9 *)glt->d3dtexture);
+                       else if (glt->sides == 6)
+                               IDirect3DCubeTexture9_Release((IDirect3DCubeTexture9 *)glt->d3dtexture);
+                       else
+                               IDirect3DTexture9_Release((IDirect3DTexture9 *)glt->d3dtexture);
+                       glt->d3dtexture = NULL;
+#endif
+                       break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               }
+       }
+}
 
-       R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
+static void r_textures_devicerestored(void)
+{
+       int i, endindex;
+       gltexture_t *glt;
+       endindex = Mem_ExpandableArray_IndexRange(&texturearray);
+       for (i = 0;i < endindex;i++)
+       {
+               glt = (gltexture_t *) Mem_ExpandableArray_RecordAtIndex(&texturearray, i);
+               if (!glt || !(glt->flags & TEXF_RENDERTARGET))
+                       continue;
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       break;
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       {
+                               HRESULT d3dresult;
+                               if (glt->tiledepth > 1)
+                               {
+                                       if (FAILED(d3dresult = IDirect3DDevice9_CreateVolumeTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->tiledepth, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DVolumeTexture9 **)&glt->d3dtexture, NULL)))
+                                               Sys_Error("IDirect3DDevice9_CreateVolumeTexture failed!");
+                               }
+                               else if (glt->sides == 6)
+                               {
+                                       if (FAILED(d3dresult = IDirect3DDevice9_CreateCubeTexture(vid_d3d9dev, glt->tilewidth, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DCubeTexture9 **)&glt->d3dtexture, NULL)))
+                                               Sys_Error("IDirect3DDevice9_CreateCubeTexture failed!");
+                               }
+                               else
+                               {
+                                       if (FAILED(d3dresult = IDirect3DDevice9_CreateTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DTexture9 **)&glt->d3dtexture, NULL)))
+                                               Sys_Error("IDirect3DDevice9_CreateTexture failed!");
+                               }
+                       }
+#endif
+                       break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               }
+       }
 }
 
-static void R_Upload(gltexture_t *glt, qbyte *data)
+
+void R_Textures_Init (void)
 {
-       int mip, width, height, internalformat;
-       qbyte *prevbuffer;
-       prevbuffer = data;
+       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);
+       Cvar_RegisterVariable (&gl_texturecompression);
+       Cvar_RegisterVariable (&gl_texturecompression_color);
+       Cvar_RegisterVariable (&gl_texturecompression_normal);
+       Cvar_RegisterVariable (&gl_texturecompression_gloss);
+       Cvar_RegisterVariable (&gl_texturecompression_glow);
+       Cvar_RegisterVariable (&gl_texturecompression_2d);
+       Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
+       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_textures_devicelost, r_textures_devicerestored);
+}
 
-       glBindTexture(GL_TEXTURE_2D, glt->image->texnum);
-       CHECKGLERROR
+void R_Textures_Frame (void)
+{
+       static int old_aniso = 0;
 
-       gl_backend_rebindtextures = true;
+       // could do procedural texture animation here, if we keep track of which
+       // textures were accessed this frame...
 
-       glt->flags &= ~GLTEXF_UPLOAD;
+       // free the resize buffers
+       resizebuffersize = 0;
+       if (resizebuffer)
+       {
+               Mem_Free(resizebuffer);
+               resizebuffer = NULL;
+       }
+       if (colorconvertbuffer)
+       {
+               Mem_Free(colorconvertbuffer);
+               colorconvertbuffer = NULL;
+       }
 
-       if (glt->flags & TEXF_FRAGMENT)
+       if (old_aniso != gl_texture_anisotropy.integer)
        {
-               if (resizebuffersize < glt->image->width * glt->image->height * glt->image->bytesperpixel)
-               {
-                       resizebuffersize = glt->image->width * glt->image->height * glt->image->bytesperpixel;
-                       if (resizebuffer)
-                               Mem_Free(resizebuffer);
-                       if (colorconvertbuffer)
-                               Mem_Free(colorconvertbuffer);
-                       resizebuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
-                       colorconvertbuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
-                       if (!resizebuffer || !colorconvertbuffer)
-                               Host_Error("R_Upload: out of memory\n");
-               }
+               gltexture_t *glt;
+               gltexturepool_t *pool;
+               GLint oldbindtexnum;
+
+               old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
 
-               if (glt->image->flags & GLTEXF_UPLOAD)
+               Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
+
+               switch(vid.renderpath)
                {
-                       Con_DPrintf("uploaded new fragments image\n");
-                       glt->image->flags &= ~GLTEXF_UPLOAD;
-                       memset(resizebuffer, 255, glt->image->width * glt->image->height * glt->image->bytesperpixel);
-                       glTexImage2D (GL_TEXTURE_2D, 0, glt->image->glinternalformat, glt->image->width, glt->image->height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, resizebuffer);
-                       CHECKGLERROR
-                       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
                        CHECKGLERROR
-                       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
-                       CHECKGLERROR
-               }
+                       GL_ActiveTexture(0);
+                       for (pool = gltexturepoolchain;pool;pool = pool->next)
+                       {
+                               for (glt = pool->gltchain;glt;glt = glt->chain)
+                               {
+                                       // only update already uploaded images
+                                       if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
+                                       {
+                                               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
 
-               if (prevbuffer == NULL)
-               {
-                       memset(resizebuffer, 255, glt->width * glt->height * glt->image->bytesperpixel);
-                       prevbuffer = resizebuffer;
-               }
-               else if (glt->textype->textype == TEXTYPE_QPALETTE)
-               {
-                       // promote paletted to RGBA, so we only have to worry about RGB and
-                       // RGBA in the rest of this code
-                       Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, glt->width * glt->height, d_8to24table);
-                       prevbuffer = colorconvertbuffer;
-               }
+                                               qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+                                               qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
 
-               glTexSubImage2D(GL_TEXTURE_2D, 0, glt->x, glt->y, glt->width, glt->height, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
-               CHECKGLERROR
-               return;
+                                               qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+                                       }
+                               }
+                       }
+                       break;
+               case RENDERPATH_D3D9:
+               case RENDERPATH_D3D10:
+               case RENDERPATH_D3D11:
+                       break;
+               }
        }
+}
 
-       glt->image->flags &= ~GLTEXF_UPLOAD;
-
-       // these are rounded up versions of the size to do better resampling
-       for (width = 1;width < glt->width;width <<= 1);
-       for (height = 1;height < glt->height;height <<= 1);
-
-       if (resizebuffersize < width * height * glt->image->bytesperpixel)
+void R_MakeResizeBufferBigger(int size)
+{
+       if (resizebuffersize < size)
        {
-               resizebuffersize = width * height * glt->image->bytesperpixel;
+               resizebuffersize = size;
                if (resizebuffer)
                        Mem_Free(resizebuffer);
                if (colorconvertbuffer)
                        Mem_Free(colorconvertbuffer);
-               resizebuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
-               colorconvertbuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
+               resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
+               colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
                if (!resizebuffer || !colorconvertbuffer)
-                       Host_Error("R_Upload: out of memory\n");
+                       Host_Error("R_Upload: out of memory");
        }
+}
 
-       if (prevbuffer == NULL)
+static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
+{
+       int textureenum = gltexturetypeenums[texturetype];
+       int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
+
+       CHECKGLERROR
+
+       if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
        {
-               width = glt->image->width;
-               height = glt->image->height;
-               memset(resizebuffer, 255, width * height * glt->image->bytesperpixel);
-               prevbuffer = resizebuffer;
+               int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
+               if (gl_texture_anisotropy.integer != aniso)
+                       Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
+               qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
        }
-       else
+       qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
+       qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
+       if (gltexturetypedimensions[texturetype] >= 3)
        {
-               if (glt->textype->textype == TEXTYPE_QPALETTE)
-               {
-                       // promote paletted to RGBA, so we only have to worry about RGB and
-                       // RGBA in the rest of this code
-                       Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, glt->width * glt->height, d_8to24table);
-                       prevbuffer = colorconvertbuffer;
-               }
+               qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
+       }
 
-               if (glt->width != width || glt->height != height)
+       CHECKGLERROR
+       if (!gl_filter_force && flags & TEXF_FORCENEAREST)
+       {
+               if (flags & TEXF_MIPMAP)
                {
-                       Image_Resample(prevbuffer, glt->width, glt->height, resizebuffer, width, height, glt->image->bytesperpixel, r_lerpimages.integer);
-                       prevbuffer = resizebuffer;
+                       qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
                }
-
-               // apply picmip/max_size limitations
-               while (width > glt->image->width || height > glt->image->height)
+               else
                {
-                       Image_MipReduce(prevbuffer, resizebuffer, &width, &height, glt->image->width, glt->image->height, glt->image->bytesperpixel);
-                       prevbuffer = resizebuffer;
+                       qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
                }
+               qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
        }
-
-       // 3 and 4 are converted by the driver to it's preferred format for the current display mode
-       internalformat = 3;
-       if (glt->flags & TEXF_ALPHA)
-               internalformat = 4;
-
-       mip = 0;
-       glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
-       CHECKGLERROR
-       if (glt->flags & TEXF_MIPMAP)
+       else if (!gl_filter_force && flags & TEXF_FORCELINEAR)
        {
-               while (width > 1 || height > 1)
+               if (flags & TEXF_MIPMAP)
                {
-                       Image_MipReduce(prevbuffer, resizebuffer, &width, &height, 1, 1, glt->image->bytesperpixel);
-                       prevbuffer = resizebuffer;
-
-                       glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
-                       CHECKGLERROR
+                       if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
+                       {
+                               qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
+                       }
+                       else
+                       {
+                               qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
+                       }
                }
-
-               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
-               CHECKGLERROR
-               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
-               CHECKGLERROR
+               else
+               {
+                       qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
+               }
+               qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
        }
        else
        {
-               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
-               CHECKGLERROR
-               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
-               CHECKGLERROR
-       }
-}
-
-static void R_FindImageForTexture(gltexture_t *glt)
-{
-       int i, j, best, best2, x, y, w, h;
-       textypeinfo_t *texinfo;
-       gltexturepool_t *pool;
-       gltextureimage_t *image, **imagechainpointer;
-       texinfo = glt->textype;
-       pool = glt->pool;
+               if (flags & TEXF_MIPMAP)
+               {
+                       qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
+               }
+               else
+               {
+                       qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
+               }
+               qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
+       }
 
-       x = 0;
-       y = 0;
-       w = glt->width;
-       h = glt->height;
-       if (glt->flags & TEXF_FRAGMENT)
+       if (textype == TEXTYPE_SHADOWMAP)
        {
-               for (imagechainpointer = &pool->imagechain;*imagechainpointer;imagechainpointer = &(*imagechainpointer)->imagechain)
+               if (vid.support.arb_shadow)
                {
-                       image = *imagechainpointer;
-                       if (image->type != GLIMAGETYPE_FRAGMENTS)
-                               continue;
-                       if (image->glformat != texinfo->glformat || image->glinternalformat != texinfo->glinternalformat)
-                               continue;
-
-                       // got a fragments texture, find a place in it if we can
-                       best = block_size;
-                       for (best = block_size, i = 0;i < block_size - w;i += texinfo->align)
+                       if (flags & TEXF_COMPARE)
                        {
-                               for (best2 = 0, j = 0;j < w;j++)
-                               {
-                                       if (image->blockallocation[i+j] >= best)
-                                               break;
-                                       if (best2 < image->blockallocation[i+j])
-                                               best2 = image->blockallocation[i+j];
-                               }
-                               if (j == w)
-                               {
-                                       // this is a valid spot
-                                       x = i;
-                                       y = best = best2;
-                               }
+                               qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
                        }
-
-                       if (best + h > block_size)
-                               continue;
-
-                       for (i = 0;i < w;i++)
-                               image->blockallocation[x + i] = best + h;
-
-                       glt->x = x;
-                       glt->y = y;
-                       glt->image = image;
-                       image->texturecount++;
-                       return;
+                       else
+                       {
+                               qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
+                       }
+                       qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
                }
-
-               image = Mem_Alloc(texturemempool, sizeof(gltextureimage_t));
-               if (image == NULL)
-                       Sys_Error("R_FindImageForTexture: ran out of memory\n");
-               //memset(image, 0, sizeof(*image));
-               image->type = GLIMAGETYPE_FRAGMENTS;
-               image->width = block_size;
-               image->height = block_size;
-               image->blockallocation = Mem_Alloc(texturemempool, block_size * sizeof(short));
-               memset(image->blockallocation, 0, block_size * sizeof(short));
-
-               x = 0;
-               y = 0;
-               for (i = 0;i < w;i++)
-                       image->blockallocation[x + i] = y + h;
+               qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
        }
-       else
-       {
-               for (imagechainpointer = &pool->imagechain;*imagechainpointer;imagechainpointer = &(*imagechainpointer)->imagechain);
-
-               image = Mem_Alloc(texturemempool, sizeof(gltextureimage_t));
-               if (image == NULL)
-                       Sys_Error("R_FindImageForTexture: ran out of memory\n");
-               //memset(image, 0, sizeof(*image));
-               image->type = GLIMAGETYPE_TILE;
-               image->blockallocation = NULL;
-
-               // calculate final size
-               if (r_max_size.integer > realmaxsize)
-                       Cvar_SetValue("r_max_size", realmaxsize);
-               for (image->width = 1;image->width < glt->width;image->width <<= 1);
-               for (image->height = 1;image->height < glt->height;image->height <<= 1);
-               for (image->width >>= r_picmip.integer;image->width > r_max_size.integer;image->width >>= 1);
-               for (image->height >>= r_picmip.integer;image->height > r_max_size.integer;image->height >>= 1);
-               if (image->width < 1) image->width = 1;
-               if (image->height < 1) image->height = 1;
-       }
-       image->glinternalformat = texinfo->glinternalformat;
-       image->glformat = texinfo->glformat;
-       image->flags = (glt->flags & (TEXF_MIPMAP | TEXF_ALPHA)) | GLTEXF_UPLOAD;
-       image->bytesperpixel = texinfo->internalbytesperpixel;
-       for (i = 1;i < MAX_GLTEXTURES;i++)
-               if (!gltexnuminuse[i])
-                       break;
-       if (i < MAX_GLTEXTURES)
-               gltexnuminuse[image->texnum = i] = true;
-       else
-               Sys_Error("R_FindImageForTexture: ran out of GL textures\n");
-       *imagechainpointer = image;
-       image->texturecount++;
 
-       glt->x = x;
-       glt->y = y;
-       glt->image = image;
+       CHECKGLERROR
 }
 
-// note: R_FindImageForTexture must be called before this
-static void R_UploadTexture (gltexture_t *glt)
+static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
 {
-       if (!(glt->flags & (GLTEXF_UPLOAD | GLTEXF_PROCEDURAL)))
-               return;
+       int i, mip, width, height, depth;
+       GLint oldbindtexnum = 0;
+       const unsigned char *prevbuffer;
+       prevbuffer = data;
 
-       if (glt->flags & GLTEXF_PROCEDURAL)
+       switch(vid.renderpath)
        {
-               if (glt->generate)
-               {
-                       if (texturebuffersize < glt->width * glt->height * glt->textype->inputbytesperpixel)
-                       {
-                               if (texturebuffer)
-                                       Mem_Free(texturebuffer);
-                               texturebuffersize = glt->width * glt->height * glt->textype->inputbytesperpixel;
-                               texturebuffer = Mem_Alloc(textureprocessingmempool, texturebuffersize);
-                       }
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               CHECKGLERROR
 
-                       glt->generate(texturebuffer, glt->width, glt->height, (void *)glt->proceduraldata, glt->proceduraldatasize);
-               }
+               // we need to restore the texture binding after finishing the upload
+               GL_ActiveTexture(0);
+               oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+               qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+               break;
+       }
+
+       // these are rounded up versions of the size to do better resampling
+       if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
+       {
+               width = glt->inputwidth;
+               height = glt->inputheight;
+               depth = glt->inputdepth;
        }
        else
        {
-               R_Upload(glt, glt->inputtexels);
-               if (glt->inputtexels)
-               {
-                       Mem_Free(glt->inputtexels);
-                       glt->inputtexels = NULL;
-                       glt->flags |= GLTEXF_DESTROYED;
-               }
-               else if (glt->flags & GLTEXF_DESTROYED)
-                       Con_Printf("R_UploadTexture: Texture %s already uploaded and destroyed.  Can not upload original image again.  Uploaded blank texture.\n", glt->identifier);
+               for (width  = 1;width  < glt->inputwidth ;width  <<= 1);
+               for (height = 1;height < glt->inputheight;height <<= 1);
+               for (depth  = 1;depth  < glt->inputdepth ;depth  <<= 1);
        }
-}
 
-static gltexture_t *R_SetupTexture(gltexturepool_t *pool, char *identifier, int crc, int width, int height, int flags, textypeinfo_t *texinfo, qbyte *data, int (*generate)(qbyte *buffer, int width, int height, void *proceduraldata, int proceduraldatasize), void *proceduraldata, int proceduraldatasize)
-{
-       gltexture_t *glt;
-       glt = Mem_Alloc(texturemempool, sizeof(gltexture_t));
-       //memset(glt, 0, sizeof(gltexture_t));
-       if (identifier)
+       R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
+       R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
+
+       if (prevbuffer == NULL)
        {
-               glt->identifier = Mem_Alloc(texturemempool, strlen(identifier)+1);
-               strcpy (glt->identifier, identifier);
+               memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
+               prevbuffer = resizebuffer;
        }
-       else
-               glt->identifier = NULL;
-       glt->pool = pool;
-       glt->chain = pool->gltchain;
-       pool->gltchain = glt;
-       glt->crc = crc;
-       glt->width = width;
-       glt->height = height;
-       glt->flags = flags;
-       glt->textype = texinfo;
-
-       if (data)
+       else if (glt->textype->textype == TEXTYPE_PALETTE)
        {
-               glt->inputtexels = Mem_Alloc(texturedatamempool, glt->width * glt->height * texinfo->inputbytesperpixel);
-               if (glt->inputtexels == NULL)
-                       Sys_Error("R_SetupTexture: out of memory\n");
-               memcpy(glt->inputtexels, data, glt->width * glt->height * texinfo->inputbytesperpixel);
+               // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
+               Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
+               prevbuffer = colorconvertbuffer;
        }
-       else
-               glt->inputtexels = NULL;
 
-       glt->generate = generate;
-       glt->proceduraldatasize = proceduraldatasize;
-       if (proceduraldatasize)
+       // 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))
        {
-               glt->proceduraldata = Mem_Alloc(texturemempool, proceduraldatasize);
-               if (glt->proceduraldata == NULL)
-                       Sys_Error("R_SetupTexture: out of memory\n");
+               // update a portion of the image
+               if (glt->texturetype != GLTEXTURETYPE_2D)
+                       Sys_Error("R_Upload: partial update of type other than 2D");
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+                       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+                       break;
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       {
+                               RECT d3drect;
+                               D3DLOCKED_RECT d3dlockedrect;
+                               int y;
+                               memset(&d3drect, 0, sizeof(d3drect));
+                               d3drect.left = fragx;
+                               d3drect.top = fragy;
+                               d3drect.right = fragx+fragwidth;
+                               d3drect.bottom = fragy+fragheight;
+                               if (IDirect3DTexture9_LockRect((IDirect3DTexture9*)glt->d3dtexture, 0, &d3dlockedrect, &d3drect, 0) == D3D_OK && d3dlockedrect.pBits)
+                               {
+                                       for (y = 0;y < fragheight;y++)
+                                               memcpy((unsigned char *)d3dlockedrect.pBits + d3dlockedrect.Pitch * y, (unsigned char *)prevbuffer + fragwidth*glt->bytesperpixel * y, fragwidth*glt->bytesperpixel);
+                                       IDirect3DTexture9_UnlockRect((IDirect3DTexture9*)glt->d3dtexture, 0);
+                               }
+                       }
+#endif
+                       break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               }
        }
        else
-               glt->proceduraldata = NULL;
-
-       R_FindImageForTexture(glt);
-       R_PrecacheTexture(glt);
+       {
+               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");
 
-       return glt;
+               // cubemaps contain multiple images and thus get processed a bit differently
+               if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
+               {
+                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                       {
+                               Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+                               prevbuffer = resizebuffer;
+                       }
+                       // picmip/max_size
+                       while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
+                       {
+                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
+                               prevbuffer = resizebuffer;
+                       }
+               }
+               mip = 0;
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       if (qglGetCompressedTexImageARB)
+                       {
+                               if (gl_texturecompression.integer >= 2)
+                                       qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
+                               else
+                                       qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
+                               CHECKGLERROR
+                       }
+                       switch(glt->texturetype)
+                       {
+                       case GLTEXTURETYPE_2D:
+                               qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+                               if (glt->flags & TEXF_MIPMAP)
+                               {
+                                       while (width > 1 || height > 1 || depth > 1)
+                                       {
+                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                               prevbuffer = resizebuffer;
+                                               qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+                                       }
+                               }
+                               break;
+                       case GLTEXTURETYPE_3D:
+                               qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+                               if (glt->flags & TEXF_MIPMAP)
+                               {
+                                       while (width > 1 || height > 1 || depth > 1)
+                                       {
+                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                               prevbuffer = resizebuffer;
+                                               qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+                                       }
+                               }
+                               break;
+                       case GLTEXTURETYPE_CUBEMAP:
+                               // convert and upload each side in turn,
+                               // from a continuous block of input texels
+                               texturebuffer = (unsigned char *)prevbuffer;
+                               for (i = 0;i < 6;i++)
+                               {
+                                       prevbuffer = texturebuffer;
+                                       texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
+                                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                                       {
+                                               Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+                                               prevbuffer = resizebuffer;
+                                       }
+                                       // picmip/max_size
+                                       while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
+                                       {
+                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
+                                               prevbuffer = resizebuffer;
+                                       }
+                                       mip = 0;
+                                       qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+                                       if (glt->flags & TEXF_MIPMAP)
+                                       {
+                                               while (width > 1 || height > 1 || depth > 1)
+                                               {
+                                                       Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                                       prevbuffer = resizebuffer;
+                                                       qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
+                                               }
+                                       }
+                               }
+                               break;
+                       case GLTEXTURETYPE_RECTANGLE:
+                               qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
+                               break;
+                       }
+                       GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
+                       qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+                       break;
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       if (!(glt->flags & TEXF_RENDERTARGET))
+                       {
+                               D3DLOCKED_RECT d3dlockedrect;
+                               D3DLOCKED_BOX d3dlockedbox;
+                               switch(glt->texturetype)
+                               {
+                               case GLTEXTURETYPE_2D:
+                                       if (IDirect3DTexture9_LockRect((IDirect3DTexture9*)glt->d3dtexture, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                                       {
+                                               if (prevbuffer)
+                                                       memcpy(d3dlockedrect.pBits, prevbuffer, width*height*glt->bytesperpixel);
+                                               else
+                                                       memset(d3dlockedrect.pBits, 255, width*height*glt->bytesperpixel);
+                                               IDirect3DTexture9_UnlockRect((IDirect3DTexture9*)glt->d3dtexture, mip);
+                                       }
+                                       mip++;
+                                       if ((glt->flags & TEXF_MIPMAP) && prevbuffer)
+                                       {
+                                               while (width > 1 || height > 1 || depth > 1)
+                                               {
+                                                       Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                                       prevbuffer = resizebuffer;
+                                                       if (IDirect3DTexture9_LockRect((IDirect3DTexture9*)glt->d3dtexture, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                                                       {
+                                                               memcpy(d3dlockedrect.pBits, prevbuffer, width*height*glt->bytesperpixel);
+                                                               IDirect3DTexture9_UnlockRect((IDirect3DTexture9*)glt->d3dtexture, mip);
+                                                       }
+                                                       mip++;
+                                               }
+                                       }
+                                       break;
+                               case GLTEXTURETYPE_3D:
+                                       if (IDirect3DVolumeTexture9_LockBox((IDirect3DVolumeTexture9*)glt->d3dtexture, mip, &d3dlockedbox, NULL, 0) == D3D_OK && d3dlockedbox.pBits)
+                                       {
+                                               // we are not honoring the RowPitch or SlicePitch, hopefully this works with all sizes
+                                               memcpy(d3dlockedbox.pBits, prevbuffer, width*height*depth*glt->bytesperpixel);
+                                               IDirect3DVolumeTexture9_UnlockBox((IDirect3DVolumeTexture9*)glt->d3dtexture, mip);
+                                       }
+                                       mip++;
+                                       if (glt->flags & TEXF_MIPMAP)
+                                       {
+                                               while (width > 1 || height > 1 || depth > 1)
+                                               {
+                                                       Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                                       prevbuffer = resizebuffer;
+                                                       if (IDirect3DVolumeTexture9_LockBox((IDirect3DVolumeTexture9*)glt->d3dtexture, mip, &d3dlockedbox, NULL, 0) == D3D_OK && d3dlockedbox.pBits)
+                                                       {
+                                                               // we are not honoring the RowPitch or SlicePitch, hopefully this works with all sizes
+                                                               memcpy(d3dlockedbox.pBits, prevbuffer, width*height*depth*glt->bytesperpixel);
+                                                               IDirect3DVolumeTexture9_UnlockBox((IDirect3DVolumeTexture9*)glt->d3dtexture, mip);
+                                                       }
+                                                       mip++;
+                                               }
+                                       }
+                                       break;
+                               case GLTEXTURETYPE_CUBEMAP:
+                                       // convert and upload each side in turn,
+                                       // from a continuous block of input texels
+                                       texturebuffer = (unsigned char *)prevbuffer;
+                                       for (i = 0;i < 6;i++)
+                                       {
+                                               prevbuffer = texturebuffer;
+                                               texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
+                                               if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                                               {
+                                                       Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
+                                                       prevbuffer = resizebuffer;
+                                               }
+                                               // picmip/max_size
+                                               while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
+                                               {
+                                                       Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
+                                                       prevbuffer = resizebuffer;
+                                               }
+                                               mip = 0;
+                                               if (IDirect3DCubeTexture9_LockRect((IDirect3DCubeTexture9*)glt->d3dtexture, (D3DCUBEMAP_FACES)i, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                                               {
+                                                       memcpy(d3dlockedrect.pBits, prevbuffer, width*height*glt->bytesperpixel);
+                                                       IDirect3DCubeTexture9_UnlockRect((IDirect3DCubeTexture9*)glt->d3dtexture, (D3DCUBEMAP_FACES)i, mip);
+                                               }
+                                               mip++;
+                                               if (glt->flags & TEXF_MIPMAP)
+                                               {
+                                                       while (width > 1 || height > 1 || depth > 1)
+                                                       {
+                                                               Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
+                                                               prevbuffer = resizebuffer;
+                                                               if (IDirect3DCubeTexture9_LockRect((IDirect3DCubeTexture9*)glt->d3dtexture, (D3DCUBEMAP_FACES)i, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                                                               {
+                                                                       memcpy(d3dlockedrect.pBits, prevbuffer, width*height*glt->bytesperpixel);
+                                                                       IDirect3DCubeTexture9_UnlockRect((IDirect3DCubeTexture9*)glt->d3dtexture, (D3DCUBEMAP_FACES)i, mip);
+                                                               }
+                                                               mip++;
+                                                       }
+                                               }
+                                       }
+                                       break;
+                               case GLTEXTURETYPE_RECTANGLE:
+                                       Sys_Error("Direct3D does not have RECTANGLE textures\n");
+                                       break;
+                               }
+                       }
+                       glt->d3daddressw = 0;
+                       if (glt->flags & TEXF_CLAMP)
+                       {
+                               glt->d3daddressu = D3DTADDRESS_CLAMP;
+                               glt->d3daddressv = D3DTADDRESS_CLAMP;
+                               if (glt->tiledepth > 1)
+                                       glt->d3daddressw = D3DTADDRESS_CLAMP;
+                       }
+                       else
+                       {
+                               glt->d3daddressu = D3DTADDRESS_WRAP;
+                               glt->d3daddressv = D3DTADDRESS_WRAP;
+                               if (glt->tiledepth > 1)
+                                       glt->d3daddressw = D3DTADDRESS_WRAP;
+                       }
+                       glt->d3dmipmaplodbias = 0;
+                       glt->d3dmaxmiplevel = 0;
+                       glt->d3dmaxmiplevelfilter = d3d_filter_nomip ? 0 : glt->d3dmaxmiplevel;
+                       if (glt->flags & TEXF_FORCELINEAR)
+                       {
+                               glt->d3dminfilter = D3DTEXF_LINEAR;
+                               glt->d3dmagfilter = D3DTEXF_LINEAR;
+                               glt->d3dmipfilter = D3DTEXF_POINT;
+                       }
+                       else if (glt->flags & TEXF_FORCENEAREST)
+                       {
+                               glt->d3dminfilter = D3DTEXF_POINT;
+                               glt->d3dmagfilter = D3DTEXF_POINT;
+                               glt->d3dmipfilter = D3DTEXF_POINT;
+                       }
+                       else if (glt->flags & TEXF_MIPMAP)
+                       {
+                               glt->d3dminfilter = d3d_filter_mipmin;
+                               glt->d3dmagfilter = d3d_filter_mipmag;
+                               glt->d3dmipfilter = d3d_filter_mipmix;
+                       }
+                       else
+                       {
+                               glt->d3dminfilter = d3d_filter_flatmin;
+                               glt->d3dmagfilter = d3d_filter_flatmag;
+                               glt->d3dmipfilter = d3d_filter_flatmix;
+                       }
+#endif
+                       break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               }
+       }
 }
 
-/*
-================
-R_LoadTexture
-================
-*/
-rtexture_t *R_LoadTexture (rtexturepool_t *rtexturepool, char *identifier, int width, int height, qbyte *data, int textype, int flags)
+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;
+       int i, size;
        gltexture_t *glt;
        gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
-       textypeinfo_t *texinfo;
-       unsigned short crc;
+       textypeinfo_t *texinfo, *texinfo2;
+       unsigned char *temppixels = NULL;
 
        if (cls.state == ca_dedicated)
                return NULL;
 
-       texinfo = R_GetTexTypeInfo(textype, flags);
+       if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
+       {
+               Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
+               return NULL;
+       }
+       if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
+       {
+               Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
+               return NULL;
+       }
+       if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
+       {
+               Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
+               return NULL;
+       }
 
-       // data can be NULL
-//     if (data == NULL)
-//             Host_Error("R_LoadTexture: \"%s\" has no data\n", identifier);
+       texinfo = R_GetTexTypeInfo(textype, flags);
+       size = width * height * depth * sides * texinfo->inputbytesperpixel;
+       if (size < 1)
+       {
+               Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
+               return NULL;
+       }
 
-       if (flags & TEXF_FRAGMENT)
+       if (textype == TEXTYPE_RGBA)
        {
-               if (width > block_size || height > block_size)
-                       Host_Error("R_LoadTexture: fragment too big, must be no more than %dx%d\n", block_size, block_size);
-               if ((width * texinfo->internalbytesperpixel) & 3)
-                       Host_Error("R_LoadTexture: incompatible width for fragment");
+               // swap bytes
+               static int rgbaswapindices[4] = {2, 1, 0, 3};
+               textype = TEXTYPE_BGRA;
+               texinfo = R_GetTexTypeInfo(textype, flags);
+               temppixels = (unsigned char *)Mem_Alloc(tempmempool, width * height * depth * sides * 4);
+               Image_CopyMux(temppixels, data, width, height*depth*sides, false, false, false, 4, 4, rgbaswapindices);
+               data = temppixels;
        }
 
        // clear the alpha flag if the texture has no transparent pixels
        switch(textype)
        {
-       case TEXTYPE_QPALETTE:
+       case TEXTYPE_PALETTE:
                if (flags & TEXF_ALPHA)
                {
                        flags &= ~TEXF_ALPHA;
-                       for (i = 0;i < width * height;i++)
+                       if (data)
                        {
-                               if (data[i] == 255)
+                               for (i = 0;i < size;i++)
                                {
-                                       flags |= TEXF_ALPHA;
-                                       break;
+                                       if (((unsigned char *)&palette[data[i]])[3] < 255)
+                                       {
+                                               flags |= TEXF_ALPHA;
+                                               break;
+                                       }
                                }
                        }
                }
                break;
-       case TEXTYPE_RGB:
-               if (flags & TEXF_ALPHA)
-                       Host_Error("R_LoadTexture: RGB has no alpha, don't specify TEXF_ALPHA\n");
-               break;
        case TEXTYPE_RGBA:
+       case TEXTYPE_BGRA:
                if (flags & TEXF_ALPHA)
                {
                        flags &= ~TEXF_ALPHA;
-                       for (i = 0;i < width * height;i++)
+                       if (data)
                        {
-                               if (data[i * 4 + 3] < 255)
+                               for (i = 3;i < size;i += 4)
                                {
-                                       flags |= TEXF_ALPHA;
-                                       break;
+                                       if (data[i] < 255)
+                                       {
+                                               flags |= TEXF_ALPHA;
+                                               break;
+                                       }
                                }
                        }
                }
                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\n");
+               Sys_Error("R_LoadTexture: unknown texture type");
        }
 
-       // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
-       if (data == NULL)
-               crc = 0;
+       texinfo2 = R_GetTexTypeInfo(textype, flags);
+       if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
+               texinfo = texinfo2;
        else
-               crc = CRC_Block(data, width*height*texinfo->inputbytesperpixel);
+               Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
 
-       // see if the texture is already present
-       if (identifier && (glt = R_FindTexture(pool, identifier)))
+       glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
+       if (identifier)
+               strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
+       glt->pool = pool;
+       glt->chain = pool->gltchain;
+       pool->gltchain = glt;
+       glt->inputwidth = width;
+       glt->inputheight = height;
+       glt->inputdepth = depth;
+       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;
+       glt->palette = palette;
+       glt->glinternalformat = texinfo->glinternalformat;
+       glt->glformat = texinfo->glformat;
+       glt->gltype = texinfo->gltype;
+       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->updatecallback = NULL;
+       glt->updatacallback_data = NULL;
+
+       GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->miplevel, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth, &glt->miplevels);
+
+       // upload the texture
+       // data may be NULL (blank texture for dynamic rendering)
+       switch(vid.renderpath)
        {
-               if (crc == glt->crc && width == glt->width && height == glt->height && texinfo == glt->textype && ((flags ^ glt->flags) & TEXF_IMPORTANTBITS) == 0 && ((flags ^ glt->flags) & GLTEXF_IMPORTANTBITS) == 0)
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               CHECKGLERROR
+               qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
                {
-                       Con_Printf("R_LoadTexture: exact match with existing texture %s\n", identifier);
-                       return (rtexture_t *)glt; // exact match, use existing
+                       D3DFORMAT d3dformat;
+                       D3DPOOL d3dpool;
+                       DWORD d3dusage;
+                       HRESULT d3dresult;
+                       d3dusage = 0;
+                       d3dpool = D3DPOOL_MANAGED;
+                       if (flags & TEXF_RENDERTARGET)
+                       {
+                               d3dusage |= D3DUSAGE_RENDERTARGET;
+                               d3dpool = D3DPOOL_DEFAULT;
+                       }
+                       switch(textype)
+                       {
+                       case TEXTYPE_PALETTE: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;break;
+                       case TEXTYPE_RGBA: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8B8G8R8 : D3DFMT_X8B8G8R8;break;
+                       case TEXTYPE_BGRA: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;break;
+                       case TEXTYPE_COLORBUFFER: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;break;
+                       case TEXTYPE_SHADOWMAP: d3dformat = D3DFMT_D16;d3dusage = D3DUSAGE_DEPTHSTENCIL;break; // note: can not use D3DUSAGE_RENDERTARGET here
+                       case TEXTYPE_ALPHA: d3dformat = D3DFMT_A8;break;
+                       default: d3dformat = D3DFMT_A8R8G8B8;Sys_Error("R_LoadTexture: unsupported texture type %i when picking D3DFMT", (int)textype);break;
+                       }
+                       glt->d3dformat = d3dformat;
+                       glt->d3dusage = d3dusage;
+                       glt->d3dpool = d3dpool;
+                       if (glt->tiledepth > 1)
+                       {
+                               if (FAILED(d3dresult = IDirect3DDevice9_CreateVolumeTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->tiledepth, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DVolumeTexture9 **)&glt->d3dtexture, NULL)))
+                                       Sys_Error("IDirect3DDevice9_CreateVolumeTexture failed!");
+                       }
+                       else if (glt->sides == 6)
+                       {
+                               if (FAILED(d3dresult = IDirect3DDevice9_CreateCubeTexture(vid_d3d9dev, glt->tilewidth, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DCubeTexture9 **)&glt->d3dtexture, NULL)))
+                                       Sys_Error("IDirect3DDevice9_CreateCubeTexture failed!");
+                       }
+                       else
+                       {
+                               if (FAILED(d3dresult = IDirect3DDevice9_CreateTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->miplevels, glt->d3dusage, (D3DFORMAT)glt->d3dformat, (D3DPOOL)glt->d3dpool, (IDirect3DTexture9 **)&glt->d3dtexture, NULL)))
+                                       Sys_Error("IDirect3DDevice9_CreateTexture failed!");
+                       }
                }
-               Con_Printf("R_LoadTexture: cache mismatch on %s, replacing old texture\n", identifier);
-               R_FreeTexture(glt);
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
 
-       return (rtexture_t *)R_SetupTexture(pool, identifier, crc, width, height, flags | GLTEXF_UPLOAD, texinfo, data, NULL, NULL, 0);
+       R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
+       if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
+               glt->bufferpixels = (unsigned char *)Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
+
+       // free any temporary processing buffer we allocated...
+       if (temppixels)
+               Mem_Free(temppixels);
+
+       // texture converting and uploading can take a while, so make sure we're sending keepalives
+       // FIXME: this causes rendering during R_Shadow_DrawLights
+//     CL_KeepaliveMessage(false);
+
+       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, int miplevel, const unsigned int *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, int miplevel, const unsigned int *palette)
+{
+       return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, miplevel, textype, GLTEXTURETYPE_3D, data, palette);
 }
 
-rtexture_t *R_ProceduralTexture (rtexturepool_t *rtexturepool, char *identifier, int width, int height, int textype, int flags, int (*generate)(qbyte *buffer, int width, int height, void *proceduraldata, int proceduraldatasize), void *proceduraldata, int proceduraldatasize)
+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)
 {
-       gltexture_t             *glt;
-       gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
-       textypeinfo_t   *texinfo;
+       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, int miplevel, const unsigned int *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)
+{
+       int flags = TEXF_RENDERTARGET | TEXF_CLAMP;
+       if (filter)
+               flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
+       else
+               flags |= TEXF_FORCENEAREST;
+       if (precision <= 16)
+               flags |= TEXF_LOWPRECISION;
+       return flags;
+}
+
+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), -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), -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), -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 = (unsigned char *)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 = 0;
+       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;
+       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;
+       }
+
+       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);
 
-       // no function is supported, for odd uses
-//     if (generate == NULL)
-//             Host_Error("R_ProceduralTexture: \"%s\" has no generate function\n", identifier);
-       if (flags & TEXF_FRAGMENT)
+       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;
+       glt->miplevels = dds_miplevels;
+
+       // texture uploading can take a while, so make sure we're sending keepalives
+       CL_KeepaliveMessage(false);
+
+       // create the texture object
+       switch(vid.renderpath)
        {
-               if (width > block_size || height > block_size)
-                       Host_Error("R_ProceduralTexture: fragment too big, must be no more than %dx%d\n", block_size, block_size);
-               if ((width * texinfo->internalbytesperpixel) & 3)
-                       Host_Error("R_ProceduralTexture: incompatible width for fragment");
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               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
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               {
+                       D3DFORMAT d3dformat;
+                       D3DPOOL d3dpool;
+                       DWORD d3dusage;
+                       switch(textype)
+                       {
+                       case TEXTYPE_BGRA: d3dformat = (flags & TEXF_ALPHA) ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;break;
+                       case TEXTYPE_DXT1: case TEXTYPE_DXT1A: d3dformat = D3DFMT_DXT1;break;
+                       case TEXTYPE_DXT3: d3dformat = D3DFMT_DXT3;break;
+                       case TEXTYPE_DXT5: d3dformat = D3DFMT_DXT5;break;
+                       default: d3dformat = D3DFMT_A8R8G8B8;Host_Error("R_LoadTextureDDSFile: unsupported texture type %i when picking D3DFMT", (int)textype);break;
+                       }
+                       d3dusage = 0;
+                       d3dpool = D3DPOOL_MANAGED;
+                       IDirect3DDevice9_CreateTexture(vid_d3d9dev, glt->tilewidth, glt->tileheight, glt->miplevels, d3dusage, d3dformat, d3dpool, (IDirect3DTexture9 **)&glt->d3dtexture, NULL);
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        }
 
-       // see if the texture is already present
-       if (identifier && (glt = R_FindTexture(pool, identifier)))
+       // upload the texture
+       // we need to restore the texture binding after finishing the upload
+       mipcomplete = false;
+
+       for (mip = 0;mip <= dds_miplevels;mip++) // <= to include the not-counted "largest" miplevel
        {
-               if (width == glt->width && height == glt->height && texinfo == glt->textype && ((flags ^ glt->flags) & TEXF_IMPORTANTBITS) == 0 && ((flags ^ glt->flags) & GLTEXF_IMPORTANTBITS) == 0)
+               mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+               if (mippixels + mipsize > dds + ddssize)
+                       break;
+               switch(vid.renderpath)
                {
-                       Con_Printf("R_LoadTexture: exact match with existing texture %s\n", identifier);
-                       return (rtexture_t *)glt; // exact match, use existing
+               case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       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
+                       }
+                       break;
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       {
+                               D3DLOCKED_RECT d3dlockedrect;
+                               if (IDirect3DTexture9_LockRect((IDirect3DTexture9*)glt->d3dtexture, mip, &d3dlockedrect, NULL, 0) == D3D_OK && d3dlockedrect.pBits)
+                               {
+                                       memcpy(d3dlockedrect.pBits, mippixels, mipsize);
+                                       IDirect3DTexture9_UnlockRect((IDirect3DTexture9*)glt->d3dtexture, mip);
+                               }
+                               break;
+                       }
+#endif
+                       break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
                }
-               Con_Printf("R_LoadTexture: cache mismatch, replacing old texture\n");
-               R_FreeTexture(glt);
+               mippixels += mipsize;
+               if (mipwidth <= 1 && mipheight <= 1)
+               {
+                       mipcomplete = true;
+                       break;
+               }
+               if (mipwidth > 1)
+                       mipwidth >>= 1;
+               if (mipheight > 1)
+                       mipheight >>= 1;
        }
 
-       return (rtexture_t *)R_SetupTexture(pool, identifier, 0, width, height, flags | GLTEXF_PROCEDURAL | GLTEXF_UPLOAD, texinfo, NULL, generate, proceduraldata, proceduraldatasize);
-}
+       // after upload we have to set some parameters...
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               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
+               break;
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               glt->d3daddressw = 0;
+               if (glt->flags & TEXF_CLAMP)
+               {
+                       glt->d3daddressu = D3DTADDRESS_CLAMP;
+                       glt->d3daddressv = D3DTADDRESS_CLAMP;
+                       if (glt->tiledepth > 1)
+                               glt->d3daddressw = D3DTADDRESS_CLAMP;
+               }
+               else
+               {
+                       glt->d3daddressu = D3DTADDRESS_WRAP;
+                       glt->d3daddressv = D3DTADDRESS_WRAP;
+                       if (glt->tiledepth > 1)
+                               glt->d3daddressw = D3DTADDRESS_WRAP;
+               }
+               glt->d3dmipmaplodbias = 0;
+               glt->d3dmaxmiplevel = 0;
+               glt->d3dmaxmiplevelfilter = 0;
+               if (glt->flags & TEXF_MIPMAP)
+               {
+                       glt->d3dminfilter = d3d_filter_mipmin;
+                       glt->d3dmagfilter = d3d_filter_mipmag;
+                       glt->d3dmipfilter = d3d_filter_mipmix;
+               }
+               else
+               {
+                       glt->d3dminfilter = d3d_filter_flatmin;
+                       glt->d3dmagfilter = d3d_filter_flatmag;
+                       glt->d3dmipfilter = d3d_filter_flatmix;
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       }
 
-int R_TextureHasAlpha(rtexture_t *rt)
-{
-       gltexture_t *glt;
-       if (!rt)
-               return false;
-       glt = (gltexture_t *)rt;
-       return (glt->flags & TEXF_ALPHA) != 0;
+       Mem_Free(dds);
+       return (rtexture_t *)glt;
 }
 
 int R_TextureWidth(rtexture_t *rt)
 {
-       if (!rt)
-               return false;
-       return ((gltexture_t *)rt)->width;
+       return rt ? ((gltexture_t *)rt)->inputwidth : 0;
 }
 
 int R_TextureHeight(rtexture_t *rt)
 {
-       if (!rt)
-               return false;
-       return ((gltexture_t *)rt)->height;
+       return rt ? ((gltexture_t *)rt)->inputheight : 0;
 }
 
-void R_FragmentLocation(rtexture_t *rt, int *x, int *y, float *fx1, float *fy1, float *fx2, float *fy2)
+void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
 {
-       gltexture_t *glt;
-       float iwidth, iheight;
-       if (cls.state == ca_dedicated)
+       gltexture_t *glt = (gltexture_t *)rt;
+       if (data == NULL)
+               Host_Error("R_UpdateTexture: no data supplied");
+       if (glt == NULL)
+               Host_Error("R_UpdateTexture: no texture supplied");
+       if (!glt->texnum && !glt->d3dtexture)
        {
-               if (x)
-                       *x = 0;
-               if (y)
-                       *y = 0;
-               if (fx1 || fy1 || fx2 || fy2)
-               {
-                       if (fx1)
-                               *fx1 = 0;
-                       if (fy1)
-                               *fy1 = 0;
-                       if (fx2)
-                               *fx2 = 1;
-                       if (fy2)
-                               *fy2 = 1;
-               }
+               Con_Printf("R_UpdateTexture: texture %p \"%s\" in pool %p has not been uploaded yet", glt, glt->identifier, glt->pool);
                return;
        }
-       if (!rt)
-               Host_Error("R_FragmentLocation: no texture supplied\n");
-       glt = (gltexture_t *)rt;
-       if (glt->flags & TEXF_FRAGMENT)
+       // update part of the texture
+       if (glt->bufferpixels)
        {
-               if (x)
-                       *x = glt->x;
-               if (y)
-                       *y = glt->y;
-               if (fx1 || fy1 || fx2 || fy2)
+               int j;
+               int bpp = glt->bytesperpixel;
+               int inputskip = width*bpp;
+               int outputskip = glt->tilewidth*bpp;
+               const unsigned char *input = data;
+               unsigned char *output = glt->bufferpixels;
+               if (x < 0)
                {
-                       iwidth = 1.0f / glt->image->width;
-                       iheight = 1.0f / glt->image->height;
-                       if (fx1)
-                               *fx1 = glt->x * iwidth;
-                       if (fy1)
-                               *fy1 = glt->y * iheight;
-                       if (fx2)
-                               *fx2 = (glt->x + glt->width) * iwidth;
-                       if (fy2)
-                               *fy2 = (glt->y + glt->height) * iheight;
+                       width += x;
+                       input -= x*bpp;
+                       x = 0;
                }
+               if (y < 0)
+               {
+                       height += y;
+                       input -= y*inputskip;
+                       y = 0;
+               }
+               if (width > glt->tilewidth - x)
+                       width = glt->tilewidth - x;
+               if (height > glt->tileheight - y)
+                       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);
        }
        else
+               R_Upload(glt, data, x, y, 0, width, height, 1);
+}
+
+int R_RealGetTexture(rtexture_t *rt)
+{
+       if (rt)
        {
-               if (x)
-                       *x = 0;
-               if (y)
-                       *y = 0;
-               if (fx1 || fy1 || fx2 || fy2)
+               gltexture_t *glt;
+               glt = (gltexture_t *)rt;
+               if (glt->flags & GLTEXF_DYNAMIC)
+                       R_UpdateDynamicTexture(glt);
+               if (glt->buffermodified && glt->bufferpixels)
                {
-                       if (fx1)
-                               *fx1 = 0;
-                       if (fy1)
-                               *fy1 = 0;
-                       if (fx2)
-                               *fx2 = 1;
-                       if (fy2)
-                               *fy2 = 1;
+                       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;
 }
 
-int R_CompatibleFragmentWidth(int width, int textype, int flags)
+void R_ClearTexture (rtexture_t *rt)
 {
-       textypeinfo_t *texinfo = R_GetTexTypeInfo(textype, flags);
-       while ((width * texinfo->internalbytesperpixel) & 3)
-               width++;
-       return width;
+       gltexture_t *glt = (gltexture_t *)rt;
+
+       R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
 }
 
-void R_UpdateTexture(rtexture_t *rt, qbyte *data)
+int R_PicmipForFlags(int flags)
 {
-       gltexture_t *glt;
-       if (rt == NULL)
-               Host_Error("R_UpdateTexture: no texture supplied\n");
-       if (data == NULL)
-               Host_Error("R_UpdateTexture: no data supplied\n");
-       glt = (gltexture_t *)rt;
-       /*
-       if (!(glt->flags & GLTEXF_PROCEDURAL))
+       int miplevel = 0;
+       if(flags & TEXF_PICMIP)
        {
-               if (glt->inputtexels == NULL)
+               miplevel += gl_picmip.integer;
+               if (flags & TEXF_ISWORLD)
                {
-                       glt->inputtexels = Mem_Alloc(texturedatamempool, glt->width * glt->height * glt->textype->inputbytesperpixel);
-                       if (glt->inputtexels == NULL)
-                               Host_Error("R_UpdateTexture: ran out of memory\n");
+                       if (r_picmipworld.integer)
+                               miplevel += gl_picmip_world.integer;
+                       else
+                               miplevel = 0;
                }
-               memcpy(glt->inputtexels, data, glt->width * glt->height * glt->textype->inputbytesperpixel);
+               else if (flags & TEXF_ISSPRITE)
+               {
+                       if (r_picmipsprites.integer)
+                               miplevel += gl_picmip_sprites.integer;
+                       else
+                               miplevel = 0;
+               }
+               else
+                       miplevel += gl_picmip_other.integer;
        }
-       R_Upload(glt, data);
-       */
-       // if it has not been uploaded yet, update the data that will be used when it is
-       if (glt->inputtexels)
-               memcpy(glt->inputtexels, data, glt->width * glt->height * glt->textype->inputbytesperpixel);
-       else
-               R_Upload(glt, data);
+       return miplevel;
 }
-