Initial image mipmapping implementation. Includes Image:set/getMipmapFilter and Image:set/getMipmapSharpness. setMipMapFilter requires a filter mode, or nil to disable mipmapping.

Enabling mipmapping on an image that does not have power-of-two dimensions will raise an error, due to inconsistent support in older graphics chipsets
This commit is contained in:
Alexander Szpakowski
2013-01-02 02:50:13 -04:00
parent ab98b0de45
commit 2739ea561a
4 changed files with 196 additions and 37 deletions
+85 -17
View File
@@ -22,9 +22,9 @@
// STD // STD
#include <cstring> // For memcpy #include <cstring> // For memcpy
#include <algorithm> // for min/max
#include <iostream> #include <iostream>
using namespace std;
namespace love namespace love
{ {
@@ -37,6 +37,7 @@ Image::Image(love::image::ImageData *data)
: width((float)(data->getWidth())) : width((float)(data->getWidth()))
, height((float)(data->getHeight())) , height((float)(data->getHeight()))
, texture(0) , texture(0)
, mipmapsharpness(0.0f)
{ {
data->retain(); data->retain();
this->data = data; this->data = data;
@@ -61,7 +62,7 @@ Image::Image(love::image::ImageData *data)
vertices[3].s = 1; vertices[3].s = 1;
vertices[3].t = 0; vertices[3].t = 0;
settings.filter = getDefaultFilter(); filter = getDefaultFilter();
} }
Image::~Image() Image::~Image()
@@ -144,26 +145,72 @@ void Image::drawq(love::graphics::Quad *quad, float x, float y, float angle, flo
void Image::setFilter(const Image::Filter &f) void Image::setFilter(const Image::Filter &f)
{ {
filter = f;
bind(); bind();
if (f.mipmap == FILTER_NEAREST || f.mipmap == FILTER_LINEAR)
{
if (!hasMipmapSupport())
throw love::Exception("Mipmaps are not supported on this system!");
if (width != next_p2(width) || height != next_p2(height))
throw love::Exception("Could not generate mipmaps: image does not have power of two dimensions!");
GLboolean aremipmapscreated;
glGetTexParameteriv(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLint *)&aremipmapscreated);
// generate mipmaps for this image if we haven't already
if (!aremipmapscreated)
{
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
glGenerateMipmap(GL_TEXTURE_2D);
else if (GLEE_EXT_framebuffer_object)
glGenerateMipmapEXT(GL_TEXTURE_2D);
else
// modify single pixel in texture to trigger mip chain generation
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)width, (GLsizei)height, GL_RGBA, GL_UNSIGNED_BYTE, getData());
}
}
setTextureFilter(f); setTextureFilter(f);
} }
Image::Filter Image::getFilter() const const Image::Filter &Image::getFilter() const
{ {
bind(); return filter;
return getTextureFilter();
} }
void Image::setWrap(Image::Wrap &w) void Image::setWrap(Image::Wrap &w)
{ {
wrap = w;
bind(); bind();
setTextureWrap(w); setTextureWrap(w);
} }
Image::Wrap Image::getWrap() const const Image::Wrap &Image::getWrap() const
{ {
return wrap;
}
void Image::setMipmapSharpness(float sharpness)
{
if (!(GLEE_VERSION_1_4 || GLEE_EXT_texture_lod_bias))
return;
// LOD bias has the range (-maxbias, maxbias)
mipmapsharpness = std::min(std::max(sharpness, -maxmipmapsharpness + 0.01f), maxmipmapsharpness - 0.01f);
bind(); bind();
return getTextureWrap(); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapsharpness);
}
float Image::getMipmapSharpness() const
{
return mipmapsharpness;
} }
void Image::bind() const void Image::bind() const
@@ -186,6 +233,8 @@ void Image::unload()
bool Image::loadVolatile() bool Image::loadVolatile()
{ {
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxmipmapsharpness);
if (hasNpot()) if (hasNpot())
return loadVolatileNPOT(); return loadVolatileNPOT();
else else
@@ -196,9 +245,10 @@ bool Image::loadVolatilePOT()
{ {
glGenTextures(1,(GLuint *)&texture); glGenTextures(1,(GLuint *)&texture);
bindTexture(texture); bindTexture(texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
@@ -221,6 +271,13 @@ bool Image::loadVolatilePOT()
GL_RGBA, GL_RGBA,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
0); 0);
if (hasMipmapSupport())
{
// auto-generate mipmaps when texture is modified, if mipmapping is enabled
bool genmipmaps = (filter.mipmap == FILTER_LINEAR) || (filter.mipmap == FILTER_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, genmipmaps ? GL_TRUE : GL_FALSE);
}
glTexSubImage2D(GL_TEXTURE_2D, glTexSubImage2D(GL_TEXTURE_2D,
0, 0,
@@ -231,9 +288,9 @@ bool Image::loadVolatilePOT()
GL_RGBA, GL_RGBA,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
data->getData()); data->getData());
setFilter(settings.filter); setFilter(filter);
setWrap(settings.wrap); setWrap(wrap);
return true; return true;
} }
@@ -242,11 +299,19 @@ bool Image::loadVolatileNPOT()
{ {
glGenTextures(1,(GLuint *)&texture); glGenTextures(1,(GLuint *)&texture);
bindTexture(texture); bindTexture(texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (hasMipmapSupport())
{
// auto-generate mipmaps when texture is modified, if mipmapping is enabled
bool genmipmaps = (filter.mipmap == FILTER_LINEAR) || (filter.mipmap == FILTER_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, genmipmaps ? GL_TRUE : GL_FALSE);
}
glTexImage2D(GL_TEXTURE_2D, glTexImage2D(GL_TEXTURE_2D,
0, 0,
@@ -257,17 +322,15 @@ bool Image::loadVolatileNPOT()
GL_RGBA, GL_RGBA,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
data->getData()); data->getData());
setFilter(settings.filter); setFilter(filter);
setWrap(settings.wrap); setWrap(wrap);
return true; return true;
} }
void Image::unloadVolatile() void Image::unloadVolatile()
{ {
settings.filter = getFilter();
settings.wrap = getWrap();
// Delete the hardware texture. // Delete the hardware texture.
if (texture != 0) if (texture != 0)
{ {
@@ -300,6 +363,11 @@ bool Image::hasNpot()
return GLEE_ARB_texture_non_power_of_two != 0; return GLEE_ARB_texture_non_power_of_two != 0;
} }
bool Image::hasMipmapSupport()
{
return (GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap) != 0;
}
} // opengl } // opengl
} // graphics } // graphics
} // love } // love
+18 -9
View File
@@ -101,11 +101,15 @@ public:
**/ **/
void setFilter(const Image::Filter &f); void setFilter(const Image::Filter &f);
Image::Filter getFilter() const; const Image::Filter &getFilter() const;
void setWrap(Image::Wrap &w); void setWrap(Image::Wrap &w);
Image::Wrap getWrap() const; const Image::Wrap &getWrap() const;
void setMipmapSharpness(float sharpness);
float getMipmapSharpness() const;
void bind() const; void bind() const;
@@ -117,6 +121,7 @@ public:
void unloadVolatile(); void unloadVolatile();
static bool hasNpot(); static bool hasNpot();
static bool hasMipmapSupport();
private: private:
@@ -138,13 +143,17 @@ private:
// The source vertices of the image. // The source vertices of the image.
vertex vertices[4]; vertex vertices[4];
// The settings we need to save when reloading. // Mipmap texture LOD bias value
struct float mipmapsharpness;
{
Image::Filter filter; float maxmipmapsharpness;
Image::Wrap wrap;
} settings; // The image's filter mode
Image::Filter filter;
// The image's wrap mode
Image::Wrap wrap;
bool loadVolatilePOT(); bool loadVolatilePOT();
bool loadVolatileNPOT(); bool loadVolatileNPOT();
+86 -11
View File
@@ -61,12 +61,22 @@ int w_Image_setFilter(lua_State *L)
return luaL_error(L, "Invalid filter mode: %s", minstr); return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, mag)) if (!Image::getConstant(magstr, mag))
return luaL_error(L, "Invalid filter mode: %s", magstr); return luaL_error(L, "Invalid filter mode: %s", magstr);
const Image::Filter curfilter = t->getFilter();
Image::Filter f; Image::Filter f;
f.min = min; f.min = min;
f.mag = mag; f.mag = mag;
f.mipmap = curfilter.mipmap;
t->setFilter(f); try
{
t->setFilter(f);
}
catch(love::Exception &e)
{
return luaL_error(L, e.what());
}
return 0; return 0;
} }
@@ -74,18 +84,61 @@ int w_Image_setFilter(lua_State *L)
int w_Image_getFilter(lua_State *L) int w_Image_getFilter(lua_State *L)
{ {
Image *t = luax_checkimage(L, 1); Image *t = luax_checkimage(L, 1);
Image::Filter f = t->getFilter(); const Image::Filter f = t->getFilter();
Image::FilterMode min = f.min;
Image::FilterMode mag = f.mag;
const char *minstr; const char *minstr;
const char *magstr; const char *magstr;
Image::getConstant(min, minstr); Image::getConstant(f.min, minstr);
Image::getConstant(mag, magstr); Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr); lua_pushstring(L, minstr);
lua_pushstring(L, magstr); lua_pushstring(L, magstr);
return 2; return 2;
} }
int w_Image_setMipmapFilter(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
const Image::Filter curfilter = i->getFilter();
Image::Filter f;
f.min = curfilter.min;
f.mag = curfilter.mag;
if (lua_isnoneornil(L, 2)) // disable mipmapping if no arguments are given
f.mipmap = Image::FILTER_NONE;
else
{
const char *filterstr = luaL_checkstring(L, 2);
if (!Image::getConstant(filterstr, f.mipmap))
return luaL_error(L, "Invalid filter mode: %s", filterstr);
}
try
{
i->setFilter(f);
}
catch(love::Exception &e)
{
return luaL_error(L, e.what());
}
return 0;
}
int w_Image_getMipmapFilter(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
const Image::Filter f = i->getFilter();
const char *filterstr;
if (Image::getConstant(f.mipmap, filterstr))
lua_pushstring(L, filterstr);
else
lua_pushnil(L); // return nil if mipmap filter is FILTER_NONE
return 1;
}
int w_Image_setWrap(lua_State *L) int w_Image_setWrap(lua_State *L)
{ {
Image *i = luax_checkimage(L, 1); Image *i = luax_checkimage(L, 1);
@@ -113,18 +166,36 @@ int w_Image_setWrap(lua_State *L)
int w_Image_getWrap(lua_State *L) int w_Image_getWrap(lua_State *L)
{ {
Image *i = luax_checkimage(L, 1); Image *i = luax_checkimage(L, 1);
Image::Wrap w = i->getWrap(); const Image::Wrap w = i->getWrap();
Image::WrapMode s = w.s;
Image::WrapMode t = w.t;
const char *sstr; const char *sstr;
const char *tstr; const char *tstr;
Image::getConstant(s, sstr); Image::getConstant(w.s, sstr);
Image::getConstant(t, tstr); Image::getConstant(w.t, tstr);
lua_pushstring(L, sstr); lua_pushstring(L, sstr);
lua_pushstring(L, tstr); lua_pushstring(L, tstr);
return 2; return 2;
} }
int w_Image_setMipmapSharpness(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
float sharpness = (float) luaL_checknumber(L, 2);
i->setMipmapSharpness(sharpness);
return 0;
}
int w_Image_getMipmapSharpness(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
float sharpness = i->getMipmapSharpness();
lua_pushnumber(L, sharpness);
return 1;
}
static const luaL_Reg functions[] = static const luaL_Reg functions[] =
{ {
{ "getWidth", w_Image_getWidth }, { "getWidth", w_Image_getWidth },
@@ -133,6 +204,10 @@ static const luaL_Reg functions[] =
{ "getFilter", w_Image_getFilter }, { "getFilter", w_Image_getFilter },
{ "setWrap", w_Image_setWrap }, { "setWrap", w_Image_setWrap },
{ "getWrap", w_Image_getWrap }, { "getWrap", w_Image_getWrap },
{ "setMipmapFilter", w_Image_setMipmapFilter },
{ "getMipmapFilter", w_Image_getMipmapFilter },
{ "setMipmapSharpness", w_Image_setMipmapSharpness },
{ "getMipmapSharpness", w_Image_getMipmapSharpness },
{ 0, 0 } { 0, 0 }
}; };
+7
View File
@@ -36,6 +36,13 @@ Image *luax_checkimage(lua_State *L, int idx);
int w_Image_getWidth(lua_State *L); int w_Image_getWidth(lua_State *L);
int w_Image_getHeight(lua_State *L); int w_Image_getHeight(lua_State *L);
int w_Image_setFilter(lua_State *L); int w_Image_setFilter(lua_State *L);
int w_image_getFilter(lua_State *L);
int w_Image_setWrap(lua_State *L);
int w_Image_getWrap(lua_State *L);
int w_Image_setMipmapFilter(lua_State *L);
int w_Image_getMipmapFilter(lua_State *L);
int w_Image_setMipmapSharpness(lua_State *L);
int w_Image_getMipmapSharpness(lua_State *L);
extern "C" int luaopen_image(lua_State *L); extern "C" int luaopen_image(lua_State *L);
} // opengl } // opengl