]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_textures.c
fix several conversion warnings
[xonotic/darkplaces.git] / gl_textures.c
index 192568fac7ed340ab2a3c8eaa5ad02536057052b..c6262baf8e1040ec2b8e83d893a319336c40e2d2 100644 (file)
@@ -8,6 +8,7 @@ extern LPDIRECT3DDEVICE9 vid_d3d9dev;
 #include "jpeg.h"
 #include "image_png.h"
 #include "intoverflow.h"
+#include "dpsoftrast.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)"};
@@ -32,10 +33,14 @@ cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_
 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"};
 cvar_t r_texture_dds_load_alphamode = {0, "r_texture_dds_load_alphamode", "1", "0: trust DDPF_ALPHAPIXELS flag, 1: texture format and brute force search if ambigous, 2: texture format only"};
+cvar_t r_texture_dds_load_logfailure = {0, "r_texture_dds_load_logfailure", "1", "log missing DDS textures to ddstexturefailures.log"};
+cvar_t r_texture_dds_swdecode = {0, "r_texture_dds_swdecode", "0", "0: don't software decode DDS, 1: software decode DDS if unsupported, 2: always software decode DDS"};
 
 qboolean       gl_filter_force = false;
 int            gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
 int            gl_filter_mag = GL_LINEAR;
+DPSOFTRAST_TEXTURE_FILTER dpsoftrast_filter_mipmap = DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE;
+DPSOFTRAST_TEXTURE_FILTER dpsoftrast_filter_nomipmap = DPSOFTRAST_TEXTURE_FILTER_NEAREST;
 
 #ifdef SUPPORTD3D
 int d3d_filter_flatmin = D3DTEXF_LINEAR;
@@ -323,6 +328,10 @@ void R_FreeTexture(rtexture_t *rt)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               if (glt->texnum)
+                       DPSOFTRAST_Texture_Free(glt->texnum);
+               break;
        }
 
        if (glt->inputtexels)
@@ -370,17 +379,18 @@ typedef struct glmode_s
 {
        const char *name;
        int minification, magnification;
+       DPSOFTRAST_TEXTURE_FILTER dpsoftrastfilter_mipmap, dpsoftrastfilter_nomipmap;
 }
 glmode_t;
 
 static glmode_t modes[6] =
 {
-       {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
-       {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
-       {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
-       {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
-       {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
-       {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
+       {"GL_NEAREST", GL_NEAREST, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR", GL_LINEAR, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR},
+       {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_LINEAR},
+       {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST, DPSOFTRAST_TEXTURE_FILTER_NEAREST_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_NEAREST},
+       {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, DPSOFTRAST_TEXTURE_FILTER_LINEAR_MIPMAP_TRIANGLE, DPSOFTRAST_TEXTURE_FILTER_LINEAR}
 };
 
 #ifdef SUPPORTD3D
@@ -437,6 +447,9 @@ static void GL_TextureMode_f (void)
        gl_filter_mag = modes[i].magnification;
        gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
 
+       dpsoftrast_filter_mipmap = modes[i].dpsoftrastfilter_mipmap;
+       dpsoftrast_filter_nomipmap = modes[i].dpsoftrastfilter_nomipmap;
+
        switch(vid.renderpath)
        {
        case RENDERPATH_GL11:
@@ -513,6 +526,13 @@ static void GL_TextureMode_f (void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               // change all the existing texture objects
+               for (pool = gltexturepoolchain;pool;pool = pool->next)
+                       for (glt = pool->gltchain;glt;glt = glt->chain)
+                               if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
+                                       DPSOFTRAST_Texture_Filter(glt->texnum, (glt->flags & TEXF_MIPMAP) ? dpsoftrast_filter_mipmap : dpsoftrast_filter_nomipmap);
+               break;
        }
 }
 
@@ -563,6 +583,7 @@ static void GL_Texture_CalcImageSize(int texturetype, int flags, int miplevel, i
        case RENDERPATH_CGGL:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                break;
        case RENDERPATH_D3D9:
 #if 0
@@ -693,6 +714,8 @@ static void r_textures_start(void)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 
        texturemempool = Mem_AllocPool("texture management", 0, NULL);
@@ -765,6 +788,8 @@ static void r_textures_devicelost(void)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       break;
                }
        }
 }
@@ -819,6 +844,8 @@ static void r_textures_devicerestored(void)
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       break;
                }
        }
 }
@@ -851,6 +878,8 @@ void R_Textures_Init (void)
        Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
        Cvar_RegisterVariable (&gl_nopartialtextureupdates);
        Cvar_RegisterVariable (&r_texture_dds_load_alphamode);
+       Cvar_RegisterVariable (&r_texture_dds_load_logfailure);
+       Cvar_RegisterVariable (&r_texture_dds_swdecode);
 
        R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap, r_textures_devicelost, r_textures_devicerestored);
 }
@@ -913,6 +942,7 @@ void R_Textures_Frame (void)
                case RENDERPATH_D3D9:
                case RENDERPATH_D3D10:
                case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
                        break;
                }
        }
@@ -1082,6 +1112,9 @@ static void R_UploadPartialTexture(gltexture_t *glt, const unsigned char *data,
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               DPSOFTRAST_Texture_UpdatePartial(glt->texnum, 0, data, fragx, fragy, fragwidth, fragheight);
+               break;
        }
 }
 
@@ -1382,6 +1415,56 @@ static void R_UploadFullTexture(gltexture_t *glt, const unsigned char *data)
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               switch(glt->texturetype)
+               {
+               case GLTEXTURETYPE_2D:
+                       DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               case GLTEXTURETYPE_3D:
+                       DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               case GLTEXTURETYPE_CUBEMAP:
+                       if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
+                       {
+                               unsigned char *combinedbuffer = (unsigned char *)Mem_Alloc(tempmempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
+                               // convert and upload each side in turn,
+                               // from a continuous block of input texels
+                               // copy the results into combinedbuffer
+                               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;
+                                       }
+                                       memcpy(combinedbuffer + i*glt->tilewidth*glt->tileheight*glt->tiledepth*glt->bytesperpixel, prevbuffer, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->bytesperpixel);
+                               }
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, combinedbuffer);
+                               Mem_Free(combinedbuffer);
+                       }
+                       else
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, prevbuffer);
+                       break;
+               }
+               if (glt->flags & TEXF_FORCELINEAR)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_LINEAR);
+               else if (glt->flags & TEXF_FORCENEAREST)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_NEAREST);
+               else if (glt->flags & TEXF_MIPMAP)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_mipmap);
+               else
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_nomipmap);
+               break;
        }
 }
 
@@ -1586,6 +1669,26 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               {
+                       int tflags = 0;
+                       switch(textype)
+                       {
+                       case TEXTYPE_PALETTE: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_RGBA: tflags = DPSOFTRAST_TEXTURE_FORMAT_RGBA8;break;
+                       case TEXTYPE_BGRA: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_COLORBUFFER: tflags = DPSOFTRAST_TEXTURE_FORMAT_BGRA8;break;
+                       case TEXTYPE_SHADOWMAP: tflags = DPSOFTRAST_TEXTURE_FORMAT_DEPTH;break;
+                       case TEXTYPE_ALPHA: tflags = DPSOFTRAST_TEXTURE_FORMAT_ALPHA8;break;
+                       default: Sys_Error("R_LoadTexture: unsupported texture type %i when picking DPSOFTRAST_TEXTURE_FLAGS", (int)textype);
+                       }
+                       if (glt->miplevels > 1) tflags |= DPSOFTRAST_TEXTURE_FLAG_MIPMAP;
+                       if (flags & TEXF_ALPHA) tflags |= DPSOFTRAST_TEXTURE_FLAG_USEALPHA;
+                       if (glt->sides == 6) tflags |= DPSOFTRAST_TEXTURE_FLAG_CUBEMAP;
+                       if (glt->flags & TEXF_CLAMP) tflags |= DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE;
+                       glt->texnum = DPSOFTRAST_Texture_New(tflags, glt->tilewidth, glt->tileheight, glt->tiledepth);
+               }
+               break;
        }
 
        R_UploadFullTexture(glt, data);
@@ -1671,6 +1774,15 @@ int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipunco
        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 premultiplied alpha, say so in the DDS file
+       if(glt->flags & TEXF_RGBMULTIPLYBYALPHA)
+       {
+               switch(internalformat)
+               {
+                       case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT2";break;
+                       case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT4";break;
+               }
+       }
        if (!bytesperblock && skipuncompressed)
                return -3; // skipped
        memset(mipinfo, 0, sizeof(mipinfo));
@@ -1765,13 +1877,14 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        gltexture_t *glt;
        gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
        textypeinfo_t *texinfo;
-       int mip, mipwidth, mipheight, mipsize;
+       int mip, mipwidth, mipheight, mipsize, mipsize_total;
        unsigned int c;
        GLint oldbindtexnum = 0;
-       const unsigned char *mippixels, *ddspixels;
+       const unsigned char *mippixels, *ddspixels, *mippixels_start;
        unsigned char *dds;
        fs_offset_t ddsfilesize;
        unsigned int ddssize;
+       qboolean force_swdecode = (r_texture_dds_swdecode.integer > 1);
 
        if (cls.state == ca_dedicated)
                return NULL;
@@ -1781,7 +1894,8 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
 
        if (!dds)
        {
-               Log_Printf("ddstexturefailures.log", "%s\n", filename);
+               if(r_texture_dds_load_logfailure.integer)
+                       Log_Printf("ddstexturefailures.log", "%s\n", filename);
                return NULL; // not found
        }
 
@@ -1829,11 +1943,6 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        }
        else if (!memcmp(dds+84, "DXT1", 4))
        {
-               if(!vid.support.ext_texture_compression_s3tc)
-               {
-                       Mem_Free(dds);
-                       return NULL;
-               }
                // 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...
@@ -1873,12 +1982,21 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        }
                }
        }
-       else if (!memcmp(dds+84, "DXT3", 4))
+       else if (!memcmp(dds+84, "DXT3", 4) || !memcmp(dds+84, "DXT2", 4))
        {
-               if(!vid.support.ext_texture_compression_s3tc)
+               if(!memcmp(dds+84, "DXT2", 4))
                {
-                       Mem_Free(dds);
-                       return NULL;
+                       if(!(flags & TEXF_RGBMULTIPLYBYALPHA))
+                       {
+                               Con_Printf("^1%s: expecting DXT3 image without premultiplied alpha, got DXT2 image with premultiplied alpha\n", filename);
+                       }
+               }
+               else
+               {
+                       if(flags & TEXF_RGBMULTIPLYBYALPHA)
+                       {
+                               Con_Printf("^1%s: expecting DXT2 image without premultiplied alpha, got DXT3 image without premultiplied alpha\n", filename);
+                       }
                }
                textype = TEXTYPE_DXT3;
                bytesperblock = 16;
@@ -1892,12 +2010,21 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                }
                // we currently always assume alpha
        }
-       else if (!memcmp(dds+84, "DXT5", 4))
+       else if (!memcmp(dds+84, "DXT5", 4) || !memcmp(dds+84, "DXT4", 4))
        {
-               if(!vid.support.ext_texture_compression_s3tc)
+               if(!memcmp(dds+84, "DXT4", 4))
                {
-                       Mem_Free(dds);
-                       return NULL;
+                       if(!(flags & TEXF_RGBMULTIPLYBYALPHA))
+                       {
+                               Con_Printf("^1%s: expecting DXT5 image without premultiplied alpha, got DXT4 image with premultiplied alpha\n", filename);
+                       }
+               }
+               else
+               {
+                       if(flags & TEXF_RGBMULTIPLYBYALPHA)
+                       {
+                               Con_Printf("^1%s: expecting DXT4 image without premultiplied alpha, got DXT5 image without premultiplied alpha\n", filename);
+                       }
                }
                textype = TEXTYPE_DXT5;
                bytesperblock = 16;
@@ -1918,10 +2045,106 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                return NULL;
        }
 
+       force_swdecode = false;
+       if(bytesperblock)
+       {
+               if(vid.support.arb_texture_compression && vid.support.ext_texture_compression_s3tc)
+               {
+                       if(r_texture_dds_swdecode.integer > 1)
+                               force_swdecode = true;
+               }
+               else
+               {
+                       if(r_texture_dds_swdecode.integer < 1)
+                       {
+                               // unsupported
+                               Mem_Free(dds);
+                               return NULL;
+                       }
+                       force_swdecode = true;
+               }
+       }
+
        // return whether this texture is transparent
        if (hasalphaflag)
                *hasalphaflag = (flags & TEXF_ALPHA) != 0;
 
+       // if we SW decode, choose 2 sizes bigger
+       if(force_swdecode)
+       {
+               // this is quarter res, so do not scale down more than we have to
+               miplevel -= 2;
+
+               if(miplevel < 0)
+                       Con_DPrintf("WARNING: fake software decoding of compressed texture %s degraded quality\n", filename);
+       }
+
+       // this is where we apply gl_picmip
+       mippixels_start = 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_start += mipsize; // just skip
+               --dds_miplevels;
+               --miplevel;
+               if (mipwidth > 1)
+                       mipwidth >>= 1;
+               if (mipheight > 1)
+                       mipheight >>= 1;
+       }
+       mipsize_total = ddssize - 128 - (mippixels_start - ddspixels);
+       mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+
+       // from here on, we do not need the ddspixels and ddssize any more (apart from the statistics entry in glt)
+
+       // fake decode S3TC if needed
+       if(force_swdecode)
+       {
+               int mipsize_new = mipsize_total / bytesperblock * 4;
+               unsigned char *mipnewpixels = (unsigned char *) Mem_Alloc(tempmempool, mipsize_new);
+               unsigned char *p = mipnewpixels;
+               for (i = bytesperblock == 16 ? 8 : 0;i < (int)mipsize_total;i += bytesperblock, p += 4)
+               {
+                       c = mippixels_start[i] + 256*mippixels_start[i+1] + 65536*mippixels_start[i+2] + 16777216*mippixels_start[i+3];
+                       p[2] = (((c >> 11) & 0x1F) + ((c >> 27) & 0x1F)) * (0.5f / 31.0f * 255.0f);
+                       p[1] = (((c >>  5) & 0x3F) + ((c >> 21) & 0x3F)) * (0.5f / 63.0f * 255.0f);
+                       p[0] = (((c      ) & 0x1F) + ((c >> 16) & 0x1F)) * (0.5f / 31.0f * 255.0f);
+                       if(textype == TEXTYPE_DXT5)
+                               p[3] = (0.5 * mippixels_start[i-8] + 0.5 * mippixels_start[i-7]);
+                       else if(textype == TEXTYPE_DXT3)
+                               p[3] = (
+                                         (mippixels_start[i-8] & 0x0F)
+                                       + (mippixels_start[i-8] >> 4)
+                                       + (mippixels_start[i-7] & 0x0F)
+                                       + (mippixels_start[i-7] >> 4)
+                                       + (mippixels_start[i-6] & 0x0F)
+                                       + (mippixels_start[i-6] >> 4)
+                                       + (mippixels_start[i-5] & 0x0F)
+                                       + (mippixels_start[i-5] >> 4)
+                                      ) * (0.125f / 15.0f * 255.0f);
+                       else
+                               p[3] = 255;
+               }
+
+               textype = TEXTYPE_BGRA;
+               bytesperblock = 0;
+               bytesperpixel = 4;
+
+               // as each block becomes a pixel, we must use pixel count for this
+               mipwidth = (mipwidth + 3) / 4;
+               mipheight = (mipheight + 3) / 4;
+               mipsize = bytesperpixel * mipwidth * mipheight;
+               mippixels_start = mipnewpixels;
+               mipsize_total = mipsize_new;
+       }
+
+       // start mip counting
+       mippixels = mippixels_start;
+
        // calculate average color if requested
        if (avgcolor)
        {
@@ -1929,27 +2152,42 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                Vector4Clear(avgcolor);
                if (bytesperblock)
                {
-                       for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
+                       for (i = bytesperblock == 16 ? 8 : 0;i < mipsize;i += bytesperblock)
                        {
-                               c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
+                               c = mippixels[i] + 256*mippixels[i+1] + 65536*mippixels[i+2] + 16777216*mippixels[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);
+                               if(textype == TEXTYPE_DXT5)
+                                       avgcolor[3] += (0.5 * mippixels[i-8] + 0.5 * mippixels[i-7]);
+                               else if(textype == TEXTYPE_DXT3)
+                                       avgcolor[3] += (
+                                                 (mippixels_start[i-8] & 0x0F)
+                                               + (mippixels_start[i-8] >> 4)
+                                               + (mippixels_start[i-7] & 0x0F)
+                                               + (mippixels_start[i-7] >> 4)
+                                               + (mippixels_start[i-6] & 0x0F)
+                                               + (mippixels_start[i-6] >> 4)
+                                               + (mippixels_start[i-5] & 0x0F)
+                                               + (mippixels_start[i-5] >> 4)
+                                              ) * (0.125f / 15.0f * 255.0f);
+                               else
+                                       avgcolor[3] += 255;
                        }
                        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
+                       avgcolor[3] *= f;
                }
                else
                {
-                       for (i = 0;i < size;i += 4)
+                       for (i = 0;i < mipsize;i += 4)
                        {
-                               avgcolor[0] += ddspixels[i+2];
-                               avgcolor[1] += ddspixels[i+1];
-                               avgcolor[2] += ddspixels[i];
-                               avgcolor[3] += ddspixels[i+3];
+                               avgcolor[0] += mippixels[i+2];
+                               avgcolor[1] += mippixels[i+1];
+                               avgcolor[2] += mippixels[i];
+                               avgcolor[3] += mippixels[i+3];
                        }
                        f = (1.0f / 255.0f) * bytesperpixel / size;
                        avgcolor[0] *= f;
@@ -1959,24 +2197,6 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                }
        }
 
-       // this is where we apply gl_picmip
-       mippixels = ddspixels;
-       mipwidth = dds_width;
-       mipheight = dds_height;
-       while(miplevel >= 1 && dds_miplevels >= 1)
-       {
-               if (mipwidth <= 1 && mipheight <= 1)
-                       break;
-               mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
-               mippixels += mipsize; // just skip
-               --dds_miplevels;
-               --miplevel;
-               if (mipwidth > 1)
-                       mipwidth >>= 1;
-               if (mipheight > 1)
-                       mipheight >>= 1;
-       }
-
        // when not requesting mipmaps, do not load them
        if(!(flags & TEXF_MIPMAP))
                dds_miplevels = 0;
@@ -1986,14 +2206,6 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        else
                flags &= ~TEXF_MIPMAP;
 
-       // if S3TC is not supported, there's very little we can do about it
-       if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
-       {
-               Mem_Free(dds);
-               Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
-               return NULL;
-       }
-
        texinfo = R_GetTexTypeInfo(textype, flags);
 
        glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
@@ -2061,6 +2273,9 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               glt->texnum = DPSOFTRAST_Texture_New(((glt->flags & TEXF_CLAMP) ? DPSOFTRAST_TEXTURE_FLAG_CLAMPTOEDGE : 0) | (dds_miplevels > 1 ? DPSOFTRAST_TEXTURE_FLAG_MIPMAP : 0), glt->tilewidth, glt->tileheight, glt->tiledepth);
+               break;
        }
 
        // upload the texture
@@ -2070,7 +2285,7 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        for (mip = 0;mip <= dds_miplevels;mip++) // <= to include the not-counted "largest" miplevel
        {
                mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
-               if (mippixels + mipsize > dds + ddssize)
+               if (mippixels + mipsize > mippixels_start + mipsize_total)
                        break;
                switch(vid.renderpath)
                {
@@ -2106,6 +2321,14 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                        break;
+               case RENDERPATH_SOFT:
+                       if (bytesperblock)
+                               Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       else
+                               DPSOFTRAST_Texture_UpdateFull(glt->texnum, mippixels);
+                       // DPSOFTRAST calculates its own mipmaps
+                       mip = dds_miplevels;
+                       break;
                }
                mippixels += mipsize;
                if (mipwidth <= 1 && mipheight <= 1)
@@ -2174,9 +2397,21 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        case RENDERPATH_D3D11:
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
+       case RENDERPATH_SOFT:
+               if (glt->flags & TEXF_FORCELINEAR)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_LINEAR);
+               else if (glt->flags & TEXF_FORCENEAREST)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, DPSOFTRAST_TEXTURE_FILTER_NEAREST);
+               else if (glt->flags & TEXF_MIPMAP)
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_mipmap);
+               else
+                       DPSOFTRAST_Texture_Filter(glt->texnum, dpsoftrast_filter_nomipmap);
+               break;
        }
 
        Mem_Free(dds);
+       if(force_swdecode)
+               Mem_Free((unsigned char *) mippixels_start);
        return (rtexture_t *)glt;
 }
 
@@ -2199,7 +2434,7 @@ void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, in
                Host_Error("R_UpdateTexture: no texture supplied");
        if (!glt->texnum && !glt->d3dtexture)
        {
-               Con_Printf("R_UpdateTexture: texture %p \"%s\" in pool %p has not been uploaded yet", (void *)glt, glt->identifier, (void *)glt->pool);
+               Con_DPrintf("R_UpdateTexture: texture %p \"%s\" in pool %p has not been uploaded yet\n", (void *)glt, glt->identifier, (void *)glt->pool);
                return;
        }
        // update part of the texture