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;
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:
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)
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)
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;
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);
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;
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
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...
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;
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;
Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
return NULL;
}
+ // we currently always assume alpha
}
else
{
}
}
- 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;
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;
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
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)
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
else
miplevel += gl_picmip_other.integer;
}
- return miplevel;
+ return max(0, miplevel);
}