/* Copyright (C) 2001-2002 Charles Hollemeersch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Texture loading/caching most of it comes from gl_draw.c where it dind't really belong */ #include "quakedef.h" #include #define GL_COLOR_INDEX8_EXT 0x80E5 extern unsigned char d_15to8table[65536]; cvar_t gl_max_size = {"gl_max_size", "1024"}; cvar_t gl_picmip = {"gl_picmip", "0"}; cvar_t gl_gloss = {"gl_gloss", "0.3"}; cvar_t gl_compress_textures = {"gl_compress_textures", "0"}; cvar_t willi_gray_colormaps = {"willi_gray_colormaps", "0"}; /* cvar_t cg_conclock = {"cg_conclock", "1", true}; byte *draw_chars; // 8*8 graphic characters qpic_t *draw_disc; qpic_t *draw_backtile; int translate_texture; int char_texture; */ int glow_texture_object; //PENTA: gl texture object of the glow texture int normcube_texture_object; //PENTA: normalization cubemap int atten1d_texture_object; int atten2d_texture_object; int atten3d_texture_object; int halo_texture_object; /* typedef struct { int texnum; float sl, tl, sh, th; } glpic_t; byte conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)]; qpic_t *conback = (qpic_t *)&conback_buffer; */ int gl_lightmap_format = 4; int gl_solid_format = 3; int gl_alpha_format = 4; int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; int gl_filter_max = GL_LINEAR; int texels; #define MAX_GLTEXTURES 1024 gltexture_t gltextures[MAX_GLTEXTURES]; int numgltextures; qboolean is_overriden; // added local prototypes. int GL_Load2DAttenTexture (void); int GL_Load3DAttenTexture (void); int GL_LoadBumpTexture (void); int GL_LoadNormalizationCubemap (void); int GL_LoadPicTexture (qpic_t *pic); /*void GL_Bind (int texnum) { if (gl_nobind.value) texnum = char_texture; if (currenttexture == texnum) return; currenttexture = texnum; //#ifdef _WIN32 // bindTexFunc (GL_TEXTURE_2D, texnum); //#else glBindTexture(GL_TEXTURE_2D, texnum); //#endif /* Don't do this anisotropy is part of the texture state, it is set when you bind the texture if (gl_texturefilteranisotropic) // anisotropic texture filtering { glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl_textureanisotropylevel); } */ //} /* ================ GL_GetOverrideName ================ */ void GL_GetOverrideName(char *identifier,char *tail,char* dest) { int i; sprintf(dest,"%s%s.tga", identifier, tail); for (i=0; imipmap) { GL_Bind (glt->texnum); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); //PENTA: also update bump maps GL_Bind (glt->texnum+1); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } } /* ================ GL_FindTexture ================ */ int GL_FindTexture (char *identifier) { int i; gltexture_t *glt; for (i=0, glt=gltextures ; iidentifier)) 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); //Old Code - Eradicator /*int i, j; unsigned *inrow; unsigned frac, fracstep; fracstep = inwidth*0x10000/outwidth; for (i=0 ; i> 1; for (j=0 ; j>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_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> 1; for (j=0 ; j>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>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; } } } /* ================ PENTA: GL_MipMapGray Operates in place, quartering the size of the texture ================ */ void GL_MipMapGray (byte *in, int width, int height) { int i, j; byte *out; width <<=2; height >>= 1; out = in; for (i=0 ; i>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 inv255 = 1.0f/255.0f; float inv127 = 1.0f/127.0f; float x,y,z,l,g; width <<=2; height >>= 1; out = in; for (i=0 ; i : added "void" as return type. =============== */ void GL_PackGloss(byte *gloss,unsigned *dest,int length) { int i; for (i=0; i Added support for big endian. *dest = *dest | LittleLong (*gloss << 24); // Added support for big endian. } } #define DETAIL_NORMAL_SCALE 10.0f //Convert the pixel at x,y in the bump map to a normal (float) void NormalFromBump(byte *pixels, int x, int y, int w, int h, vec3_t rez) { float c, cx, cy, dcx, dcy, sqlen, reciplen; float oneOver255 = 1/255.0f; /* Expand [0,255] texel values to the [0,1] range. */ c = pixels[y*w + x] * oneOver255; /* Expand the texel to its right. */ cx = pixels[y*w + (x+1)%w] * oneOver255; /* Expand the texel one up. */ cy = pixels[((y+1)%h)*w + x] * oneOver255; dcx = DETAIL_NORMAL_SCALE * (c - cx); dcy = DETAIL_NORMAL_SCALE * (c - cy); /* Normalize the vector. */ sqlen = dcx*dcx + dcy*dcy + 1; reciplen = 1.0f/(float)sqrt(sqlen); rez[0] = dcx*reciplen; rez[1] = -dcy*reciplen; rez[2] = reciplen; } /* =============== PENTA: =============== */ void GL_MixDetailNormal(byte *bump, byte *dest, int w, int h) { int i,j; float inv127 = 1/127.0f; vec3_t n1, n2, tan, bin, rez, xn, yn; vec3_t temp = {0,1.0,0}; for (i=0; i>= (int)gl_picmip.value; scaled_height >>= (int)gl_picmip.value; if (scaled_width > gl_max_size.value) scaled_width = gl_max_size.value; if (scaled_height > gl_max_size.value) scaled_height = gl_max_size.value; if (scaled_width * scaled_height > sizeof(scaled)/4) Sys_Error ("GL_LoadTexture: too big"); if ( gl_texcomp && ((int)gl_compress_textures.value) & 1 ) { texturemode = GL_COMPRESSED_RGBA_ARB; } else { texturemode = GL_RGBA;//PENTA: Always upload rgb it doesn't make any difference for nvidia cards (& others) //texturemode = alpha ? gl_alpha_format : gl_solid_format; } #if 0 if (mipmap) gluBuild2DMipmaps (GL_TEXTURE_2D, texturemode, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans); else if (scaled_width == width && scaled_height == height) glTexImage2D (GL_TEXTURE_2D, 0, texturemode, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); else { gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, trans, scaled_width, scaled_height, GL_UNSIGNED_BYTE, scaled); glTexImage2D (GL_TEXTURE_2D, 0, texturemode, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } #else texels += scaled_width * scaled_height; if (scaled_width == width && scaled_height == height) { if (!mipmap) { glTexImage2D (GL_TEXTURE_2D, 0, texturemode, scaled_width, scaled_height, 0, GL_RGBA, 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, texturemode, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); if (mipmap) { int miplevel; 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, texturemode, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } } done: ; #endif if (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); } /* =============== genNormalMap Generate a normal map from the given heightmap =============== */ unsigned int * genNormalMap(byte *pixels, int w, int h, float scale) { int i, j, wr, hr; unsigned char r, g, b; static unsigned nmap[1024*1024]; float sqlen, reciplen, nx, ny, nz; const float oneOver255 = 1.0f/255.0f; float c, cx, cy, dcx, dcy; wr = w; hr = h; for (i=0; i Added support for big endian. } } return &nmap[0]; } /* =============== GL_UploadBump Upload an bump map (converts it to a normal map first...) =============== */ void GL_UploadBump(byte *data, int width, int height, qboolean mipmap, byte* gloss) { static unsigned char scaled[1024*1024]; // [512*256]; static unsigned char scaledgloss[1024*1024]; // [512*256]; int scaled_width, scaled_height; byte *nmap; int texturemode; if ( gl_texcomp && ((int)gl_compress_textures.value) & 2 ) { texturemode = GL_COMPRESSED_RGBA_ARB; } else { texturemode = GL_RGBA; } //Resize to power of 2 and maximum texture size for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) ; for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) ; scaled_width >>= (int)gl_picmip.value; scaled_height >>= (int)gl_picmip.value; if (scaled_width > gl_max_size.value) scaled_width = gl_max_size.value; if (scaled_height > gl_max_size.value) scaled_height = gl_max_size.value; if (scaled_width * scaled_height > sizeof(scaled)) Sys_Error ("GL_LoadTexture: too big"); //To resize or not to resize if (scaled_width == width && scaled_height == height) { memcpy (scaled, data, width*height); memcpy (scaledgloss, gloss, 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); GL_Resample8BitTexture (gloss, width, height, scaledgloss, scaled_width, scaled_height); } if (is_overriden) nmap = (byte *)genNormalMap(scaled,scaled_width,scaled_height,10.0f); else nmap = (byte *)genNormalMap(scaled,scaled_width,scaled_height,4.0f); GL_PackGloss(scaledgloss, (unsigned int*)nmap, scaled_width*scaled_height); glTexImage2D (GL_TEXTURE_2D, 0, texturemode, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nmap); //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (mipmap) { int miplevel; miplevel = 0; while (scaled_width > 1 || scaled_height > 1) { GL_MipMapNormal(nmap,scaled_width,scaled_height); //GL_MipMapGray((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, texturemode, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nmap); } } if (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_UploadNormal Upload as an normal map =============== */ void GL_UploadNormal(unsigned int *data, int width, int height, qboolean mipmap) { static unsigned int scaled[1024*1024]; // [512*256]; int scaled_width, scaled_height; int texturemode; if ( gl_texcomp && ((int)gl_compress_textures.value) & 2 ) { texturemode = GL_COMPRESSED_RGBA_ARB; } else { texturemode = GL_RGBA; } //Resize to power of 2 and maximum texture size for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) ; for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) ; scaled_width >>= (int)gl_picmip.value; scaled_height >>= (int)gl_picmip.value; if (scaled_width > gl_max_size.value) scaled_width = gl_max_size.value; if (scaled_height > gl_max_size.value) scaled_height = gl_max_size.value; if (scaled_width * scaled_height > sizeof(scaled)) Sys_Error ("GL_LoadTexture: too big"); //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 ((unsigned*)data, width, height, scaled, scaled_width, scaled_height); } GL_Normalize((byte*)scaled, scaled_width, scaled_height); glTexImage2D (GL_TEXTURE_2D, 0, texturemode, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (mipmap) { int miplevel; 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, texturemode, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } } if (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); } unsigned trans[1024*1024]; // FIXME, temporary static unsigned char glosspix[1024*1024]; static unsigned char detailpix[1024*1024]; #define RED_MASK 0x00FF0000 #define GREEN_MASK 0x0000FF00 #define BLUE_MASK 0x000000FF /* ================ GL_LoadTextureFromFile Load a cache texture from its file ================ */ static void GL_LoadTextureFromFile(gltexture_t *glt) { int i, width, height, gwidth, gheight, dwidth, dheight; char *glossname; char *detailname; char *basename; char *filename = glt->identifier; Con_DPrintf("Loading texture %s\n", glt->identifier); if (glt->loadtype == TEXTURE_CUBEMAP) { GL_LoadCubemapTexture(glt); return; } if (!strcmp(filename,"$white")) { //a uniform white texture trans[0] = LittleLong ((255 << 24)|(255 << 16)|(255 << 8)|(255)); width = height = 1; } else if (!strcmp(filename,"$black")) { //a uniform black texture trans[0] = LittleLong ((0 << 24)|(0 << 16)|(0 << 8)|(0)); width = height = 1; } else if (!strcmp(filename,"$flat")) { //a flat normal map with no gloss trans[0] = LittleLong ((0 << 24)|(127 << 16)|(255 << 8)|(127)); width = height = 1; } else if (!strcmp(COM_FileExtension(filename),"roq")) { Con_Printf("Loading video texture from %s\n",filename); Roq_SetupTexture(glt, filename) ; return; } else { int rez; char b[MAX_QPATH*3+2], *pb, *b2; qboolean hasgloss = false; qboolean hasdetailbump = false; if (glt->loadtype == TEXTURE_NORMAL) { strcpy(b,filename); pb = b; basename = b; //Does it have a detail bumpmap while((*pb) && ((*pb) != '+')) pb++; if ((*pb) == '+') { (*pb) = 0; pb++; //So we found a detail bumpmap load it to the buffer (it's grayscale) detailname = b; basename = pb; rez = LoadTextureInPlace(detailname, 1, (unsigned char*)&detailpix[0], &dwidth, &dheight); if (!rez) { dwidth = dheight = 1; Con_Printf("Texture not found %s\n",detailname); } else hasdetailbump = true; } //Does it have packed gloss? pb = basename; while((*pb) && ((*pb) != '|')) pb++; if ((*pb) == '|') { (*pb) = 0; pb++; glossname = pb; rez = LoadTextureInPlace(glossname, 1, (unsigned char*)&glosspix[0], &gwidth, &gheight); if (!rez) { gwidth = gheight = 1; Con_Printf("Texture not found %s\n",glossname); } else hasgloss = true; } } else { basename = filename; } rez = LoadTextureInPlace(basename, 4, (unsigned char*)&trans[0], &width, &height); if (!rez) { trans[0] = LittleLong ((255 << 24)|(255 << 16)|(255 << 8)|(255)); width = height = 1; Con_Printf("Texture not found %s\n",basename); } if (hasdetailbump) { if ((dwidth != width) || (dheight != height)) { Con_Printf("\002Warning: %s Detail normalmap must have same size as normal map\n",detailname); } GL_MixDetailNormal(detailpix, (unsigned char *)trans, width, height); } if (hasgloss) { if ((gwidth != width) || (gheight != height)) { Con_Printf("\002Warning: %s Gray gloss map must have same size as normal map\n",glossname); } GL_PackGloss(glosspix, trans, width*height); } } GL_Bind(glt->texnum); glt->width = width; glt->height = height; glt->gltype = GL_TEXTURE_2D; if (glt->loadtype == TEXTURE_RGB) { GL_Upload32 (&trans[0], width, height, glt->mipmap, true); } else if (glt->loadtype == TEXTURE_NORMAL) { GL_UploadNormal(&trans[0], width, height, glt->mipmap); }; //texture_extension_number++; return; } /* ================ GL_CacheTexture Add a texture to the cache ================ */ gltexture_t *GL_CacheTexture (char *filename, qboolean mipmap, int type) { gltexture_t *glt; int i; // see if the texture is allready present if (filename[0]) { for (i=0, glt=gltextures ; iidentifier) && (glt->mipmap == mipmap)) { //found one, return it... return &gltextures[i]; } } } // load a new one glt = &gltextures[numgltextures]; numgltextures++; //Con_Printf("Load texture: %s %i %i\n",identifier,width,height); strncpy (glt->identifier, filename, sizeof(glt->identifier)); glt->texnum = texture_extension_number; texture_extension_number++; glt->mipmap = mipmap; glt->dynamic = NULL; glt->loadtype = type; GL_LoadTextureFromFile(glt); return glt; } /* ================ GL_ShutdownTextures Free some memory allocated for textures ================ */ void GL_ShutdownTextures(void) { int i; gltexture_t *glt; for (i=0, glt=gltextures ; iidentifier, glt->texnum, glt->loadtype, (glt->dynamic == NULL ? 0 : 1), glt->width, glt->height); } } /* int GL_LoadLuma(char *identifier, qboolean mipmap) { qboolean alpha; FILE *f; char filename[MAX_OSPATH]; int width, height; if ( willi_gray_colormaps.value ) return 0; //try to load an overrided texture GL_GetOverrideName(identifier,"_luma",filename); if ( LoadTextureInPlace(filename, 4, (unsigned char*)&trans[0], &width, &height) ) { Con_DPrintf("Using luma map for %s\n",identifier); is_overriden = true; //force it to upload a 32 bit texture alpha = true; GL_Bind(texture_extension_number); GL_Upload32 (trans, width, height, mipmap, alpha); texture_extension_number++; return texture_extension_number-1; } else { return 0; } } */ char *face_names[] = { "posx", "negx", "posy", "negy", "posz", "negz", }; /** Load a cubemap into the texture cache (used for a.o. light filters) glt contains a valid texture identifier where the map needs to be bound glt contains a "prexix" where the cubmap files are */ int GL_LoadCubemapTexture (gltexture_t *glt) { int i, width, height; FILE *f; char filename[MAX_OSPATH]; int texturemode; if ( gl_texcomp && ((int)gl_compress_textures.value) & 1) { texturemode = GL_COMPRESSED_RGBA_ARB; } else { texturemode = GL_RGBA; } glt->gltype = GL_TEXTURE_CUBE_MAP_ARB; glEnable(GL_TEXTURE_CUBE_MAP_ARB); glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, glt->texnum); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); for (i = 0; i < 6; i++) { sprintf(filename,"%s%s.tga", glt->identifier, face_names[i]); LoadTextureInPlace(filename, 4, (unsigned char*)&trans[0], &width, &height); if ((width == 0) || (height == 0)) { Con_Printf("Texture not found: %s\n",filename); } else { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB+i, 0, texturemode, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &trans[0]); } } //glEnable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_CUBE_MAP_ARB); glt->width = width; glt->height = height; glt->mipmap = false; glt->gltype = GL_TEXTURE_CUBE_MAP_ARB; texture_extension_number++; return texture_extension_number-1; } /* ================ GL_LoadPicTexture ================ */ int GL_LoadPicTexture (qpic_t *pic) { return 0; //FIXME: remove this routine } /****************************************/ static GLenum oldtarget = GL_TEXTURE0_ARB; void GL_SelectTexture (GLenum target) { if (!gl_mtexable) return; qglActiveTextureARB(target); if (target == oldtarget) return; cnttextures[oldtarget-GL_TEXTURE0_ARB] = currenttexture; currenttexture = cnttextures[target-GL_TEXTURE0_ARB]; oldtarget = target; } float sqr(float p) { return p*p; } #define ATTEN_VOLUME_SIZE 64 extern qboolean gl_mirroronce; /* =============== PENTA: Load texture for 2d atten =============== */ int GL_Load2DAttenTexture(void) { float centerx, centery, radiussq; int s,t, err; byte *data; data = malloc(ATTEN_VOLUME_SIZE*ATTEN_VOLUME_SIZE); if (!data) return 0; // check memory here! centerx = ATTEN_VOLUME_SIZE/2.0; centery = ATTEN_VOLUME_SIZE/2.0; radiussq = ATTEN_VOLUME_SIZE/2.0; radiussq = radiussq*radiussq; for (s = 0; s < ATTEN_VOLUME_SIZE; s++) { for (t = 0; t < ATTEN_VOLUME_SIZE; t++) { float DistSq = sqr(s-centerx)+sqr(t-centery); if (DistSq < radiussq) { byte value; float FallOff = (radiussq - DistSq) / radiussq; //FallOff *= FallOff; value = FallOff*255.0; data[t * ATTEN_VOLUME_SIZE + s] = value; } else { data[t * ATTEN_VOLUME_SIZE + s] = 0; } } } GL_Bind(texture_extension_number); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, ATTEN_VOLUME_SIZE, ATTEN_VOLUME_SIZE, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); free(data); texture_extension_number++; err = glGetError(); if (err != GL_NO_ERROR) { Con_Printf("%s",gluErrorString(err)); } return texture_extension_number-1; } /* =============== PENTA: Load texture for 3d atten (on gf3) =============== */ int GL_Load3DAttenTexture(void) { float centerx, centery, centerz, radiussq; int s,t,r, err, volumeSize; byte *data; if (gl_cardtype == GENERIC || gl_cardtype == GEFORCE) return 0;//PA: //Penta we need special matrix setup with the mirror once stuff so only use it if the path supports it if (gl_mirroronce && gl_cardtype == ARB) { volumeSize = ATTEN_VOLUME_SIZE/2; centerx = 0.0; centery = 0.0; centerz = 0.0; radiussq = ATTEN_VOLUME_SIZE/2; radiussq = radiussq*radiussq; } else { volumeSize = ATTEN_VOLUME_SIZE; centerx = volumeSize/2.0; centery = volumeSize/2.0; centerz = volumeSize/2.0; radiussq = volumeSize/2.0; radiussq = radiussq*radiussq; } data = malloc(volumeSize*volumeSize*volumeSize); if (!data) return 0; for (s = 0; s < volumeSize; s++) { for (t = 0; t < volumeSize; t++) { for (r = 0; r < volumeSize; r++) { float DistSq = sqr(s-centerx)+sqr(t-centery)+sqr(r-centerz); if (DistSq < radiussq) { byte value; float FallOff = (radiussq - DistSq) / radiussq; //FallOff *= FallOff; value = FallOff*255.0; data[r * volumeSize * volumeSize + t * volumeSize + s] = value; //data[r * volumeSize * volumeSize + t * volumeSize + s *4+ 1] = value; //data[r * volumeSize * volumeSize + t * volumeSize + s *4+ 2] = value; //data[r * volumeSize * volumeSize + t * volumeSize + s *4+ 3] = value; } else { data[r * volumeSize * volumeSize + t * volumeSize + s] = 0; //data[r * volumeSize * volumeSize + t * volumeSize + s *4+ 1] = 0; //data[r * volumeSize * volumeSize + t * volumeSize + s *4+ 2] = 0; //data[r * volumeSize * volumeSize + t * volumeSize + s *4+ 3] = 0; } } } } glBindTexture(GL_TEXTURE_3D, texture_extension_number); qglTexImage3DEXT(GL_TEXTURE_3D, 0, GL_LUMINANCE8, volumeSize, volumeSize, volumeSize, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); if (gl_mirroronce) { glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_MIRROR_CLAMP_TO_EDGE_ATI ); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_MIRROR_CLAMP_TO_EDGE_ATI ); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_MIRROR_CLAMP_TO_EDGE_ATI ); } else { glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); } glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); free(data); texture_extension_number++; err = glGetError(); if (err != GL_NO_ERROR) { Con_Printf("%s",gluErrorString(err)); } return texture_extension_number-1; } int GL_LoadBlackTexture(void) { char data[4]; int err; data[0] = data[1] = data[2] = data[3] = 0; GL_Bind(texture_extension_number); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); texture_extension_number++; err = glGetError(); if (err != GL_NO_ERROR) { Con_Printf("%s",gluErrorString(err)); } return texture_extension_number-1; } /* Code from: bumpdemo.c - glamour demo of "normal perturbation" mapping by Nvidia */ void getCubeVector(int i, int cubesize, int x, int y, float *vector) { float s, t, sc, tc, mag; s = ((float)x + 0.5f) / (float)cubesize; t = ((float)y + 0.5f) / (float)cubesize; sc = s*2.0f - 1.0f; tc = t*2.0f - 1.0f; switch (i) { case 0: vector[0] = 1.0f; vector[1] = -tc; vector[2] = -sc; break; case 1: vector[0] = -1.0; vector[1] = -tc; vector[2] = sc; break; case 2: vector[0] = sc; vector[1] = 1.0; vector[2] = tc; break; case 3: vector[0] = sc; vector[1] = -1.0; vector[2] = -tc; break; case 4: vector[0] = sc; vector[1] = -tc; vector[2] = 1.0; break; case 5: vector[0] = -sc; vector[1] = -tc; vector[2] = -1.0; break; } mag = 1.0f/sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); vector[0] *= mag; vector[1] *= mag; vector[2] *= mag; } #define NC_SIZE 128//Size of the cube faces int GL_LoadNormalizationCubemap() { vec3_t vec; int i, x, y; char pixels [NC_SIZE*NC_SIZE*3]; glEnable(GL_TEXTURE_CUBE_MAP_ARB); glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, texture_extension_number); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); for (i = 0; i < 6; i++) { for (y = 0; y < NC_SIZE; y++) { for (x = 0; x < NC_SIZE; x++) { getCubeVector(i, NC_SIZE, x, y, vec); pixels[3*(y*NC_SIZE+x) + 0] = (unsigned char)(128 + 127*vec[0]); pixels[3*(y*NC_SIZE+x) + 1] = (unsigned char)(128 + 127*vec[1]); pixels[3*(y*NC_SIZE+x) + 2] = (unsigned char)(128 + 127*vec[2]); } } glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB+i, 0, GL_RGB8, NC_SIZE, NC_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixels); } glDisable(GL_TEXTURE_CUBE_MAP_ARB); texture_extension_number++; return texture_extension_number-1; }