mirror of
https://github.com/love2d/love.git
synced 2026-08-15 07:41:11 +02:00
Fix Issue #476: Add HDR Canvas (thanks, Alexander Szpakowski!).
* Add `love.graphics.isSupported("hdrcanvas")' to query whether the hardware
actually supports HDR canvases.
* Add optional third argument to `love.graphics.newCanvas(w,h, type)', where
`type' is one of "normal" or "hdr" and defaults to "normal". HDR canvases
use floating point that can have values > 1 to store pixels, allowing for
HDR rendering and other higher level lighting techniques when combined with
pixel effects.
* Add `canvas:getType()'.
This commit is contained in:
@@ -156,6 +156,7 @@ StringMap<Graphics::PointStyle, Graphics::POINT_MAX_ENUM> Graphics::pointStyles(
|
||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
|
||||
{
|
||||
{ "canvas", Graphics::SUPPORT_CANVAS },
|
||||
{ "hdrcanvas", Graphics::SUPPORT_HDR_CANVAS },
|
||||
{ "pixeleffect", Graphics::SUPPORT_PIXELEFFECT },
|
||||
{ "npot", Graphics::SUPPORT_NPOT },
|
||||
{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
|
||||
|
||||
@@ -85,6 +85,7 @@ public:
|
||||
enum Support
|
||||
{
|
||||
SUPPORT_CANVAS = 1,
|
||||
SUPPORT_HDR_CANVAS,
|
||||
SUPPORT_PIXELEFFECT,
|
||||
SUPPORT_NPOT,
|
||||
SUPPORT_SUBTRACTIVE,
|
||||
|
||||
@@ -42,9 +42,10 @@ struct FramebufferStrategy
|
||||
* @param[out] img Texture name
|
||||
* @param[in] width Width of framebuffer
|
||||
* @param[in] height Height of framebuffer
|
||||
* @param[in] texture_type Type of the canvas texture.
|
||||
* @return Creation status
|
||||
*/
|
||||
virtual GLenum createFBO(GLuint &, GLuint &, GLuint &, int, int)
|
||||
virtual GLenum createFBO(GLuint &, GLuint &, GLuint &, int, int, Canvas::TextureType)
|
||||
{
|
||||
return GL_FRAMEBUFFER_UNSUPPORTED;
|
||||
}
|
||||
@@ -60,7 +61,7 @@ struct FramebufferStrategy
|
||||
|
||||
struct FramebufferStrategyGL3 : public FramebufferStrategy
|
||||
{
|
||||
virtual GLenum createFBO(GLuint &framebuffer, GLuint &depth_stencil, GLuint &img, int width, int height)
|
||||
virtual GLenum createFBO(GLuint &framebuffer, GLuint &depth_stencil, GLuint &img, int width, int height, Canvas::TextureType texture_type)
|
||||
{
|
||||
// get currently bound fbo to reset to it later
|
||||
GLint current_fbo;
|
||||
@@ -80,12 +81,26 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
|
||||
GL_RENDERBUFFER, depth_stencil);
|
||||
|
||||
// generate texture save target
|
||||
GLint internalFormat;
|
||||
GLenum format;
|
||||
switch (texture_type)
|
||||
{
|
||||
case Canvas::TYPE_HDR:
|
||||
internalFormat = GL_RGBA16F;
|
||||
format = GL_FLOAT;
|
||||
break;
|
||||
case Canvas::TYPE_NORMAL:
|
||||
default:
|
||||
internalFormat = GL_RGBA8;
|
||||
format = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
glGenTextures(1, &img);
|
||||
bindTexture(img);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
|
||||
0, GL_RGBA, format, NULL);
|
||||
bindTexture(0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, img, 0);
|
||||
@@ -113,7 +128,7 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
|
||||
|
||||
struct FramebufferStrategyPackedEXT : public FramebufferStrategy
|
||||
{
|
||||
virtual GLenum createFBO(GLuint &framebuffer, GLuint &depth_stencil, GLuint &img, int width, int height)
|
||||
virtual GLenum createFBO(GLuint &framebuffer, GLuint &depth_stencil, GLuint &img, int width, int height, Canvas::TextureType texture_type)
|
||||
{
|
||||
GLint current_fbo;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, ¤t_fbo);
|
||||
@@ -133,12 +148,27 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
|
||||
GL_RENDERBUFFER_EXT, depth_stencil);
|
||||
|
||||
// generate texture save target
|
||||
GLint internalFormat;
|
||||
GLenum format;
|
||||
switch (texture_type)
|
||||
{
|
||||
case Canvas::TYPE_HDR:
|
||||
internalFormat = GL_RGBA16F;
|
||||
format = GL_FLOAT;
|
||||
break;
|
||||
case Canvas::TYPE_NORMAL:
|
||||
default:
|
||||
internalFormat = GL_RGBA8;
|
||||
format = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
glGenTextures(1, &img);
|
||||
bindTexture(img);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
|
||||
0, GL_RGBA, format, NULL);
|
||||
bindTexture(0);
|
||||
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
||||
GL_TEXTURE_2D, img, 0);
|
||||
@@ -167,7 +197,7 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
|
||||
|
||||
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
|
||||
{
|
||||
virtual GLenum createFBO(GLuint &framebuffer, GLuint &stencil, GLuint &img, int width, int height)
|
||||
virtual GLenum createFBO(GLuint &framebuffer, GLuint &stencil, GLuint &img, int width, int height, Canvas::TextureType texture_type)
|
||||
{
|
||||
GLint current_fbo;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, ¤t_fbo);
|
||||
@@ -185,12 +215,26 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
|
||||
GL_RENDERBUFFER_EXT, stencil);
|
||||
|
||||
// generate texture save target
|
||||
GLint internalFormat;
|
||||
GLenum format;
|
||||
switch (texture_type)
|
||||
{
|
||||
case Canvas::TYPE_HDR:
|
||||
internalFormat = GL_RGBA16F;
|
||||
format = GL_FLOAT;
|
||||
break;
|
||||
case Canvas::TYPE_NORMAL:
|
||||
default:
|
||||
internalFormat = GL_RGBA8;
|
||||
format = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
glGenTextures(1, &img);
|
||||
bindTexture(img);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
|
||||
0, GL_RGBA, format, NULL);
|
||||
bindTexture(0);
|
||||
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
||||
GL_TEXTURE_2D, img, 0);
|
||||
@@ -207,7 +251,7 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
|
||||
bool isSupported()
|
||||
{
|
||||
GLuint fb, stencil, img;
|
||||
GLenum status = createFBO(fb, stencil, img, 2, 2);
|
||||
GLenum status = createFBO(fb, stencil, img, 2, 2, Canvas::TYPE_NORMAL);
|
||||
deleteFBO(fb, stencil, img);
|
||||
return status == GL_FRAMEBUFFER_COMPLETE;
|
||||
}
|
||||
@@ -225,7 +269,7 @@ FramebufferStrategyEXT strategyEXT;
|
||||
|
||||
Canvas *Canvas::current = NULL;
|
||||
|
||||
static void loadStrategy()
|
||||
static void getStrategy()
|
||||
{
|
||||
if (!strategy)
|
||||
{
|
||||
@@ -240,9 +284,10 @@ static void loadStrategy()
|
||||
}
|
||||
}
|
||||
|
||||
Canvas::Canvas(int width, int height)
|
||||
Canvas::Canvas(int width, int height, TextureType texture_type)
|
||||
: width(width)
|
||||
, height(height)
|
||||
, texture_type(texture_type)
|
||||
{
|
||||
float w = static_cast<float>(width);
|
||||
float h = static_cast<float>(height);
|
||||
@@ -267,7 +312,7 @@ Canvas::Canvas(int width, int height)
|
||||
vertices[3].s = 1;
|
||||
vertices[3].t = 1;
|
||||
|
||||
loadStrategy();
|
||||
getStrategy();
|
||||
|
||||
loadVolatile();
|
||||
}
|
||||
@@ -283,10 +328,15 @@ Canvas::~Canvas()
|
||||
|
||||
bool Canvas::isSupported()
|
||||
{
|
||||
loadStrategy();
|
||||
getStrategy();
|
||||
return (strategy != &strategyNone);
|
||||
}
|
||||
|
||||
bool Canvas::isHdrSupported()
|
||||
{
|
||||
return GLEE_VERSION_3_0 || GLEE_ARB_texture_float;
|
||||
}
|
||||
|
||||
void Canvas::bindDefaultCanvas()
|
||||
{
|
||||
if (current != NULL)
|
||||
@@ -441,7 +491,7 @@ Image::Wrap Canvas::getWrap() const
|
||||
|
||||
bool Canvas::loadVolatile()
|
||||
{
|
||||
status = strategy->createFBO(fbo, depth_stencil, img, width, height);
|
||||
status = strategy->createFBO(fbo, depth_stencil, img, width, height, texture_type);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
return false;
|
||||
|
||||
@@ -489,6 +539,23 @@ void Canvas::drawv(const Matrix &t, const vertex *v) const
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
bool Canvas::getConstant(const char *in, Canvas::TextureType &out)
|
||||
{
|
||||
return textureTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Canvas::getConstant(Canvas::TextureType in, const char *&out)
|
||||
{
|
||||
return textureTypes.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM>::Entry Canvas::textureTypeEntries[] =
|
||||
{
|
||||
{"normal", Canvas::TYPE_NORMAL},
|
||||
{"hdr", Canvas::TYPE_HDR},
|
||||
};
|
||||
StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM> Canvas::textureTypes(Canvas::textureTypeEntries, sizeof(Canvas::textureTypeEntries));
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -42,19 +42,15 @@ namespace opengl
|
||||
class Canvas : public DrawQable, public Volatile
|
||||
{
|
||||
public:
|
||||
Canvas(int width, int height);
|
||||
enum TextureType {
|
||||
TYPE_NORMAL,
|
||||
TYPE_HDR,
|
||||
TYPE_MAX_ENUM
|
||||
};
|
||||
|
||||
Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
|
||||
virtual ~Canvas();
|
||||
|
||||
static bool isSupported();
|
||||
|
||||
unsigned int getStatus() const
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
static Canvas *current;
|
||||
static void bindDefaultCanvas();
|
||||
|
||||
void startGrab();
|
||||
void stopGrab();
|
||||
|
||||
@@ -78,9 +74,27 @@ public:
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
|
||||
unsigned int getStatus() const
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
TextureType getTextureType() const
|
||||
{
|
||||
return texture_type;
|
||||
}
|
||||
|
||||
bool loadVolatile();
|
||||
void unloadVolatile();
|
||||
|
||||
static bool isSupported();
|
||||
static bool isHdrSupported();
|
||||
static bool getConstant(const char *in, TextureType &out);
|
||||
static bool getConstant(TextureType in, const char *&out);
|
||||
|
||||
static Canvas *current;
|
||||
static void bindDefaultCanvas();
|
||||
|
||||
private:
|
||||
friend class PixelEffect;
|
||||
GLuint getTextureName() const
|
||||
@@ -94,6 +108,8 @@ private:
|
||||
GLuint depth_stencil;
|
||||
GLuint img;
|
||||
|
||||
TextureType texture_type;
|
||||
|
||||
vertex vertices[4];
|
||||
|
||||
GLenum status;
|
||||
@@ -101,10 +117,13 @@ private:
|
||||
struct
|
||||
{
|
||||
Image::Filter filter;
|
||||
Image::Wrap wrap;
|
||||
Image::Wrap wrap;
|
||||
} settings;
|
||||
|
||||
void drawv(const Matrix &t, const vertex *v) const;
|
||||
|
||||
static StringMap<TextureType, TYPE_MAX_ENUM>::Entry textureTypeEntries[];
|
||||
static StringMap<TextureType, TYPE_MAX_ENUM> textureTypes;
|
||||
};
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -397,9 +397,15 @@ ParticleSystem *Graphics::newParticleSystem(Image *image, int size)
|
||||
return new ParticleSystem(image, size);
|
||||
}
|
||||
|
||||
Canvas *Graphics::newCanvas(int width, int height)
|
||||
Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type)
|
||||
{
|
||||
Canvas *canvas = new Canvas(width, height);
|
||||
if (texture_type == Canvas::TYPE_HDR && !Canvas::isHdrSupported())
|
||||
throw Exception("HDR Canvases are not supported by your OpenGL implementation");
|
||||
|
||||
while (GL_NO_ERROR != glGetError())
|
||||
/* clear opengl error flag */;
|
||||
|
||||
Canvas *canvas = new Canvas(width, height, texture_type);
|
||||
GLenum err = canvas->getStatus();
|
||||
|
||||
// everything ok, return canvas (early out)
|
||||
|
||||
@@ -269,7 +269,7 @@ public:
|
||||
|
||||
ParticleSystem *newParticleSystem(Image *image, int size);
|
||||
|
||||
Canvas *newCanvas(int width, int height);
|
||||
Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL);
|
||||
|
||||
PixelEffect *newPixelEffect(const std::string &code);
|
||||
|
||||
|
||||
@@ -181,6 +181,16 @@ int w_Canvas_getHeight(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Canvas_getType(lua_State *L)
|
||||
{
|
||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||
Canvas::TextureType type = canvas->getTextureType();
|
||||
const char *str;
|
||||
Canvas::getConstant(type, str);
|
||||
lua_pushstring(L, str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "renderTo", w_Canvas_renderTo },
|
||||
@@ -192,6 +202,7 @@ static const luaL_Reg functions[] =
|
||||
{ "clear", w_Canvas_clear },
|
||||
{ "getWidth", w_Canvas_getWidth },
|
||||
{ "getHeight", w_Canvas_getHeight },
|
||||
{ "getType", w_Canvas_getType },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ int w_Canvas_getWrap(lua_State *L);
|
||||
int w_Canvas_clear(lua_State *L);
|
||||
int w_Canvas_getWidth(lua_State *L);
|
||||
int w_Canvas_getHeight(lua_State *L);
|
||||
int w_Canvas_getType(lua_State *L);
|
||||
extern "C" int luaopen_canvas(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -395,14 +395,17 @@ int w_newParticleSystem(lua_State *L)
|
||||
int w_newCanvas(lua_State *L)
|
||||
{
|
||||
// check if width and height are given. else default to screen dimensions.
|
||||
int width = luaL_optint(L, 1, instance->getWidth());
|
||||
int height = luaL_optint(L, 2, instance->getHeight());
|
||||
glGetError(); // clear opengl error flag
|
||||
int width = luaL_optint(L, 1, instance->getWidth());
|
||||
int height = luaL_optint(L, 2, instance->getHeight());
|
||||
const char *str = luaL_optstring(L, 3, "normal");
|
||||
|
||||
Canvas::TextureType texture_type;
|
||||
Canvas::getConstant(str, texture_type);
|
||||
|
||||
Canvas *canvas = NULL;
|
||||
try
|
||||
{
|
||||
canvas = instance->newCanvas(width, height);
|
||||
canvas = instance->newCanvas(width, height, texture_type);
|
||||
}
|
||||
catch(Exception &e)
|
||||
{
|
||||
@@ -820,6 +823,10 @@ int w_isSupported(lua_State *L)
|
||||
if (!Canvas::isSupported())
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_HDR_CANVAS:
|
||||
if (!Canvas::isHdrSupported())
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_PIXELEFFECT:
|
||||
if (!PixelEffect::isSupported())
|
||||
supported = false;
|
||||
|
||||
Reference in New Issue
Block a user