diff --git a/.hgignore b/.hgignore index 5869abae8..12c3162a3 100644 --- a/.hgignore +++ b/.hgignore @@ -7,6 +7,10 @@ glob:extra/reshax/Debug/ glob:extra/reshax/resources.h glob:extra/reshax/resources.cpp glob:*.obj +glob:*.o +glob:*.dirstamp +glob:*.m4 +glob:*.Po glob:*.dll glob:*.user glob:*.suo diff --git a/platform/macosx/love.xcodeproj/project.pbxproj b/platform/macosx/love.xcodeproj/project.pbxproj index 501fa02dc..d059530c8 100644 --- a/platform/macosx/love.xcodeproj/project.pbxproj +++ b/platform/macosx/love.xcodeproj/project.pbxproj @@ -187,6 +187,8 @@ A98D914410507C97008E03F2 /* EncodedImageData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A98D914310507C97008E03F2 /* EncodedImageData.cpp */; }; A9B4BA9C1045937F001DBC80 /* ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9B4BA9A1045937F001DBC80 /* ParticleSystem.cpp */; }; A9B4BA9D1045937F001DBC80 /* wrap_ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9B4BA9B1045937F001DBC80 /* wrap_ParticleSystem.cpp */; }; + A9BD60741226C988007DEC63 /* Framebuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9BD60701226C988007DEC63 /* Framebuffer.cpp */; }; + A9BD60751226C988007DEC63 /* wrap_Framebuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9BD60721226C988007DEC63 /* wrap_Framebuffer.cpp */; }; A9CF0E8610B9EB1000E6F37E /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9CF0E8510B9EB1000E6F37E /* utf8.cpp */; }; A9D307EA106635C3004FEDF8 /* physfs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9D307E9106635C3004FEDF8 /* physfs.framework */; }; A9D307F2106635D3004FEDF8 /* physfs.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9D307E9106635C3004FEDF8 /* physfs.framework */; }; @@ -598,6 +600,10 @@ A9B4BA991045937F001DBC80 /* wrap_ParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_ParticleSystem.h; sourceTree = ""; }; A9B4BA9A1045937F001DBC80 /* ParticleSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParticleSystem.cpp; sourceTree = ""; }; A9B4BA9B1045937F001DBC80 /* wrap_ParticleSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_ParticleSystem.cpp; sourceTree = ""; }; + A9BD60701226C988007DEC63 /* Framebuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Framebuffer.cpp; sourceTree = ""; }; + A9BD60711226C988007DEC63 /* Framebuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Framebuffer.h; sourceTree = ""; }; + A9BD60721226C988007DEC63 /* wrap_Framebuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Framebuffer.cpp; sourceTree = ""; }; + A9BD60731226C988007DEC63 /* wrap_Framebuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Framebuffer.h; sourceTree = ""; }; A9BFAA851137C1CE005FE0AD /* ThreadModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadModule.h; sourceTree = ""; }; A9CF0E8410B9EB1000E6F37E /* utf8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utf8.h; sourceTree = ""; }; A9CF0E8510B9EB1000E6F37E /* utf8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utf8.cpp; sourceTree = ""; }; @@ -985,6 +991,8 @@ children = ( A93E6A8710420AC2007D418B /* Font.cpp */, A93E6A8810420AC2007D418B /* Font.h */, + A9BD60701226C988007DEC63 /* Framebuffer.cpp */, + A9BD60711226C988007DEC63 /* Framebuffer.h */, A93E6A8910420AC2007D418B /* GLee.c */, A93E6A8A10420AC2007D418B /* GLee.h */, A93E6A8B10420AC2007D418B /* Glyph.cpp */, @@ -1001,6 +1009,8 @@ A93E6A9610420AC2007D418B /* SpriteBatch.h */, A93E6A9910420AC2007D418B /* wrap_Font.cpp */, A93E6A9A10420AC2007D418B /* wrap_Font.h */, + A9BD60721226C988007DEC63 /* wrap_Framebuffer.cpp */, + A9BD60731226C988007DEC63 /* wrap_Framebuffer.h */, A93E6A9B10420AC2007D418B /* wrap_Glyph.cpp */, A93E6A9C10420AC2007D418B /* wrap_Glyph.h */, A93E6A9D10420AC3007D418B /* wrap_Graphics.cpp */, @@ -1660,6 +1670,8 @@ A946CE821172BCD7005E1462 /* wrap_EncodedImageData.cpp in Sources */, A946D3BB117681BD005E1462 /* FontData.cpp in Sources */, A946D3C611768D69005E1462 /* wrap_FontData.cpp in Sources */, + A9BD60741226C988007DEC63 /* Framebuffer.cpp in Sources */, + A9BD60751226C988007DEC63 /* wrap_Framebuffer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/common/types.h b/src/common/types.h index 063135094..08ea9b7c7 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -52,6 +52,7 @@ namespace love GRAPHICS_PARTICLE_SYSTEM_ID, GRAPHICS_SPRITE_BATCH_ID, GRAPHICS_VERTEX_BUFFER_ID, + GRAPHICS_FRAMEBUFFER_ID, // Image IMAGE_IMAGE_DATA_ID, @@ -116,6 +117,7 @@ namespace love const bits GRAPHICS_PARTICLE_SYSTEM_T = (bits(1) << GRAPHICS_PARTICLE_SYSTEM_ID) | GRAPHICS_DRAWABLE_T; const bits GRAPHICS_SPRITE_BATCH_T = (bits(1) << GRAPHICS_SPRITE_BATCH_ID) | GRAPHICS_DRAWABLE_T; const bits GRAPHICS_VERTEX_BUFFER_T = (bits(1) << GRAPHICS_VERTEX_BUFFER_ID) | GRAPHICS_DRAWABLE_T; + const bits GRAPHICS_FRAMEBUFFER_T = (bits(1) << GRAPHICS_FRAMEBUFFER_ID) | GRAPHICS_DRAWABLE_T; // Image. const bits IMAGE_IMAGE_DATA_T = (bits(1) << IMAGE_IMAGE_DATA_ID) | DATA_T; diff --git a/src/modules/graphics/opengl/Framebuffer.cpp b/src/modules/graphics/opengl/Framebuffer.cpp new file mode 100644 index 000000000..0a372a1ab --- /dev/null +++ b/src/modules/graphics/opengl/Framebuffer.cpp @@ -0,0 +1,324 @@ +#include "Framebuffer.h" +#include + +#include + +#include +#include +#include +#include +#include +#include +using namespace std; + +namespace { + + // functions to get opengl capabilities at runtime + vector tokenize(const string& str) + { + vector tokens; + + istringstream iss( str ); + copy(istream_iterator(iss), istream_iterator(), + back_inserter< vector >(tokens)); + return tokens; + } + + float getOpenGLVersionNumber() + { + vector tokens = tokenize( (const char*)glGetString(GL_VERSION) ); + stringstream toNumber( tokens.at(0) ); + + double version; + toNumber >> version; + return version; + } + + bool hasFramebufferExtension() + { + vector ext = tokenize( (const char*)glGetString(GL_EXTENSIONS) ); + return find(ext.begin(), ext.end(), "GL_EXT_framebuffer_object") != ext.end(); + } + + + // strategy for fbo creation, interchangable at runtime: + // none, opengl >= 3.0, extensions + struct FramebufferStrategy { + /// create a new framebuffer, depthbuffer and texture + /** + * @param[out] framebuffer Framebuffer name + * @param[out] depthbuffer Depthbuffer name + * @param[out] img Texture name + * @param[in] width Width of framebuffer + * @param[in] height Height of framebuffer + * @return Creation status + */ + virtual GLenum createFBO(GLuint& framebuffer, GLuint& depthbuffer, GLuint& img, int width, int height) + { return GL_FRAMEBUFFER_UNSUPPORTED; } + /// remove objects + /** + * @param[in] framebuffer Framebuffer name + * @param[in] depthbuffer Depthbuffer name + * @param[in] img Texture name + */ + virtual void deleteFBO(GLuint framebuffer, GLuint depthbuffer, GLuint img) {} + virtual void bindFBO(GLuint framebuffer) {} + }; + +#ifdef GL_VERSION_3_0 + struct FramebufferStrategyGL3 : public FramebufferStrategy { + virtual GLenum createFBO(GLuint& framebuffer, GLuint& depthbuffer, GLuint& img, int width, int height) + { + // generate depth buffer + glGenRenderbuffers(1, &depthbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + // generate texture save target + glGenTextures(1, &img); + glBindTexture(GL_TEXTURE_2D, 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); + glBindTexture(GL_TEXTURE_2D, 0); + + // create framebuffer + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, img, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, depthbuffer); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + // unbind buffers and texture + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return status; + } + virtual void deleteFBO(GLuint framebuffer, GLuint depthbuffer, GLuint img) + { + glDeleteTextures(1, &img); + glDeleteRenderbuffers(1, &depthbuffer); + glDeleteFramebuffers(1, &framebuffer); + } + + virtual void bindFBO(GLuint framebuffer) + { + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + } + }; +#endif + + struct FramebufferStrategyEXT : public FramebufferStrategy { + + virtual GLenum createFBO(GLuint& framebuffer, GLuint& depthbuffer, GLuint& img, int width, int height) + { + // generate depth buffer + glGenRenderbuffersEXT(1, &depthbuffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, width, height); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + // generate texture save target + glGenTextures(1, &img); + glBindTexture(GL_TEXTURE_2D, 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); + glBindTexture(GL_TEXTURE_2D, 0); + + // create framebuffer + glGenFramebuffersEXT(1, &framebuffer); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, img, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, depthbuffer); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + // unbind buffers and texture + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + return status; + } + + virtual void deleteFBO(GLuint framebuffer, GLuint depthbuffer, GLuint img) + { + glDeleteTextures(1, &img); + glDeleteRenderbuffersEXT(1, &depthbuffer); + glDeleteFramebuffersEXT(1, &framebuffer); + } + + virtual void bindFBO(GLuint framebuffer) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer); + } + }; + + FramebufferStrategy* strategy = NULL; + + FramebufferStrategy strategyNone; +#ifdef GL_VERSION_3_0 + FramebufferStrategyGL3 strategyGL3; +#endif + FramebufferStrategyEXT strategyEXT; + +}; + +namespace love +{ +namespace graphics +{ +namespace opengl +{ + Framebuffer* Framebuffer::current = NULL; + + Framebuffer::Framebuffer(int width, int height) : + width(width), height(height) + { + + // world coordinates + vertices[0].x = 0; vertices[0].y = 0; + vertices[1].x = 0; vertices[1].y = height; + vertices[2].x = width; vertices[2].y = height; + vertices[3].x = width; vertices[3].y = 0; + + // texture coordinates + vertices[0].s = 0; vertices[0].t = 1; + vertices[1].s = 0; vertices[1].t = 0; + vertices[2].s = 1; vertices[2].t = 0; + vertices[3].s = 1; vertices[3].t = 1; + + if (!strategy) { +#ifdef GL_VERSION_3_0 + if (getOpenGLVersionNumber() >= 3.0) + strategy = &strategyGL3; + else if (hasFramebufferExtension()) +#else + if (hasFramebufferExtension()) +#endif + strategy = &strategyEXT; + else + strategy = &strategyNone; + + } + + loadVolatile(); + } + + Framebuffer::~Framebuffer() + { + // reset framebuffer if still using this one + if (current == this) + stopGrab(); + + unloadVolatile(); + } + + void Framebuffer::bindDefaultBuffer() + { + if (current != NULL) + current->stopGrab(); + } + + void Framebuffer::startGrab() + { + // already grabbing + if (current == this) + return; + + // cleanup after previous fbo + if (current != NULL) + glPopAttrib(); + + // bind buffer and clear screen + glPushAttrib(GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + strategy->bindFBO(fbo); + glClearColor(.0f, .0f, .0f, .0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, width, height); + + // indicate we are using this fbo + current = this; + } + + void Framebuffer::stopGrab() + { + // i am not grabbing. leave me alone + if (current != this) + return; + + // bind default + strategy->bindFBO( 0 ); + glPopAttrib(); + current = NULL; + } + + void Framebuffer::draw(float x, float y, float angle, float sx, float sy, float ox, float oy) const + { + static Matrix t; + t.setTransformation(x, y, angle, sx, sy, ox, oy); + + glPushMatrix(); + glMultMatrixf((const GLfloat*)t.getElements()); + + glBindTexture(GL_TEXTURE_2D, img); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&vertices[0].x); + glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&vertices[0].s); + glDrawArrays(GL_QUADS, 0, 4); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + glPopMatrix(); + } + + love::image::ImageData * Framebuffer::getImageData(love::image::Image * image) + { + int row = 4 * width; + int size = row * height; + + // see Graphics::newScreenshot. OpenGL reads from lower-left, + // but we need the pixels from upper-left. + GLubyte* pixels = new GLubyte[size]; + GLubyte* screenshot = new GLubyte[size]; + + strategy->bindFBO( fbo ); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + if (current) + strategy->bindFBO( current->fbo ); + else + strategy->bindFBO( 0 ); + + GLubyte* src = pixels - row; // second line of buffer + GLubyte* dst = screenshot + size; // end of buffer + + for (int i = 0; i < height; ++i) + memcpy(dst -= row, src += row, row); + + love::image::ImageData * img = image->newImageData(width, height, (void*)screenshot); + + delete[] screenshot; + delete[] pixels; + + return img; + } + + bool Framebuffer::loadVolatile() + { + status = strategy->createFBO(fbo, depthbuffer, img, width, height); + return (status == GL_FRAMEBUFFER_COMPLETE); + } + + void Framebuffer::unloadVolatile() + { + strategy->deleteFBO(fbo, depthbuffer, img); + } + +} // opengl +} // graphics +} // love diff --git a/src/modules/graphics/opengl/Framebuffer.h b/src/modules/graphics/opengl/Framebuffer.h new file mode 100644 index 000000000..648d03026 --- /dev/null +++ b/src/modules/graphics/opengl/Framebuffer.h @@ -0,0 +1,55 @@ +#ifndef LOVE_GRAPHICS_FRAMEBUFFER_H +#define LOVE_GRAPHICS_FRAMEBUFFER_H + +#include +#include +#include +#include +#include +#include +#include "GLee.h" + +namespace love +{ +namespace graphics +{ +namespace opengl +{ + + class Framebuffer : public Drawable, public Volatile + { + public: + Framebuffer(int width, int height); + virtual ~Framebuffer(); + + unsigned int getStatus() const { return status; } + + static Framebuffer* current; + static void bindDefaultBuffer(); + + void startGrab(); + void stopGrab(); + + virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy) const; + love::image::ImageData * getImageData(love::image::Image * image); + + bool loadVolatile(); + void unloadVolatile(); + + private: + GLsizei width; + GLsizei height; + GLuint fbo; + GLuint depthbuffer; + GLuint img; + + vertex vertices[4]; + + GLenum status; + }; + +} // opengl +} // graphics +} // love + +#endif // LOVE_GRAPHICS_FRAMEBUFFER_H diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 8fc08a4cc..5bed02392 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -477,6 +477,11 @@ namespace opengl return new ParticleSystem(image, size); } + Framebuffer * Graphics::newFramebuffer(int width, int height) + { + return new Framebuffer(width, height); + } + void Graphics::setColor(Color c) { glColor4ubv(&c.r); diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 2eaeb80fd..1fc653abc 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -1,14 +1,14 @@ /** * Copyright (c) 2006-2010 LOVE Development Team -* +* * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. -* +* * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: -* +* * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be @@ -43,6 +43,7 @@ #include "Quad.h" #include "SpriteBatch.h" #include "ParticleSystem.h" +#include "Framebuffer.h" namespace love { @@ -95,13 +96,13 @@ namespace opengl // Default values. DisplayState() { - color.r = 255; - color.g = 255; - color.b = 255; + color.r = 255; + color.g = 255; + color.b = 255; color.a = 255; - backgroundColor.r = 0; - backgroundColor.g = 0; - backgroundColor.b = 0; + backgroundColor.r = 0; + backgroundColor.g = 0; + backgroundColor.b = 0; backgroundColor.a = 255; blendMode = Graphics::BLEND_ALPHA; colorMode = Graphics::COLOR_MODULATE; @@ -126,7 +127,7 @@ namespace opengl Graphics(); virtual ~Graphics(); - + // Implements Module. const char * getName() const; @@ -165,18 +166,18 @@ namespace opengl * when the game reloads. **/ void reset(); - + /** * Clears the screen. **/ void clear(); /** - * Flips buffers. (Rendered geometry is + * Flips buffers. (Rendered geometry is * presented on screen). **/ void present(); - + /** * Sets the window's icon. **/ @@ -207,13 +208,13 @@ namespace opengl /** * This native Lua function gets available modes * from SDL and returns them as a table on the following format: - * - * { - * { width = 800, height = 600 }, + * + * { + * { width = 800, height = 600 }, * { width = 1024, height = 768 }, * ... * } - * + * * Only fullscreen modes are returned here, as all * window sizes are supported (normally). **/ @@ -245,7 +246,7 @@ namespace opengl **/ Image * newImage(love::filesystem::File * file); Image * newImage(love::image::ImageData * data); - + /** * Creates a Frame **/ @@ -255,11 +256,13 @@ namespace opengl * Creates a Font object. **/ Font * newFont(love::font::FontData * data); - + SpriteBatch * newSpriteBatch(Image * image, int size, int usage); ParticleSystem * newParticleSystem(Image * image, int size); - + + Framebuffer * newFramebuffer(int width, int height); + /** * Sets the foreground color. **/ @@ -271,7 +274,7 @@ namespace opengl Color getColor(); /** - * Sets the background Color. + * Sets the background Color. **/ void setBackgroundColor(Color c); @@ -383,7 +386,7 @@ namespace opengl PointStyle getPointStyle(); /** - * Gets the maximum point size supported. + * Gets the maximum point size supported. * This may vary from computer to computer. **/ int getMaxPointSize(); @@ -406,7 +409,7 @@ namespace opengl void print(const char * str, float x, float y , float angle); /** - * Draws text at the specified coordinates, with rotation and + * Draws text at the specified coordinates, with rotation and * scaling. * @param x The x-coordinate. * @param y The y-coordinate. @@ -416,7 +419,7 @@ namespace opengl void print(const char * str, float x, float y , float angle, float s); /** - * Draws text at the specified coordinates, with rotation and + * Draws text at the specified coordinates, with rotation and * scaling along both axes. * @param x The x-coordinate. * @param y The y-coordinate. @@ -452,7 +455,7 @@ namespace opengl * @param y2 Second y-coordinate. **/ void line(float x1, float y1, float x2, float y2); - + /** * Draws a series of lines connecting the given vertices. * @param ... Vertex components (x1, y1, x2, y2, etc.) diff --git a/src/modules/graphics/opengl/wrap_Framebuffer.cpp b/src/modules/graphics/opengl/wrap_Framebuffer.cpp new file mode 100644 index 000000000..5bb67fdc9 --- /dev/null +++ b/src/modules/graphics/opengl/wrap_Framebuffer.cpp @@ -0,0 +1,57 @@ +#include "wrap_Framebuffer.h" + +namespace love +{ +namespace graphics +{ +namespace opengl +{ + Framebuffer * luax_checkfbo(lua_State * L, int idx) + { + return luax_checktype(L, idx, "Framebuffer", GRAPHICS_FRAMEBUFFER_T); + } + + int w_Framebuffer_renderTo(lua_State * L) + { + // As startGrab() clears the framebuffer, better not allow + // grabbing inside another grabbing + if (Framebuffer::current != NULL) { + Framebuffer::bindDefaultBuffer(); + return luaL_error(L, "Current render target not the default framebuffer!"); + } + + Framebuffer * fbo = luax_checkfbo(L, 1); + if (!lua_isfunction(L, 2)) + return luaL_error(L, "Need a function to render to fbo"); + + fbo->startGrab(); + lua_settop(L, 2); // make sure the function is on top of the stack + lua_call(L, 0, 0); + fbo->stopGrab(); + + return 0; + } + + int w_Framebuffer_getImageData(lua_State * L) + { + Framebuffer * fbo = luax_checkfbo(L, 1); + love::image::Image * image = luax_getmodule(L, "image", MODULE_IMAGE_T); + love::image::ImageData * img = fbo->getImageData( image ); + luax_newtype(L, "ImageData", IMAGE_IMAGE_DATA_T, (void *)img); + return 1; + } + + static const luaL_Reg functions[] = { + { "renderTo", w_Framebuffer_renderTo }, + { "getImageData", w_Framebuffer_getImageData }, + { 0, 0 } + }; + + int luaopen_framebuffer(lua_State * L) + { + return luax_register_type(L, "Framebuffer", functions); + } + +} // opengl +} // graphics +} // love diff --git a/src/modules/graphics/opengl/wrap_Framebuffer.h b/src/modules/graphics/opengl/wrap_Framebuffer.h new file mode 100644 index 000000000..ecb491604 --- /dev/null +++ b/src/modules/graphics/opengl/wrap_Framebuffer.h @@ -0,0 +1,24 @@ +#ifndef LOVE_GRAPHICS_OPENGL_WRAP_FBO_H +#define LOVE_GRAPHICS_OPENGL_WRAP_FBO_H + +// LOVE +#include +#include "Framebuffer.h" + +namespace love +{ +namespace graphics +{ +namespace opengl +{ + //see Framebuffer.h + Framebuffer * luax_checkfbo(lua_State * L, int idx); + int w_Framebuffer_renderTo(lua_State * L); + int w_Framebuffer_getImageData(lua_State * L); + int luaopen_framebuffer(lua_State * L); + +} // opengl +} // graphics +} // love + +#endif // LOVE_GRAPHICS_OPENGL_WRAP_FBO_H diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 8b146f04b..38e1c691c 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -84,7 +84,7 @@ namespace opengl instance->setIcon(image); return 0; } - + int w_setCaption(lua_State * L) { const char * str = luaL_checkstring(L, 1); @@ -292,6 +292,46 @@ namespace opengl return 1; } + int w_newFramebuffer(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 + Framebuffer * framebuffer = instance->newFramebuffer(width, height); + + //and there we go with the status... still disliked + if (framebuffer->getStatus() != GL_FRAMEBUFFER_COMPLETE) { + switch (framebuffer->getStatus()) { + case GL_FRAMEBUFFER_UNSUPPORTED: + return luaL_error(L, "Cannot create Framebuffer: " + "Not supported by your OpenGL implementation"); + // remaining error codes are highly unlikely: + case GL_FRAMEBUFFER_UNDEFINED: + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return luaL_error(L, "Cannot create Framebuffer: " + "Error in implementation (please inform the love devs)"); + default: + // my intel hda card wrongly returns 0 to glCheckFramebufferStatus() but sets + // no error flag. I think it meant to return GL_FRAMEBUFFER_UNSUPPORTED, but who + // knows. + if (glGetError() == GL_NO_ERROR) + return luaL_error(L, "Cannot create Framebuffer: " + "May not be supported by your OpenGL implementation."); + // the remaining error is an indication of a serious fuckup since it should + // only be returned if glCheckFramebufferStatus() was called with the wrong + // arguments. + return luaL_error(L, "Cannot create Framebuffer: Aliens did it (OpenGL error code: %d)", glGetError()); + } + } + luax_newtype(L, "Framebuffer", GRAPHICS_FRAMEBUFFER_T, (void*)framebuffer); + return 1; + } + int w_setColor(lua_State * L) { Color c; @@ -567,6 +607,21 @@ namespace opengl return 1; } + int w_setRenderTarget(lua_State * L) + { + // called with nil or none -> reset to default buffer + if (lua_isnoneornil(L,1)) { + Framebuffer::bindDefaultBuffer(); + return 0; + } + + Framebuffer * fbo = luax_checkfbo(L, 1); + // this unbinds the previous fbo + fbo->startGrab(); + + return 0; + } + /** * Draws an Image at the specified coordinates, with rotation and * scaling along both axes. @@ -829,6 +884,7 @@ namespace opengl { "newImageFont", w_newImageFont }, { "newSpriteBatch", w_newSpriteBatch }, { "newParticleSystem", w_newParticleSystem }, + { "newFramebuffer", w_newFramebuffer }, { "setColor", w_setColor }, { "getColor", w_getColor }, @@ -856,6 +912,7 @@ namespace opengl { "getPointStyle", w_getPointStyle }, { "getMaxPointSize", w_getMaxPointSize }, { "newScreenshot", w_newScreenshot }, + { "setRenderTarget", w_setRenderTarget }, { "draw", w_draw }, { "drawq", w_drawq }, @@ -866,7 +923,7 @@ namespace opengl { "setCaption", w_setCaption }, { "getCaption", w_getCaption }, - + { "setIcon", w_setIcon }, { "getWidth", w_getWidth }, @@ -906,6 +963,7 @@ namespace opengl luaopen_frame, luaopen_spritebatch, luaopen_particlesystem, + luaopen_framebuffer, 0 }; diff --git a/src/modules/graphics/opengl/wrap_Graphics.h b/src/modules/graphics/opengl/wrap_Graphics.h index 2ad3a95f7..776978351 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.h +++ b/src/modules/graphics/opengl/wrap_Graphics.h @@ -1,14 +1,14 @@ /** * Copyright (c) 2006-2010 LOVE Development Team -* +* * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. -* +* * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: -* +* * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be @@ -28,6 +28,7 @@ #include "wrap_Quad.h" #include "wrap_SpriteBatch.h" #include "wrap_ParticleSystem.h" +#include "wrap_Framebuffer.h" #include "Graphics.h" namespace love @@ -58,6 +59,7 @@ namespace opengl int w_newImageFont(lua_State * L); int w_newSpriteBatch(lua_State * L); int w_newParticleSystem(lua_State * L); + int w_newFramebuffer(lua_State * L); // commetns in function int w_setColor(lua_State * L); int w_getColor(lua_State * L); int w_setBackgroundColor(lua_State * L); @@ -82,6 +84,7 @@ namespace opengl int w_getPointStyle(lua_State * L); int w_getMaxPointSize(lua_State * L); int w_newScreenshot(lua_State * L); + int w_setRenderTarget(lua_State * L); int w_draw(lua_State * L); int w_drawq(lua_State * L); int w_drawTest(lua_State * L); diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index 8277a9a73..387a32300 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -787,6 +787,8 @@ function love.errhand(msg) return end + love.graphics.setRenderTarget() + -- Load. love.graphics.setScissor() love.graphics.setBackgroundColor(89, 157, 220) diff --git a/src/scripts/boot.lua.h b/src/scripts/boot.lua.h index c1d38313b..8519940ab 100644 --- a/src/scripts/boot.lua.h +++ b/src/scripts/boot.lua.h @@ -1157,102 +1157,104 @@ const unsigned char boot_lua[] = 0x74,0x20,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73, 0x2E,0x69,0x73,0x43,0x72,0x65,0x61,0x74,0x65,0x64,0x28,0x29,0x20,0x74,0x68, 0x65,0x6E,0x0D,0x0A,0x09,0x09,0x72,0x65,0x74,0x75,0x72,0x6E,0x0D,0x0A,0x09, - 0x65,0x6E,0x64,0x0D,0x0A,0x0D,0x0A,0x09,0x2D,0x2D,0x20,0x4C,0x6F,0x61,0x64, - 0x2E,0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69, - 0x63,0x73,0x2E,0x73,0x65,0x74,0x53,0x63,0x69,0x73,0x73,0x6F,0x72,0x28,0x29, - 0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69,0x63, - 0x73,0x2E,0x73,0x65,0x74,0x42,0x61,0x63,0x6B,0x67,0x72,0x6F,0x75,0x6E,0x64, - 0x43,0x6F,0x6C,0x6F,0x72,0x28,0x38,0x39,0x2C,0x20,0x31,0x35,0x37,0x2C,0x20, - 0x32,0x32,0x30,0x29,0x0D,0x0A,0x09,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x66,0x6F, - 0x6E,0x74,0x20,0x3D,0x20,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68, - 0x69,0x63,0x73,0x2E,0x6E,0x65,0x77,0x46,0x6F,0x6E,0x74,0x28,0x6C,0x6F,0x76, - 0x65,0x2E,0x5F,0x76,0x65,0x72,0x61,0x5F,0x74,0x74,0x66,0x2C,0x20,0x31,0x34, - 0x29,0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69, - 0x63,0x73,0x2E,0x73,0x65,0x74,0x46,0x6F,0x6E,0x74,0x28,0x66,0x6F,0x6E,0x74, - 0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70, - 0x68,0x69,0x63,0x73,0x2E,0x73,0x65,0x74,0x43,0x6F,0x6C,0x6F,0x72,0x28,0x32, - 0x35,0x35,0x2C,0x20,0x32,0x35,0x35,0x2C,0x20,0x32,0x35,0x35,0x2C,0x20,0x32, - 0x35,0x35,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x74, - 0x72,0x61,0x63,0x65,0x20,0x3D,0x20,0x64,0x65,0x62,0x75,0x67,0x2E,0x74,0x72, - 0x61,0x63,0x65,0x62,0x61,0x63,0x6B,0x28,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C, - 0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x63,0x6C, - 0x65,0x61,0x72,0x28,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x63,0x61,0x6C, - 0x20,0x65,0x72,0x72,0x20,0x3D,0x20,0x7B,0x7D,0x0D,0x0A,0x0D,0x0A,0x09,0x74, - 0x61,0x62,0x6C,0x65,0x2E,0x69,0x6E,0x73,0x65,0x72,0x74,0x28,0x65,0x72,0x72, - 0x2C,0x20,0x22,0x45,0x72,0x72,0x6F,0x72,0x5C,0x6E,0x22,0x29,0x0D,0x0A,0x09, - 0x74,0x61,0x62,0x6C,0x65,0x2E,0x69,0x6E,0x73,0x65,0x72,0x74,0x28,0x65,0x72, - 0x72,0x2C,0x20,0x6D,0x73,0x67,0x2E,0x2E,0x22,0x5C,0x6E,0x5C,0x6E,0x22,0x29, - 0x0D,0x0A,0x0D,0x0A,0x09,0x66,0x6F,0x72,0x20,0x6C,0x20,0x69,0x6E,0x20,0x73, - 0x74,0x72,0x69,0x6E,0x67,0x2E,0x67,0x6D,0x61,0x74,0x63,0x68,0x28,0x74,0x72, - 0x61,0x63,0x65,0x2C,0x20,0x22,0x28,0x2E,0x2D,0x29,0x5C,0x6E,0x22,0x29,0x20, - 0x64,0x6F,0x0D,0x0A,0x09,0x09,0x69,0x66,0x20,0x6E,0x6F,0x74,0x20,0x73,0x74, - 0x72,0x69,0x6E,0x67,0x2E,0x6D,0x61,0x74,0x63,0x68,0x28,0x6C,0x2C,0x20,0x22, - 0x62,0x6F,0x6F,0x74,0x2E,0x6C,0x75,0x61,0x22,0x29,0x20,0x74,0x68,0x65,0x6E, - 0x0D,0x0A,0x09,0x09,0x09,0x6C,0x20,0x3D,0x20,0x73,0x74,0x72,0x69,0x6E,0x67, - 0x2E,0x67,0x73,0x75,0x62,0x28,0x6C,0x2C,0x20,0x22,0x73,0x74,0x61,0x63,0x6B, - 0x20,0x74,0x72,0x61,0x63,0x65,0x62,0x61,0x63,0x6B,0x3A,0x22,0x2C,0x20,0x22, - 0x54,0x72,0x61,0x63,0x65,0x62,0x61,0x63,0x6B,0x5C,0x6E,0x22,0x29,0x0D,0x0A, - 0x09,0x09,0x09,0x74,0x61,0x62,0x6C,0x65,0x2E,0x69,0x6E,0x73,0x65,0x72,0x74, - 0x28,0x65,0x72,0x72,0x2C,0x20,0x6C,0x29,0x0D,0x0A,0x09,0x09,0x65,0x6E,0x64, - 0x0D,0x0A,0x09,0x65,0x6E,0x64,0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x63,0x61, - 0x6C,0x20,0x70,0x20,0x3D,0x20,0x74,0x61,0x62,0x6C,0x65,0x2E,0x63,0x6F,0x6E, - 0x63,0x61,0x74,0x28,0x65,0x72,0x72,0x2C,0x20,0x22,0x5C,0x6E,0x22,0x29,0x0D, - 0x0A,0x0D,0x0A,0x09,0x70,0x20,0x3D,0x20,0x73,0x74,0x72,0x69,0x6E,0x67,0x2E, - 0x67,0x73,0x75,0x62,0x28,0x70,0x2C,0x20,0x22,0x5C,0x74,0x22,0x2C,0x20,0x22, - 0x22,0x29,0x0D,0x0A,0x09,0x70,0x20,0x3D,0x20,0x73,0x74,0x72,0x69,0x6E,0x67, - 0x2E,0x67,0x73,0x75,0x62,0x28,0x70,0x2C,0x20,0x22,0x25,0x5B,0x73,0x74,0x72, - 0x69,0x6E,0x67,0x20,0x5C,0x22,0x28,0x2E,0x2D,0x29,0x5C,0x22,0x25,0x5D,0x22, - 0x2C,0x20,0x22,0x25,0x31,0x22,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x63, - 0x61,0x6C,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E,0x20,0x64,0x72,0x61, - 0x77,0x28,0x29,0x0D,0x0A,0x09,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61, - 0x70,0x68,0x69,0x63,0x73,0x2E,0x63,0x6C,0x65,0x61,0x72,0x28,0x29,0x0D,0x0A, - 0x09,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73, - 0x2E,0x70,0x72,0x69,0x6E,0x74,0x66,0x28,0x70,0x2C,0x20,0x37,0x30,0x2C,0x20, - 0x37,0x30,0x2C,0x20,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69, - 0x63,0x73,0x2E,0x67,0x65,0x74,0x57,0x69,0x64,0x74,0x68,0x28,0x29,0x20,0x2D, - 0x20,0x37,0x30,0x29,0x0D,0x0A,0x09,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72, - 0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x70,0x72,0x65,0x73,0x65,0x6E,0x74,0x28, - 0x29,0x0D,0x0A,0x09,0x65,0x6E,0x64,0x0D,0x0A,0x0D,0x0A,0x09,0x64,0x72,0x61, - 0x77,0x28,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x65, - 0x2C,0x20,0x61,0x2C,0x20,0x62,0x2C,0x20,0x63,0x0D,0x0A,0x09,0x77,0x68,0x69, - 0x6C,0x65,0x20,0x74,0x72,0x75,0x65,0x20,0x64,0x6F,0x0D,0x0A,0x09,0x09,0x65, - 0x2C,0x20,0x61,0x2C,0x20,0x62,0x2C,0x20,0x63,0x20,0x3D,0x20,0x6C,0x6F,0x76, - 0x65,0x2E,0x65,0x76,0x65,0x6E,0x74,0x2E,0x77,0x61,0x69,0x74,0x28,0x29,0x0D, - 0x0A,0x0D,0x0A,0x09,0x09,0x69,0x66,0x20,0x65,0x20,0x3D,0x3D,0x20,0x22,0x71, - 0x22,0x20,0x74,0x68,0x65,0x6E,0x0D,0x0A,0x09,0x09,0x09,0x72,0x65,0x74,0x75, - 0x72,0x6E,0x0D,0x0A,0x09,0x09,0x65,0x6E,0x64,0x0D,0x0A,0x09,0x09,0x69,0x66, - 0x20,0x65,0x20,0x3D,0x3D,0x20,0x22,0x6B,0x70,0x22,0x20,0x61,0x6E,0x64,0x20, - 0x61,0x20,0x3D,0x3D,0x20,0x22,0x65,0x73,0x63,0x61,0x70,0x65,0x22,0x20,0x74, - 0x68,0x65,0x6E,0x0D,0x0A,0x09,0x09,0x09,0x72,0x65,0x74,0x75,0x72,0x6E,0x0D, - 0x0A,0x09,0x09,0x65,0x6E,0x64,0x0D,0x0A,0x0D,0x0A,0x09,0x09,0x64,0x72,0x61, - 0x77,0x28,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x65,0x6E,0x64,0x0D,0x0A,0x0D,0x0A, - 0x65,0x6E,0x64,0x0D,0x0A,0x0D,0x0A,0x0D,0x0A,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, + 0x65,0x6E,0x64,0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72, + 0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x73,0x65,0x74,0x52,0x65,0x6E,0x64,0x65, + 0x72,0x54,0x61,0x72,0x67,0x65,0x74,0x28,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x2D, + 0x2D,0x20,0x4C,0x6F,0x61,0x64,0x2E,0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E, + 0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x73,0x65,0x74,0x53,0x63,0x69, + 0x73,0x73,0x6F,0x72,0x28,0x29,0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67, + 0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x73,0x65,0x74,0x42,0x61,0x63,0x6B, + 0x67,0x72,0x6F,0x75,0x6E,0x64,0x43,0x6F,0x6C,0x6F,0x72,0x28,0x38,0x39,0x2C, + 0x20,0x31,0x35,0x37,0x2C,0x20,0x32,0x32,0x30,0x29,0x0D,0x0A,0x09,0x6C,0x6F, + 0x63,0x61,0x6C,0x20,0x66,0x6F,0x6E,0x74,0x20,0x3D,0x20,0x6C,0x6F,0x76,0x65, + 0x2E,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x6E,0x65,0x77,0x46,0x6F, + 0x6E,0x74,0x28,0x6C,0x6F,0x76,0x65,0x2E,0x5F,0x76,0x65,0x72,0x61,0x5F,0x74, + 0x74,0x66,0x2C,0x20,0x31,0x34,0x29,0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E, + 0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x73,0x65,0x74,0x46,0x6F,0x6E, + 0x74,0x28,0x66,0x6F,0x6E,0x74,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x76, + 0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x73,0x65,0x74,0x43, + 0x6F,0x6C,0x6F,0x72,0x28,0x32,0x35,0x35,0x2C,0x20,0x32,0x35,0x35,0x2C,0x20, + 0x32,0x35,0x35,0x2C,0x20,0x32,0x35,0x35,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C, + 0x6F,0x63,0x61,0x6C,0x20,0x74,0x72,0x61,0x63,0x65,0x20,0x3D,0x20,0x64,0x65, + 0x62,0x75,0x67,0x2E,0x74,0x72,0x61,0x63,0x65,0x62,0x61,0x63,0x6B,0x28,0x29, + 0x0D,0x0A,0x0D,0x0A,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68, + 0x69,0x63,0x73,0x2E,0x63,0x6C,0x65,0x61,0x72,0x28,0x29,0x0D,0x0A,0x0D,0x0A, + 0x09,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x65,0x72,0x72,0x20,0x3D,0x20,0x7B,0x7D, + 0x0D,0x0A,0x0D,0x0A,0x09,0x74,0x61,0x62,0x6C,0x65,0x2E,0x69,0x6E,0x73,0x65, + 0x72,0x74,0x28,0x65,0x72,0x72,0x2C,0x20,0x22,0x45,0x72,0x72,0x6F,0x72,0x5C, + 0x6E,0x22,0x29,0x0D,0x0A,0x09,0x74,0x61,0x62,0x6C,0x65,0x2E,0x69,0x6E,0x73, + 0x65,0x72,0x74,0x28,0x65,0x72,0x72,0x2C,0x20,0x6D,0x73,0x67,0x2E,0x2E,0x22, + 0x5C,0x6E,0x5C,0x6E,0x22,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x66,0x6F,0x72,0x20, + 0x6C,0x20,0x69,0x6E,0x20,0x73,0x74,0x72,0x69,0x6E,0x67,0x2E,0x67,0x6D,0x61, + 0x74,0x63,0x68,0x28,0x74,0x72,0x61,0x63,0x65,0x2C,0x20,0x22,0x28,0x2E,0x2D, + 0x29,0x5C,0x6E,0x22,0x29,0x20,0x64,0x6F,0x0D,0x0A,0x09,0x09,0x69,0x66,0x20, + 0x6E,0x6F,0x74,0x20,0x73,0x74,0x72,0x69,0x6E,0x67,0x2E,0x6D,0x61,0x74,0x63, + 0x68,0x28,0x6C,0x2C,0x20,0x22,0x62,0x6F,0x6F,0x74,0x2E,0x6C,0x75,0x61,0x22, + 0x29,0x20,0x74,0x68,0x65,0x6E,0x0D,0x0A,0x09,0x09,0x09,0x6C,0x20,0x3D,0x20, + 0x73,0x74,0x72,0x69,0x6E,0x67,0x2E,0x67,0x73,0x75,0x62,0x28,0x6C,0x2C,0x20, + 0x22,0x73,0x74,0x61,0x63,0x6B,0x20,0x74,0x72,0x61,0x63,0x65,0x62,0x61,0x63, + 0x6B,0x3A,0x22,0x2C,0x20,0x22,0x54,0x72,0x61,0x63,0x65,0x62,0x61,0x63,0x6B, + 0x5C,0x6E,0x22,0x29,0x0D,0x0A,0x09,0x09,0x09,0x74,0x61,0x62,0x6C,0x65,0x2E, + 0x69,0x6E,0x73,0x65,0x72,0x74,0x28,0x65,0x72,0x72,0x2C,0x20,0x6C,0x29,0x0D, + 0x0A,0x09,0x09,0x65,0x6E,0x64,0x0D,0x0A,0x09,0x65,0x6E,0x64,0x0D,0x0A,0x0D, + 0x0A,0x09,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x70,0x20,0x3D,0x20,0x74,0x61,0x62, + 0x6C,0x65,0x2E,0x63,0x6F,0x6E,0x63,0x61,0x74,0x28,0x65,0x72,0x72,0x2C,0x20, + 0x22,0x5C,0x6E,0x22,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x70,0x20,0x3D,0x20,0x73, + 0x74,0x72,0x69,0x6E,0x67,0x2E,0x67,0x73,0x75,0x62,0x28,0x70,0x2C,0x20,0x22, + 0x5C,0x74,0x22,0x2C,0x20,0x22,0x22,0x29,0x0D,0x0A,0x09,0x70,0x20,0x3D,0x20, + 0x73,0x74,0x72,0x69,0x6E,0x67,0x2E,0x67,0x73,0x75,0x62,0x28,0x70,0x2C,0x20, + 0x22,0x25,0x5B,0x73,0x74,0x72,0x69,0x6E,0x67,0x20,0x5C,0x22,0x28,0x2E,0x2D, + 0x29,0x5C,0x22,0x25,0x5D,0x22,0x2C,0x20,0x22,0x25,0x31,0x22,0x29,0x0D,0x0A, + 0x0D,0x0A,0x09,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x66,0x75,0x6E,0x63,0x74,0x69, + 0x6F,0x6E,0x20,0x64,0x72,0x61,0x77,0x28,0x29,0x0D,0x0A,0x09,0x09,0x6C,0x6F, + 0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x63,0x6C,0x65, + 0x61,0x72,0x28,0x29,0x0D,0x0A,0x09,0x09,0x6C,0x6F,0x76,0x65,0x2E,0x67,0x72, + 0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x70,0x72,0x69,0x6E,0x74,0x66,0x28,0x70, + 0x2C,0x20,0x37,0x30,0x2C,0x20,0x37,0x30,0x2C,0x20,0x6C,0x6F,0x76,0x65,0x2E, + 0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x67,0x65,0x74,0x57,0x69,0x64, + 0x74,0x68,0x28,0x29,0x20,0x2D,0x20,0x37,0x30,0x29,0x0D,0x0A,0x09,0x09,0x6C, + 0x6F,0x76,0x65,0x2E,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x2E,0x70,0x72, + 0x65,0x73,0x65,0x6E,0x74,0x28,0x29,0x0D,0x0A,0x09,0x65,0x6E,0x64,0x0D,0x0A, + 0x0D,0x0A,0x09,0x64,0x72,0x61,0x77,0x28,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x6C, + 0x6F,0x63,0x61,0x6C,0x20,0x65,0x2C,0x20,0x61,0x2C,0x20,0x62,0x2C,0x20,0x63, + 0x0D,0x0A,0x09,0x77,0x68,0x69,0x6C,0x65,0x20,0x74,0x72,0x75,0x65,0x20,0x64, + 0x6F,0x0D,0x0A,0x09,0x09,0x65,0x2C,0x20,0x61,0x2C,0x20,0x62,0x2C,0x20,0x63, + 0x20,0x3D,0x20,0x6C,0x6F,0x76,0x65,0x2E,0x65,0x76,0x65,0x6E,0x74,0x2E,0x77, + 0x61,0x69,0x74,0x28,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x09,0x69,0x66,0x20,0x65, + 0x20,0x3D,0x3D,0x20,0x22,0x71,0x22,0x20,0x74,0x68,0x65,0x6E,0x0D,0x0A,0x09, + 0x09,0x09,0x72,0x65,0x74,0x75,0x72,0x6E,0x0D,0x0A,0x09,0x09,0x65,0x6E,0x64, + 0x0D,0x0A,0x09,0x09,0x69,0x66,0x20,0x65,0x20,0x3D,0x3D,0x20,0x22,0x6B,0x70, + 0x22,0x20,0x61,0x6E,0x64,0x20,0x61,0x20,0x3D,0x3D,0x20,0x22,0x65,0x73,0x63, + 0x61,0x70,0x65,0x22,0x20,0x74,0x68,0x65,0x6E,0x0D,0x0A,0x09,0x09,0x09,0x72, + 0x65,0x74,0x75,0x72,0x6E,0x0D,0x0A,0x09,0x09,0x65,0x6E,0x64,0x0D,0x0A,0x0D, + 0x0A,0x09,0x09,0x64,0x72,0x61,0x77,0x28,0x29,0x0D,0x0A,0x0D,0x0A,0x09,0x65, + 0x6E,0x64,0x0D,0x0A,0x0D,0x0A,0x65,0x6E,0x64,0x0D,0x0A,0x0D,0x0A,0x0D,0x0A, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, - 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x0D,0x0A,0x2D,0x2D,0x20,0x54,0x68, - 0x65,0x20,0x72,0x6F,0x6F,0x74,0x20,0x6F,0x66,0x20,0x61,0x6C,0x6C,0x20,0x63, - 0x61,0x6C,0x6C,0x73,0x2E,0x0D,0x0A,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, + 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x0D, + 0x0A,0x2D,0x2D,0x20,0x54,0x68,0x65,0x20,0x72,0x6F,0x6F,0x74,0x20,0x6F,0x66, + 0x20,0x61,0x6C,0x6C,0x20,0x63,0x61,0x6C,0x6C,0x73,0x2E,0x0D,0x0A,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, - 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x0D,0x0A,0x0D,0x0A,0x6C,0x6F,0x63,0x61,0x6C, + 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x0D,0x0A,0x0D, + 0x0A,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x3D, + 0x20,0x78,0x70,0x63,0x61,0x6C,0x6C,0x28,0x6C,0x6F,0x76,0x65,0x2E,0x62,0x6F, + 0x6F,0x74,0x2C,0x20,0x65,0x72,0x72,0x6F,0x72,0x5F,0x70,0x72,0x69,0x6E,0x74, + 0x65,0x72,0x29,0x0D,0x0A,0x69,0x66,0x20,0x6E,0x6F,0x74,0x20,0x72,0x65,0x73, + 0x75,0x6C,0x74,0x20,0x74,0x68,0x65,0x6E,0x20,0x72,0x65,0x74,0x75,0x72,0x6E, + 0x20,0x65,0x6E,0x64,0x0D,0x0A,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x72,0x65,0x73, + 0x75,0x6C,0x74,0x20,0x3D,0x20,0x78,0x70,0x63,0x61,0x6C,0x6C,0x28,0x6C,0x6F, + 0x76,0x65,0x2E,0x69,0x6E,0x69,0x74,0x2C,0x20,0x6C,0x6F,0x76,0x65,0x2E,0x65, + 0x72,0x72,0x68,0x61,0x6E,0x64,0x29,0x0D,0x0A,0x69,0x66,0x20,0x6E,0x6F,0x74, + 0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x74,0x68,0x65,0x6E,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6E,0x20,0x65,0x6E,0x64,0x0D,0x0A,0x6C,0x6F,0x63,0x61,0x6C, 0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x3D,0x20,0x78,0x70,0x63,0x61,0x6C, - 0x6C,0x28,0x6C,0x6F,0x76,0x65,0x2E,0x62,0x6F,0x6F,0x74,0x2C,0x20,0x65,0x72, - 0x72,0x6F,0x72,0x5F,0x70,0x72,0x69,0x6E,0x74,0x65,0x72,0x29,0x0D,0x0A,0x69, - 0x66,0x20,0x6E,0x6F,0x74,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x74,0x68, - 0x65,0x6E,0x20,0x72,0x65,0x74,0x75,0x72,0x6E,0x20,0x65,0x6E,0x64,0x0D,0x0A, - 0x6C,0x6F,0x63,0x61,0x6C,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x3D,0x20, - 0x78,0x70,0x63,0x61,0x6C,0x6C,0x28,0x6C,0x6F,0x76,0x65,0x2E,0x69,0x6E,0x69, - 0x74,0x2C,0x20,0x6C,0x6F,0x76,0x65,0x2E,0x65,0x72,0x72,0x68,0x61,0x6E,0x64, - 0x29,0x0D,0x0A,0x69,0x66,0x20,0x6E,0x6F,0x74,0x20,0x72,0x65,0x73,0x75,0x6C, - 0x74,0x20,0x74,0x68,0x65,0x6E,0x20,0x72,0x65,0x74,0x75,0x72,0x6E,0x20,0x65, - 0x6E,0x64,0x0D,0x0A,0x6C,0x6F,0x63,0x61,0x6C,0x20,0x72,0x65,0x73,0x75,0x6C, - 0x74,0x20,0x3D,0x20,0x78,0x70,0x63,0x61,0x6C,0x6C,0x28,0x6C,0x6F,0x76,0x65, - 0x2E,0x72,0x75,0x6E,0x2C,0x20,0x6C,0x6F,0x76,0x65,0x2E,0x65,0x72,0x72,0x68, - 0x61,0x6E,0x64,0x29,0x0D,0x0A,0x69,0x66,0x20,0x6E,0x6F,0x74,0x20,0x72,0x65, - 0x73,0x75,0x6C,0x74,0x20,0x74,0x68,0x65,0x6E,0x20,0x72,0x65,0x74,0x75,0x72, - 0x6E,0x20,0x65,0x6E,0x64,0x0D,0x0A, + 0x6C,0x28,0x6C,0x6F,0x76,0x65,0x2E,0x72,0x75,0x6E,0x2C,0x20,0x6C,0x6F,0x76, + 0x65,0x2E,0x65,0x72,0x72,0x68,0x61,0x6E,0x64,0x29,0x0D,0x0A,0x69,0x66,0x20, + 0x6E,0x6F,0x74,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x74,0x68,0x65,0x6E, + 0x20,0x72,0x65,0x74,0x75,0x72,0x6E,0x20,0x65,0x6E,0x64,0x0D,0x0A, }; // [/boot.lua]