making a better texture managment system for tenebrae
making a better texture managment system for tenebrae
As the title says, im trying to refurbish tenebraes texture managment system.
So far i got most working but im stumbling on a few buggers so anyone willing to give a hand is most welcome.
Whats done so far:
Moved from using boolean checks to struct to avoid having a ton of different image upload code.
Added correct check for fullbrights on luma.
Updated zlib Png and Tga code to newest.
Bugs:
Moving the genNormalmap function from GL_UploadBump to GL_UpLoad8 breaks bumpmapping causing all kinds of weird behaviour with both bump and gloss (gloss in stripes vertical and horizontal).
Moving to a struct based system broke luma allthough i think it was allready broken. Fullbrights on plats doors etc. Fixed by doing a check for fullbrights in gl_model.c Mod_LoadTextures.
New Tga code can handle gray tga but im not sure if my size check (1 for gray 4 for color) works as expected. Png works like a charm though.
So far i got most working but im stumbling on a few buggers so anyone willing to give a hand is most welcome.
Whats done so far:
Moved from using boolean checks to struct to avoid having a ton of different image upload code.
Added correct check for fullbrights on luma.
Updated zlib Png and Tga code to newest.
Bugs:
Moving the genNormalmap function from GL_UploadBump to GL_UpLoad8 breaks bumpmapping causing all kinds of weird behaviour with both bump and gloss (gloss in stripes vertical and horizontal).
Moving to a struct based system broke luma allthough i think it was allready broken. Fullbrights on plats doors etc. Fixed by doing a check for fullbrights in gl_model.c Mod_LoadTextures.
New Tga code can handle gray tga but im not sure if my size check (1 for gray 4 for color) works as expected. Png works like a charm though.
Productivity is a state of mind.
Re: making a better texture managment system for tenebrae
If you want, take a look at QuakeForge's tga code: it's very fast and for reading, fully implemented (I went through the spec), though I can't guarantee correctness for all cases.
Unless I'm mistaken, GL_UpLoad8 doesn't sound appropriate for normal maps, as they would be 3xN bit (usually 24) rgb (though I've heard of using alpha for height). I haven't tried to implement normal or bump maps yet, so I can't say more. However...
Unless I'm mistaken, GL_UpLoad8 doesn't sound appropriate for normal maps, as they would be 3xN bit (usually 24) rgb (though I've heard of using alpha for height). I haven't tried to implement normal or bump maps yet, so I can't say more. However...
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
Re: making a better texture managment system for tenebrae
its fullbright if it uses the last 16 bytes of the palette. though you can measure how many fullbright palette indexes there should be by comparing different parts of the colormap.
I think fullbrights are generally additively blended, which is wrong really, but means that you can just use an rgb texture with the non-fullbright pixels coloured black.
tenebrae traditionally takes the regular 8bit paletted image, generates height as 4*palette index, then converts that height into normals.
whereas if you give it a _bump texture, it does exactly the same but doesn't multiply it by 4.
while a _norm texture is directly uploaded as-is. many existing _norm textures have the height at that pixel stored in the alpha channel for various sorts of offset mapping, but not sure tenebrae ever supported that.
I think fullbrights are generally additively blended, which is wrong really, but means that you can just use an rgb texture with the non-fullbright pixels coloured black.
tenebrae traditionally takes the regular 8bit paletted image, generates height as 4*palette index, then converts that height into normals.
whereas if you give it a _bump texture, it does exactly the same but doesn't multiply it by 4.
while a _norm texture is directly uploaded as-is. many existing _norm textures have the height at that pixel stored in the alpha channel for various sorts of offset mapping, but not sure tenebrae ever supported that.
Re: making a better texture managment system for tenebrae
The fullbright range is actually in the colormap:
It's the last byte of the colormap file, which is why the size of colormap.lmp is 16385 instead of 16384. It's also why you will not find much (if any) mention of fullbrights in the software renderer.
As an aside, QF uses alpha blending for fullbrights. ie, the pixels are actually replaced rather than added. I guess one of the few engines that gets it "right".
Code: Select all
viddef.fullbright = 256 - viddef.colormap8[256 * VID_GRADES];
As an aside, QF uses alpha blending for fullbrights. ie, the pixels are actually replaced rather than added. I guess one of the few engines that gets it "right".
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
Re: making a better texture managment system for tenebrae
'right' is defined by what the content you're targetting uses.
Re: making a better texture managment system for tenebrae
True, thus the quotes
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
Re: making a better texture managment system for tenebrae
ok gonna use your input and try to improve on it thanks.
Productivity is a state of mind.
Re: making a better texture managment system for tenebrae
btw tenebrae does no checking at all for fullbright in GL_Draw.c it just uploads the _luma image directly to GL_Upload32 with alpha set hence my comment about it being sortof broken initially.
Though the blend modes in GL_Rsurf.c and GL_Rmain.c should handle that. Something is going wrong though so i might have to move those out and into the actuall function for luma.
Tenebrae uses an odd system GL_Upload8 handles both normal bump and gloss maps besides checking for alpha pixels and doing palette conversions. So its a bit hard working with.
Though the blend modes in GL_Rsurf.c and GL_Rmain.c should handle that. Something is going wrong though so i might have to move those out and into the actuall function for luma.
Tenebrae uses an odd system GL_Upload8 handles both normal bump and gloss maps besides checking for alpha pixels and doing palette conversions. So its a bit hard working with.
Productivity is a state of mind.
Re: making a better texture managment system for tenebrae
slowly getting there...
fixed fullbrights on plats/doors still todo fullbright on models.
bit of texture corruption detected (might be my updated tga code hmm ???).
cubemap projection acts a little weird on the mirror effect (works fine for the lavaball effect).
seems my checking for fullbright on textures instead of relying on the blendmodes and user quality also fixed a long standing bug where the image went dark after being in slime/lava (odd one ?).
fixed fullbrights on plats/doors still todo fullbright on models.
bit of texture corruption detected (might be my updated tga code hmm ???).
cubemap projection acts a little weird on the mirror effect (works fine for the lavaball effect).
seems my checking for fullbright on textures instead of relying on the blendmodes and user quality also fixed a long standing bug where the image went dark after being in slime/lava (odd one ?).
Productivity is a state of mind.
Re: making a better texture managment system for tenebrae
Ok first succes story.
guess im getting there
code for all this untill now
not including stuff made in other source files heh.
texture sorting is really messy ugh
guess im getting there
code for all this untill now
Code: Select all
/* messy stuff below warning */
static unsigned int trans[4096*4096];
static unsigned char glosspix[4096*4096];
static unsigned char bumppix[4096*4096];
/*
================
GL_FindTexture
================
*/
int GL_FindTexture (char *identifier)
{
int i;
gltexture_t *glt;
for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
{
if (!strcmp (identifier, glt->identifier))
{
return gltextures[i].texnum;
}
}
return -1;
}
/*
================
GL_ResampleTextureLerpLine
Interpolates between pixels on line - Eradicator
================
*/
void GL_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
{
int j, xi, oldx = 0, f, fstep, endx;
fstep = (int) (inwidth * 65536.0f / outwidth);
endx = (inwidth-1);
for (j = 0,f = 0; j < outwidth; j++, f += fstep)
{
xi = (int) f >> 16;
if (xi != oldx)
{
in += (xi - oldx) * 4;
oldx = xi;
}
if (xi < endx)
{
int lerp = f & 0xFFFF;
*out++ = (byte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
*out++ = (byte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
*out++ = (byte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
*out++ = (byte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
}
else
{
*out++ = in[0];
*out++ = in[1];
*out++ = in[2];
*out++ = in[3];
}
}
}
/*
================
GL_ResampleTexture
================
*/
void GL_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
{
//New Interpolated Texture Code - Eradicator
int i, j, yi, oldy, f, fstep, endy = (inheight-1);
byte *inrow, *out, *row1, *row2;
out = outdata;
fstep = (int) (inheight*65536.0f/outheight);
row1 = malloc(outwidth*4);
row2 = malloc(outwidth*4);
inrow = indata;
oldy = 0;
GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
for (i = 0, f = 0; i < outheight; i++,f += fstep)
{
yi = f >> 16;
if (yi < endy)
{
int lerp = f & 0xFFFF;
if (yi != oldy)
{
inrow = (byte *)indata + inwidth*4*yi;
if (yi == oldy + 1)
{
memcpy(row1, row2, outwidth * 4);
}
else
{
GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
}
GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
oldy = yi;
}
j = outwidth - 4;
while(j >= 0)
{
out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
out[ 4] = (byte) ((((row2[ 4] - row1[ 4]) * lerp) >> 16) + row1[ 4]);
out[ 5] = (byte) ((((row2[ 5] - row1[ 5]) * lerp) >> 16) + row1[ 5]);
out[ 6] = (byte) ((((row2[ 6] - row1[ 6]) * lerp) >> 16) + row1[ 6]);
out[ 7] = (byte) ((((row2[ 7] - row1[ 7]) * lerp) >> 16) + row1[ 7]);
out[ 8] = (byte) ((((row2[ 8] - row1[ 8]) * lerp) >> 16) + row1[ 8]);
out[ 9] = (byte) ((((row2[ 9] - row1[ 9]) * lerp) >> 16) + row1[ 9]);
out[10] = (byte) ((((row2[10] - row1[10]) * lerp) >> 16) + row1[10]);
out[11] = (byte) ((((row2[11] - row1[11]) * lerp) >> 16) + row1[11]);
out[12] = (byte) ((((row2[12] - row1[12]) * lerp) >> 16) + row1[12]);
out[13] = (byte) ((((row2[13] - row1[13]) * lerp) >> 16) + row1[13]);
out[14] = (byte) ((((row2[14] - row1[14]) * lerp) >> 16) + row1[14]);
out[15] = (byte) ((((row2[15] - row1[15]) * lerp) >> 16) + row1[15]);
out += 16;
row1 += 16;
row2 += 16;
j -= 4;
}
if (j & 2)
{
out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
out[ 4] = (byte) ((((row2[ 4] - row1[ 4]) * lerp) >> 16) + row1[ 4]);
out[ 5] = (byte) ((((row2[ 5] - row1[ 5]) * lerp) >> 16) + row1[ 5]);
out[ 6] = (byte) ((((row2[ 6] - row1[ 6]) * lerp) >> 16) + row1[ 6]);
out[ 7] = (byte) ((((row2[ 7] - row1[ 7]) * lerp) >> 16) + row1[ 7]);
out += 8;
row1 += 8;
row2 += 8;
}
if (j & 1)
{
out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
out += 4;
row1 += 4;
row2 += 4;
}
row1 -= outwidth*4;
row2 -= outwidth*4;
}
else
{
if (yi != oldy)
{
inrow = (byte *)indata + inwidth*4*yi;
if (yi == oldy + 1)
{
memcpy(row1, row2, outwidth * 4);
}
else
{
GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
}
oldy = yi;
}
memcpy(out, row1, outwidth * 4);
}
}
free(row1);
free(row2);
}
/*
================
GL_Resample8BitTexture -- JACK
================
*/
void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight)
{
int i, j;
unsigned char *inrow;
unsigned frac, fracstep;
fracstep = inwidth*0x10000/outwidth;
for (i=0 ; i<outheight ; i++, out += outwidth)
{
inrow = in + inwidth*(i*inheight/outheight);
frac = fracstep >> 1;
for (j=0 ; j<outwidth ; j+=4)
{
out[j] = inrow[frac>>16];
frac += fracstep;
out[j+1] = inrow[frac>>16];
frac += fracstep;
out[j+2] = inrow[frac>>16];
frac += fracstep;
out[j+3] = inrow[frac>>16];
frac += fracstep;
}
}
}
/*
================
GL_MipMap
Operates in place, quartering the size of the texture
================
*/
void GL_MipMap (byte *in, int width, int height)
{
int i, j;
byte *out;
width <<=2;
height >>= 1;
out = in;
for (i=0 ; i<height ; i++, in+=width)
{
for (j=0 ; j<width ; j+=8, out+=4, in+=8)
{
out[0] = (in[0] + in[4] + in[width+0] + in[width+4])>>2;
out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2;
out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2;
out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2;
}
}
}
/*
================
GL_MipMapNormal
Operates in place, quartering the size of the texture
Works on normal maps instead of rgb
================
*/
void GL_MipMapNormal (byte *in, int width, int height)
{
int i, j;
byte *out;
float x, y, z, l, mag00, mag01, mag10, mag11;
width <<= 2;
height >>= 1;
out = in;
for (i=0 ; i<height ; i++, in+=width)
{
for (j=0 ; j<width ; j+=8, out+=4, in+=8)
{
mag00 = ONEOVER255 * in[3];
mag01 = ONEOVER255 * in[7];
mag10 = ONEOVER255 * in[width+3];
mag11 = ONEOVER255 * in[width+7];
x = mag00*(ONEOVER127*in[0]-1.0)+
mag01*(ONEOVER127*in[4]-1.0)+
mag10*(ONEOVER127*in[width+0]-1.0)+
mag11*(ONEOVER127*in[width+4]-1.0);
y = mag00*(ONEOVER127*in[1]-1.0)+
mag01*(ONEOVER127*in[5]-1.0)+
mag10*(ONEOVER127*in[width+1]-1.0)+
mag11*(ONEOVER127*in[width+5]-1.0);
z = mag00*(ONEOVER127*in[2]-1.0)+
mag01*(ONEOVER127*in[6]-1.0)+
mag10*(ONEOVER127*in[width+2]-1.0)+
mag11*(ONEOVER127*in[width+6]-1.0);
l = sqrt(x * x + y * y + z * z);
if (l == 0.0)
{
x = 0.0;
y = 0.0;
z = 1.0;
}
else
{
//normalize it.
l=1/l;
x *=l;
y *=l;
z *=l;
}
out[0] = (unsigned char)128 + 127*x;
out[1] = (unsigned char)128 + 127*y;
out[2] = (unsigned char)128 + 127*z;
l = l/4.0;
if (l > 1.0)
{
out[3] = 255;
}
else
{
out[3] = (byte)(255.0 * l);
}
}
}
}
/*
================
GL_MipMap8Bit
Mipping for 8 bit textures
================
*/
void GL_MipMap8Bit (byte *in, int width, int height)
{
int i, j;
unsigned short r, g, b;
byte *out, *at1, *at2, *at3, *at4;
height >>= 1;
out = in;
for (i=0 ; i<height ; i++, in+=width)
{
for (j=0 ; j<width ; j+=2, out+=1, in+=2)
{
at1 = (byte *) (d_8to24table + in[0]);
at2 = (byte *) (d_8to24table + in[1]);
at3 = (byte *) (d_8to24table + in[width+0]);
at4 = (byte *) (d_8to24table + in[width+1]);
r = (at1[0]+at2[0]+at3[0]+at4[0]);
r>>=5;
g = (at1[1]+at2[1]+at3[1]+at4[1]);
g>>=5;
b = (at1[2]+at2[2]+at3[2]+at4[2]);
b>>=5;
out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)];
}
}
}
/*
===============
GL_MakeNormal
===============
*/
void GL_MakeNormal(byte *in, int width, int height)
{
int i, j;
byte *out;
float x, y, z, l;
width <<= 2;
out = in;
for (i=0 ; i<height ; i++)
{
for (j=0 ; j<width ; j+=4, out+=4, in+=4)
{
x = (ONEOVER127 * in[0] - 1.0);
y = (ONEOVER127 * in[1] - 1.0);
z = (ONEOVER127 * in[2] - 1.0);
l = sqrt(x * x + y * y + z * z);
if (l == 0.0f)
{
x = 0.0;
y = 0.0;
z = 1.0;
}
else
{
//normalize it.
l = 1.0f / l;
x *=l;
y *=l;
z *=l;
}
out[0] = (unsigned char)128 + 127 * x;
out[1] = (unsigned char)128 + 127 * y;
out[2] = (unsigned char)128 + 127 * z;
}
}
}
/*
===============
GL_NormalFromHeightMap
===============
*/
unsigned int *GL_NormalFromHeightMap(byte *in, int width, int height, int mode)
{
int i, j, wr, hr;
unsigned char r, g, b;
static unsigned int out[4096*4096];
float sqlen, reciplen, nx, ny, nz;
float c, cx, cy, dcx, dcy;
wr = width;
hr = height;
for (i=0; i<height; i++)
{
for (j=0; j<width; j++)
{
/* Expand [0,255] texel values to the [0,1] range. */
c = in[i * wr + j] * ONEOVER255;
/* Expand the texel to its right. */
cx = in[i * wr + (j + 1) % wr] * ONEOVER255;
/* Expand the texel one up. */
cy = in[((i + 1) % hr) * wr + j] * ONEOVER255;
/* scale it according to source */
dcx = (mode & TEX_SCALE) ? 10.0f : 4.0f * (c - cx);
dcy = (mode & TEX_SCALE) ? 10.0f : 4.0f * (c - cy);
/* Normalize the vector. */
sqlen = dcx * dcx + dcy * dcy + 1;
reciplen = 1.0f / (float) sqrt(sqlen);
nx = dcx * reciplen;
ny = -dcy * reciplen;
nz = reciplen;
/* Repack the normalized vector into an RGB unsigned byte
vector in the normal map image. */
r = (byte) (128 + 127 * nx);
g = (byte) (128 + 127 * ny);
b = (byte) (128 + 127 * nz);
/* The highest resolution mipmap level always has a
unit length magnitude. */
out[i * width + j] = LittleLong ((in[i * wr + j] << 24) | (b << 16) | (g << 8) | (r)); // <AWE> Added support for big endian.
}
}
return &out[0];
}
/*
===============
GL_PackGloss
Packs a gloss byte in destination
===============
*/
void GL_PackGloss(byte *gloss, unsigned *dest, int length)
{
int i;
for (i=0; i<length; i++, gloss++, dest++)
{
*dest = *dest & LittleLong (0x00FFFFFF); // <AWE> Added support for big endian.
*dest = *dest | LittleLong (*gloss << 24); // <AWE> Added support for big endian.
}
}
/*
================
ScaleDimensions
================
*/
void GL_ScaleDimensions (int width, int height, int *scaled_width, int *scaled_height, int mode)
{
int maxsize;
for (*scaled_width = 1 ; *scaled_width < width ; *scaled_width *= 2);
for (*scaled_height = 1 ; *scaled_height < height ; *scaled_height *= 2);
if (mode & TEX_MIPMAP)
{
*scaled_width >>= (int)gl_picmip.value;
*scaled_height >>= (int)gl_picmip.value;
}
maxsize = (mode & TEX_MIPMAP) ? gl_max_size.value : gl_max_size_default;
*scaled_width = bound(1, *scaled_width, maxsize);
*scaled_height = bound(1, *scaled_height, maxsize);
}
/*
===============
GL_UploadColor
===============
*/
void GL_UploadColor (unsigned *data, int width, int height, int mode)
{
static unsigned scaled[4096*4096];
int scaled_width, scaled_height;
int internal_format, format;
if ( gl_gray_colormaps.value )
{
Q_memset(data, 0x7f, width * height * 4);
}
GL_ScaleDimensions (width, height, &scaled_width, &scaled_height, mode);
format = ( gl_texcomp && ((int)gl_compress_textures.value) & 1 ) ? GL_COMPRESSED_RGBA_ARB : GL_RGBA;
internal_format = (mode & TEX_ALPHA) ? gl_alpha_format : gl_solid_format;
if (scaled_width == width && scaled_height == height)
{
if (!(mode & TEX_MIPMAP))
{
glTexImage2D (GL_TEXTURE_2D, 0, internal_format, scaled_width, scaled_height, 0, format, GL_UNSIGNED_BYTE, data);
goto done;
}
memcpy (scaled, data, width * height * 4);
}
else
{
GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);
}
glTexImage2D (GL_TEXTURE_2D, 0, internal_format, scaled_width, scaled_height, 0, format, GL_UNSIGNED_BYTE, scaled);
if (mode & TEX_MIPMAP)
{
int miplevel = 0;
while (scaled_width > 1 || scaled_height > 1)
{
GL_MipMap ((byte *)scaled, scaled_width, scaled_height);
scaled_width >>= 1;
scaled_height >>= 1;
if (scaled_width < 1)
{
scaled_width = 1;
}
if (scaled_height < 1)
{
scaled_height = 1;
}
miplevel++;
glTexImage2D (GL_TEXTURE_2D, miplevel, internal_format, scaled_width, scaled_height, 0, format, GL_UNSIGNED_BYTE, scaled);
}
}
done:
;
if (mode & TEX_MIPMAP)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
else
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
if (gl_texturefilteranisotropic)
{
glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl_textureanisotropylevel);
}
}
void GL_UploadColor8 (byte *data, int width, int height, int mode)
{
static unsigned char scaled[4096*4096];
int scaled_width, scaled_height;
GL_ScaleDimensions (width, height, &scaled_width, &scaled_height, mode);
if (scaled_width == width && scaled_height == height)
{
if (!(mode & TEX_MIPMAP))
{
glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data);
goto done;
}
memcpy (scaled, data, width*height);
}
else
{
GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height);
}
glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
if (mode & TEX_MIPMAP)
{
int miplevel = 0;
while (scaled_width > 1 || scaled_height > 1)
{
GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height);
scaled_width >>= 1;
scaled_height >>= 1;
if (scaled_width < 1)
{
scaled_width = 1;
}
if (scaled_height < 1)
{
scaled_height = 1;
}
miplevel++;
glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
}
}
done:
;
if (mode & TEX_MIPMAP)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
else
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
if (gl_texturefilteranisotropic)
{
glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl_textureanisotropylevel);
}
}
void GL_UploadNormal(unsigned int *data, int width, int height, int mode)
{
static unsigned int scaled[4096*4096];
int scaled_width, scaled_height;
int internal_format, format;
//Resize to power of 2 and maximum texture size
GL_ScaleDimensions (width, height, &scaled_width, &scaled_height, mode);
//To resize or not to resize
if (scaled_width == width && scaled_height == height)
{
memcpy (scaled, data, width*height*4);
scaled_width = width;
scaled_height = height;
}
else
{
GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);
}
GL_MakeNormal((byte *)scaled, scaled_width, scaled_height);
format = ( gl_texcomp && ((int)gl_compress_textures.value) & 1 ) ? GL_COMPRESSED_RGBA_ARB : GL_RGBA;
internal_format = (mode & TEX_ALPHA) ? gl_alpha_format : gl_solid_format;
glTexImage2D (GL_TEXTURE_2D, 0, internal_format, scaled_width, scaled_height, 0, format, GL_UNSIGNED_BYTE, scaled);
if (mode & TEX_MIPMAP)
{
int miplevel = 0;
while (scaled_width > 1 || scaled_height > 1)
{
GL_MipMapNormal((byte *)scaled, scaled_width, scaled_height);
scaled_width >>= 1;
scaled_height >>= 1;
if (scaled_width < 1)
{
scaled_width = 1;
}
if (scaled_height < 1)
{
scaled_height = 1;
}
miplevel++;
glTexImage2D (GL_TEXTURE_2D, miplevel, internal_format, scaled_width, scaled_height, 0, format, GL_UNSIGNED_BYTE, scaled);
}
}
if (mode & TEX_MIPMAP)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
else
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
if (gl_texturefilteranisotropic)
{
glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl_textureanisotropylevel);
}
}
void GL_UploadBump(byte *data, int width, int height, int mode)
{
static unsigned char scaled[4096*4096];
byte *scalednormal;
int scaled_width, scaled_height;
int internal_format, format;
//Resize to power of 2 and maximum texture size
GL_ScaleDimensions (width, height, &scaled_width, &scaled_height, mode);
//To resize or not to resize
if (scaled_width == width && scaled_height == height)
{
memcpy (scaled, data, width*height);
scaled_width = width;
scaled_height = height;
}
else
{
//Just picks pixels so grayscale is equivalent with 8 bit.
GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height);
}
scalednormal = (unsigned char *) GL_NormalFromHeightMap(scaled, width, height, mode);
format = ( gl_texcomp && ((int)gl_compress_textures.value) & 1 ) ? GL_COMPRESSED_RGBA_ARB : GL_RGBA;
internal_format = (mode & TEX_ALPHA) ? gl_alpha_format : gl_solid_format;
glTexImage2D (GL_TEXTURE_2D, 0, internal_format, scaled_width, scaled_height, 0, format, GL_UNSIGNED_BYTE, scalednormal);
if (mode & TEX_MIPMAP)
{
int miplevel = 0;
while (scaled_width > 1 || scaled_height > 1)
{
GL_MipMapNormal(scalednormal, scaled_width, scaled_height);
scaled_width >>= 1;
scaled_height >>= 1;
if (scaled_width < 1)
{
scaled_width = 1;
}
if (scaled_height < 1)
{
scaled_height = 1;
}
miplevel++;
glTexImage2D (GL_TEXTURE_2D, miplevel, internal_format, scaled_width, scaled_height, 0, format, GL_UNSIGNED_BYTE, scalednormal);
}
}
if (mode & TEX_MIPMAP)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
else
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
if (gl_texturefilteranisotropic)
{
glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl_textureanisotropylevel);
}
}
/*
===============
GL_GetOverrideName
===============
*/
void GL_GetOverrideName(char *identifier, char *tail, char *dest)
{
unsigned i;
sprintf(dest, "override/%s%s.ext", identifier, tail);
for (i=0; i<strlen(dest); i++)
{
if (dest[i] == '*')
{
dest[i] = '#';
}
}
}
/*
===============
GL_ConvertPalette
Handles palette conversion and searches
the image for alpha pixels.
===============
*/
void GL_ConvertPalette (unsigned char *data, unsigned int *trans2, unsigned char *bumppix2, int size, int mode)
{
int i, p;
/* check pixels */
if (mode & TEX_FULLBRIGHT)
{
// this is a fullbright mask, so make all non-fullbright
// colors transparent
mode |= TEX_ALPHA;
for (i=0 ; i<size ; i++)
{
p = data[i];
if (p < 224)
{
// transparent
trans2[i] = d_8to24table[p] & 0x00FFFFFF;
bumppix2[i] = d_8to8graytable[p] & 0x00FFFFFF;
}
else
{
// fullbright
trans2[i] = d_8to24table[p];
bumppix2[i] = d_8to8graytable[p];
}
}
}
else if (mode & TEX_ALPHA)
{
/* set to invalid at start */
mode &= ~TEX_ALPHA;
for (i=0 ; i<size ; i++)
{
p = data[i];
/* if byte equals 255 then we have an alpha channel */
if (p == 255)
{
mode |= TEX_ALPHA;
}
trans2[i] = d_8to24table[p];
bumppix2[i] = d_8to8graytable[p];
}
}
else
{
/* no alpha convert the palette and upload like a 3 component texture */
mode &= ~TEX_ALPHA;
for (i=0 ; i<size ; i+=4)
{
trans2[i] = d_8to24table[data[i]];
trans2[i+1] = d_8to24table[data[i+1]];
trans2[i+2] = d_8to24table[data[i+2]];
trans2[i+3] = d_8to24table[data[i+3]];
bumppix2[i] = d_8to8graytable[data[i]];
bumppix2[i+1] = d_8to8graytable[data[i+1]];
bumppix2[i+2] = d_8to8graytable[data[i+2]];
bumppix2[i+3] = d_8to8graytable[data[i+3]];
}
}
}
/*
===============
GL_ConvertPixels
Handles Color conversion
===============
*/
void GL_ConvertPixels (unsigned int *in, unsigned char *out, int size)
{
int i;
byte r, g, b;
for (i=0; i<size; i++)
{
r = (in[i] & 0x00FF0000) >> 16;
g = (in[i] & 0x0000FF00) >> 8;
b = (in[i] & 0x000000FF);
out[i] = (r + g + b) / 3;
}
}
/*
===============
GL_LoadFromFile
Attempt at making a better texture managment system.
Still a few todo's models are allways fullbright.
===============
*/
void GL_LoadFromFile (char *identifier, int width, int height, byte *data, int mode)
{
int size;
char colormap[MAX_OSPATH];
char normalmap[MAX_OSPATH];
char bumpmap[MAX_OSPATH];
char glossmap[MAX_OSPATH];
// get size
size = width * height;
// upload a colormap
GL_GetOverrideName(identifier, "", colormap);
if (Image_LoadTextureInPlace(colormap, 4, (unsigned char *)&trans[0], &width, &height))
{
Con_DPrintf("Overriding colormap for %s\n", identifier);
mode |= TEX_SCALE;
GL_ConvertPixels (trans, bumppix, size);
}
else
{
Con_DPrintf("Applying 8 to 32 bit conversion for %s\n", identifier);
GL_ConvertPalette (data, trans, bumppix, size, mode);
}
//upload scrap texels
if (!strcmp("scrap", identifier))
{
GL_UploadColor (trans, width, height, mode);
return;
}
//force it to upload a 32 bit texture
GL_Bind(texture_extension_number);
GL_UploadColor (trans, width, height, mode);
texture_extension_number++;
if (!COM_CheckParm("-nobumpmaps") && (sh_bumpmaps.value) && (mode & TEX_BUMP))
{
GL_Bind(texture_extension_number);
GL_GetOverrideName(identifier, "_norm", normalmap);
// now try a normalmap
if (Image_LoadTextureInPlace(normalmap, 4, (unsigned char *)&trans[0], &width, &height))
{
int gwidth = 0, gheight = 0;
Con_DPrintf("Overriding normal map for %s\n",identifier);
GL_GetOverrideName(identifier, "_gloss", glossmap);
if (Image_LoadTextureInPlace(glossmap, 1, &glosspix[0], &gwidth, &gheight))
{
Con_DPrintf("Overriding gloss map for %s\n", identifier);
}
else
{
//set some gloss by default
gwidth = width;
gheight = height;
Q_memset(&glosspix[0], 255*gl_gloss.value, size);
}
// if the textures match pack gloss in the alpha channel of dest
if ((gwidth != width) && (gheight != height))
{
Con_SafePrintf("\002Warning: %s Gloss map must have same size as color map\n", identifier);
}
else
{
GL_PackGloss(glosspix, trans, size);
}
GL_UploadNormal (trans, width, height, mode);
}
else
{
// if no normalmap try a greyscale bumpmap
GL_GetOverrideName(identifier, "_bump", bumpmap);
if (Image_LoadTextureInPlace(bumpmap, 1, (unsigned char *)&bumppix[0], &width, &height))
{
int gwidth = 0, gheight = 0;
Con_DPrintf("Overriding bump map for %s\n", identifier);
GL_GetOverrideName(identifier, "_gloss", glossmap);
if (Image_LoadTextureInPlace(glossmap, 1, &glosspix[0], &gwidth, &gheight))
{
Con_DPrintf("Overriding gloss map for %s\n", identifier);
}
else
{
//set some gloss by default
gwidth = width;
gheight = height;
Q_memset(&glosspix[0], 255 * gl_gloss.value, size);
}
// if the textures match pack gloss in the alpha channel of dest
if ((gwidth != width) && (gheight != height))
{
Con_SafePrintf("\002Warning: %s Gloss map must have same size as color map\n", identifier);
}
else
{
GL_PackGloss(glosspix, (unsigned int *)bumppix, size);
}
}
GL_UploadBump (bumppix, width, height, mode);
}
}
texture_extension_number++;
}
/*
===============
GL_LoadLumaTexture
===============
*/
int GL_LoadLumaTexture(char *identifier, int width, int height, int mode)
{
int i;
char lumamap[MAX_OSPATH];
if (gl_gray_colormaps.value) return;
GL_GetOverrideName(identifier, "_luma", lumamap);
if (Image_LoadTextureInPlace(lumamap, 4, (unsigned char *)&trans[0], &width, &height))
{
/* scale the heightmap on lumas wtf ??? */
mode |= TEX_SCALE;
/* check if the alpha channel really works */
if (mode & TEX_ALPHA)
{
mode &= ~TEX_ALPHA;
for (i=0 ; i<width*height ; i++)
{
if ((((trans)[i] >> 24) & 0xFF) < 255)
{
mode |= TEX_ALPHA;
break;
}
}
}
GL_Bind(texture_extension_number);
GL_UploadColor (trans, width, height, mode);
texture_extension_number++;
return texture_extension_number-1;
}
return 0;
}
/*
================
GL_LoadTexture
================
*/
int GL_LoadTexture (char *identifier, int width, int height, byte *data, int mode)
{
int i, scaled_width, scaled_height;
gltexture_t *glt;
GL_ScaleDimensions (width, height, &scaled_width, &scaled_height, mode);
// see if the texture is allready present
if (identifier[0])
{
for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
{
if (!strncmp(identifier, glt->identifier, sizeof(glt->identifier)))
{
if (width != glt->width || height != glt->height || scaled_width != glt->scaled_width || scaled_height != glt->scaled_height)
{
Con_Printf ("%s: warning: textures dont match\n", identifier);
}
return gltextures[i].texnum;
}
}
}
glt = &gltextures[numgltextures];
numgltextures++;
strncpy (glt->identifier, identifier, sizeof(glt->identifier) -1);
glt->texnum = texture_extension_number;
glt->width = width;
glt->height = height;
glt->scaled_width = scaled_width;
glt->scaled_height = scaled_height;
glt->mode = mode;
GL_LoadFromFile(identifier, width, height, data, mode);
return texture_extension_number-2;
}
texture sorting is really messy ugh
Productivity is a state of mind.
Re: making a better texture managment system for tenebrae
Source code.
http://code.google.com/p/realm/download ... z&can=2&q=
If someone could have a look it would be much appreciated as im running into a weird bug.
Try diving in slime and notice the lightmaps turning off... also happens with lava but not with water.
Source has project files for both msvc 2010 and codeblocks.
http://code.google.com/p/realm/download ... z&can=2&q=
If someone could have a look it would be much appreciated as im running into a weird bug.
Try diving in slime and notice the lightmaps turning off... also happens with lava but not with water.
Source has project files for both msvc 2010 and codeblocks.
Productivity is a state of mind.
Re: making a better texture managment system for tenebrae
agh ok the diving in slime lava thingy was the pain flash resetting the lightmap brightness tenebrae doesnt seem to take into account that it could have invalid values so i rewritten the entire thing.
another bug to look at is when changing level while being damaged the pain flash stays on in the next level ...
besides the above i think i managed to make it a whole lot better now.
another bug to look at is when changing level while being damaged the pain flash stays on in the next level ...
besides the above i think i managed to make it a whole lot better now.
Productivity is a state of mind.
Re: making a better texture managment system for tenebrae
Fullbrights - with an alpha/replace blend instead of add, check out the red pixels on the right-hand arrow leading to e1 in start.bsp - they're actually darker than if fullbrights were disabled (especially noticeable with a LIT file).
The formula I use is "final = max (diffuse * lightmap * overbright, fullbright)" which corrects this but - as Spike pointed out - won't work well with external textures which assume additive blending. IMO getting the native texture look correct should overrule that. Some day I'll do a second shader for externals.
The formula I use is "final = max (diffuse * lightmap * overbright, fullbright)" which corrects this but - as Spike pointed out - won't work well with external textures which assume additive blending. IMO getting the native texture look correct should overrule that. Some day I'll do a second shader for externals.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
We knew the words, we knew the score, we knew what we were fighting for
Re: making a better texture managment system for tenebrae
The screenshot is all nifty looking and stuff.
The night is young. How else can I annoy the world before sunsrise? Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Re: making a better texture managment system for tenebrae
allmost done with a modder friendly version so if someone wants a bare bone engine with realtime lights bumpmapping and shadow volumes this might fit just that
code was cleaned up extensively and i added overbrights and hardware gamma. fullbrights tended to look a bit weird in this so i ditched it but if someone like they can allways add it. Its not really nessesary though if you have luma textures. Also it uses qc for a few things like halo effects dynamic color maps and noshadow but a skilled programmer could probably extended the basic rt_light system in it to cover these instead (much easier).
theres a few places where i lost track and i commented these so if anyone experiences problems try looking at my comments and change the code back as outlined by me.
most of these are related to blendmodes and which tmu is active comming out of a function.
example here.
the code is a bit less of a mess but i should probably merge the luma poly renderer with the brush poly renderer to cut down on uneeded gunk.
the lightmap code was allmost ripped apart cause i found out that the dynamic light code was 98% disabled so i lobotomized it completely and now only the lightmap creation code remains, dynamic light is done in hardware with ARB or NV shaders in the respective renderer for your gfx card.
The sh_lightmapbright cvar was replaced by a float pointer to get the values for lightmap brightness from rt_light (it defaults to 0.3f). the cvar still exist but its use is now as a test function to boost the lightmap brightness a bit.
A built in editor would probably help immensely for creating the edo files tenebrae uses for realtime lights.
Caustics was removed for now untill i find a better way to do it without having the mess that it was.
Water fog will probably be changed to use mh's system of parsing the TURB textures for there color, the code it uses now makes it really hard to see where you go underwater at times because the fog is as dense as pea soup. world fog is adjustable from the edo files and looks ok.
code was cleaned up extensively and i added overbrights and hardware gamma. fullbrights tended to look a bit weird in this so i ditched it but if someone like they can allways add it. Its not really nessesary though if you have luma textures. Also it uses qc for a few things like halo effects dynamic color maps and noshadow but a skilled programmer could probably extended the basic rt_light system in it to cover these instead (much easier).
theres a few places where i lost track and i commented these so if anyone experiences problems try looking at my comments and change the code back as outlined by me.
most of these are related to blendmodes and which tmu is active comming out of a function.
example here.
Code: Select all
void DrawTextureChains (void)
{
int i;
msurface_t *s;
texture_t *t;
glDepthMask (GL_TRUE);
GL_DisableMultitexture();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_EnableMultitexture();
if (sh_colormaps.value)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
else
{
//No colormaps: Color maps are bound on tmu 0 so disable it
//and let tmu 1 modulate itself with the light map brightness
glDisable(GL_REGISTER_COMBINERS_NV);
GL_DisableTMU(GL_TEXTURE0_ARB);
GL_SelectTexture(GL_TEXTURE1_ARB);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
}
if (gl_overbright.value > 0)
{
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
switch ((int) gl_overbright.value)
{
case 1:
glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
break;
case 2:
glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 4);
break;
default:
glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
break;
}
}
if (gl_wireframe.value)
{
GL_DisableTMU(GL_TEXTURE0_ARB);
GL_DisableTMU(GL_TEXTURE1_ARB);
}
for (i=0 ; i<cl.worldmodel->numtextures ; i++)
{
t = cl.worldmodel->textures[i];
if (!t) continue;
s = t->texturechain;
if (!s) continue;
// works perfectly fine
// so why they changed it is beyond me...
if (i == skytexturenum)
{
R_DrawSkyChain (s);
}
else
{
if (s->flags & SURF_DRAWTURB) continue;
for (/**/ ; s ; s = s->texturechain)
{
R_RenderBrushPoly (s);
c_brush_polys++;
}
}
t->texturechain = NULL;
}
for (i=0 ; i<cl.worldmodel->numtextures ; i++)
{
t = cl.worldmodel->textures[i];
if (!t) continue;
s = t->texturechain;
if (!s) continue;
// no luma in sky so its not correct but doesnt hurt any.
if (i == skytexturenum)
{
R_DrawSkyChain (s);
}
else
{
if (s->flags & SURF_DRAWTURB) continue;
for (/**/ ; s ; s = s->texturechain)
{
R_RenderBrushPolyLuma (s);
c_brush_polys++;
}
}
t->texturechain = NULL;
}
GL_DisableMultitexture();
if (gl_overbright.value > 0)
{
glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
}
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDepthMask (GL_FALSE); // was GL_TRUE i suspect an oversight but correct me if im wrong.
}
the lightmap code was allmost ripped apart cause i found out that the dynamic light code was 98% disabled so i lobotomized it completely and now only the lightmap creation code remains, dynamic light is done in hardware with ARB or NV shaders in the respective renderer for your gfx card.
The sh_lightmapbright cvar was replaced by a float pointer to get the values for lightmap brightness from rt_light (it defaults to 0.3f). the cvar still exist but its use is now as a test function to boost the lightmap brightness a bit.
A built in editor would probably help immensely for creating the edo files tenebrae uses for realtime lights.
Caustics was removed for now untill i find a better way to do it without having the mess that it was.
Water fog will probably be changed to use mh's system of parsing the TURB textures for there color, the code it uses now makes it really hard to see where you go underwater at times because the fog is as dense as pea soup. world fog is adjustable from the edo files and looks ok.
Productivity is a state of mind.