]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_textures.c
move EXACTSPECULARMATH to the "lightweight" permutation system too, as there is no...
[xonotic/darkplaces.git] / gl_textures.c
index 67301b1c9eb56d74b06e1eea7aee7b4dad5315e9..c358bf593d2c6a1450dee39bcb246a97d5bc6efb 100644 (file)
@@ -27,6 +27,7 @@ cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0",
 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"};
+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"};
 
 qboolean       gl_filter_force = false;
 int            gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
@@ -185,11 +186,11 @@ static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
        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)
+               if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.ext_texture_compression_s3tc)
                        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)
+               if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.ext_texture_compression_s3tc)
                        return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
                return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
        case TEXTYPE_ALPHA:
@@ -589,8 +590,9 @@ void R_Textures_Init (void)
        Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
        Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
        Cvar_RegisterVariable (&gl_nopartialtextureupdates);
+       Cvar_RegisterVariable (&r_texture_dds_load_alphamode);
 
-       R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
+       R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap, NULL, NULL);
 }
 
 void R_Textures_Frame (void)
@@ -810,7 +812,7 @@ static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int
        else
        {
                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");
+                       Sys_Error("R_Upload \"%s\": partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n", glt->identifier);
 
                // cubemaps contain multiple images and thus get processed a bit differently
                if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
@@ -1090,7 +1092,7 @@ rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char
     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)
+int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed, qboolean hasalpha)
 {
        gltexture_t *glt = (gltexture_t *)rt;
        unsigned char *dds;
@@ -1165,13 +1167,15 @@ int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipunco
        else
        {
                dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
-               dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
+               dds_format_flags = 0x40; // DDPF_RGB
        }
        if (mipmaps)
        {
                dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
                dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
        }
+       if(hasalpha)
+               dds_format_flags |= 0x1; // DDPF_ALPHAPIXELS
        memcpy(dds, "DDS ", 4);
        StoreLittleLong(dds+4, ddssize);
        StoreLittleLong(dds+8, dds_flags);
@@ -1208,7 +1212,7 @@ int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipunco
        return ret ? ddssize : -5;
 }
 
-rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
+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;
@@ -1252,7 +1256,11 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        dds_height = BuffLittleLong(dds+12);
        ddspixels = dds + 128;
 
-       flags &= ~TEXF_ALPHA;
+       if(r_texture_dds_load_alphamode.integer == 0)
+               if(!(dds_format_flags & 0x1)) // DDPF_ALPHAPIXELS
+                       flags &= ~TEXF_ALPHA;
+
+       //flags &= ~TEXF_ALPHA; // disabled, as we DISABLE TEXF_ALPHA in the alpha detection, not enable it!
        if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
        {
                // very sloppy BGRA 32bit identification
@@ -1266,15 +1274,23 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        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;
+               if((r_texture_dds_load_alphamode.integer == 1) && (flags & TEXF_ALPHA))
+               {
+                       // 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))
        {
+               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...
@@ -1289,16 +1305,38 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        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;
+               if(r_texture_dds_load_alphamode.integer && (flags & TEXF_ALPHA))
+               {
+                       if(r_texture_dds_load_alphamode.integer == 1)
+                       {
+                               // check alpha
+                               for (i = 0;i < size;i += bytesperblock)
+                                       if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
+                                       {
+                                               // NOTE: this assumes sizeof(unsigned int) == 4
+                                               unsigned int data = * (unsigned int *) &(ddspixels[i+4]);
+                                               // check if data, in base 4, contains a digit 3 (DXT1: transparent pixel)
+                                               if(data & (data<<1) & 0xAAAAAAAA)//rgh
+                                                       break;
+                                       }
+                               if (i < size)
+                                       textype = TEXTYPE_DXT1A;
+                               else
+                                       flags &= ~TEXF_ALPHA;
+                       }
+                       else
+                       {
+                               flags &= ~TEXF_ALPHA;
+                       }
+               }
        }
        else if (!memcmp(dds+84, "DXT3", 4))
        {
+               if(!vid.support.ext_texture_compression_s3tc)
+               {
+                       Mem_Free(dds);
+                       return NULL;
+               }
                textype = TEXTYPE_DXT3;
                bytesperblock = 16;
                bytesperpixel = 0;
@@ -1309,9 +1347,15 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
                        return NULL;
                }
+               // we currently always assume alpha
        }
        else if (!memcmp(dds+84, "DXT5", 4))
        {
+               if(!vid.support.ext_texture_compression_s3tc)
+               {
+                       Mem_Free(dds);
+                       return NULL;
+               }
                textype = TEXTYPE_DXT5;
                bytesperblock = 16;
                bytesperpixel = 0;
@@ -1322,6 +1366,7 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                        Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
                        return NULL;
                }
+               // we currently always assume alpha
        }
        else
        {
@@ -1371,7 +1416,29 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                }
        }
 
-       if (dds_miplevels > 1)
+       // this is where we apply gl_picmip
+       mippixels = ddspixels;
+       mipwidth = dds_width;
+       mipheight = dds_height;
+       while(miplevel >= 1 && dds_miplevels >= 1)
+       {
+               if (mipwidth <= 1 && mipheight <= 1)
+                       break;
+               mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+               mippixels += mipsize; // just skip
+               --dds_miplevels;
+               --miplevel;
+               if (mipwidth > 1)
+                       mipwidth >>= 1;
+               if (mipheight > 1)
+                       mipheight >>= 1;
+       }
+
+       // when not requesting mipmaps, do not load them
+       if(!(flags & TEXF_MIPMAP))
+               dds_miplevels = 0;
+
+       if (dds_miplevels >= 1)
                flags |= TEXF_MIPMAP;
        else
                flags &= ~TEXF_MIPMAP;
@@ -1391,8 +1458,8 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        glt->pool = pool;
        glt->chain = pool->gltchain;
        pool->gltchain = glt;
-       glt->inputwidth = dds_width;
-       glt->inputheight = dds_height;
+       glt->inputwidth = mipwidth;
+       glt->inputheight = mipheight;
        glt->inputdepth = 1;
        glt->flags = flags;
        glt->textype = texinfo;
@@ -1404,8 +1471,8 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        glt->bytesperpixel = texinfo->internalbytesperpixel;
        glt->sides = 1;
        glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
-       glt->tilewidth = dds_width;
-       glt->tileheight = dds_height;
+       glt->tilewidth = mipwidth;
+       glt->tileheight = mipheight;
        glt->tiledepth = 1;
 
        // texture uploading can take a while, so make sure we're sending keepalives
@@ -1418,11 +1485,9 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
        oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
        qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
        qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
-       mippixels = ddspixels;
-       mipwidth = dds_width;
-       mipheight = dds_height;
        mipcomplete = false;
-       for (mip = 0;mip < dds_miplevels+1;mip++)
+
+       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)
@@ -1446,7 +1511,7 @@ rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filen
                if (mipheight > 1)
                        mipheight >>= 1;
        }
-       if (dds_miplevels > 1 && !mipcomplete)
+       if (dds_miplevels >= 1 && !mipcomplete)
        {
                // need to set GL_TEXTURE_MAX_LEVEL
                qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
@@ -1564,5 +1629,5 @@ int R_PicmipForFlags(int flags)
                else
                        miplevel += gl_picmip_other.integer;
        }
-       return miplevel;
+       return max(0, miplevel);
 }