making a better texture managment system for tenebrae

Discuss anything not covered by any of the other categories.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

making a better texture managment system for tenebrae

Post by revelator »

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.
Productivity is a state of mind.
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: making a better texture managment system for tenebrae

Post by taniwha »

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...
Leave others their otherness.
http://quakeforge.net/
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: making a better texture managment system for tenebrae

Post by Spike »

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.
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: making a better texture managment system for tenebrae

Post by taniwha »

The fullbright range is actually in the colormap:

Code: Select all

viddef.fullbright = 256 - viddef.colormap8[256 * VID_GRADES];
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".
Leave others their otherness.
http://quakeforge.net/
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: making a better texture managment system for tenebrae

Post by Spike »

'right' is defined by what the content you're targetting uses. :P
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: making a better texture managment system for tenebrae

Post by taniwha »

True, thus the quotes :)
Leave others their otherness.
http://quakeforge.net/
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: making a better texture managment system for tenebrae

Post by revelator »

ok gonna use your input and try to improve on it :) thanks.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: making a better texture managment system for tenebrae

Post by revelator »

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 :S 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.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: making a better texture managment system for tenebrae

Post by revelator »

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 ?).
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: making a better texture managment system for tenebrae

Post by revelator »

Ok first succes story.

Image

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;
}
not including stuff made in other source files heh.
texture sorting is really messy ugh :S
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: making a better texture managment system for tenebrae

Post by revelator »

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.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: making a better texture managment system for tenebrae

Post by revelator »

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.
Productivity is a state of mind.
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: making a better texture managment system for tenebrae

Post by mh »

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.
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
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: making a better texture managment system for tenebrae

Post by Baker »

The screenshot is all nifty looking and stuff. :D
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: making a better texture managment system for tenebrae

Post by revelator »

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.

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 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.
Productivity is a state of mind.
Post Reply