diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 5f4205150..cd8403f0e 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -156,6 +156,7 @@ StringMap Graphics::pointStyles( StringMap::Entry Graphics::supportEntries[] = { { "canvas", Graphics::SUPPORT_CANVAS }, + { "hdrcanvas", Graphics::SUPPORT_HDR_CANVAS }, { "pixeleffect", Graphics::SUPPORT_PIXELEFFECT }, { "npot", Graphics::SUPPORT_NPOT }, { "subtractive", Graphics::SUPPORT_SUBTRACTIVE }, diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 13d2cc660..ed0da81a2 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -85,6 +85,7 @@ public: enum Support { SUPPORT_CANVAS = 1, + SUPPORT_HDR_CANVAS, SUPPORT_PIXELEFFECT, SUPPORT_NPOT, SUPPORT_SUBTRACTIVE, diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 560c6e99b..72708f4a1 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -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(width); float h = static_cast(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::Entry Canvas::textureTypeEntries[] = +{ + {"normal", Canvas::TYPE_NORMAL}, + {"hdr", Canvas::TYPE_HDR}, +}; +StringMap Canvas::textureTypes(Canvas::textureTypeEntries, sizeof(Canvas::textureTypeEntries)); + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index b226a36d1..0da184631 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -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::Entry textureTypeEntries[]; + static StringMap textureTypes; }; } // opengl diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 70b55c7d7..e518e544d 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -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) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index b7dfa2b1a..ca0a26798 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -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); diff --git a/src/modules/graphics/opengl/wrap_Canvas.cpp b/src/modules/graphics/opengl/wrap_Canvas.cpp index 53d9ac637..3af47051c 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -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 } }; diff --git a/src/modules/graphics/opengl/wrap_Canvas.h b/src/modules/graphics/opengl/wrap_Canvas.h index 20c70941a..924fe833a 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.h +++ b/src/modules/graphics/opengl/wrap_Canvas.h @@ -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 diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 685833da1..90202a941 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -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;