mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2025-02-05 07:21:17 +00:00
if the lightmap textures are 1024x512 instead of 128x128, all original Q2 levels will only need one lightmap texture (instead of max 26 or so) and even maps that needed all 127 (the 128th was the dynamic lightmap) won't need more than 4. This should result in less glBindTexture() calls. (Note: When I wrote "1 lightmap texture" I meant 4, because where the old renderer dynamically blended the up to 4 lightmaps/surface, I put them in 4 textures that belong together and are alle passed to and blended in the fragment shader)
826 lines
17 KiB
C
826 lines
17 KiB
C
/*
|
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
* Copyright (C) 2016-2017 Daniel Gibson
|
|
*
|
|
* 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 handling for OpenGL3
|
|
*
|
|
* =======================================================================
|
|
*/
|
|
|
|
#include "header/local.h"
|
|
|
|
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}
|
|
};
|
|
|
|
int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
|
|
int gl_filter_max = GL_LINEAR;
|
|
|
|
gl3image_t gl3textures[MAX_GL3TEXTURES];
|
|
int numgl3textures;
|
|
|
|
void
|
|
GL3_TextureMode(char *string)
|
|
{
|
|
const int num_modes = sizeof(modes)/sizeof(modes[0]);
|
|
int i;
|
|
|
|
for (i = 0; i < num_modes; i++)
|
|
{
|
|
if (!Q_stricmp(modes[i].name, string))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == num_modes)
|
|
{
|
|
R_Printf(PRINT_ALL, "bad filter name\n");
|
|
return;
|
|
}
|
|
|
|
gl_filter_min = modes[i].minimize;
|
|
gl_filter_max = modes[i].maximize;
|
|
|
|
/* clamp selected anisotropy */
|
|
if (gl3config.anisotropic)
|
|
{
|
|
if (gl_anisotropic->value > gl3config.max_anisotropy)
|
|
{
|
|
ri.Cvar_SetValue("gl_anisotropic", gl3config.max_anisotropy);
|
|
}
|
|
else if (gl_anisotropic->value < 1.0)
|
|
{
|
|
ri.Cvar_SetValue("gl_anisotropic", 1.0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ri.Cvar_SetValue("gl_anisotropic", 0.0);
|
|
}
|
|
|
|
gl3image_t *glt;
|
|
|
|
/* change all the existing mipmap texture objects */
|
|
for (i = 0, glt = gl3textures; i < numgl3textures; i++, glt++)
|
|
{
|
|
if ((glt->type != it_pic) && (glt->type != it_sky))
|
|
{
|
|
GL3_SelectTMU(GL_TEXTURE0);
|
|
GL3_Bind(glt->texnum);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
|
|
|
/* Set anisotropic filter if supported and enabled */
|
|
if (gl3config.anisotropic && gl_anisotropic->value)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic->value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
GL3_Bind(GLuint texnum)
|
|
{
|
|
extern gl3image_t *draw_chars;
|
|
|
|
if (gl_nobind->value && draw_chars) /* performance evaluation option */
|
|
{
|
|
texnum = draw_chars->texnum;
|
|
}
|
|
|
|
if (gl3state.currenttexture == texnum)
|
|
{
|
|
return;
|
|
}
|
|
|
|
gl3state.currenttexture = texnum;
|
|
GL3_SelectTMU(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texnum);
|
|
}
|
|
|
|
void
|
|
GL3_BindLightmap(int lightmapnum)
|
|
{
|
|
int i=0;
|
|
if(lightmapnum < 0 || lightmapnum >= MAX_LIGHTMAPS)
|
|
{
|
|
R_Printf(PRINT_ALL, "WARNING: Invalid lightmapnum %i used!\n", lightmapnum);
|
|
return;
|
|
}
|
|
|
|
if (gl3state.currentlightmap == lightmapnum)
|
|
{
|
|
return;
|
|
}
|
|
|
|
gl3state.currentlightmap = lightmapnum;
|
|
for(i=0; i<MAX_LIGHTMAPS_PER_SURFACE; ++i)
|
|
{
|
|
// this assumes that GL_TEXTURE<i+1> = GL_TEXTURE<i> + 1
|
|
// at least for GL_TEXTURE0 .. GL_TEXTURE31 that's true
|
|
GL3_SelectTMU(GL_TEXTURE1+i);
|
|
glBindTexture(GL_TEXTURE_2D, gl3state.lightmap_textureIDs[lightmapnum][i]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns has_alpha
|
|
*/
|
|
qboolean
|
|
GL3_Upload32(unsigned *data, int width, int height, qboolean mipmap)
|
|
{
|
|
qboolean res;
|
|
|
|
int samples;
|
|
int i, c;
|
|
byte *scan;
|
|
int comp;
|
|
|
|
c = width * height;
|
|
scan = ((byte *)data) + 3;
|
|
samples = gl3_solid_format;
|
|
comp = gl3_tex_solid_format;
|
|
|
|
for (i = 0; i < c; i++, scan += 4)
|
|
{
|
|
if (*scan != 255)
|
|
{
|
|
samples = gl3_alpha_format;
|
|
comp = gl3_tex_alpha_format;
|
|
break;
|
|
}
|
|
}
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, comp, width, height,
|
|
0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
|
|
res = (samples == gl3_alpha_format);
|
|
|
|
if (mipmap)
|
|
{
|
|
// TODO: some hardware may require mipmapping disabled for NPOT textures!
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
|
}
|
|
else
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
|
}
|
|
|
|
if (mipmap && gl3config.anisotropic && gl_anisotropic->value)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic->value);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns has_alpha
|
|
*/
|
|
qboolean
|
|
GL3_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, "GL3_Upload8: too large");
|
|
}
|
|
|
|
for (i = 0; i < s; i++)
|
|
{
|
|
p = data[i];
|
|
trans[i] = d_8to24table[p];
|
|
|
|
/* transparent, so scan around for
|
|
another color to avoid alpha fringes */
|
|
if (p == 255)
|
|
{
|
|
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 GL3_Upload32(trans, width, height, mipmap);
|
|
}
|
|
|
|
/*
|
|
* This is also used as an entry point for the generated r_notexture
|
|
*/
|
|
gl3image_t *
|
|
GL3_LoadPic(char *name, byte *pic, int width, int realwidth,
|
|
int height, int realheight, imagetype_t type, int bits)
|
|
{
|
|
gl3image_t *image = NULL;
|
|
GLuint texNum=0;
|
|
int i;
|
|
|
|
qboolean nolerp = false;
|
|
|
|
if (gl_nolerp_list != NULL && gl_nolerp_list->string != NULL)
|
|
{
|
|
nolerp = strstr(gl_nolerp_list->string, name) != NULL;
|
|
}
|
|
/* find a free gl3image_t */
|
|
for (i = 0, image = gl3textures; i < numgl3textures; i++, image++)
|
|
{
|
|
if (image->texnum == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == numgl3textures)
|
|
{
|
|
if (numgl3textures == MAX_GL3TEXTURES)
|
|
{
|
|
ri.Sys_Error(ERR_DROP, "MAX_GLTEXTURES");
|
|
}
|
|
|
|
numgl3textures++;
|
|
}
|
|
|
|
image = &gl3textures[i];
|
|
|
|
if (strlen(name) >= sizeof(image->name))
|
|
{
|
|
ri.Sys_Error(ERR_DROP, "GL3_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);
|
|
STUB_ONCE("TODO: Implement and call GL3_FloodFillSkin()!");
|
|
}
|
|
|
|
image->scrap = false; // FIXME: not sure if we need scrap anymore..
|
|
|
|
glGenTextures(1, &texNum);
|
|
|
|
image->texnum = texNum;
|
|
|
|
GL3_SelectTMU(GL_TEXTURE0);
|
|
GL3_Bind(texNum);
|
|
|
|
if (bits == 8)
|
|
{
|
|
image->has_alpha = GL3_Upload8(pic, width, height,
|
|
(image->type != it_pic && image->type != it_sky),
|
|
image->type == it_sky);
|
|
}
|
|
else
|
|
{
|
|
image->has_alpha = GL3_Upload32((unsigned *)pic, width, height,
|
|
(image->type != it_pic && image->type != it_sky));
|
|
}
|
|
|
|
// TODO: I don't think we even need image->upload_*
|
|
image->upload_width = width; // upload_width; /* after power of 2 and scales */
|
|
image->upload_height = height; // upload_height;
|
|
//image->paletted = uploaded_paletted;
|
|
|
|
if (realwidth && realheight)
|
|
{
|
|
if ((realwidth <= image->width) && (realheight <= image->height))
|
|
{
|
|
image->width = realwidth;
|
|
image->height = realheight;
|
|
}
|
|
else
|
|
{
|
|
R_Printf(PRINT_DEVELOPER,
|
|
"Warning, image '%s' has hi-res replacement smaller than the original! (%d x %d) < (%d x %d)\n",
|
|
name, image->width, image->height, realwidth, realheight);
|
|
}
|
|
}
|
|
|
|
image->sl = 0;
|
|
image->sh = 1;
|
|
image->tl = 0;
|
|
image->th = 1;
|
|
|
|
if (nolerp)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
}
|
|
#if 0 // TODO: do we need the scrap? (probably not)
|
|
/* load little pics into the scrap */
|
|
if (!nolerp && (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);
|
|
R_Bind(image->texnum);
|
|
|
|
if (bits == 8)
|
|
{
|
|
image->has_alpha = R_Upload8(pic, width, height,
|
|
(image->type != it_pic && image->type != it_sky),
|
|
image->type == it_sky);
|
|
}
|
|
else
|
|
{
|
|
image->has_alpha = R_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;
|
|
|
|
if (realwidth && realheight)
|
|
{
|
|
if ((realwidth <= image->width) && (realheight <= image->height))
|
|
{
|
|
image->width = realwidth;
|
|
image->height = realheight;
|
|
}
|
|
else
|
|
{
|
|
R_Printf(PRINT_DEVELOPER,
|
|
"Warning, image '%s' has hi-res replacement smaller than the original! (%d x %d) < (%d x %d)\n",
|
|
name, image->width, image->height, realwidth, realheight);
|
|
}
|
|
}
|
|
|
|
image->sl = 0;
|
|
image->sh = 1;
|
|
image->tl = 0;
|
|
image->th = 1;
|
|
|
|
if (nolerp)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
}
|
|
}
|
|
#endif // 0
|
|
return image;
|
|
}
|
|
|
|
static gl3image_t *
|
|
LoadWal(char *origname)
|
|
{
|
|
miptex_t *mt;
|
|
int width, height, ofs;
|
|
gl3image_t *image;
|
|
char name[256];
|
|
|
|
Q_strlcpy(name, origname, sizeof(name));
|
|
|
|
/* Add the extension */
|
|
if (strcmp(COM_FileExtension(name), "wal"))
|
|
{
|
|
Q_strlcat(name, ".wal", sizeof(name));
|
|
}
|
|
|
|
ri.FS_LoadFile(name, (void **)&mt);
|
|
|
|
if (!mt)
|
|
{
|
|
R_Printf(PRINT_ALL, "LoadWal: can't load %s\n", name);
|
|
return gl3_notexture;
|
|
}
|
|
|
|
width = LittleLong(mt->width);
|
|
height = LittleLong(mt->height);
|
|
ofs = LittleLong(mt->offsets[0]);
|
|
|
|
image = GL3_LoadPic(name, (byte *)mt + ofs, width, 0, height, 0, it_wall, 8);
|
|
|
|
ri.FS_FreeFile((void *)mt);
|
|
|
|
return image;
|
|
}
|
|
|
|
/*
|
|
* Finds or loads the given image
|
|
*/
|
|
gl3image_t *
|
|
GL3_FindImage(char *name, imagetype_t type)
|
|
{
|
|
gl3image_t *image;
|
|
int i, len;
|
|
byte *pic;
|
|
int width, height;
|
|
char *ptr;
|
|
char namewe[256];
|
|
int realwidth = 0, realheight = 0;
|
|
const char* ext;
|
|
|
|
if (!name)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ext = COM_FileExtension(name);
|
|
if(!ext[0])
|
|
{
|
|
/* file has no extension */
|
|
return NULL;
|
|
}
|
|
|
|
len = strlen(name);
|
|
|
|
/* Remove the extension */
|
|
memset(namewe, 0, 256);
|
|
memcpy(namewe, name, len - 4);
|
|
|
|
if (len < 5)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* fix backslashes */
|
|
while ((ptr = strchr(name, '\\')))
|
|
{
|
|
*ptr = '/';
|
|
}
|
|
|
|
/* look for it */
|
|
for (i = 0, image = gl3textures; i < numgl3textures; i++, image++)
|
|
{
|
|
if (!strcmp(name, image->name))
|
|
{
|
|
image->registration_sequence = registration_sequence;
|
|
return image;
|
|
}
|
|
}
|
|
|
|
/* load the pic from disk */
|
|
pic = NULL;
|
|
|
|
if (strcmp(ext, "pcx") == 0)
|
|
{
|
|
if (gl_retexturing->value)
|
|
{
|
|
GetPCXInfo(name, &realwidth, &realheight);
|
|
if(realwidth == 0)
|
|
{
|
|
/* No texture found */
|
|
return NULL;
|
|
}
|
|
|
|
/* try to load a tga, png or jpg (in that order/priority) */
|
|
if ( LoadSTB(namewe, "tga", &pic, &width, &height)
|
|
|| LoadSTB(namewe, "png", &pic, &width, &height)
|
|
|| LoadSTB(namewe, "jpg", &pic, &width, &height) )
|
|
{
|
|
/* upload tga or png or jpg */
|
|
image = GL3_LoadPic(name, pic, width, realwidth, height,
|
|
realheight, type, 32);
|
|
}
|
|
else
|
|
{
|
|
/* PCX if no TGA/PNG/JPEG available (exists always) */
|
|
LoadPCX(name, &pic, NULL, &width, &height);
|
|
|
|
if (!pic)
|
|
{
|
|
/* No texture found */
|
|
return NULL;
|
|
}
|
|
|
|
/* Upload the PCX */
|
|
image = GL3_LoadPic(name, pic, width, 0, height, 0, type, 8);
|
|
}
|
|
}
|
|
else /* gl_retexture is not set */
|
|
{
|
|
LoadPCX(name, &pic, NULL, &width, &height);
|
|
|
|
if (!pic)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
image = GL3_LoadPic(name, pic, width, 0, height, 0, type, 8);
|
|
}
|
|
}
|
|
else if (strcmp(ext, "wal") == 0)
|
|
{
|
|
if (gl_retexturing->value)
|
|
{
|
|
/* Get size of the original texture */
|
|
GetWalInfo(name, &realwidth, &realheight);
|
|
if(realwidth == 0)
|
|
{
|
|
/* No texture found */
|
|
return NULL;
|
|
}
|
|
|
|
/* try to load a tga, png or jpg (in that order/priority) */
|
|
if ( LoadSTB(namewe, "tga", &pic, &width, &height)
|
|
|| LoadSTB(namewe, "png", &pic, &width, &height)
|
|
|| LoadSTB(namewe, "jpg", &pic, &width, &height) )
|
|
{
|
|
/* upload tga or png or jpg */
|
|
image = GL3_LoadPic(name, pic, width, realwidth, height, realheight, type, 32);
|
|
}
|
|
else
|
|
{
|
|
/* WAL if no TGA/PNG/JPEG available (exists always) */
|
|
image = LoadWal(namewe);
|
|
}
|
|
|
|
if (!image)
|
|
{
|
|
/* No texture found */
|
|
return NULL;
|
|
}
|
|
}
|
|
else /* gl_retexture is not set */
|
|
{
|
|
image = LoadWal(name);
|
|
|
|
if (!image)
|
|
{
|
|
/* No texture found */
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(ext, "tga") == 0 || strcmp(ext, "png") == 0 || strcmp(ext, "jpg") == 0)
|
|
{
|
|
char tmp_name[256];
|
|
|
|
realwidth = 0;
|
|
realheight = 0;
|
|
|
|
strcpy(tmp_name, namewe);
|
|
strcat(tmp_name, ".wal");
|
|
GetWalInfo(tmp_name, &realwidth, &realheight);
|
|
|
|
if (realwidth == 0 || realheight == 0) {
|
|
/* It's a sky or model skin. */
|
|
strcpy(tmp_name, namewe);
|
|
strcat(tmp_name, ".pcx");
|
|
GetPCXInfo(tmp_name, &realwidth, &realheight);
|
|
}
|
|
|
|
/* TODO: not sure if not having realwidth/heigth is bad - a tga/png/jpg
|
|
* was requested, after all, so there might be no corresponding wal/pcx?
|
|
* if (realwidth == 0 || realheight == 0) return NULL;
|
|
*/
|
|
|
|
if(LoadSTB(name, ext, &pic, &width, &height))
|
|
{
|
|
image = GL3_LoadPic(name, pic, width, realwidth, height, realheight, type, 32);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (pic)
|
|
{
|
|
free(pic);
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
gl3image_t *
|
|
GL3_RegisterSkin(char *name)
|
|
{
|
|
return GL3_FindImage(name, it_skin);
|
|
}
|
|
|
|
/*
|
|
* Any image that was not touched on
|
|
* this registration sequence
|
|
* will be freed.
|
|
*/
|
|
void
|
|
GL3_FreeUnusedImages(void)
|
|
{
|
|
int i;
|
|
gl3image_t *image;
|
|
|
|
/* never free r_notexture or particle texture */
|
|
gl3_notexture->registration_sequence = registration_sequence;
|
|
gl3_particletexture->registration_sequence = registration_sequence;
|
|
|
|
for (i = 0, image = gl3textures; i < numgl3textures; 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 */
|
|
glDeleteTextures(1, &image->texnum);
|
|
memset(image, 0, sizeof(*image));
|
|
}
|
|
}
|
|
|
|
void
|
|
GL3_ShutdownImages(void)
|
|
{
|
|
int i;
|
|
gl3image_t *image;
|
|
|
|
for (i = 0, image = gl3textures; i < numgl3textures; i++, image++)
|
|
{
|
|
if (!image->registration_sequence)
|
|
{
|
|
continue; /* free image_t slot */
|
|
}
|
|
|
|
/* free it */
|
|
glDeleteTextures(1, &image->texnum);
|
|
memset(image, 0, sizeof(*image));
|
|
}
|
|
}
|
|
|
|
static qboolean IsNPOT(int v)
|
|
{
|
|
unsigned int uv = v;
|
|
// just try all the power of two values between 1 and 1 << 15 (32k)
|
|
for(unsigned int i=0; i<16; ++i)
|
|
{
|
|
unsigned int pot = (1u << i);
|
|
if(uv & pot)
|
|
{
|
|
return uv != pot;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
GL3_ImageList_f(void)
|
|
{
|
|
int i, texels=0;
|
|
gl3image_t *image;
|
|
const char *formatstrings[2] = {
|
|
"RGB ",
|
|
"RGBA"
|
|
};
|
|
|
|
const char* potstrings[2] = {
|
|
" POT", "NPOT"
|
|
};
|
|
|
|
R_Printf(PRINT_ALL, "------------------\n");
|
|
|
|
for (i = 0, image = gl3textures; i < numgl3textures; i++, image++)
|
|
{
|
|
int w, h;
|
|
qboolean isNPOT = false;
|
|
if (image->texnum == 0)
|
|
{
|
|
continue;
|
|
}
|
|
w = image->upload_width;
|
|
h = image->upload_height;
|
|
|
|
isNPOT = IsNPOT(w) || IsNPOT(h);
|
|
|
|
texels += w*h;
|
|
|
|
switch (image->type)
|
|
{
|
|
case it_skin:
|
|
R_Printf(PRINT_ALL, "M");
|
|
break;
|
|
case it_sprite:
|
|
R_Printf(PRINT_ALL, "S");
|
|
break;
|
|
case it_wall:
|
|
R_Printf(PRINT_ALL, "W");
|
|
break;
|
|
case it_pic:
|
|
R_Printf(PRINT_ALL, "P");
|
|
break;
|
|
case it_sky:
|
|
R_Printf(PRINT_ALL, "Y");
|
|
break;
|
|
default:
|
|
R_Printf(PRINT_ALL, "?");
|
|
break;
|
|
}
|
|
|
|
R_Printf(PRINT_ALL, " %3i %3i %s %s: %s\n", w, h,
|
|
formatstrings[image->has_alpha], potstrings[isNPOT], image->name);
|
|
}
|
|
|
|
R_Printf(PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels);
|
|
}
|