spingle/source/gl_texmgr.c

1589 lines
37 KiB
C
Raw Normal View History

2019-11-24 20:45:15 -08:00
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
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.
*/
//gl_texmgr.c -- texture manager. manages opengl texture images
2019-11-24 20:45:15 -08:00
2019-12-02 07:07:37 -08:00
#include "q_defs.h"
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
const int32_t gl_solid_format = 3;
const int32_t gl_alpha_format = 4;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
static cvar_t gl_texturemode = {"gl_texturemode", "", CVAR_ARCHIVE};
static cvar_t gl_texture_anisotropy = {"gl_texture_anisotropy", "1", CVAR_ARCHIVE};
static cvar_t gl_max_size = {"gl_max_size", "0", CVAR_NONE};
static cvar_t gl_picmip = {"gl_picmip", "0", CVAR_NONE};
static GLint gl_hardware_maxsize;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
#define MAX_GLTEXTURES 2048
2019-11-25 16:49:58 -08:00
static int32_t numgltextures;
2019-11-25 17:40:18 -08:00
static gltexture_t *active_gltextures, *free_gltextures;
gltexture_t *notexture, *nulltexture;
2019-11-24 20:45:15 -08:00
2019-11-25 14:05:12 -08:00
uint32_t d_8to24table[256];
uint32_t d_8to24table_fbright[256];
uint32_t d_8to24table_fbright_fence[256];
uint32_t d_8to24table_nobright[256];
uint32_t d_8to24table_nobright_fence[256];
uint32_t d_8to24table_conchars[256];
uint32_t d_8to24table_shirt[256];
uint32_t d_8to24table_pants[256];
2019-11-24 20:45:15 -08:00
/*
================================================================================
2019-11-25 17:40:18 -08:00
COMMANDS
2019-11-24 20:45:15 -08:00
================================================================================
*/
typedef struct
{
2019-11-25 17:40:18 -08:00
int32_t magfilter;
int32_t minfilter;
2019-12-03 05:23:56 -08:00
char name[32];
2019-11-24 20:45:15 -08:00
} glmode_t;
2019-11-25 17:40:18 -08:00
static glmode_t glmodes[] =
{
{GL_NEAREST, GL_NEAREST, "GL_NEAREST"},
{GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, "GL_NEAREST_MIPMAP_NEAREST"},
{GL_NEAREST, GL_NEAREST_MIPMAP_LINEAR, "GL_NEAREST_MIPMAP_LINEAR"},
{GL_LINEAR, GL_LINEAR, "GL_LINEAR"},
{GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, "GL_LINEAR_MIPMAP_NEAREST"},
{GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, "GL_LINEAR_MIPMAP_LINEAR"},
2019-11-24 20:45:15 -08:00
};
2019-11-25 16:49:58 -08:00
#define NUM_GLMODES (int32_t)(sizeof(glmodes)/sizeof(glmodes[0]))
static int32_t glmode_idx = NUM_GLMODES - 1; /* trilinear */
2019-11-24 20:45:15 -08:00
/*
===============
TexMgr_DescribeTextureModes_f -- report available texturemodes
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_DescribeTextureModes_f(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0; i < NUM_GLMODES; i++)
Con_SafePrintf(" %2" PRIi32 ": %s\n", i + 1, glmodes[i].name);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
Con_Printf("%" PRIi32 " modes\n", i);
2019-11-24 20:45:15 -08:00
}
/*
===============
TexMgr_SetFilterModes
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_SetFilterModes(gltexture_t *glt)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
GL_Bind(glt);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_NEAREST)
2019-11-24 20:45:15 -08:00
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
2019-11-25 17:40:18 -08:00
else if(glt->flags & TEXPREF_LINEAR)
2019-11-24 20:45:15 -08:00
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
2019-11-25 17:40:18 -08:00
else if(glt->flags & TEXPREF_MIPMAP)
2019-11-24 20:45:15 -08:00
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glmodes[glmode_idx].magfilter);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glmodes[glmode_idx].minfilter);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy.value);
}
else
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glmodes[glmode_idx].magfilter);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glmodes[glmode_idx].magfilter);
}
}
/*
===============
TexMgr_TextureMode_f -- called when gl_texturemode changes
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_TextureMode_f(cvar_t *var)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
gltexture_t *glt;
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 13:20:03 -08:00
(void)var;
2019-11-25 17:40:18 -08:00
for(i = 0; i < NUM_GLMODES; i++)
2019-11-24 20:45:15 -08:00
{
2019-12-07 09:27:26 -08:00
if(!strcmp(glmodes[i].name, gl_texturemode.string))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glmode_idx != i)
2019-11-24 20:45:15 -08:00
{
glmode_idx = i;
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
TexMgr_SetFilterModes(glt);
Sbar_Changed(); //sbar graphics need to be redrawn with new filter mode
2019-11-24 20:45:15 -08:00
//FIXME: warpimages need to be redrawn, too.
}
return;
}
}
2019-11-25 17:40:18 -08:00
for(i = 0; i < NUM_GLMODES; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!q_strcasecmp(glmodes[i].name, gl_texturemode.string))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Cvar_SetQuick(&gl_texturemode, glmodes[i].name);
2019-11-24 20:45:15 -08:00
return;
}
}
i = atoi(gl_texturemode.string);
2019-11-25 17:40:18 -08:00
if(i >= 1 && i <= NUM_GLMODES)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Cvar_SetQuick(&gl_texturemode, glmodes[i - 1].name);
2019-11-24 20:45:15 -08:00
return;
}
2019-11-25 17:40:18 -08:00
Con_Printf("\"%s\" is not a valid texturemode\n", gl_texturemode.string);
Cvar_SetQuick(&gl_texturemode, glmodes[glmode_idx].name);
2019-11-24 20:45:15 -08:00
}
/*
===============
TexMgr_Anisotropy_f -- called when gl_texture_anisotropy changes
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_Anisotropy_f(cvar_t *var)
2019-11-24 20:45:15 -08:00
{
2019-11-25 13:20:03 -08:00
(void)var;
2019-11-25 17:40:18 -08:00
if(gl_texture_anisotropy.value < 1)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Cvar_SetQuick(&gl_texture_anisotropy, "1");
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
else if(gl_texture_anisotropy.value > gl_max_anisotropy)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Cvar_SetValueQuick(&gl_texture_anisotropy, gl_max_anisotropy);
2019-11-24 20:45:15 -08:00
}
else
{
2019-11-25 17:40:18 -08:00
gltexture_t *glt;
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
/* TexMgr_SetFilterModes (glt);*/
if(glt->flags & TEXPREF_MIPMAP)
{
GL_Bind(glt);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glmodes[glmode_idx].magfilter);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glmodes[glmode_idx].minfilter);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy.value);
}
2019-11-24 20:45:15 -08:00
}
}
}
/*
===============
TexMgr_Imagelist_f -- report loaded textures
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_Imagelist_f(void)
2019-11-24 20:45:15 -08:00
{
float mb;
float texels = 0;
2019-11-25 17:40:18 -08:00
gltexture_t *glt;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_SafePrintf(" %4" PRIi32 " x%4" PRIi32 " %s\n", glt->width, glt->height, glt->name);
if(glt->flags & TEXPREF_MIPMAP)
2019-11-24 20:45:15 -08:00
texels += glt->width * glt->height * 4.0f / 3.0f;
else
texels += (glt->width * glt->height);
}
mb = texels * (Cvar_VariableValue("vid_bpp") / 8.0f) / 0x100000;
2019-11-25 17:40:18 -08:00
Con_Printf("%" PRIi32 " textures %" PRIi32 " pixels %1.1f megabytes\n", numgltextures, (int32_t)texels, mb);
2019-11-24 20:45:15 -08:00
}
/*
===============
TexMgr_Imagedump_f -- dump all current textures to TGA files
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_Imagedump_f(void)
2019-11-24 20:45:15 -08:00
{
char tganame[MAX_OSPATH], tempname[MAX_OSPATH], dirname[MAX_OSPATH];
2019-11-25 17:40:18 -08:00
gltexture_t *glt;
2019-11-24 20:45:15 -08:00
byte *buffer;
char *c;
//create directory
q_snprintf(dirname, sizeof(dirname), "%s/imagedump", com_gamedir);
2019-11-25 17:40:18 -08:00
Sys_mkdir(dirname);
2019-11-24 20:45:15 -08:00
//loop through textures
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
q_strlcpy(tempname, glt->name, sizeof(tempname));
while((c = strchr(tempname, ':'))) * c = '_';
while((c = strchr(tempname, '/'))) * c = '_';
while((c = strchr(tempname, '*'))) * c = '_';
2019-11-24 20:45:15 -08:00
q_snprintf(tganame, sizeof(tganame), "imagedump/%s.tga", tempname);
2019-11-25 17:40:18 -08:00
GL_Bind(glt);
glPixelStorei(GL_PACK_ALIGNMENT, 1); /* for widths that aren't a multiple of 4 */
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_ALPHA)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
buffer = (byte *) malloc(glt->width * glt->height * 4);
2019-11-24 20:45:15 -08:00
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
2019-11-25 17:40:18 -08:00
Image_WriteTGA(tganame, buffer, glt->width, glt->height, 32, true);
2019-11-24 20:45:15 -08:00
}
else
{
2019-11-25 17:40:18 -08:00
buffer = (byte *) malloc(glt->width * glt->height * 3);
2019-11-24 20:45:15 -08:00
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
2019-11-25 17:40:18 -08:00
Image_WriteTGA(tganame, buffer, glt->width, glt->height, 24, true);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
free(buffer);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
Con_Printf("dumped %" PRIi32 " textures to %s\n", numgltextures, dirname);
2019-11-24 20:45:15 -08:00
}
/*
===============
TexMgr_FrameUsage -- report texture memory usage for this frame
===============
*/
2019-11-25 17:40:18 -08:00
float TexMgr_FrameUsage(void)
2019-11-24 20:45:15 -08:00
{
float mb;
float texels = 0;
2019-11-25 17:40:18 -08:00
gltexture_t *glt;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->visframe == r_framecount)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_MIPMAP)
2019-11-24 20:45:15 -08:00
texels += glt->width * glt->height * 4.0f / 3.0f;
else
texels += (glt->width * glt->height);
}
}
mb = texels * (Cvar_VariableValue("vid_bpp") / 8.0f) / 0x100000;
return mb;
}
/*
================================================================================
2019-11-25 17:40:18 -08:00
TEXTURE MANAGER
2019-11-24 20:45:15 -08:00
================================================================================
*/
/*
================
TexMgr_FindTexture
================
*/
2019-11-25 17:40:18 -08:00
gltexture_t *TexMgr_FindTexture(qmodel_t *owner, const char *name)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
gltexture_t *glt;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(name)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->owner == owner && !strcmp(glt->name, name))
2019-11-24 20:45:15 -08:00
return glt;
}
}
return NULL;
}
/*
================
TexMgr_NewTexture
================
*/
2019-11-25 17:40:18 -08:00
gltexture_t *TexMgr_NewTexture(void)
2019-11-24 20:45:15 -08:00
{
gltexture_t *glt;
2019-11-25 17:40:18 -08:00
if(numgltextures == MAX_GLTEXTURES)
2019-11-24 20:45:15 -08:00
Sys_Error("numgltextures == MAX_GLTEXTURES\n");
glt = free_gltextures;
free_gltextures = glt->next;
glt->next = active_gltextures;
active_gltextures = glt;
glGenTextures(1, &glt->texnum);
numgltextures++;
return glt;
}
2019-11-25 17:40:18 -08:00
static void GL_DeleteTexture(gltexture_t *texture);
2019-11-24 20:45:15 -08:00
//ericw -- workaround for preventing TexMgr_FreeTexture during TexMgr_ReloadImages
2019-11-25 15:28:38 -08:00
static bool in_reload_images;
2019-11-24 20:45:15 -08:00
/*
================
TexMgr_FreeTexture
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_FreeTexture(gltexture_t *kill)
2019-11-24 20:45:15 -08:00
{
gltexture_t *glt;
2019-11-25 17:40:18 -08:00
if(in_reload_images)
2019-11-24 20:45:15 -08:00
return;
2019-11-25 13:20:03 -08:00
2019-11-25 17:40:18 -08:00
if(kill == NULL)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Con_Printf("TexMgr_FreeTexture: NULL texture\n");
2019-11-24 20:45:15 -08:00
return;
}
2019-11-25 17:40:18 -08:00
if(active_gltextures == kill)
2019-11-24 20:45:15 -08:00
{
active_gltextures = kill->next;
kill->next = free_gltextures;
free_gltextures = kill;
GL_DeleteTexture(kill);
numgltextures--;
return;
}
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->next == kill)
2019-11-24 20:45:15 -08:00
{
glt->next = kill->next;
kill->next = free_gltextures;
free_gltextures = kill;
GL_DeleteTexture(kill);
numgltextures--;
return;
}
}
2019-11-25 17:40:18 -08:00
Con_Printf("TexMgr_FreeTexture: not found\n");
2019-11-24 20:45:15 -08:00
}
/*
================
TexMgr_FreeTextures
compares each bit in "flags" to the one in glt->flags only if that bit is active in "mask"
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_FreeTextures(uint32_t flags, uint32_t mask)
2019-11-24 20:45:15 -08:00
{
gltexture_t *glt, *next;
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = next)
2019-11-24 20:45:15 -08:00
{
next = glt->next;
2019-11-25 17:40:18 -08:00
if((glt->flags & mask) == (flags & mask))
TexMgr_FreeTexture(glt);
2019-11-24 20:45:15 -08:00
}
}
/*
================
TexMgr_FreeTexturesForOwner
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_FreeTexturesForOwner(qmodel_t *owner)
2019-11-24 20:45:15 -08:00
{
gltexture_t *glt, *next;
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = next)
2019-11-24 20:45:15 -08:00
{
next = glt->next;
2019-11-25 17:40:18 -08:00
if(glt && glt->owner == owner)
TexMgr_FreeTexture(glt);
2019-11-24 20:45:15 -08:00
}
}
/*
================
TexMgr_DeleteTextureObjects
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_DeleteTextureObjects(void)
2019-11-24 20:45:15 -08:00
{
gltexture_t *glt;
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
GL_DeleteTexture(glt);
2019-11-24 20:45:15 -08:00
}
}
/*
================================================================================
2019-11-25 17:40:18 -08:00
INIT
2019-11-24 20:45:15 -08:00
================================================================================
*/
/*
=================
TexMgr_LoadPalette -- johnfitz -- was VID_SetPalette, moved here, renamed, rewritten
=================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_LoadPalette(void)
2019-11-24 20:45:15 -08:00
{
byte *pal, *src, *dst;
2019-11-25 16:49:58 -08:00
int32_t i, mark;
2019-11-24 20:45:15 -08:00
FILE *f;
2019-11-25 17:40:18 -08:00
COM_FOpenFile("gfx/palette.lmp", &f, NULL);
if(!f)
Sys_Error("Couldn't load gfx/palette.lmp");
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
mark = Hunk_LowMark();
pal = Hunk_AllocName(768, __func__);
2019-11-25 17:40:18 -08:00
fread(pal, 1, 768, f);
2019-11-24 20:45:15 -08:00
fclose(f);
//standard palette, 255 is transparent
dst = (byte *)d_8to24table;
src = pal;
2019-11-25 17:40:18 -08:00
for(i = 0; i < 256; i++)
2019-11-24 20:45:15 -08:00
{
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = 255;
}
((byte *) &d_8to24table[255]) [3] = 0;
//fullbright palette, 0-223 are black (for additive blending)
2019-11-25 17:40:18 -08:00
src = pal + 224 * 3;
2019-11-24 20:45:15 -08:00
dst = (byte *) &d_8to24table_fbright[224];
2019-11-25 17:40:18 -08:00
for(i = 224; i < 256; i++)
2019-11-24 20:45:15 -08:00
{
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = 255;
}
2019-11-25 17:40:18 -08:00
for(i = 0; i < 224; i++)
2019-11-24 20:45:15 -08:00
{
dst = (byte *) &d_8to24table_fbright[i];
dst[3] = 255;
dst[2] = dst[1] = dst[0] = 0;
}
//nobright palette, 224-255 are black (for additive blending)
dst = (byte *)d_8to24table_nobright;
src = pal;
2019-11-25 17:40:18 -08:00
for(i = 0; i < 256; i++)
2019-11-24 20:45:15 -08:00
{
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = 255;
}
2019-11-25 17:40:18 -08:00
for(i = 224; i < 256; i++)
2019-11-24 20:45:15 -08:00
{
dst = (byte *) &d_8to24table_nobright[i];
dst[3] = 255;
dst[2] = dst[1] = dst[0] = 0;
}
//fullbright palette, for fence textures
2019-11-25 17:40:18 -08:00
memcpy(d_8to24table_fbright_fence, d_8to24table_fbright, 256 * 4);
2019-11-24 20:45:15 -08:00
d_8to24table_fbright_fence[255] = 0; // Alpha of zero.
//nobright palette, for fence textures
2019-11-25 17:40:18 -08:00
memcpy(d_8to24table_nobright_fence, d_8to24table_nobright, 256 * 4);
2019-11-24 20:45:15 -08:00
d_8to24table_nobright_fence[255] = 0; // Alpha of zero.
//conchars palette, 0 and 255 are transparent
2019-11-25 17:40:18 -08:00
memcpy(d_8to24table_conchars, d_8to24table, 256 * 4);
2019-11-24 20:45:15 -08:00
((byte *) &d_8to24table_conchars[0]) [3] = 0;
2019-11-25 17:40:18 -08:00
Hunk_FreeToLowMark(mark);
2019-11-24 20:45:15 -08:00
}
/*
================
TexMgr_NewGame
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_NewGame(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
TexMgr_FreeTextures(0, TEXPREF_PERSIST); //deletes all textures where TEXPREF_PERSIST is unset
TexMgr_LoadPalette();
2019-11-24 20:45:15 -08:00
}
/*
=============
TexMgr_RecalcWarpImageSize -- called during init, and after a vid_restart
choose safe warpimage size and resize existing warpimage textures
=============
*/
2019-11-25 17:40:18 -08:00
void TexMgr_RecalcWarpImageSize(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
// int32_t oldsize = gl_warpimagesize;
int32_t mark;
2019-11-24 20:45:15 -08:00
gltexture_t *glt;
byte *dummy;
//
// find the new correct size
//
2019-11-25 17:40:18 -08:00
gl_warpimagesize = TexMgr_SafeTextureSize(512);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
while(gl_warpimagesize > vid.width)
2019-11-24 20:45:15 -08:00
gl_warpimagesize >>= 1;
2019-11-25 17:40:18 -08:00
while(gl_warpimagesize > vid.height)
2019-11-24 20:45:15 -08:00
gl_warpimagesize >>= 1;
// resize the textures in opengl
mark = Hunk_LowMark();
dummy = Hunk_AllocName(gl_warpimagesize * gl_warpimagesize * 4, __func__);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_WARPIMAGE)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
GL_Bind(glt);
glTexImage2D(GL_TEXTURE_2D, 0, gl_solid_format, gl_warpimagesize, gl_warpimagesize, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummy);
2019-11-24 20:45:15 -08:00
glt->width = glt->height = gl_warpimagesize;
}
}
2019-11-25 17:40:18 -08:00
Hunk_FreeToLowMark(mark);
2019-11-24 20:45:15 -08:00
}
/*
================
TexMgr_Init
must be called before any texture loading
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_Init(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-25 17:40:18 -08:00
static byte notexture_data[16] = {159, 91, 83, 255, 0, 0, 0, 255, 0, 0, 0, 255, 159, 91, 83, 255}; //black and pink checker
static byte nulltexture_data[16] = {127, 191, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 127, 191, 255, 255}; //black and blue checker
2019-11-24 20:45:15 -08:00
extern texture_t *r_notexture_mip, *r_notexture_mip2;
// init texture list
2019-11-25 17:40:18 -08:00
free_gltextures = (gltexture_t *) Hunk_AllocName(MAX_GLTEXTURES * sizeof(gltexture_t), "gltextures");
2019-11-24 20:45:15 -08:00
active_gltextures = NULL;
2019-11-25 17:40:18 -08:00
for(i = 0; i < MAX_GLTEXTURES - 1; i++)
free_gltextures[i].next = &free_gltextures[i + 1];
2019-11-24 20:45:15 -08:00
free_gltextures[i].next = NULL;
numgltextures = 0;
// palette
2019-11-25 17:40:18 -08:00
TexMgr_LoadPalette();
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
Cvar_RegisterVariable(&gl_max_size);
Cvar_RegisterVariable(&gl_picmip);
Cvar_RegisterVariable(&gl_texture_anisotropy);
Cvar_SetCallback(&gl_texture_anisotropy, &TexMgr_Anisotropy_f);
2019-11-24 20:45:15 -08:00
gl_texturemode.string = glmodes[glmode_idx].name;
2019-11-25 17:40:18 -08:00
Cvar_RegisterVariable(&gl_texturemode);
Cvar_SetCallback(&gl_texturemode, &TexMgr_TextureMode_f);
Cmd_AddCommand("gl_describetexturemodes", &TexMgr_DescribeTextureModes_f);
Cmd_AddCommand("imagelist", &TexMgr_Imagelist_f);
Cmd_AddCommand("imagedump", &TexMgr_Imagedump_f);
2019-11-24 20:45:15 -08:00
// poll max size from hardware
2019-11-25 17:40:18 -08:00
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_hardware_maxsize);
2019-11-24 20:45:15 -08:00
// load notexture images
2019-11-25 17:40:18 -08:00
notexture = TexMgr_LoadImage(NULL, "notexture", 2, 2, SRC_RGBA, notexture_data, "", (src_offset_t)notexture_data, TEXPREF_NEAREST | TEXPREF_PERSIST | TEXPREF_NOPICMIP);
nulltexture = TexMgr_LoadImage(NULL, "nulltexture", 2, 2, SRC_RGBA, nulltexture_data, "", (src_offset_t)nulltexture_data, TEXPREF_NEAREST | TEXPREF_PERSIST | TEXPREF_NOPICMIP);
2019-11-24 20:45:15 -08:00
//have to assign these here becuase Mod_Init is called before TexMgr_Init
r_notexture_mip->gltexture = r_notexture_mip2->gltexture = notexture;
//set safe size for warpimages
gl_warpimagesize = 0;
2019-11-25 17:40:18 -08:00
TexMgr_RecalcWarpImageSize();
2019-11-24 20:45:15 -08:00
}
/*
================================================================================
2019-11-25 17:40:18 -08:00
IMAGE LOADING
2019-11-24 20:45:15 -08:00
================================================================================
*/
/*
================
TexMgr_Pad -- return smallest power of two greater than or equal to s
================
*/
2019-11-25 17:40:18 -08:00
int32_t TexMgr_Pad(int32_t s)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-25 17:40:18 -08:00
for(i = 1; i < s; i <<= 1)
2019-11-24 20:45:15 -08:00
;
return i;
}
/*
===============
TexMgr_SafeTextureSize -- return a size with hardware and user prefs in mind
===============
*/
2019-11-25 17:40:18 -08:00
int32_t TexMgr_SafeTextureSize(int32_t s)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!gl_texture_NPOT)
2019-11-24 20:45:15 -08:00
s = TexMgr_Pad(s);
2019-11-25 17:40:18 -08:00
if((int32_t)gl_max_size.value > 0)
2019-11-25 16:49:58 -08:00
s = q_min(TexMgr_Pad((int32_t)gl_max_size.value), s);
2019-11-24 20:45:15 -08:00
s = q_min(gl_hardware_maxsize, s);
return s;
}
/*
================
TexMgr_PadConditional -- only pad if a texture of that size would be padded. (used for tex coords)
================
*/
2019-11-25 17:40:18 -08:00
int32_t TexMgr_PadConditional(int32_t s)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(s < TexMgr_SafeTextureSize(s))
2019-11-24 20:45:15 -08:00
return TexMgr_Pad(s);
else
return s;
}
/*
================
TexMgr_MipMapW
================
*/
2019-11-25 17:40:18 -08:00
static unsigned *TexMgr_MipMapW(unsigned *data, int32_t width, int32_t height)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, size;
byte *out, *in;
2019-11-24 20:45:15 -08:00
out = in = (byte *)data;
2019-11-25 17:40:18 -08:00
size = (width * height) >> 1;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0; i < size; i++, out += 4, in += 8)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
out[0] = (in[0] + in[4]) >> 1;
out[1] = (in[1] + in[5]) >> 1;
out[2] = (in[2] + in[6]) >> 1;
out[3] = (in[3] + in[7]) >> 1;
2019-11-24 20:45:15 -08:00
}
return data;
}
/*
================
TexMgr_MipMapH
================
*/
2019-11-25 17:40:18 -08:00
static unsigned *TexMgr_MipMapH(unsigned *data, int32_t width, int32_t height)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, j;
byte *out, *in;
2019-11-24 20:45:15 -08:00
out = in = (byte *)data;
2019-11-25 17:40:18 -08:00
height >>= 1;
width <<= 2;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0; i < height; i++, in += width)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(j = 0; j < width; j += 4, out += 4, in += 4)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
out[0] = (in[0] + in[width + 0]) >> 1;
out[1] = (in[1] + in[width + 1]) >> 1;
out[2] = (in[2] + in[width + 2]) >> 1;
out[3] = (in[3] + in[width + 3]) >> 1;
2019-11-24 20:45:15 -08:00
}
}
return data;
}
/*
================
TexMgr_ResampleTexture -- bilinear resample
================
*/
2019-11-25 17:40:18 -08:00
static unsigned *TexMgr_ResampleTexture(unsigned *in, int32_t inwidth, int32_t inheight, bool alpha)
2019-11-24 20:45:15 -08:00
{
byte *nwpx, *nepx, *swpx, *sepx, *dest;
unsigned xfrac, yfrac, x, y, modx, mody, imodx, imody, injump, outjump;
unsigned *out;
2019-11-25 16:49:58 -08:00
int32_t i, j, outwidth, outheight;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(inwidth == TexMgr_Pad(inwidth) && inheight == TexMgr_Pad(inheight))
2019-11-24 20:45:15 -08:00
return in;
outwidth = TexMgr_Pad(inwidth);
outheight = TexMgr_Pad(inheight);
out = Hunk_AllocName(outwidth * outheight * 4, __func__);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
xfrac = ((inwidth - 1) << 16) / (outwidth - 1);
yfrac = ((inheight - 1) << 16) / (outheight - 1);
2019-11-24 20:45:15 -08:00
y = outjump = 0;
2019-11-25 17:40:18 -08:00
for(i = 0; i < outheight; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
mody = (y >> 8) & 0xFF;
2019-11-24 20:45:15 -08:00
imody = 256 - mody;
2019-11-25 17:40:18 -08:00
injump = (y >> 16) * inwidth;
2019-11-24 20:45:15 -08:00
x = 0;
2019-11-25 17:40:18 -08:00
for(j = 0; j < outwidth; j++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
modx = (x >> 8) & 0xFF;
2019-11-24 20:45:15 -08:00
imodx = 256 - modx;
2019-11-25 17:40:18 -08:00
nwpx = (byte *)(in + (x >> 16) + injump);
2019-11-24 20:45:15 -08:00
nepx = nwpx + 4;
2019-11-25 17:40:18 -08:00
swpx = nwpx + inwidth * 4;
2019-11-24 20:45:15 -08:00
sepx = swpx + 4;
dest = (byte *)(out + outjump + j);
2019-11-25 17:40:18 -08:00
dest[0] = (nwpx[0] * imodx * imody + nepx[0] * modx * imody + swpx[0] * imodx * mody + sepx[0] * modx * mody) >> 16;
dest[1] = (nwpx[1] * imodx * imody + nepx[1] * modx * imody + swpx[1] * imodx * mody + sepx[1] * modx * mody) >> 16;
dest[2] = (nwpx[2] * imodx * imody + nepx[2] * modx * imody + swpx[2] * imodx * mody + sepx[2] * modx * mody) >> 16;
if(alpha)
dest[3] = (nwpx[3] * imodx * imody + nepx[3] * modx * imody + swpx[3] * imodx * mody + sepx[3] * modx * mody) >> 16;
2019-11-24 20:45:15 -08:00
else
dest[3] = 255;
x += xfrac;
}
outjump += outwidth;
y += yfrac;
}
return out;
}
/*
===============
TexMgr_AlphaEdgeFix
eliminate pink edges on sprites, etc.
operates in place on 32bit data
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_AlphaEdgeFix(byte *data, int32_t width, int32_t height)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, j, n = 0, b, c[3] = {0, 0, 0},
lastrow, thisrow, nextrow,
lastpix, thispix, nextpix;
byte *dest = data;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0; i < height; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
lastrow = width * 4 * ((i == 0) ? height - 1 : i - 1);
2019-11-24 20:45:15 -08:00
thisrow = width * 4 * i;
2019-11-25 17:40:18 -08:00
nextrow = width * 4 * ((i == height - 1) ? 0 : i + 1);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(j = 0; j < width; j++, dest += 4)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(dest[3]) //not transparent
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
lastpix = 4 * ((j == 0) ? width - 1 : j - 1);
2019-11-24 20:45:15 -08:00
thispix = 4 * j;
2019-11-25 17:40:18 -08:00
nextpix = 4 * ((j == width - 1) ? 0 : j + 1);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
b = lastrow + lastpix;
if(data[b + 3])
{
c[0] += data[b];
c[1] += data[b + 1];
c[2] += data[b + 2];
n++;
}
b = thisrow + lastpix;
if(data[b + 3])
{
c[0] += data[b];
c[1] += data[b + 1];
c[2] += data[b + 2];
n++;
}
b = nextrow + lastpix;
if(data[b + 3])
{
c[0] += data[b];
c[1] += data[b + 1];
c[2] += data[b + 2];
n++;
}
b = lastrow + thispix;
if(data[b + 3])
{
c[0] += data[b];
c[1] += data[b + 1];
c[2] += data[b + 2];
n++;
}
b = nextrow + thispix;
if(data[b + 3])
{
c[0] += data[b];
c[1] += data[b + 1];
c[2] += data[b + 2];
n++;
}
b = lastrow + nextpix;
if(data[b + 3])
{
c[0] += data[b];
c[1] += data[b + 1];
c[2] += data[b + 2];
n++;
}
b = thisrow + nextpix;
if(data[b + 3])
{
c[0] += data[b];
c[1] += data[b + 1];
c[2] += data[b + 2];
n++;
}
b = nextrow + nextpix;
if(data[b + 3])
{
c[0] += data[b];
c[1] += data[b + 1];
c[2] += data[b + 2];
n++;
}
2019-11-24 20:45:15 -08:00
//average all non-transparent neighbors
2019-11-25 17:40:18 -08:00
if(n)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
dest[0] = (byte)(c[0] / n);
dest[1] = (byte)(c[1] / n);
dest[2] = (byte)(c[2] / n);
2019-11-24 20:45:15 -08:00
n = c[0] = c[1] = c[2] = 0;
}
}
}
}
/*
===============
TexMgr_PadEdgeFixW -- special case of AlphaEdgeFix for textures that only need it because they were padded
operates in place on 32bit data, and expects unpadded height and width values
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_PadEdgeFixW(byte *data, int32_t width, int32_t height)
2019-11-24 20:45:15 -08:00
{
byte *src, *dst;
2019-11-25 16:49:58 -08:00
int32_t i, padw, padh;
2019-11-24 20:45:15 -08:00
padw = TexMgr_PadConditional(width);
padh = TexMgr_PadConditional(height);
//copy last full column to first empty column, leaving alpha byte at zero
src = data + (width - 1) * 4;
2019-11-25 17:40:18 -08:00
for(i = 0; i < padh; i++)
2019-11-24 20:45:15 -08:00
{
src[4] = src[0];
src[5] = src[1];
src[6] = src[2];
src += padw * 4;
}
//copy first full column to last empty column, leaving alpha byte at zero
src = data;
dst = data + (padw - 1) * 4;
2019-11-25 17:40:18 -08:00
for(i = 0; i < padh; i++)
2019-11-24 20:45:15 -08:00
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
src += padw * 4;
dst += padw * 4;
}
}
/*
===============
TexMgr_PadEdgeFixH -- special case of AlphaEdgeFix for textures that only need it because they were padded
operates in place on 32bit data, and expects unpadded height and width values
===============
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_PadEdgeFixH(byte *data, int32_t width, int32_t height)
2019-11-24 20:45:15 -08:00
{
byte *src, *dst;
2019-11-25 16:49:58 -08:00
int32_t i, padw, padh;
2019-11-24 20:45:15 -08:00
padw = TexMgr_PadConditional(width);
padh = TexMgr_PadConditional(height);
//copy last full row to first empty row, leaving alpha byte at zero
dst = data + height * padw * 4;
src = dst - padw * 4;
2019-11-25 17:40:18 -08:00
for(i = 0; i < padw; i++)
2019-11-24 20:45:15 -08:00
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
src += 4;
dst += 4;
}
//copy first full row to last empty row, leaving alpha byte at zero
dst = data + (padh - 1) * padw * 4;
src = data;
2019-11-25 17:40:18 -08:00
for(i = 0; i < padw; i++)
2019-11-24 20:45:15 -08:00
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
src += 4;
dst += 4;
}
}
/*
================
TexMgr_8to32
================
*/
2019-11-25 17:40:18 -08:00
static unsigned *TexMgr_8to32(byte *in, int32_t pixels, uint32_t *usepal)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
unsigned *out, *data;
out = data = Hunk_AllocName(pixels * 4, __func__);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0; i < pixels; i++)
2019-11-24 20:45:15 -08:00
*out++ = usepal[*in++];
return data;
}
/*
================
TexMgr_PadImageW -- return image with width padded up to power-of-two dimentions
================
*/
2019-11-25 17:40:18 -08:00
static byte *TexMgr_PadImageW(byte *in, int32_t width, int32_t height, byte padbyte)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t i, j, outwidth;
2019-11-24 20:45:15 -08:00
byte *out, *data;
2019-11-25 17:40:18 -08:00
if(width == TexMgr_Pad(width))
2019-11-24 20:45:15 -08:00
return in;
outwidth = TexMgr_Pad(width);
out = data = Hunk_AllocName(outwidth * height, __func__);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0; i < height; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(j = 0; j < width; j++)
2019-11-24 20:45:15 -08:00
*out++ = *in++;
2019-11-25 17:40:18 -08:00
for(; j < outwidth; j++)
2019-11-24 20:45:15 -08:00
*out++ = padbyte;
}
return data;
}
/*
================
TexMgr_PadImageH -- return image with height padded up to power-of-two dimentions
================
*/
2019-11-25 17:40:18 -08:00
static byte *TexMgr_PadImageH(byte *in, int32_t width, int32_t height, byte padbyte)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t i, srcpix, dstpix;
2019-11-24 20:45:15 -08:00
byte *data, *out;
2019-11-25 17:40:18 -08:00
if(height == TexMgr_Pad(height))
2019-11-24 20:45:15 -08:00
return in;
srcpix = width * height;
dstpix = width * TexMgr_Pad(height);
out = data = Hunk_AllocName(dstpix, __func__);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0; i < srcpix; i++)
2019-11-24 20:45:15 -08:00
*out++ = *in++;
2019-11-25 17:40:18 -08:00
for(; i < dstpix; i++)
2019-11-24 20:45:15 -08:00
*out++ = padbyte;
return data;
}
/*
================
TexMgr_LoadImage32 -- handles 32bit source data
================
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_LoadImage32(gltexture_t *glt, unsigned *data)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t internalformat, miplevel, mipwidth, mipheight, picmip;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(!gl_texture_NPOT)
2019-11-24 20:45:15 -08:00
{
// resample up
2019-11-25 17:40:18 -08:00
data = TexMgr_ResampleTexture(data, glt->width, glt->height, glt->flags & TEXPREF_ALPHA);
2019-11-24 20:45:15 -08:00
glt->width = TexMgr_Pad(glt->width);
glt->height = TexMgr_Pad(glt->height);
}
// mipmap down
2019-11-25 16:49:58 -08:00
picmip = (glt->flags & TEXPREF_NOPICMIP) ? 0 : q_max((int32_t)gl_picmip.value, 0);
2019-11-25 17:40:18 -08:00
mipwidth = TexMgr_SafeTextureSize(glt->width >> picmip);
mipheight = TexMgr_SafeTextureSize(glt->height >> picmip);
while((int32_t) glt->width > mipwidth)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
TexMgr_MipMapW(data, glt->width, glt->height);
2019-11-24 20:45:15 -08:00
glt->width >>= 1;
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_ALPHA)
TexMgr_AlphaEdgeFix((byte *)data, glt->width, glt->height);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
while((int32_t) glt->height > mipheight)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
TexMgr_MipMapH(data, glt->width, glt->height);
2019-11-24 20:45:15 -08:00
glt->height >>= 1;
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_ALPHA)
TexMgr_AlphaEdgeFix((byte *)data, glt->width, glt->height);
2019-11-24 20:45:15 -08:00
}
// upload
2019-11-25 17:40:18 -08:00
GL_Bind(glt);
2019-11-24 20:45:15 -08:00
internalformat = (glt->flags & TEXPREF_ALPHA) ? gl_alpha_format : gl_solid_format;
2019-11-25 17:40:18 -08:00
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, glt->width, glt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
2019-11-24 20:45:15 -08:00
// upload mipmaps
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_MIPMAP)
2019-11-24 20:45:15 -08:00
{
mipwidth = glt->width;
mipheight = glt->height;
2019-11-25 17:40:18 -08:00
for(miplevel = 1; mipwidth > 1 || mipheight > 1; miplevel++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(mipwidth > 1)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
TexMgr_MipMapW(data, mipwidth, mipheight);
2019-11-24 20:45:15 -08:00
mipwidth >>= 1;
}
2019-11-25 17:40:18 -08:00
if(mipheight > 1)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
TexMgr_MipMapH(data, mipwidth, mipheight);
2019-11-24 20:45:15 -08:00
mipheight >>= 1;
}
2019-11-25 17:40:18 -08:00
glTexImage2D(GL_TEXTURE_2D, miplevel, internalformat, mipwidth, mipheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
2019-11-24 20:45:15 -08:00
}
}
// set filter modes
2019-11-25 17:40:18 -08:00
TexMgr_SetFilterModes(glt);
2019-11-24 20:45:15 -08:00
}
/*
================
TexMgr_LoadImage8 -- handles 8bit source data, then passes it to LoadImage32
================
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_LoadImage8(gltexture_t *glt, byte *data)
2019-11-24 20:45:15 -08:00
{
extern cvar_t gl_fullbrights;
2019-11-25 15:28:38 -08:00
bool padw = false, padh = false;
2019-11-24 20:45:15 -08:00
byte padbyte;
2019-11-25 14:05:12 -08:00
uint32_t *usepal;
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
// HACK
if(strstr(glt->name, "shot1sid") && glt->width == 32 && glt->height == 32 && CRC_Block(data, 1024) == 65393)
2019-11-24 20:45:15 -08:00
{
// This texture in b_shell1.bsp has some of the first 32 pixels painted white.
// They are invisible in software, but look really ugly in GL. So we just copy
// 32 pixels from the bottom to make it look nice.
2019-11-25 17:40:18 -08:00
memcpy(data, data + 32 * 31, 32);
2019-11-24 20:45:15 -08:00
}
// detect false alpha cases
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_ALPHA && !(glt->flags & TEXPREF_CONCHARS))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(i = 0; i < (int32_t)(glt->width * glt->height); i++)
if(data[i] == 255) //transparent index
2019-11-24 20:45:15 -08:00
break;
2019-11-25 17:40:18 -08:00
if(i == (int32_t)(glt->width * glt->height))
2019-11-24 20:45:15 -08:00
glt->flags -= TEXPREF_ALPHA;
}
// choose palette and padbyte
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_FULLBRIGHT)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_ALPHA)
2019-11-24 20:45:15 -08:00
usepal = d_8to24table_fbright_fence;
else
usepal = d_8to24table_fbright;
padbyte = 0;
}
2019-11-25 17:40:18 -08:00
else if(glt->flags & TEXPREF_NOBRIGHT && gl_fullbrights.value)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_ALPHA)
2019-11-24 20:45:15 -08:00
usepal = d_8to24table_nobright_fence;
else
usepal = d_8to24table_nobright;
padbyte = 0;
}
2019-11-25 17:40:18 -08:00
else if(glt->flags & TEXPREF_CONCHARS)
2019-11-24 20:45:15 -08:00
{
usepal = d_8to24table_conchars;
padbyte = 0;
}
else
{
usepal = d_8to24table;
padbyte = 255;
}
// pad each dimention, but only if it's not going to be downsampled later
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_PAD)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if((int32_t) glt->width < TexMgr_SafeTextureSize(glt->width))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
data = TexMgr_PadImageW(data, glt->width, glt->height, padbyte);
2019-11-24 20:45:15 -08:00
glt->width = TexMgr_Pad(glt->width);
padw = true;
}
2019-11-25 17:40:18 -08:00
if((int32_t) glt->height < TexMgr_SafeTextureSize(glt->height))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
data = TexMgr_PadImageH(data, glt->width, glt->height, padbyte);
2019-11-24 20:45:15 -08:00
glt->height = TexMgr_Pad(glt->height);
padh = true;
}
}
// convert to 32bit
data = (byte *)TexMgr_8to32(data, glt->width * glt->height, usepal);
// fix edges
2019-11-25 17:40:18 -08:00
if(glt->flags & TEXPREF_ALPHA)
TexMgr_AlphaEdgeFix(data, glt->width, glt->height);
2019-11-24 20:45:15 -08:00
else
{
2019-11-25 17:40:18 -08:00
if(padw)
TexMgr_PadEdgeFixW(data, glt->source_width, glt->source_height);
if(padh)
TexMgr_PadEdgeFixH(data, glt->source_width, glt->source_height);
2019-11-24 20:45:15 -08:00
}
// upload it
2019-11-25 17:40:18 -08:00
TexMgr_LoadImage32(glt, (unsigned *)data);
2019-11-24 20:45:15 -08:00
}
/*
================
TexMgr_LoadLightmap -- handles lightmap data
================
*/
2019-11-25 17:40:18 -08:00
static void TexMgr_LoadLightmap(gltexture_t *glt, byte *data)
2019-11-24 20:45:15 -08:00
{
// upload it
2019-11-25 17:40:18 -08:00
GL_Bind(glt);
glTexImage2D(GL_TEXTURE_2D, 0, lightmap_bytes, glt->width, glt->height, 0, gl_lightmap_format, GL_UNSIGNED_BYTE, data);
2019-11-24 20:45:15 -08:00
// set filter modes
2019-11-25 17:40:18 -08:00
TexMgr_SetFilterModes(glt);
2019-11-24 20:45:15 -08:00
}
/*
================
TexMgr_LoadImage -- the one entry point for loading all textures
================
*/
2019-11-25 17:40:18 -08:00
gltexture_t *TexMgr_LoadImage(qmodel_t *owner, const char *name, int32_t width, int32_t height, enum srcformat format,
byte *data, const char *source_file, src_offset_t source_offset, unsigned flags)
2019-11-24 20:45:15 -08:00
{
2019-11-25 14:09:48 -08:00
uint16_t crc;
2019-11-24 20:45:15 -08:00
gltexture_t *glt;
2019-11-25 16:49:58 -08:00
int32_t mark;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(isDedicated)
2019-11-24 20:45:15 -08:00
return NULL;
// cache check
2019-11-25 17:40:18 -08:00
switch(format)
2019-11-24 20:45:15 -08:00
{
case SRC_INDEXED:
crc = CRC_Block(data, width * height);
break;
case SRC_LIGHTMAP:
crc = CRC_Block(data, width * height * lightmap_bytes);
break;
case SRC_RGBA:
crc = CRC_Block(data, width * height * 4);
break;
default: /* not reachable but avoids compiler warnings */
crc = 0;
}
2019-11-25 17:40:18 -08:00
if((flags & TEXPREF_OVERWRITE) && (glt = TexMgr_FindTexture(owner, name)))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->source_crc == crc)
2019-11-24 20:45:15 -08:00
return glt;
}
else
2019-11-25 17:40:18 -08:00
glt = TexMgr_NewTexture();
2019-11-24 20:45:15 -08:00
// copy data
glt->owner = owner;
2019-11-25 17:40:18 -08:00
q_strlcpy(glt->name, name, sizeof(glt->name));
2019-11-24 20:45:15 -08:00
glt->width = width;
glt->height = height;
glt->flags = flags;
glt->shirt = -1;
glt->pants = -1;
2019-11-25 17:40:18 -08:00
q_strlcpy(glt->source_file, source_file, sizeof(glt->source_file));
2019-11-24 20:45:15 -08:00
glt->source_offset = source_offset;
glt->source_format = format;
glt->source_width = width;
glt->source_height = height;
glt->source_crc = crc;
//upload it
mark = Hunk_LowMark();
2019-11-25 17:40:18 -08:00
switch(glt->source_format)
2019-11-24 20:45:15 -08:00
{
case SRC_INDEXED:
2019-11-25 17:40:18 -08:00
TexMgr_LoadImage8(glt, data);
2019-11-24 20:45:15 -08:00
break;
case SRC_LIGHTMAP:
2019-11-25 17:40:18 -08:00
TexMgr_LoadLightmap(glt, data);
2019-11-24 20:45:15 -08:00
break;
case SRC_RGBA:
2019-11-25 17:40:18 -08:00
TexMgr_LoadImage32(glt, (unsigned *)data);
2019-11-24 20:45:15 -08:00
break;
}
Hunk_FreeToLowMark(mark);
return glt;
}
/*
================================================================================
2019-11-25 17:40:18 -08:00
COLORMAPPING AND TEXTURE RELOADING
2019-11-24 20:45:15 -08:00
================================================================================
*/
/*
================
TexMgr_ReloadImage -- reloads a texture, and colormaps it if needed
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_ReloadImage(gltexture_t *glt, int32_t shirt, int32_t pants)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
byte translation[256];
byte *src, *dst, *data = NULL, *translated;
int32_t mark, size, i;
2019-11-24 20:45:15 -08:00
//
// get source data
//
2019-11-25 17:40:18 -08:00
mark = Hunk_LowMark();
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(glt->source_file[0] && glt->source_offset)
2019-11-24 20:45:15 -08:00
{
//lump inside file
long size;
FILE *f;
COM_FOpenFile(glt->source_file, &f, NULL);
2019-11-25 17:40:18 -08:00
if(!f)
2019-11-24 20:45:15 -08:00
goto invalid;
2019-11-25 17:40:18 -08:00
fseek(f, glt->source_offset, SEEK_CUR);
size = (long)(glt->source_width * glt->source_height);
2019-11-24 20:45:15 -08:00
/* should be SRC_INDEXED, but no harm being paranoid: */
2019-11-25 17:40:18 -08:00
if(glt->source_format == SRC_RGBA)
2019-11-24 20:45:15 -08:00
size *= 4;
2019-11-25 17:40:18 -08:00
else if(glt->source_format == SRC_LIGHTMAP)
2019-11-24 20:45:15 -08:00
size *= lightmap_bytes;
data = Hunk_AllocName(size, __func__);
2019-11-25 17:40:18 -08:00
fread(data, 1, size, f);
fclose(f);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
else if(glt->source_file[0] && !glt->source_offset)
data = Image_LoadImage(glt->source_file, (int32_t *)&glt->source_width, (int32_t *)&glt->source_height); //simple file
else if(!glt->source_file[0] && glt->source_offset)
2019-11-24 20:45:15 -08:00
data = (byte *) glt->source_offset; //image in memory
2019-11-25 17:40:18 -08:00
if(!data)
2019-11-24 20:45:15 -08:00
{
invalid:
2019-11-25 17:40:18 -08:00
Con_Printf("TexMgr_ReloadImage: invalid source for %s\n", glt->name);
2019-11-24 20:45:15 -08:00
Hunk_FreeToLowMark(mark);
return;
}
glt->width = glt->source_width;
glt->height = glt->source_height;
//
// apply shirt and pants colors
//
// if shirt and pants are -1,-1, use existing shirt and pants colors
// if existing shirt and pants colors are -1,-1, don't bother colormapping
2019-11-25 17:40:18 -08:00
if(shirt > -1 && pants > -1)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(glt->source_format == SRC_INDEXED)
2019-11-24 20:45:15 -08:00
{
glt->shirt = shirt;
glt->pants = pants;
}
else
2019-11-25 17:40:18 -08:00
Con_Printf("TexMgr_ReloadImage: can't colormap a non SRC_INDEXED texture: %s\n", glt->name);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
if(glt->shirt > -1 && glt->pants > -1)
2019-11-24 20:45:15 -08:00
{
//create new translation table
2019-11-25 17:40:18 -08:00
for(i = 0; i < 256; i++)
2019-11-24 20:45:15 -08:00
translation[i] = i;
shirt = glt->shirt * 16;
2019-11-25 17:40:18 -08:00
if(shirt < 128)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(i = 0; i < 16; i++)
translation[TOP_RANGE + i] = shirt + i;
2019-11-24 20:45:15 -08:00
}
else
{
2019-11-25 17:40:18 -08:00
for(i = 0; i < 16; i++)
translation[TOP_RANGE + i] = shirt + 15 - i;
2019-11-24 20:45:15 -08:00
}
pants = glt->pants * 16;
2019-11-25 17:40:18 -08:00
if(pants < 128)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(i = 0; i < 16; i++)
translation[BOTTOM_RANGE + i] = pants + i;
2019-11-24 20:45:15 -08:00
}
else
{
2019-11-25 17:40:18 -08:00
for(i = 0; i < 16; i++)
translation[BOTTOM_RANGE + i] = pants + 15 - i;
2019-11-24 20:45:15 -08:00
}
//translate texture
size = glt->width * glt->height;
dst = translated = Hunk_AllocName(size, __func__);
2019-11-24 20:45:15 -08:00
src = data;
2019-11-25 17:40:18 -08:00
for(i = 0; i < size; i++)
2019-11-24 20:45:15 -08:00
*dst++ = translation[*src++];
data = translated;
}
//
// upload it
//
2019-11-25 17:40:18 -08:00
switch(glt->source_format)
2019-11-24 20:45:15 -08:00
{
case SRC_INDEXED:
2019-11-25 17:40:18 -08:00
TexMgr_LoadImage8(glt, data);
2019-11-24 20:45:15 -08:00
break;
case SRC_LIGHTMAP:
2019-11-25 17:40:18 -08:00
TexMgr_LoadLightmap(glt, data);
2019-11-24 20:45:15 -08:00
break;
case SRC_RGBA:
2019-11-25 17:40:18 -08:00
TexMgr_LoadImage32(glt, (unsigned *)data);
2019-11-24 20:45:15 -08:00
break;
}
Hunk_FreeToLowMark(mark);
}
/*
================
TexMgr_ReloadImages -- reloads all texture images. called only by vid_restart
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_ReloadImages(void)
2019-11-24 20:45:15 -08:00
{
gltexture_t *glt;
in_reload_images = true;
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
2019-11-24 20:45:15 -08:00
{
glGenTextures(1, &glt->texnum);
2019-11-25 17:40:18 -08:00
TexMgr_ReloadImage(glt, -1, -1);
2019-11-24 20:45:15 -08:00
}
2019-11-25 13:20:03 -08:00
2019-11-24 20:45:15 -08:00
in_reload_images = false;
}
/*
================
TexMgr_ReloadNobrightImages -- reloads all texture that were loaded with the nobright palette. called when gl_fullbrights changes
================
*/
2019-11-25 17:40:18 -08:00
void TexMgr_ReloadNobrightImages(void)
2019-11-24 20:45:15 -08:00
{
gltexture_t *glt;
2019-11-25 17:40:18 -08:00
for(glt = active_gltextures; glt; glt = glt->next)
if(glt->flags & TEXPREF_NOBRIGHT)
2019-11-24 20:45:15 -08:00
TexMgr_ReloadImage(glt, -1, -1);
}
/*
================================================================================
2019-11-25 17:40:18 -08:00
TEXTURE BINDING / TEXTURE UNIT SWITCHING
2019-11-24 20:45:15 -08:00
================================================================================
*/
2019-11-25 17:40:18 -08:00
static GLuint currenttexture[3] = {GL_UNUSED_TEXTURE, GL_UNUSED_TEXTURE, GL_UNUSED_TEXTURE}; // to avoid unnecessary texture sets
static GLenum currenttarget = GL_TEXTURE0_ARB;
bool mtexenabled = false;
2019-11-24 20:45:15 -08:00
/*
================
GL_SelectTexture -- johnfitz -- rewritten
================
*/
2019-11-25 17:40:18 -08:00
void GL_SelectTexture(GLenum target)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(target == currenttarget)
2019-11-24 20:45:15 -08:00
return;
2019-11-25 13:20:03 -08:00
2019-11-24 20:45:15 -08:00
GL_SelectTextureFunc(target);
currenttarget = target;
}
/*
================
GL_DisableMultitexture -- selects texture unit 0
================
*/
void GL_DisableMultitexture(void)
{
2019-11-25 17:40:18 -08:00
if(mtexenabled)
2019-11-24 20:45:15 -08:00
{
glDisable(GL_TEXTURE_2D);
GL_SelectTexture(GL_TEXTURE0_ARB);
mtexenabled = false;
}
}
/*
================
GL_EnableMultitexture -- selects texture unit 1
================
*/
void GL_EnableMultitexture(void)
{
2019-11-25 17:40:18 -08:00
if(gl_mtexable)
2019-11-24 20:45:15 -08:00
{
GL_SelectTexture(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
mtexenabled = true;
}
}
/*
================
GL_Bind -- johnfitz -- heavy revision
================
*/
2019-11-25 17:40:18 -08:00
void GL_Bind(gltexture_t *texture)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!texture)
2019-11-24 20:45:15 -08:00
texture = nulltexture;
2019-11-25 17:40:18 -08:00
if(texture->texnum != currenttexture[currenttarget - GL_TEXTURE0_ARB])
2019-11-24 20:45:15 -08:00
{
currenttexture[currenttarget - GL_TEXTURE0_ARB] = texture->texnum;
2019-11-25 17:40:18 -08:00
glBindTexture(GL_TEXTURE_2D, texture->texnum);
2019-11-24 20:45:15 -08:00
texture->visframe = r_framecount;
}
}
/*
================
GL_DeleteTexture -- ericw
Wrapper around glDeleteTextures that also clears the given texture number
from our per-TMU cached texture binding table.
================
*/
2019-11-25 17:40:18 -08:00
static void GL_DeleteTexture(gltexture_t *texture)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
glDeleteTextures(1, &texture->texnum);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(texture->texnum == currenttexture[0]) currenttexture[0] = GL_UNUSED_TEXTURE;
if(texture->texnum == currenttexture[1]) currenttexture[1] = GL_UNUSED_TEXTURE;
if(texture->texnum == currenttexture[2]) currenttexture[2] = GL_UNUSED_TEXTURE;
2019-11-24 20:45:15 -08:00
texture->texnum = 0;
}
/*
================
GL_ClearBindings -- ericw
2019-11-25 13:20:03 -08:00
2019-11-24 20:45:15 -08:00
Invalidates cached bindings, so the next GL_Bind calls for each TMU will
make real glBindTexture calls.
Call this after changing the binding outside of GL_Bind.
================
*/
void GL_ClearBindings(void)
{
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-25 17:40:18 -08:00
for(i = 0; i < 3; i++)
2019-11-24 20:45:15 -08:00
{
currenttexture[i] = GL_UNUSED_TEXTURE;
}
}