forked from 0ad/0ad
janwas
d6f31e4157
h_mgr: add h_get_refcnt; saw h_alloc was a mess and split it up mem: remove obsolete 'scope' This was SVN commit r2759.
709 lines
19 KiB
C++
Executable File
709 lines
19 KiB
C++
Executable File
#include "precompiled.h"
|
|
|
|
#include "../res.h"
|
|
#include "ogl.h"
|
|
#include "tex.h"
|
|
#include "ogl_tex.h"
|
|
|
|
#include "lib.h"
|
|
|
|
|
|
static GLint default_filter = GL_LINEAR; // all legal GL *minify* values
|
|
static uint default_bpp = 32; // 16 or 32
|
|
|
|
//----------------------------------------------------------------------------
|
|
// OpenGL helper routines
|
|
//----------------------------------------------------------------------------
|
|
|
|
static bool filter_is_known(GLint filter)
|
|
{
|
|
switch(filter)
|
|
{
|
|
case GL_NEAREST:
|
|
case GL_LINEAR:
|
|
case GL_NEAREST_MIPMAP_NEAREST:
|
|
case GL_LINEAR_MIPMAP_NEAREST:
|
|
case GL_NEAREST_MIPMAP_LINEAR:
|
|
case GL_LINEAR_MIPMAP_LINEAR:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static bool filter_uses_mipmaps(GLint filter)
|
|
{
|
|
switch(filter)
|
|
{
|
|
case GL_NEAREST_MIPMAP_NEAREST:
|
|
case GL_LINEAR_MIPMAP_NEAREST:
|
|
case GL_NEAREST_MIPMAP_LINEAR:
|
|
case GL_LINEAR_MIPMAP_LINEAR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// determine OpenGL texture format, given <bpp> and Tex <flags>.
|
|
// also choose an internal format based on the global <default_bpp>
|
|
// performance vs. quality setting.
|
|
//
|
|
// rationale: we override the user's previous internal format preference.
|
|
// this is reasonable: 1) internal format is mostly performance optimization
|
|
// 2) if it does turn out to be significant, better to reevaluate the
|
|
// format decision after a reload than keep the user's setting.
|
|
static int get_gl_fmt(int bpp, int flags, GLenum* fmt, GLint* int_fmt)
|
|
{
|
|
const bool alpha = (flags & TEX_ALPHA) != 0;
|
|
const bool bgr = (flags & TEX_BGR ) != 0;
|
|
const bool grey = (flags & TEX_GREY ) != 0;
|
|
const int dxt = flags & TEX_DXT;
|
|
|
|
// in case we fail
|
|
*fmt = 0;
|
|
*int_fmt = 0;
|
|
|
|
// S3TC
|
|
if(dxt != 0)
|
|
{
|
|
switch(dxt)
|
|
{
|
|
case 1:
|
|
*fmt = alpha? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
|
break;
|
|
case 3:
|
|
*fmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
|
break;
|
|
case 5:
|
|
*fmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
|
break;
|
|
default:
|
|
debug_warn("get_gl_fmt: invalid DXT value");
|
|
return ERR_TEX_FMT_INVALID;
|
|
}
|
|
|
|
// note: S3TC textures don't need an internal format, since they're
|
|
// uploaded via glCompressedTexImage2DARB. we'll set it anyway for
|
|
// consistency.
|
|
*int_fmt = *fmt;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// true => 8 bits per component; otherwise, 4
|
|
const bool high_quality = (default_bpp == 32);
|
|
|
|
switch(bpp)
|
|
{
|
|
case 8:
|
|
debug_assert(grey);
|
|
*fmt = GL_LUMINANCE;
|
|
*int_fmt = high_quality? GL_LUMINANCE8 : GL_LUMINANCE4;
|
|
return 0;
|
|
case 16:
|
|
*fmt = GL_LUMINANCE_ALPHA;
|
|
*int_fmt = high_quality? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE4_ALPHA4;
|
|
return 0;
|
|
case 24:
|
|
debug_assert(!alpha);
|
|
*fmt = bgr? GL_BGR : GL_RGB;
|
|
// note: BGR can't be used as internal format
|
|
*int_fmt = high_quality? GL_RGB8 : GL_RGB4;
|
|
return 0;
|
|
case 32:
|
|
debug_assert(alpha);
|
|
*fmt = bgr? GL_BGRA : GL_RGBA;
|
|
// note: BGR can't be used as internal format
|
|
*int_fmt = high_quality? GL_RGBA8 : GL_RGBA4;
|
|
return 0;
|
|
default:
|
|
debug_warn("get_gl_fmt: invalid bpp");
|
|
return ERR_TEX_FMT_INVALID;
|
|
}
|
|
|
|
UNREACHABLE;
|
|
}
|
|
|
|
|
|
// return a token for glTexParameteri that will enable automatic mipmap
|
|
// generation, or GL_FALSE if the GL implementation doesn't support it.
|
|
// does not cache the result.
|
|
//
|
|
// rationale: we don't assume GL_GENERATE_MIPMAP and GL_GENERATE_MIPMAP_SGIS
|
|
// have the same values, although this is implied by the spec governing
|
|
// 'promoted' ARB extensions. checking for both the old extension and
|
|
// core 1.4 is future-proof.
|
|
static GLint detect_auto_mipmap_gen()
|
|
{
|
|
// OpenGL 1.4 core. we don't assume this is supported by the driver,
|
|
// since software implementations usually only have 1.1.
|
|
if(oglHaveVersion("1.4"))
|
|
return GL_GENERATE_MIPMAP;
|
|
|
|
// widespread extension
|
|
if(oglHaveExtension("GL_SGIS_generate_mipmap"))
|
|
return GL_GENERATE_MIPMAP_SGIS;
|
|
|
|
// neither supported; need to build manually, e.g. with gluBuild2DMipmaps.
|
|
return GL_FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// change default upload settings - these affect performance vs. quality.
|
|
// may be overridden for individual textures via ogl_tex_upload parameters.
|
|
// pass 0 to keep the current setting; defaults and legal values are:
|
|
// - filter: GL_LINEAR; any valid OpenGL minification filter
|
|
// - bpp : 32; 16 or 32 (this toggles between RGBA4 and RGBA8)
|
|
void ogl_tex_set_default_upload(GLint filter, uint bpp)
|
|
{
|
|
if(filter)
|
|
{
|
|
debug_assert(filter_is_known(filter));
|
|
default_filter = filter;
|
|
}
|
|
if(bpp)
|
|
{
|
|
debug_assert(bpp == 16 || bpp == 32);
|
|
default_bpp = bpp;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// texture resource implementation
|
|
//----------------------------------------------------------------------------
|
|
|
|
// ideally we would split OglTex into data and state objects as in
|
|
// snd.cpp's SndData / VSrc. this gives us the benefits of caching while
|
|
// still leaving each "instance" (state object, which owns a data reference)
|
|
// free to change its state. however, unlike in OpenAL, there is no state
|
|
// independent of the data object - all parameters are directly tied to the
|
|
// GL texture object. therefore, splitting them up is impossible.
|
|
// (we shouldn't even keep the texel data in memory since that's already
|
|
// covered by the FS cache).
|
|
//
|
|
// given that multiple "instances" share the state stored here, we conclude:
|
|
// - a refcount is necessary to prevent ogl_tex_upload from freeing
|
|
// <t> as long as other instances are active.
|
|
// - concurrent use risks cross-talk (if the 2nd "instance" changes state and
|
|
// the first is reloaded, its state may change to that of the 2nd)
|
|
//
|
|
// as bad as it sounds, the latter issue isn't a problem:
|
|
// multiple instances of the same texture where someone changes its
|
|
// internal format aren't expected. even if it is reloaded, the differing
|
|
// state is not critical.
|
|
// the alternative is even worse: disabling *all* caching/reuse would
|
|
// really hurt performance and h_mgr doesn't support disallowing reuse of
|
|
// active objects only (would break the index lookup code, since
|
|
// multiple instances may exist).
|
|
|
|
struct OglTex
|
|
{
|
|
Tex t;
|
|
|
|
// allocated by OglTex_reload; indicates the texture is currently uploaded.
|
|
GLuint id;
|
|
|
|
// determined from Tex by gl_get_fmt (called from OglTex_reload);
|
|
// user settings passed to ogl_tex_upload will override this until the
|
|
// next actual reload.
|
|
GLenum fmt;
|
|
GLint int_fmt;
|
|
|
|
// set to <default_filter> by OglTex_init; user settings passed to
|
|
// ogl_tex_upload will permanently override this.
|
|
GLint filter;
|
|
|
|
// flags influencing reload behavior
|
|
// .. either we have the texture in memory (referenced by t.hm),
|
|
// or it's already been uploaded to OpenGL => no reload necessary.
|
|
// needs to be a flag so it can be reset in Tex_dtor.
|
|
uint is_loaded : 1;
|
|
uint has_been_uploaded : 1;
|
|
uint was_wrapped : 1;
|
|
|
|
// to which Texture Mapping Unit was this bound?
|
|
// used when re-binding after reload.
|
|
uint tmu : 8;
|
|
};
|
|
|
|
H_TYPE_DEFINE(OglTex);
|
|
|
|
static void OglTex_init(OglTex* ot, va_list args)
|
|
{
|
|
Tex* wrapped_tex = va_arg(args, Tex*);
|
|
if(wrapped_tex)
|
|
{
|
|
ot->t = *wrapped_tex;
|
|
ot->was_wrapped = 1;
|
|
}
|
|
|
|
// set to default (once)
|
|
ot->filter = default_filter;
|
|
}
|
|
|
|
|
|
static void OglTex_dtor(OglTex* ot)
|
|
{
|
|
(void)tex_free(&ot->t);
|
|
|
|
glDeleteTextures(1, &ot->id);
|
|
ot->id = 0;
|
|
|
|
// need to clear this so actual reloads (triggered by h_reload)
|
|
// actually reload.
|
|
ot->is_loaded = 0;
|
|
}
|
|
|
|
|
|
static int OglTex_reload(OglTex* ot, const char* fn, Handle h)
|
|
{
|
|
if(ot->is_loaded)
|
|
return 0;
|
|
|
|
Tex* const t = &ot->t;
|
|
|
|
if(!ot->was_wrapped)
|
|
RETURN_ERR(tex_load(fn, &ot->t));
|
|
|
|
// always override previous settings, since format in
|
|
// texture file may have changed (e.g. 24 -> 32 bpp).
|
|
CHECK_ERR(get_gl_fmt(t->bpp, t->flags, &ot->fmt, &ot->int_fmt));
|
|
|
|
ot->is_loaded = 1;
|
|
|
|
glGenTextures(1, &ot->id);
|
|
|
|
// re-upload if necessary
|
|
if(ot->has_been_uploaded)
|
|
CHECK_ERR(ogl_tex_upload(h, ot->filter, ot->int_fmt));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// load and return a handle to the texture given in <fn>.
|
|
// for a list of supported formats, see tex.h's tex_load.
|
|
Handle ogl_tex_load(const char* fn, uint flags)
|
|
{
|
|
Tex* wrapped_tex = 0; // we're loading from file
|
|
return h_alloc(H_OglTex, fn, flags, wrapped_tex);
|
|
}
|
|
|
|
|
|
// make the given Tex object ready for use as an OpenGL texture
|
|
// and return a handle to it. this will be as if its contents
|
|
// had been loaded by ogl_tex_load.
|
|
//
|
|
// we need only add bookkeeping information and "wrap" it in
|
|
// a resource object (accessed via Handle), hence the name.
|
|
//
|
|
// <fn> isn't strictly needed but should describe the texture so that
|
|
// h_filename will return a meaningful comment for debug purposes.
|
|
Handle ogl_tex_wrap(Tex* t, const char* fn, uint flags)
|
|
{
|
|
return h_alloc(H_OglTex, fn, flags, t);
|
|
}
|
|
|
|
|
|
// free all resources associated with the texture and make further
|
|
// use of it impossible. (subject to refcount)
|
|
int ogl_tex_free(Handle& ht)
|
|
{
|
|
return h_free(ht, H_OglTex);
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ogl_tex_validate(const uint line, const OglTex* ot)
|
|
{
|
|
RETURN_ERR(tex_validate(line, &ot->t));
|
|
|
|
const char* msg = 0;
|
|
int err = -1;
|
|
|
|
// width, height
|
|
// (note: this is done here because tex.cpp doesn't impose any
|
|
// restrictions on dimensions, while OpenGL does).
|
|
GLsizei w = (GLsizei)ot->t.w;
|
|
GLsizei h = (GLsizei)ot->t.h;
|
|
// if w or h is 0, texture file probably not loaded successfully.
|
|
if(w == 0 || h == 0)
|
|
msg = "width or height is 0 - texture probably not loaded successfully";
|
|
// greater than max supported tex dimension?
|
|
// no-op if oglInit not yet called
|
|
if(w > (GLsizei)ogl_max_tex_size || h > (GLsizei)ogl_max_tex_size)
|
|
msg = "texture dimensions exceed OpenGL implementation limit";
|
|
// both NV_texture_rectangle and subtexture require work for the client
|
|
// (changing tex coords) => we'll just disallow non-power of 2 textures.
|
|
// TODO: ARB_texture_non_power_of_two
|
|
if(!is_pow2(w) || !is_pow2(h))
|
|
msg = "width or height is not a power-of-2";
|
|
|
|
// texel format
|
|
GLenum fmt = (GLenum)ot->fmt;
|
|
if(!fmt)
|
|
msg = "texel format is 0";
|
|
// can't really check against a list of valid formats - loaders
|
|
// may define their own. not necessary anyway - if non-0, assume
|
|
// loader knows what it's doing, and that the format is valid.
|
|
|
|
// upload parameters, set by ogl_tex_upload(Handle), or 0
|
|
GLint filter = ot->filter;
|
|
if(filter != 0 && !filter_is_known(filter))
|
|
msg = "invalid filter";
|
|
// as with the texel format above, there is not anything we can do
|
|
// to verify ot->int_fmt is correct (even 0 is valid).
|
|
if(ot->tmu >= 128)
|
|
msg = "TMU invalid? it's >= 128!";
|
|
|
|
if(msg)
|
|
{
|
|
debug_printf("ogl_tex_validate at line %d failed: %s (error code %d)\n", line, msg, err);
|
|
debug_warn("ogl_tex_validate failed");
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define CHECK_OGL_TEX(ot) CHECK_ERR(ogl_tex_validate(__LINE__, ot))
|
|
|
|
|
|
|
|
|
|
// upload the texture to OpenGL. texture filter and [internal] format
|
|
// may be specified to override the global defaults (see below).
|
|
// side effects:
|
|
// - enables texturing on TMU 0 and binds the texture to it;
|
|
// - frees the texel data! see ogl_tex_get_data.
|
|
int ogl_tex_upload(const Handle ht, GLint filter_ovr, GLint int_fmt_ovr, GLenum fmt_ovr)
|
|
{
|
|
H_DEREF(ht, OglTex, ot);
|
|
CHECK_OGL_TEX(ot);
|
|
|
|
// someone's requesting upload, but has already been uploaded.
|
|
// this happens if a cached texture is "loaded". no work to do.
|
|
if(ot->id && ot->t.hm <= 0)
|
|
return 0;
|
|
|
|
CHECK_OGL_TEX(ot); // must come after check above to avoid false alarms
|
|
|
|
const char* fn = h_filename(ht);
|
|
if(!fn)
|
|
{
|
|
fn = "(could not determine filename)";
|
|
debug_warn("ogl_tex_upload(Handle): h_filename failed");
|
|
}
|
|
|
|
// allow user override of format/settings
|
|
if(filter_ovr) ot->filter = filter_ovr;
|
|
if(int_fmt_ovr) ot->int_fmt = int_fmt_ovr;
|
|
if(fmt_ovr) ot->fmt = fmt_ovr;
|
|
|
|
// convenient local copies. note: have been validated by CHECK_TEX.
|
|
GLsizei w = (GLsizei)ot->t.w;
|
|
GLsizei h = (GLsizei)ot->t.h;
|
|
u32 bpp = ot->t.bpp; // used for S3TC/mipmap size calc
|
|
GLenum fmt = ot->fmt;
|
|
GLint filter = ot->filter;
|
|
GLint int_fmt = ot->int_fmt;
|
|
void* tex_data = tex_get_data(&ot->t);
|
|
|
|
// does filter call for uploading mipmaps?
|
|
const bool need_mipmaps = filter_uses_mipmaps(filter);
|
|
static GLint auto_mipmap_gen;
|
|
ONCE(auto_mipmap_gen = detect_auto_mipmap_gen());
|
|
|
|
|
|
// we know ht is valid (H_DEREF above), but ogl_tex_bind can
|
|
// fail in debug builds if OglTex.id isn't a valid texture name
|
|
CHECK_ERR(ogl_tex_bind(ht, ot->tmu));
|
|
|
|
// set upload params
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
|
const GLint mag_filter = (filter == GL_NEAREST)? GL_NEAREST : GL_LINEAR;
|
|
// magnify can only be linear or nearest
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
|
|
|
|
/*
|
|
There are various combinations of desires/abilities, relating to how
|
|
(and whether) mipmaps should be generated. Currently there are only
|
|
2^4 such combinations:
|
|
|
|
/mipmaps available in texture
|
|
#######
|
|
/mipmaps needed
|
|
#######
|
|
.---+---+---+---.
|
|
| Au| Mu| Nu| Nu|#-auto mipmap generation available
|
|
|---+---+---+---|#
|
|
| Ac| Mc| Nc| Nc|# #-texture is compressed
|
|
|---+---+---+---|# #
|
|
| X | Mc| Nc| Nc| #
|
|
|---+---+---+---| #
|
|
| G | Mu| Nu| Nu|
|
|
`---+---+---+---'
|
|
|
|
Au (auto_uncomp) = auto_mipmap_gen, then 'Nu'
|
|
Ac (auto_comp) = auto_mipmap_gen, then 'Nc'
|
|
X (broken_comp) = failure; just fall back to GL_LINEAR and 'Nc'
|
|
G (glubuild) = gluBuild2DMipmaps
|
|
Mu (mipped_uncomp) = glTexImage2D, mipmap levels
|
|
Mc (mipped_comp) = glCompressedTexImage2DARB, mipmap levels
|
|
Nu (normal_uncomp) = glTexImage2D
|
|
Nc (normal_comp) = glCompressedTexImage2DARB
|
|
|
|
if (Au || Ac)
|
|
enable automatic mipmap generation
|
|
switch to 'N*'
|
|
if (X)
|
|
set GL_LINEAR
|
|
switch to 'Nc'
|
|
if (G)
|
|
gluBuild2DMipmaps
|
|
if (Nu)
|
|
glTexImage2D
|
|
if (Nc)
|
|
glCompressedTexImage2DARB
|
|
if (Mu)
|
|
for each mipmap level
|
|
glTexImage2D
|
|
if (Mc)
|
|
for each mipmap level
|
|
glCompressedTexImage2DARB
|
|
|
|
[This documentation feels like more than is really necessary, but
|
|
hopefully it'll prevent the logic getting horribly tangled...]
|
|
*/
|
|
|
|
const bool is_s3tc = ogl_dxt_from_fmt(fmt) != 0;
|
|
const bool has_mipmaps = (ot->t.flags & TEX_MIPMAPS) != 0;
|
|
|
|
enum UploadState
|
|
{
|
|
auto_uncomp, auto_comp,
|
|
mipped_uncomp, mipped_comp,
|
|
normal_uncomp, normal_comp,
|
|
broken_comp,
|
|
glubuild
|
|
};
|
|
static const int states[4][4] = {
|
|
{ auto_uncomp, mipped_uncomp, normal_uncomp, normal_uncomp },
|
|
{ auto_comp, mipped_comp, normal_comp, normal_comp },
|
|
{ broken_comp, mipped_comp, normal_comp, normal_comp },
|
|
{ glubuild, mipped_uncomp, normal_uncomp, normal_uncomp }
|
|
};
|
|
int state = states[auto_mipmap_gen ? (is_s3tc ? 1 : 0) : (is_s3tc ? 2 : 3)] // row
|
|
[need_mipmaps ? (has_mipmaps ? 1 : 0) : (has_mipmaps ? 2 : 3)]; // column
|
|
|
|
if(state == auto_uncomp || state == auto_comp)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, auto_mipmap_gen, GL_TRUE);
|
|
state = (state == auto_uncomp)? normal_uncomp : normal_comp;
|
|
}
|
|
|
|
if(state == broken_comp)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
state = normal_comp;
|
|
}
|
|
|
|
if(state == glubuild)
|
|
gluBuild2DMipmaps(GL_TEXTURE_2D, int_fmt, w, h, fmt, GL_UNSIGNED_BYTE, tex_data);
|
|
else if(state == normal_uncomp)
|
|
glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, w, h, 0, fmt, GL_UNSIGNED_BYTE, tex_data);
|
|
else if(state == normal_comp)
|
|
{
|
|
const GLsizei tex_size = w * h * bpp / 8;
|
|
glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, fmt, w, h, 0, tex_size, tex_data);
|
|
}
|
|
else if(state == mipped_uncomp || state == mipped_comp)
|
|
{
|
|
int level = 0;
|
|
GLsizei level_w = w;
|
|
GLsizei level_h = h;
|
|
char* mipmap_data = (char*)tex_data;
|
|
|
|
while(level_w || level_h) // loop until the 1x1 mipmap level has just been processed
|
|
{
|
|
// If the texture is non-square, one of the dimensions will become
|
|
// 0 before the other. To satisfy OpenGL's expectations, change it
|
|
// back to 1.
|
|
if(level_w == 0) level_w = 1;
|
|
if(level_h == 0) level_h = 1;
|
|
|
|
GLsizei tex_size;
|
|
if(state == mipped_uncomp)
|
|
{
|
|
tex_size = level_w * level_h * bpp/8;
|
|
glTexImage2D(GL_TEXTURE_2D, level, int_fmt, level_w, level_h, 0, fmt, GL_UNSIGNED_BYTE, mipmap_data);
|
|
}
|
|
else // state == mipped_comp
|
|
{
|
|
// Round up to an integer number of 4x4 blocks
|
|
tex_size = (GLsizei)(round_up(level_w, 4) * round_up(level_h, 4) * bpp/8);
|
|
glCompressedTexImage2DARB(GL_TEXTURE_2D, level, fmt, level_w, level_h, 0, tex_size, mipmap_data);
|
|
}
|
|
|
|
mipmap_data += tex_size;
|
|
level++;
|
|
level_w /= 2;
|
|
level_h /= 2;
|
|
}
|
|
}
|
|
else
|
|
debug_warn("Invalid state in ogl_tex_upload");
|
|
|
|
// see rationale at declaration of OglTex
|
|
int refs = h_get_refcnt(ht);
|
|
if(refs > 0)
|
|
tex_free(&ot->t);
|
|
|
|
ot->has_been_uploaded = 1;
|
|
|
|
oglCheck();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// BindTexture: bind a GL texture object to current active unit
|
|
void CRenderer::BindTexture(int unit,GLuint tex)
|
|
{
|
|
#if 0
|
|
glActiveTextureARB(GL_TEXTURE0+unit);
|
|
if (tex==m_ActiveTextures[unit]) return;
|
|
|
|
if (tex) {
|
|
glBindTexture(GL_TEXTURE_2D,tex);
|
|
if (!m_ActiveTextures[unit]) {
|
|
glEnable(GL_TEXTURE_2D);
|
|
}
|
|
} else if (m_ActiveTextures[unit]) {
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
m_ActiveTextures[unit]=tex;
|
|
#endif
|
|
|
|
glActiveTextureARB(GL_TEXTURE0+unit);
|
|
|
|
glBindTexture(GL_TEXTURE_2D,tex);
|
|
if (tex) {
|
|
glEnable(GL_TEXTURE_2D);
|
|
} else {
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
m_ActiveTextures[unit]=tex;
|
|
}
|
|
*/
|
|
|
|
|
|
// bind the texture to the specified unit [number] in preparation for
|
|
// using it in rendering. assumes multitexturing is available.
|
|
// side effects:
|
|
// - enables (or disables, if <ht> == 0) texturing on the given unit.
|
|
//
|
|
// note: there are many call sites of glActiveTextureARB, so caching
|
|
// those and ignoring redundant sets isn't feasible.
|
|
int ogl_tex_bind(const Handle ht, GLenum unit)
|
|
{
|
|
int id = 0;
|
|
|
|
// special case: avoid dereference and disable texturing directly.
|
|
if(ht == 0)
|
|
goto disable_texturing;
|
|
|
|
{
|
|
// (we can't use H_DEREF because it exits immediately)
|
|
OglTex* ot = H_USER_DATA(ht, OglTex);
|
|
if(!ot)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
CHECK_ERR(ERR_INVALID_HANDLE);
|
|
UNREACHABLE;
|
|
}
|
|
|
|
CHECK_OGL_TEX(ot);
|
|
|
|
#ifndef NDEBUG
|
|
if(!ot->id)
|
|
{
|
|
debug_warn("ogl_tex_bind: OglTex.id is not a valid texture");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
id = ot->id;
|
|
ot->tmu = unit;
|
|
}
|
|
|
|
disable_texturing:
|
|
glActiveTextureARB(GL_TEXTURE0+unit);
|
|
glBindTexture(GL_TEXTURE_2D, id);
|
|
if(id)
|
|
glEnable(GL_TEXTURE_2D);
|
|
else
|
|
glDisable(GL_TEXTURE_2D);
|
|
return 0;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
// retrieve texture dimensions and bits per pixel.
|
|
// all params are optional and filled if non-NULL.
|
|
int ogl_tex_get_size(Handle ht, int* w, int* h, int* bpp)
|
|
{
|
|
H_DEREF(ht, OglTex, ot);
|
|
CHECK_OGL_TEX(ot);
|
|
|
|
if(w)
|
|
*w = ot->t.w;
|
|
if(h)
|
|
*h = ot->t.h;
|
|
if(bpp)
|
|
*bpp = ot->t.bpp;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// retrieve Tex.flags and the corresponding OpenGL format.
|
|
// all params are optional and filled if non-NULL.
|
|
int ogl_tex_get_format(Handle ht, int* flags, GLenum* fmt)
|
|
{
|
|
H_DEREF(ht, OglTex, ot);
|
|
CHECK_OGL_TEX(ot);
|
|
|
|
if(flags)
|
|
*flags = ot->t.flags;
|
|
if(fmt)
|
|
*fmt = ot->fmt;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// retrieve pointer to texel data.
|
|
//
|
|
// note: this memory is freed after a successful ogl_tex_upload for
|
|
// this texture. after that, the pointer we retrieve is NULL but
|
|
// the function doesn't fail (negative return value) by design.
|
|
// if you still need to get at the data, add a reference before
|
|
// uploading it or read directly from OpenGL (discouraged).
|
|
int ogl_tex_get_data(Handle ht, void** p)
|
|
{
|
|
H_DEREF(ht, OglTex, ot);
|
|
CHECK_OGL_TEX(ot);
|
|
|
|
*p = tex_get_data(&ot->t);
|
|
return 0;
|
|
}
|