You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1571 lines
33 KiB
1571 lines
33 KiB
/* |
|
Copyright (C) 1997-2001 Id Software, Inc. |
|
|
|
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. |
|
|
|
*/ |
|
|
|
#include "gl_local.h" |
|
|
|
image_t gltextures[MAX_GLTEXTURES]; |
|
int numgltextures; |
|
int base_textureid; // gltextures[i] = base_textureid+i |
|
|
|
static byte intensitytable[256]; |
|
static unsigned char gammatable[256]; |
|
|
|
cvar_t *intensity; |
|
|
|
unsigned d_8to24table[256]; |
|
|
|
qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky ); |
|
qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap); |
|
|
|
|
|
int gl_solid_format = 3; |
|
int gl_alpha_format = 4; |
|
|
|
int gl_tex_solid_format = 3; |
|
int gl_tex_alpha_format = 4; |
|
|
|
int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; |
|
int gl_filter_max = GL_LINEAR; |
|
|
|
void GL_SetTexturePalette( unsigned palette[256] ) |
|
{ |
|
int i; |
|
unsigned char temptable[768]; |
|
|
|
for ( i = 0; i < 256; i++ ) |
|
{ |
|
temptable[i*3+0] = ( palette[i] >> 0 ) & 0xff; |
|
temptable[i*3+1] = ( palette[i] >> 8 ) & 0xff; |
|
temptable[i*3+2] = ( palette[i] >> 16 ) & 0xff; |
|
} |
|
|
|
if ( qglColorTableEXT && gl_ext_palettedtexture->value ) |
|
{ |
|
qglColorTableEXT( GL_SHARED_TEXTURE_PALETTE_EXT, |
|
GL_RGB, |
|
256, |
|
GL_RGB, |
|
GL_UNSIGNED_BYTE, |
|
temptable ); |
|
} |
|
} |
|
|
|
void GL_EnableMultitexture( qboolean enable ) |
|
{ |
|
if ( !qglSelectTextureSGIS ) |
|
return; |
|
|
|
if ( enable ) |
|
{ |
|
GL_SelectTexture( GL_TEXTURE1_SGIS ); |
|
qglEnable( GL_TEXTURE_2D ); |
|
GL_TexEnv( GL_REPLACE ); |
|
} |
|
else |
|
{ |
|
GL_SelectTexture( GL_TEXTURE1_SGIS ); |
|
qglDisable( GL_TEXTURE_2D ); |
|
GL_TexEnv( GL_REPLACE ); |
|
} |
|
GL_SelectTexture( GL_TEXTURE0_SGIS ); |
|
GL_TexEnv( GL_REPLACE ); |
|
} |
|
|
|
void GL_SelectTexture( GLenum texture ) |
|
{ |
|
int tmu; |
|
|
|
if ( !qglSelectTextureSGIS ) |
|
return; |
|
|
|
if ( texture == GL_TEXTURE0_SGIS ) |
|
tmu = 0; |
|
else |
|
tmu = 1; |
|
|
|
if ( tmu == gl_state.currenttmu ) |
|
return; |
|
|
|
gl_state.currenttmu = tmu; |
|
|
|
if ( tmu == 0 ) |
|
qglSelectTextureSGIS( GL_TEXTURE0_SGIS ); |
|
else |
|
qglSelectTextureSGIS( GL_TEXTURE1_SGIS ); |
|
} |
|
|
|
void GL_TexEnv( GLenum mode ) |
|
{ |
|
static int lastmodes[2] = { -1, -1 }; |
|
|
|
if ( mode != lastmodes[gl_state.currenttmu] ) |
|
{ |
|
qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode ); |
|
lastmodes[gl_state.currenttmu] = mode; |
|
} |
|
} |
|
|
|
void GL_Bind (int texnum) |
|
{ |
|
extern image_t *draw_chars; |
|
|
|
if (gl_nobind->value && draw_chars) // performance evaluation option |
|
texnum = draw_chars->texnum; |
|
if ( gl_state.currenttextures[gl_state.currenttmu] == texnum) |
|
return; |
|
gl_state.currenttextures[gl_state.currenttmu] = texnum; |
|
qglBindTexture (GL_TEXTURE_2D, texnum); |
|
} |
|
|
|
void GL_MBind( GLenum target, int texnum ) |
|
{ |
|
GL_SelectTexture( target ); |
|
if ( target == GL_TEXTURE0_SGIS ) |
|
{ |
|
if ( gl_state.currenttextures[0] == texnum ) |
|
return; |
|
} |
|
else |
|
{ |
|
if ( gl_state.currenttextures[1] == texnum ) |
|
return; |
|
} |
|
GL_Bind( texnum ); |
|
} |
|
|
|
typedef struct |
|
{ |
|
char *name; |
|
int minimize, maximize; |
|
} glmode_t; |
|
|
|
glmode_t modes[] = { |
|
{"GL_NEAREST", GL_NEAREST, GL_NEAREST}, |
|
{"GL_LINEAR", GL_LINEAR, GL_LINEAR}, |
|
{"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, |
|
{"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, |
|
{"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, |
|
{"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} |
|
}; |
|
|
|
#define NUM_GL_MODES (sizeof(modes) / sizeof (glmode_t)) |
|
|
|
typedef struct |
|
{ |
|
char *name; |
|
int mode; |
|
} gltmode_t; |
|
|
|
gltmode_t gl_alpha_modes[] = { |
|
{"default", 4}, |
|
{"GL_RGBA", GL_RGBA}, |
|
{"GL_RGBA8", GL_RGBA8}, |
|
{"GL_RGB5_A1", GL_RGB5_A1}, |
|
{"GL_RGBA4", GL_RGBA4}, |
|
{"GL_RGBA2", GL_RGBA2}, |
|
}; |
|
|
|
#define NUM_GL_ALPHA_MODES (sizeof(gl_alpha_modes) / sizeof (gltmode_t)) |
|
|
|
gltmode_t gl_solid_modes[] = { |
|
{"default", 3}, |
|
{"GL_RGB", GL_RGB}, |
|
{"GL_RGB8", GL_RGB8}, |
|
{"GL_RGB5", GL_RGB5}, |
|
{"GL_RGB4", GL_RGB4}, |
|
{"GL_R3_G3_B2", GL_R3_G3_B2}, |
|
#ifdef GL_RGB2_EXT |
|
{"GL_RGB2", GL_RGB2_EXT}, |
|
#endif |
|
}; |
|
|
|
#define NUM_GL_SOLID_MODES (sizeof(gl_solid_modes) / sizeof (gltmode_t)) |
|
|
|
/* |
|
=============== |
|
GL_TextureMode |
|
=============== |
|
*/ |
|
void GL_TextureMode( char *string ) |
|
{ |
|
int i; |
|
image_t *glt; |
|
|
|
for (i=0 ; i< NUM_GL_MODES ; i++) |
|
{ |
|
if ( !Q_stricmp( modes[i].name, string ) ) |
|
break; |
|
} |
|
|
|
if (i == NUM_GL_MODES) |
|
{ |
|
ri.Con_Printf (PRINT_ALL, "bad filter name\n"); |
|
return; |
|
} |
|
|
|
gl_filter_min = modes[i].minimize; |
|
gl_filter_max = modes[i].maximize; |
|
|
|
// change all the existing mipmap texture objects |
|
for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++) |
|
{ |
|
if (glt->type != it_pic && glt->type != it_sky ) |
|
{ |
|
GL_Bind (glt->texnum); |
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); |
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); |
|
} |
|
} |
|
} |
|
|
|
/* |
|
=============== |
|
GL_TextureAlphaMode |
|
=============== |
|
*/ |
|
void GL_TextureAlphaMode( char *string ) |
|
{ |
|
int i; |
|
|
|
for (i=0 ; i< NUM_GL_ALPHA_MODES ; i++) |
|
{ |
|
if ( !Q_stricmp( gl_alpha_modes[i].name, string ) ) |
|
break; |
|
} |
|
|
|
if (i == NUM_GL_ALPHA_MODES) |
|
{ |
|
ri.Con_Printf (PRINT_ALL, "bad alpha texture mode name\n"); |
|
return; |
|
} |
|
|
|
gl_tex_alpha_format = gl_alpha_modes[i].mode; |
|
} |
|
|
|
/* |
|
=============== |
|
GL_TextureSolidMode |
|
=============== |
|
*/ |
|
void GL_TextureSolidMode( char *string ) |
|
{ |
|
int i; |
|
|
|
for (i=0 ; i< NUM_GL_SOLID_MODES ; i++) |
|
{ |
|
if ( !Q_stricmp( gl_solid_modes[i].name, string ) ) |
|
break; |
|
} |
|
|
|
if (i == NUM_GL_SOLID_MODES) |
|
{ |
|
ri.Con_Printf (PRINT_ALL, "bad solid texture mode name\n"); |
|
return; |
|
} |
|
|
|
gl_tex_solid_format = gl_solid_modes[i].mode; |
|
} |
|
|
|
/* |
|
=============== |
|
GL_ImageList_f |
|
=============== |
|
*/ |
|
void GL_ImageList_f (void) |
|
{ |
|
int i; |
|
image_t *image; |
|
int texels; |
|
const char *palstrings[2] = |
|
{ |
|
"RGB", |
|
"PAL" |
|
}; |
|
|
|
ri.Con_Printf (PRINT_ALL, "------------------\n"); |
|
texels = 0; |
|
|
|
for (i=0, image=gltextures ; i<numgltextures ; i++, image++) |
|
{ |
|
if (image->texnum <= 0) |
|
continue; |
|
texels += image->upload_width*image->upload_height; |
|
switch (image->type) |
|
{ |
|
case it_skin: |
|
ri.Con_Printf (PRINT_ALL, "M"); |
|
break; |
|
case it_sprite: |
|
ri.Con_Printf (PRINT_ALL, "S"); |
|
break; |
|
case it_wall: |
|
ri.Con_Printf (PRINT_ALL, "W"); |
|
break; |
|
case it_pic: |
|
ri.Con_Printf (PRINT_ALL, "P"); |
|
break; |
|
default: |
|
ri.Con_Printf (PRINT_ALL, " "); |
|
break; |
|
} |
|
|
|
ri.Con_Printf (PRINT_ALL, " %3i %3i %s: %s\n", |
|
image->upload_width, image->upload_height, palstrings[image->paletted], image->name); |
|
} |
|
ri.Con_Printf (PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels); |
|
} |
|
|
|
|
|
/* |
|
============================================================================= |
|
|
|
scrap allocation |
|
|
|
Allocate all the little status bar obejcts into a single texture |
|
to crutch up inefficient hardware / drivers |
|
|
|
============================================================================= |
|
*/ |
|
|
|
#define MAX_SCRAPS 1 |
|
#define BLOCK_WIDTH 256 |
|
#define BLOCK_HEIGHT 256 |
|
|
|
int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; |
|
byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT]; |
|
qboolean scrap_dirty; |
|
|
|
// returns a texture number and the position inside it |
|
int Scrap_AllocBlock (int w, int h, int *x, int *y) |
|
{ |
|
int i, j; |
|
int best, best2; |
|
int texnum; |
|
|
|
for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++) |
|
{ |
|
best = BLOCK_HEIGHT; |
|
|
|
for (i=0 ; i<BLOCK_WIDTH-w ; i++) |
|
{ |
|
best2 = 0; |
|
|
|
for (j=0 ; j<w ; j++) |
|
{ |
|
if (scrap_allocated[texnum][i+j] >= best) |
|
break; |
|
if (scrap_allocated[texnum][i+j] > best2) |
|
best2 = scrap_allocated[texnum][i+j]; |
|
} |
|
if (j == w) |
|
{ // this is a valid spot |
|
*x = i; |
|
*y = best = best2; |
|
} |
|
} |
|
|
|
if (best + h > BLOCK_HEIGHT) |
|
continue; |
|
|
|
for (i=0 ; i<w ; i++) |
|
scrap_allocated[texnum][*x + i] = best + h; |
|
|
|
return texnum; |
|
} |
|
|
|
return -1; |
|
// Sys_Error ("Scrap_AllocBlock: full"); |
|
} |
|
|
|
int scrap_uploads; |
|
|
|
void Scrap_Upload (void) |
|
{ |
|
scrap_uploads++; |
|
GL_Bind(TEXNUM_SCRAPS); |
|
GL_Upload8 (scrap_texels[0], BLOCK_WIDTH, BLOCK_HEIGHT, false, false ); |
|
scrap_dirty = false; |
|
} |
|
|
|
/* |
|
================================================================= |
|
|
|
PCX LOADING |
|
|
|
================================================================= |
|
*/ |
|
|
|
|
|
/* |
|
============== |
|
LoadPCX |
|
============== |
|
*/ |
|
void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height) |
|
{ |
|
byte *raw; |
|
pcx_t *pcx; |
|
int x, y; |
|
int len; |
|
int dataByte, runLength; |
|
byte *out, *pix; |
|
|
|
*pic = NULL; |
|
*palette = NULL; |
|
|
|
// |
|
// load the file |
|
// |
|
len = ri.FS_LoadFile (filename, (void **)&raw); |
|
if (!raw) |
|
{ |
|
ri.Con_Printf (PRINT_DEVELOPER, "Bad pcx file %s\n", filename); |
|
return; |
|
} |
|
|
|
// |
|
// parse the PCX file |
|
// |
|
pcx = (pcx_t *)raw; |
|
|
|
pcx->xmin = LittleShort(pcx->xmin); |
|
pcx->ymin = LittleShort(pcx->ymin); |
|
pcx->xmax = LittleShort(pcx->xmax); |
|
pcx->ymax = LittleShort(pcx->ymax); |
|
pcx->hres = LittleShort(pcx->hres); |
|
pcx->vres = LittleShort(pcx->vres); |
|
pcx->bytes_per_line = LittleShort(pcx->bytes_per_line); |
|
pcx->palette_type = LittleShort(pcx->palette_type); |
|
|
|
raw = &pcx->data; |
|
|
|
if (pcx->manufacturer != 0x0a |
|
|| pcx->version != 5 |
|
|| pcx->encoding != 1 |
|
|| pcx->bits_per_pixel != 8 |
|
|| pcx->xmax >= 640 |
|
|| pcx->ymax >= 480) |
|
{ |
|
ri.Con_Printf (PRINT_ALL, "Bad pcx file %s\n", filename); |
|
return; |
|
} |
|
|
|
out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); |
|
|
|
*pic = out; |
|
|
|
pix = out; |
|
|
|
if (palette) |
|
{ |
|
*palette = malloc(768); |
|
memcpy (*palette, (byte *)pcx + len - 768, 768); |
|
} |
|
|
|
if (width) |
|
*width = pcx->xmax+1; |
|
if (height) |
|
*height = pcx->ymax+1; |
|
|
|
for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) |
|
{ |
|
for (x=0 ; x<=pcx->xmax ; ) |
|
{ |
|
dataByte = *raw++; |
|
|
|
if((dataByte & 0xC0) == 0xC0) |
|
{ |
|
runLength = dataByte & 0x3F; |
|
dataByte = *raw++; |
|
} |
|
else |
|
runLength = 1; |
|
|
|
while(runLength-- > 0) |
|
pix[x++] = dataByte; |
|
} |
|
|
|
} |
|
|
|
if ( raw - (byte *)pcx > len) |
|
{ |
|
ri.Con_Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename); |
|
free (*pic); |
|
*pic = NULL; |
|
} |
|
|
|
ri.FS_FreeFile (pcx); |
|
} |
|
|
|
/* |
|
========================================================= |
|
|
|
TARGA LOADING |
|
|
|
========================================================= |
|
*/ |
|
|
|
typedef struct _TargaHeader { |
|
unsigned char id_length, colormap_type, image_type; |
|
unsigned short colormap_index, colormap_length; |
|
unsigned char colormap_size; |
|
unsigned short x_origin, y_origin, width, height; |
|
unsigned char pixel_size, attributes; |
|
} TargaHeader; |
|
|
|
|
|
/* |
|
============= |
|
LoadTGA |
|
============= |
|
*/ |
|
void LoadTGA (char *name, byte **pic, int *width, int *height) |
|
{ |
|
int columns, rows, numPixels; |
|
byte *pixbuf; |
|
int row, column; |
|
byte *buf_p; |
|
byte *buffer; |
|
int length; |
|
TargaHeader targa_header; |
|
byte *targa_rgba; |
|
byte tmp[2]; |
|
|
|
*pic = NULL; |
|
|
|
// |
|
// load the file |
|
// |
|
length = ri.FS_LoadFile (name, (void **)&buffer); |
|
if (!buffer) |
|
{ |
|
ri.Con_Printf (PRINT_DEVELOPER, "Bad tga file %s\n", name); |
|
return; |
|
} |
|
|
|
buf_p = buffer; |
|
|
|
targa_header.id_length = *buf_p++; |
|
targa_header.colormap_type = *buf_p++; |
|
targa_header.image_type = *buf_p++; |
|
|
|
tmp[0] = buf_p[0]; |
|
tmp[1] = buf_p[1]; |
|
targa_header.colormap_index = LittleShort ( *((short *)tmp) ); |
|
buf_p+=2; |
|
tmp[0] = buf_p[0]; |
|
tmp[1] = buf_p[1]; |
|
targa_header.colormap_length = LittleShort ( *((short *)tmp) ); |
|
buf_p+=2; |
|
targa_header.colormap_size = *buf_p++; |
|
targa_header.x_origin = LittleShort ( *((short *)buf_p) ); |
|
buf_p+=2; |
|
targa_header.y_origin = LittleShort ( *((short *)buf_p) ); |
|
buf_p+=2; |
|
targa_header.width = LittleShort ( *((short *)buf_p) ); |
|
buf_p+=2; |
|
targa_header.height = LittleShort ( *((short *)buf_p) ); |
|
buf_p+=2; |
|
targa_header.pixel_size = *buf_p++; |
|
targa_header.attributes = *buf_p++; |
|
|
|
if (targa_header.image_type!=2 |
|
&& targa_header.image_type!=10) |
|
ri.Sys_Error (ERR_DROP, "LoadTGA: Only type 2 and 10 targa RGB images supported\n"); |
|
|
|
if (targa_header.colormap_type !=0 |
|
|| (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) |
|
ri.Sys_Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); |
|
|
|
columns = targa_header.width; |
|
rows = targa_header.height; |
|
numPixels = columns * rows; |
|
|
|
if (width) |
|
*width = columns; |
|
if (height) |
|
*height = rows; |
|
|
|
targa_rgba = malloc (numPixels*4); |
|
*pic = targa_rgba; |
|
|
|
if (targa_header.id_length != 0) |
|
buf_p += targa_header.id_length; // skip TARGA image comment |
|
|
|
if (targa_header.image_type==2) { // Uncompressed, RGB images |
|
for(row=rows-1; row>=0; row--) { |
|
pixbuf = targa_rgba + row*columns*4; |
|
for(column=0; column<columns; column++) { |
|
unsigned char red,green,blue,alphabyte; |
|
switch (targa_header.pixel_size) { |
|
case 24: |
|
|
|
blue = *buf_p++; |
|
green = *buf_p++; |
|
red = *buf_p++; |
|
*pixbuf++ = red; |
|
*pixbuf++ = green; |
|
*pixbuf++ = blue; |
|
*pixbuf++ = 255; |
|
break; |
|
case 32: |
|
blue = *buf_p++; |
|
green = *buf_p++; |
|
red = *buf_p++; |
|
alphabyte = *buf_p++; |
|
*pixbuf++ = red; |
|
*pixbuf++ = green; |
|
*pixbuf++ = blue; |
|
*pixbuf++ = alphabyte; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
else if (targa_header.image_type==10) { // Runlength encoded RGB images |
|
unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j; |
|
for(row=rows-1; row>=0; row--) { |
|
pixbuf = targa_rgba + row*columns*4; |
|
for(column=0; column<columns; ) { |
|
packetHeader= *buf_p++; |
|
packetSize = 1 + (packetHeader & 0x7f); |
|
if (packetHeader & 0x80) { // run-length packet |
|
switch (targa_header.pixel_size) { |
|
case 24: |
|
blue = *buf_p++; |
|
green = *buf_p++; |
|
red = *buf_p++; |
|
alphabyte = 255; |
|
break; |
|
case 32: |
|
blue = *buf_p++; |
|
green = *buf_p++; |
|
red = *buf_p++; |
|
alphabyte = *buf_p++; |
|
break; |
|
} |
|
|
|
for(j=0;j<packetSize;j++) { |
|
*pixbuf++=red; |
|
*pixbuf++=green; |
|
*pixbuf++=blue; |
|
*pixbuf++=alphabyte; |
|
column++; |
|
if (column==columns) { // run spans across rows |
|
column=0; |
|
if (row>0) |
|
row--; |
|
else |
|
goto breakOut; |
|
pixbuf = targa_rgba + row*columns*4; |
|
} |
|
} |
|
} |
|
else { // non run-length packet |
|
for(j=0;j<packetSize;j++) { |
|
switch (targa_header.pixel_size) { |
|
case 24: |
|
blue = *buf_p++; |
|
green = *buf_p++; |
|
red = *buf_p++; |
|
*pixbuf++ = red; |
|
*pixbuf++ = green; |
|
*pixbuf++ = blue; |
|
*pixbuf++ = 255; |
|
break; |
|
case 32: |
|
blue = *buf_p++; |
|
green = *buf_p++; |
|
red = *buf_p++; |
|
alphabyte = *buf_p++; |
|
*pixbuf++ = red; |
|
*pixbuf++ = green; |
|
*pixbuf++ = blue; |
|
*pixbuf++ = alphabyte; |
|
break; |
|
} |
|
column++; |
|
if (column==columns) { // pixel packet run spans across rows |
|
column=0; |
|
if (row>0) |
|
row--; |
|
else |
|
goto breakOut; |
|
pixbuf = targa_rgba + row*columns*4; |
|
} |
|
} |
|
} |
|
} |
|
breakOut:; |
|
} |
|
} |
|
|
|
ri.FS_FreeFile (buffer); |
|
} |
|
|
|
|
|
/* |
|
==================================================================== |
|
|
|
IMAGE FLOOD FILLING |
|
|
|
==================================================================== |
|
*/ |
|
|
|
|
|
/* |
|
================= |
|
Mod_FloodFillSkin |
|
|
|
Fill background pixels so mipmapping doesn't have haloes |
|
================= |
|
*/ |
|
|
|
typedef struct |
|
{ |
|
short x, y; |
|
} floodfill_t; |
|
|
|
// must be a power of 2 |
|
#define FLOODFILL_FIFO_SIZE 0x1000 |
|
#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) |
|
|
|
#define FLOODFILL_STEP( off, dx, dy ) \ |
|
{ \ |
|
if (pos[off] == fillcolor) \ |
|
{ \ |
|
pos[off] = 255; \ |
|
fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ |
|
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ |
|
} \ |
|
else if (pos[off] != 255) fdc = pos[off]; \ |
|
} |
|
|
|
void R_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) |
|
{ |
|
byte fillcolor = *skin; // assume this is the pixel to fill |
|
floodfill_t fifo[FLOODFILL_FIFO_SIZE]; |
|
int inpt = 0, outpt = 0; |
|
int filledcolor = -1; |
|
int i; |
|
|
|
if (filledcolor == -1) |
|
{ |
|
filledcolor = 0; |
|
// attempt to find opaque black |
|
for (i = 0; i < 256; ++i) |
|
if (d_8to24table[i] == (255 << 0)) // alpha 1.0 |
|
{ |
|
filledcolor = i; |
|
break; |
|
} |
|
} |
|
|
|
// can't fill to filled color or to transparent color (used as visited marker) |
|
if ((fillcolor == filledcolor) || (fillcolor == 255)) |
|
{ |
|
//printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); |
|
return; |
|
} |
|
|
|
fifo[inpt].x = 0, fifo[inpt].y = 0; |
|
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; |
|
|
|
while (outpt != inpt) |
|
{ |
|
int x = fifo[outpt].x, y = fifo[outpt].y; |
|
int fdc = filledcolor; |
|
byte *pos = &skin[x + skinwidth * y]; |
|
|
|
outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; |
|
|
|
if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); |
|
if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); |
|
if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); |
|
if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); |
|
skin[x + skinwidth * y] = fdc; |
|
} |
|
} |
|
|
|
//======================================================= |
|
|
|
|
|
/* |
|
================ |
|
GL_ResampleTexture |
|
================ |
|
*/ |
|
void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) |
|
{ |
|
int i, j; |
|
unsigned *inrow, *inrow2; |
|
unsigned frac, fracstep; |
|
unsigned p1[1024], p2[1024]; |
|
byte *pix1, *pix2, *pix3, *pix4; |
|
|
|
fracstep = inwidth*0x10000/outwidth; |
|
|
|
frac = fracstep>>2; |
|
for (i=0 ; i<outwidth ; i++) |
|
{ |
|
p1[i] = 4*(frac>>16); |
|
frac += fracstep; |
|
} |
|
frac = 3*(fracstep>>2); |
|
for (i=0 ; i<outwidth ; i++) |
|
{ |
|
p2[i] = 4*(frac>>16); |
|
frac += fracstep; |
|
} |
|
|
|
for (i=0 ; i<outheight ; i++, out += outwidth) |
|
{ |
|
inrow = in + inwidth*(int)((i+0.25)*inheight/outheight); |
|
inrow2 = in + inwidth*(int)((i+0.75)*inheight/outheight); |
|
frac = fracstep >> 1; |
|
for (j=0 ; j<outwidth ; j++) |
|
{ |
|
pix1 = (byte *)inrow + p1[j]; |
|
pix2 = (byte *)inrow + p2[j]; |
|
pix3 = (byte *)inrow2 + p1[j]; |
|
pix4 = (byte *)inrow2 + p2[j]; |
|
((byte *)(out+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2; |
|
((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; |
|
((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; |
|
((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; |
|
} |
|
} |
|
} |
|
|
|
/* |
|
================ |
|
GL_LightScaleTexture |
|
|
|
Scale up the pixel values in a texture to increase the |
|
lighting range |
|
================ |
|
*/ |
|
void GL_LightScaleTexture (unsigned *in, int inwidth, int inheight, qboolean only_gamma ) |
|
{ |
|
if ( only_gamma ) |
|
{ |
|
int i, c; |
|
byte *p; |
|
|
|
p = (byte *)in; |
|
|
|
c = inwidth*inheight; |
|
for (i=0 ; i<c ; i++, p+=4) |
|
{ |
|
p[0] = gammatable[p[0]]; |
|
p[1] = gammatable[p[1]]; |
|
p[2] = gammatable[p[2]]; |
|
} |
|
} |
|
else |
|
{ |
|
int i, c; |
|
byte *p; |
|
|
|
p = (byte *)in; |
|
|
|
c = inwidth*inheight; |
|
for (i=0 ; i<c ; i++, p+=4) |
|
{ |
|
p[0] = gammatable[intensitytable[p[0]]]; |
|
p[1] = gammatable[intensitytable[p[1]]]; |
|
p[2] = gammatable[intensitytable[p[2]]]; |
|
} |
|
} |
|
} |
|
|
|
/* |
|
================ |
|
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_Upload32 |
|
|
|
Returns has_alpha |
|
=============== |
|
*/ |
|
void GL_BuildPalettedTexture( unsigned char *paletted_texture, unsigned char *scaled, int scaled_width, int scaled_height ) |
|
{ |
|
int i; |
|
|
|
for ( i = 0; i < scaled_width * scaled_height; i++ ) |
|
{ |
|
unsigned int r, g, b, c; |
|
|
|
r = ( scaled[0] >> 3 ) & 31; |
|
g = ( scaled[1] >> 2 ) & 63; |
|
b = ( scaled[2] >> 3 ) & 31; |
|
|
|
c = r | ( g << 5 ) | ( b << 11 ); |
|
|
|
paletted_texture[i] = gl_state.d_16to8table[c]; |
|
|
|
scaled += 4; |
|
} |
|
} |
|
|
|
int upload_width, upload_height; |
|
qboolean uploaded_paletted; |
|
|
|
qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap) |
|
{ |
|
int samples; |
|
unsigned scaled[256*256]; |
|
unsigned char paletted_texture[256*256]; |
|
int scaled_width, scaled_height; |
|
int i, c; |
|
byte *scan; |
|
int comp; |
|
|
|
uploaded_paletted = false; |
|
|
|
for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) |
|
; |
|
if (gl_round_down->value && scaled_width > width && mipmap) |
|
scaled_width >>= 1; |
|
for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) |
|
; |
|
if (gl_round_down->value && scaled_height > height && mipmap) |
|
scaled_height >>= 1; |
|
|
|
// let people sample down the world textures for speed |
|
if (mipmap) |
|
{ |
|
scaled_width >>= (int)gl_picmip->value; |
|
scaled_height >>= (int)gl_picmip->value; |
|
} |
|
|
|
// don't ever bother with >256 textures |
|
if (scaled_width > 256) |
|
scaled_width = 256; |
|
if (scaled_height > 256) |
|
scaled_height = 256; |
|
|
|
if (scaled_width < 1) |
|
scaled_width = 1; |
|
if (scaled_height < 1) |
|
scaled_height = 1; |
|
|
|
upload_width = scaled_width; |
|
upload_height = scaled_height; |
|
|
|
if (scaled_width * scaled_height > sizeof(scaled)/4) |
|
ri.Sys_Error (ERR_DROP, "GL_Upload32: too big"); |
|
|
|
// scan the texture for any non-255 alpha |
|
c = width*height; |
|
scan = ((byte *)data) + 3; |
|
samples = gl_solid_format; |
|
for (i=0 ; i<c ; i++, scan += 4) |
|
{ |
|
if ( *scan != 255 ) |
|
{ |
|
samples = gl_alpha_format; |
|
break; |
|
} |
|
} |
|
|
|
if (samples == gl_solid_format) |
|
comp = gl_tex_solid_format; |
|
else if (samples == gl_alpha_format) |
|
comp = gl_tex_alpha_format; |
|
else { |
|
ri.Con_Printf (PRINT_ALL, |
|
"Unknown number of texture components %i\n", |
|
samples); |
|
comp = samples; |
|
} |
|
|
|
#if 0 |
|
if (mipmap) |
|
gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans); |
|
else if (scaled_width == width && scaled_height == height) |
|
qglTexImage2D (GL_TEXTURE_2D, 0, comp, 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); |
|
qglTexImage2D (GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); |
|
} |
|
#else |
|
|
|
if (scaled_width == width && scaled_height == height) |
|
{ |
|
if (!mipmap) |
|
{ |
|
if ( qglColorTableEXT && gl_ext_palettedtexture->value && samples == gl_solid_format ) |
|
{ |
|
uploaded_paletted = true; |
|
GL_BuildPalettedTexture( paletted_texture, ( unsigned char * ) data, scaled_width, scaled_height ); |
|
qglTexImage2D( GL_TEXTURE_2D, |
|
0, |
|
GL_COLOR_INDEX8_EXT, |
|
scaled_width, |
|
scaled_height, |
|
0, |
|
GL_COLOR_INDEX, |
|
GL_UNSIGNED_BYTE, |
|
paletted_texture ); |
|
} |
|
else |
|
{ |
|
qglTexImage2D (GL_TEXTURE_2D, 0, comp, 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); |
|
|
|
GL_LightScaleTexture (scaled, scaled_width, scaled_height, !mipmap ); |
|
|
|
if ( qglColorTableEXT && gl_ext_palettedtexture->value && ( samples == gl_solid_format ) ) |
|
{ |
|
uploaded_paletted = true; |
|
GL_BuildPalettedTexture( paletted_texture, ( unsigned char * ) scaled, scaled_width, scaled_height ); |
|
qglTexImage2D( GL_TEXTURE_2D, |
|
0, |
|
GL_COLOR_INDEX8_EXT, |
|
scaled_width, |
|
scaled_height, |
|
0, |
|
GL_COLOR_INDEX, |
|
GL_UNSIGNED_BYTE, |
|
paletted_texture ); |
|
} |
|
else |
|
{ |
|
qglTexImage2D( GL_TEXTURE_2D, 0, comp, 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++; |
|
if ( qglColorTableEXT && gl_ext_palettedtexture->value && samples == gl_solid_format ) |
|
{ |
|
uploaded_paletted = true; |
|
GL_BuildPalettedTexture( paletted_texture, ( unsigned char * ) scaled, scaled_width, scaled_height ); |
|
qglTexImage2D( GL_TEXTURE_2D, |
|
miplevel, |
|
GL_COLOR_INDEX8_EXT, |
|
scaled_width, |
|
scaled_height, |
|
0, |
|
GL_COLOR_INDEX, |
|
GL_UNSIGNED_BYTE, |
|
paletted_texture ); |
|
} |
|
else |
|
{ |
|
qglTexImage2D (GL_TEXTURE_2D, miplevel, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); |
|
} |
|
} |
|
} |
|
done: ; |
|
#endif |
|
|
|
|
|
if (mipmap) |
|
{ |
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); |
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); |
|
} |
|
else |
|
{ |
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); |
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); |
|
} |
|
|
|
return (samples == gl_alpha_format); |
|
} |
|
|
|
/* |
|
=============== |
|
GL_Upload8 |
|
|
|
Returns has_alpha |
|
=============== |
|
*/ |
|
/* |
|
static qboolean IsPowerOf2( int value ) |
|
{ |
|
int i = 1; |
|
|
|
|
|
while ( 1 ) |
|
{ |
|
if ( value == i ) |
|
return true; |
|
if ( i > value ) |
|
return false; |
|
i <<= 1; |
|
} |
|
} |
|
*/ |
|
|
|
qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky ) |
|
{ |
|
unsigned trans[512*256]; |
|
int i, s; |
|
int p; |
|
|
|
s = width*height; |
|
|
|
if (s > sizeof(trans)/4) |
|
ri.Sys_Error (ERR_DROP, "GL_Upload8: too large"); |
|
|
|
if ( qglColorTableEXT && |
|
gl_ext_palettedtexture->value && |
|
is_sky ) |
|
{ |
|
qglTexImage2D( GL_TEXTURE_2D, |
|
0, |
|
GL_COLOR_INDEX8_EXT, |
|
width, |
|
height, |
|
0, |
|
GL_COLOR_INDEX, |
|
GL_UNSIGNED_BYTE, |
|
data ); |
|
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); |
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); |
|
} |
|
else |
|
{ |
|
for (i=0 ; i<s ; i++) |
|
{ |
|
p = data[i]; |
|
trans[i] = d_8to24table[p]; |
|
|
|
if (p == 255) |
|
{ // transparent, so scan around for another color |
|
// to avoid alpha fringes |
|
// FIXME: do a full flood fill so mips work... |
|
if (i > width && data[i-width] != 255) |
|
p = data[i-width]; |
|
else if (i < s-width && data[i+width] != 255) |
|
p = data[i+width]; |
|
else if (i > 0 && data[i-1] != 255) |
|
p = data[i-1]; |
|
else if (i < s-1 && data[i+1] != 255) |
|
p = data[i+1]; |
|
else |
|
p = 0; |
|
// copy rgb components |
|
((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0]; |
|
((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1]; |
|
((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2]; |
|
} |
|
} |
|
|
|
return GL_Upload32 (trans, width, height, mipmap); |
|
} |
|
} |
|
|
|
|
|
/* |
|
================ |
|
GL_LoadPic |
|
|
|
This is also used as an entry point for the generated r_notexture |
|
================ |
|
*/ |
|
image_t *GL_LoadPic (char *name, byte *pic, int width, int height, imagetype_t type, int bits) |
|
{ |
|
image_t *image; |
|
int i; |
|
|
|
// find a free image_t |
|
for (i=0, image=gltextures ; i<numgltextures ; i++,image++) |
|
{ |
|
if (!image->texnum) |
|
break; |
|
} |
|
if (i == numgltextures) |
|
{ |
|
if (numgltextures == MAX_GLTEXTURES) |
|
ri.Sys_Error (ERR_DROP, "MAX_GLTEXTURES"); |
|
numgltextures++; |
|
} |
|
image = &gltextures[i]; |
|
|
|
if (strlen(name) >= sizeof(image->name)) |
|
ri.Sys_Error (ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name); |
|
strcpy (image->name, name); |
|
image->registration_sequence = registration_sequence; |
|
|
|
image->width = width; |
|
image->height = height; |
|
image->type = type; |
|
|
|
if (type == it_skin && bits == 8) |
|
R_FloodFillSkin(pic, width, height); |
|
|
|
// load little pics into the scrap |
|
if (image->type == it_pic && bits == 8 |
|
&& image->width < 64 && image->height < 64) |
|
{ |
|
int x, y; |
|
int i, j, k; |
|
int texnum; |
|
|
|
texnum = Scrap_AllocBlock (image->width, image->height, &x, &y); |
|
if (texnum == -1) |
|
goto nonscrap; |
|
scrap_dirty = true; |
|
|
|
// copy the texels into the scrap block |
|
k = 0; |
|
for (i=0 ; i<image->height ; i++) |
|
for (j=0 ; j<image->width ; j++, k++) |
|
scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = pic[k]; |
|
image->texnum = TEXNUM_SCRAPS + texnum; |
|
image->scrap = true; |
|
image->has_alpha = true; |
|
image->sl = (x+0.01)/(float)BLOCK_WIDTH; |
|
image->sh = (x+image->width-0.01)/(float)BLOCK_WIDTH; |
|
image->tl = (y+0.01)/(float)BLOCK_WIDTH; |
|
image->th = (y+image->height-0.01)/(float)BLOCK_WIDTH; |
|
} |
|
else |
|
{ |
|
nonscrap: |
|
image->scrap = false; |
|
image->texnum = TEXNUM_IMAGES + (image - gltextures); |
|
GL_Bind(image->texnum); |
|
if (bits == 8) |
|
image->has_alpha = GL_Upload8 (pic, width, height, (image->type != it_pic && image->type != it_sky), image->type == it_sky ); |
|
else |
|
image->has_alpha = GL_Upload32 ((unsigned *)pic, width, height, (image->type != it_pic && image->type != it_sky) ); |
|
image->upload_width = upload_width; // after power of 2 and scales |
|
image->upload_height = upload_height; |
|
image->paletted = uploaded_paletted; |
|
image->sl = 0; |
|
image->sh = 1; |
|
image->tl = 0; |
|
image->th = 1; |
|
} |
|
|
|
return image; |
|
} |
|
|
|
|
|
/* |
|
================ |
|
GL_LoadWal |
|
================ |
|
*/ |
|
image_t *GL_LoadWal (char *name) |
|
{ |
|
miptex_t *mt; |
|
int width, height, ofs; |
|
image_t *image; |
|
|
|
ri.FS_LoadFile (name, (void **)&mt); |
|
if (!mt) |
|
{ |
|
ri.Con_Printf (PRINT_ALL, "GL_FindImage: can't load %s\n", name); |
|
return r_notexture; |
|
} |
|
|
|
width = LittleLong (mt->width); |
|
height = LittleLong (mt->height); |
|
ofs = LittleLong (mt->offsets[0]); |
|
|
|
image = GL_LoadPic (name, (byte *)mt + ofs, width, height, it_wall, 8); |
|
|
|
ri.FS_FreeFile ((void *)mt); |
|
|
|
return image; |
|
} |
|
|
|
/* |
|
=============== |
|
GL_FindImage |
|
|
|
Finds or loads the given image |
|
=============== |
|
*/ |
|
image_t *GL_FindImage (char *name, imagetype_t type) |
|
{ |
|
image_t *image; |
|
int i, len; |
|
byte *pic, *palette; |
|
int width, height; |
|
|
|
if (!name) |
|
return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: NULL name"); |
|
len = strlen(name); |
|
if (len<5) |
|
return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: bad name: %s", name); |
|
|
|
// look for it |
|
for (i=0, image=gltextures ; i<numgltextures ; i++,image++) |
|
{ |
|
if (!strcmp(name, image->name)) |
|
{ |
|
image->registration_sequence = registration_sequence; |
|
return image; |
|
} |
|
} |
|
|
|
// |
|
// load the pic from disk |
|
// |
|
pic = NULL; |
|
palette = NULL; |
|
if (!strcmp(name+len-4, ".pcx")) |
|
{ |
|
LoadPCX (name, &pic, &palette, &width, &height); |
|
if (!pic) |
|
return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name); |
|
image = GL_LoadPic (name, pic, width, height, type, 8); |
|
} |
|
else if (!strcmp(name+len-4, ".wal")) |
|
{ |
|
image = GL_LoadWal (name); |
|
} |
|
else if (!strcmp(name+len-4, ".tga")) |
|
{ |
|
LoadTGA (name, &pic, &width, &height); |
|
if (!pic) |
|
return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name); |
|
image = GL_LoadPic (name, pic, width, height, type, 32); |
|
} |
|
else |
|
return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: bad extension on: %s", name); |
|
|
|
|
|
if (pic) |
|
free(pic); |
|
if (palette) |
|
free(palette); |
|
|
|
return image; |
|
} |
|
|
|
|
|
|
|
/* |
|
=============== |
|
R_RegisterSkin |
|
=============== |
|
*/ |
|
struct image_s *R_RegisterSkin (char *name) |
|
{ |
|
return GL_FindImage (name, it_skin); |
|
} |
|
|
|
|
|
/* |
|
================ |
|
GL_FreeUnusedImages |
|
|
|
Any image that was not touched on this registration sequence |
|
will be freed. |
|
================ |
|
*/ |
|
void GL_FreeUnusedImages (void) |
|
{ |
|
int i; |
|
image_t *image; |
|
|
|
// never free r_notexture or particle texture |
|
r_notexture->registration_sequence = registration_sequence; |
|
r_particletexture->registration_sequence = registration_sequence; |
|
|
|
for (i=0, image=gltextures ; i<numgltextures ; i++, image++) |
|
{ |
|
if (image->registration_sequence == registration_sequence) |
|
continue; // used this sequence |
|
if (!image->registration_sequence) |
|
continue; // free image_t slot |
|
if (image->type == it_pic) |
|
continue; // don't free pics |
|
// free it |
|
qglDeleteTextures (1, &image->texnum); |
|
memset (image, 0, sizeof(*image)); |
|
} |
|
} |
|
|
|
|
|
/* |
|
=============== |
|
Draw_GetPalette |
|
=============== |
|
*/ |
|
int Draw_GetPalette (void) |
|
{ |
|
int i; |
|
int r, g, b; |
|
unsigned v; |
|
byte *pic, *pal; |
|
int width, height; |
|
|
|
// get the palette |
|
|
|
LoadPCX ("pics/colormap.pcx", &pic, &pal, &width, &height); |
|
if (!pal) |
|
ri.Sys_Error (ERR_FATAL, "Couldn't load pics/colormap.pcx"); |
|
|
|
for (i=0 ; i<256 ; i++) |
|
{ |
|
r = pal[i*3+0]; |
|
g = pal[i*3+1]; |
|
b = pal[i*3+2]; |
|
|
|
v = (255<<24) + (r<<0) + (g<<8) + (b<<16); |
|
d_8to24table[i] = LittleLong(v); |
|
} |
|
|
|
d_8to24table[255] &= LittleLong(0xffffff); // 255 is transparent |
|
|
|
free (pic); |
|
free (pal); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
/* |
|
=============== |
|
GL_InitImages |
|
=============== |
|
*/ |
|
void GL_InitImages (void) |
|
{ |
|
int i, j; |
|
float g = vid_gamma->value; |
|
|
|
registration_sequence = 1; |
|
|
|
// init intensity conversions |
|
intensity = ri.Cvar_Get ("intensity", "2", 0); |
|
|
|
if ( intensity->value <= 1 ) |
|
ri.Cvar_Set( "intensity", "1" ); |
|
|
|
gl_state.inverse_intensity = 1 / intensity->value; |
|
|
|
Draw_GetPalette (); |
|
|
|
if ( qglColorTableEXT ) |
|
{ |
|
ri.FS_LoadFile( "pics/16to8.dat", &gl_state.d_16to8table ); |
|
if ( !gl_state.d_16to8table ) |
|
ri.Sys_Error( ERR_FATAL, "Couldn't load pics/16to8.pcx"); |
|
} |
|
|
|
if ( gl_config.renderer & ( GL_RENDERER_VOODOO | GL_RENDERER_VOODOO2 ) ) |
|
{ |
|
g = 1.0F; |
|
} |
|
|
|
for ( i = 0; i < 256; i++ ) |
|
{ |
|
if ( g == 1 ) |
|
{ |
|
gammatable[i] = i; |
|
} |
|
else |
|
{ |
|
float inf; |
|
|
|
inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5; |
|
if (inf < 0) |
|
inf = 0; |
|
if (inf > 255) |
|
inf = 255; |
|
gammatable[i] = inf; |
|
} |
|
} |
|
|
|
for (i=0 ; i<256 ; i++) |
|
{ |
|
j = i*intensity->value; |
|
if (j > 255) |
|
j = 255; |
|
intensitytable[i] = j; |
|
} |
|
} |
|
|
|
/* |
|
=============== |
|
GL_ShutdownImages |
|
=============== |
|
*/ |
|
void GL_ShutdownImages (void) |
|
{ |
|
int i; |
|
image_t *image; |
|
|
|
for (i=0, image=gltextures ; i<numgltextures ; i++, image++) |
|
{ |
|
if (!image->registration_sequence) |
|
continue; // free image_t slot |
|
// free it |
|
qglDeleteTextures (1, &image->texnum); |
|
memset (image, 0, sizeof(*image)); |
|
} |
|
} |
|
|
|
|