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 r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1", "0 = never upload textures until used, 1 = upload most textures before use (exceptions: rarely used skin colormap layers), 2 = upload all textures before use (can increase texture memory usage significantly)"};
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", "1", "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", "1", "whether to compress 2d (hud/menu) textures other than the font"};
+cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "1", "whether to compress lightmaps in q3bsp format levels"};
+cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "1", "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)"};
int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
int gl_filter_mag = GL_LINEAR;
// note: this must not conflict with TEXF_ flags in r_textures.h
// cleared when a texture is uploaded
-#define GLTEXF_UPLOAD 0x00010000
+#define GLTEXF_UPLOAD 0x00010000
// bitmask for mismatch checking
#define GLTEXF_IMPORTANTBITS (0)
// set when image is uploaded and freed
-#define GLTEXF_DESTROYED 0x00040000
+#define GLTEXF_DESTROYED 0x00040000
+// dynamic texture (treat texnum == 0 differently)
+#define GLTEXF_DYNAMIC 0x00080000
typedef struct textypeinfo_s
{
- int textype;
+ textype_t textype;
int inputbytesperpixel;
int internalbytesperpixel;
+ float glinternalbytesperpixel;
int glformat;
int glinternalformat;
}
textypeinfo_t;
-static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, GL_RGBA , 3};
-static textypeinfo_t textype_rgb = {TEXTYPE_RGB , 3, 3, GL_RGB , 3};
-static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, GL_RGBA , 3};
-static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, GL_RGBA , 4};
-static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, GL_RGBA , 4};
+static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 3};
+static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 4};
+static textypeinfo_t textype_palette_compress = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB};
+static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB};
+static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 3};
+static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 4};
+static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB};
+static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB};
+static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 3};
+static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 4};
+static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB};
+static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB};
#define GLTEXTURETYPE_1D 0
#define GLTEXTURETYPE_2D 1
// (must be identical in rtexture_t)
int texnum; // GL texture slot number
+ // dynamic texture stuff [11/22/2007 Black]
+ // used to hold the texture number of dirty textures
+ int dirtytexnum;
+ updatecallback_t updatecallback;
+ void *updatacallback_data;
+ // --- [11/22/2007 Black]
+
// 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;
// name of the texture (this might be removed someday), no duplicates
- char identifier[32];
+ 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
static unsigned char *texturebuffer;
static int texturebuffersize = 0;
-static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
+static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
{
- if (flags & TEXF_ALPHA)
+ if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && gl_support_texture_compression)
{
- switch(textype)
+ if (flags & TEXF_ALPHA)
{
- case TEXTYPE_PALETTE:
- return &textype_palette_alpha;
- case TEXTYPE_RGB:
- Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed");
- return NULL;
- case TEXTYPE_RGBA:
- return &textype_rgba_alpha;
- default:
- Host_Error("R_GetTexTypeInfo: unknown texture format");
- return NULL;
+ switch(textype)
+ {
+ case TEXTYPE_PALETTE:
+ return &textype_palette_alpha_compress;
+ case TEXTYPE_RGBA:
+ return &textype_rgba_alpha_compress;
+ case TEXTYPE_BGRA:
+ return &textype_bgra_alpha_compress;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format");
+ return NULL;
+ }
+ }
+ else
+ {
+ switch(textype)
+ {
+ case TEXTYPE_PALETTE:
+ return &textype_palette_compress;
+ case TEXTYPE_RGBA:
+ return &textype_rgba_compress;
+ case TEXTYPE_BGRA:
+ return &textype_bgra_compress;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format");
+ return NULL;
+ }
}
}
else
{
- switch(textype)
+ if (flags & TEXF_ALPHA)
{
- case TEXTYPE_PALETTE:
- return &textype_palette;
- case TEXTYPE_RGB:
- return &textype_rgb;
- case TEXTYPE_RGBA:
- return &textype_rgba;
- default:
- Host_Error("R_GetTexTypeInfo: unknown texture format");
- return NULL;
+ switch(textype)
+ {
+ case TEXTYPE_PALETTE:
+ return &textype_palette_alpha;
+ case TEXTYPE_RGBA:
+ return &textype_rgba_alpha;
+ case TEXTYPE_BGRA:
+ return &textype_bgra_alpha;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format");
+ return NULL;
+ }
+ }
+ else
+ {
+ switch(textype)
+ {
+ case TEXTYPE_PALETTE:
+ return &textype_palette;
+ case TEXTYPE_RGBA:
+ return &textype_rgba;
+ case TEXTYPE_BGRA:
+ return &textype_bgra;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format");
+ return NULL;
+ }
}
}
+ return NULL; // this line only to hush compiler warnings
+}
+
+// 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->dirtytexnum && glt->flags & GLTEXF_DYNAMIC ) {
+ glt->dirtytexnum = glt->texnum;
+ // mark it as dirty, so R_RealGetTexture gets called
+ glt->texnum = -1;
+ }
+}
+
+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;
+ glt->dirtytexnum = 0;
+}
+
+static void R_UpdateDynamicTexture(gltexture_t *glt) {
+ glt->texnum = glt->dirtytexnum;
+ // reset dirtytexnum again (not dirty anymore)
+ glt->dirtytexnum = 0;
+ // TODO: now assert that t->texnum != 0 ?
+ if( glt->updatecallback ) {
+ glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
+ }
}
static void R_UploadTexture(gltexture_t *t);
{
gltexture_t *glt;
glt = (gltexture_t *)rt;
+ if (glt->flags & GLTEXF_DYNAMIC)
+ R_UpdateDynamicTexture(glt);
if (glt->flags & GLTEXF_UPLOAD)
R_UploadTexture(glt);
+
return glt->texnum;
}
else
}
}
- return size * glt->textype->internalbytesperpixel * glt->sides;
+ return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
}
void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
Cvar_RegisterVariable (&r_lerpimages);
Cvar_RegisterVariable (&r_precachetextures);
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);
R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
}
CHECKGLERROR
}
-static void R_Upload(gltexture_t *glt, unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
+static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
{
int i, mip, width, height, depth;
GLint oldbindtexnum;
- unsigned char *prevbuffer;
+ const unsigned char *prevbuffer;
prevbuffer = data;
CHECKGLERROR
}
else if (glt->textype->textype == TEXTYPE_PALETTE)
{
- // promote paletted to RGBA, so we only have to worry about RGB and
- // RGBA in the rest of this code
- Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
+ // 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;
}
{
if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
{
- Image_Resample(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, glt->bytesperpixel, r_lerpimages.integer);
+ 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_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth, glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
prevbuffer = resizebuffer;
}
}
mip = 0;
+ if (gl_support_texture_compression)
+ {
+ 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_1D:
{
while (width > 1 || height > 1 || depth > 1)
{
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
prevbuffer = resizebuffer;
qglTexImage1D(GL_TEXTURE_1D, mip++, glt->glinternalformat, width, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
}
{
while (width > 1 || height > 1 || depth > 1)
{
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
prevbuffer = resizebuffer;
qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
}
{
while (width > 1 || height > 1 || depth > 1)
{
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->bytesperpixel);
+ 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, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
}
case GLTEXTURETYPE_CUBEMAP:
// convert and upload each side in turn,
// from a continuous block of input texels
- texturebuffer = prevbuffer;
+ 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_Resample(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, glt->bytesperpixel, r_lerpimages.integer);
+ 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_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth, glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
prevbuffer = resizebuffer;
}
mip = 0;
{
while (width > 1 || height > 1 || depth > 1)
{
- Image_MipReduce(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1, glt->bytesperpixel);
+ Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
prevbuffer = resizebuffer;
qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, GL_UNSIGNED_BYTE, prevbuffer);CHECKGLERROR
}
Con_Printf("R_UploadTexture: Texture %s already uploaded and destroyed. Can not upload original image again. Uploaded blank texture.\n", glt->identifier);
}
-static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, int textype, int texturetype, const unsigned char *data, const unsigned int *palette)
+static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
{
int i, size;
gltexture_t *glt;
}
}
break;
- case TEXTYPE_RGB:
- if (flags & TEXF_ALPHA)
- Host_Error("R_LoadTexture: RGB has no alpha, don't specify TEXF_ALPHA");
- break;
case TEXTYPE_RGBA:
+ case TEXTYPE_BGRA:
if (flags & TEXF_ALPHA)
{
flags &= ~TEXF_ALPHA;
glt->bytesperpixel = texinfo->internalbytesperpixel;
glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
glt->texnum = -1;
+ // init the dynamic texture attributes, too [11/22/2007 Black]
+ glt->dirtytexnum = 0;
+ glt->updatecallback = NULL;
+ glt->updatacallback_data = NULL;
if (data)
{
return (rtexture_t *)glt;
}
-rtexture_t *R_LoadTexture1D(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, int textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTexture1D(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
{
return R_SetupTexture(rtexturepool, identifier, width, 1, 1, 1, flags, textype, GLTEXTURETYPE_1D, data, palette);
}
-rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, int textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
{
return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, 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, int textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
{
return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
}
-rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, int textype, int flags, const unsigned int *palette)
+rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
{
return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
}
return rt ? ((gltexture_t *)rt)->inputheight : 0;
}
-void R_UpdateTexture(rtexture_t *rt, unsigned char *data, int x, int y, int width, int height)
+void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
{
gltexture_t *glt;
if (rt == NULL)