Merge in default

--HG--
branch : minor
This commit is contained in:
Bart van Strien
2012-10-14 17:23:35 +02:00
49 changed files with 773 additions and 236 deletions
+65
View File
@@ -0,0 +1,65 @@
/**
* Copyright (c) 2006-2012 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
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
// LOVE
#include "Module.h"
#include "Exception.h"
// std
#include <map>
#include <utility>
#include <string>
namespace
{
std::map<std::string, love::Module*> registry;
} // anonymous namespace
namespace love
{
void Module::registerInstance(Module *instance)
{
if (instance == NULL)
throw Exception("Module instance is NULL");
std::string name(instance->getName());
std::map<std::string, Module*>::iterator it = registry.find(name);
if (registry.end() != it)
{
if (it->second == instance)
return;
throw Exception("Module %s already registered!", instance->getName());
}
registry.insert(make_pair(name, instance));
}
Module *Module::getInstance(const char *name)
{
std::map<std::string, Module*>::iterator it = registry.find(std::string(name));
if (registry.end() == it)
return NULL;
return it->second;
}
} // namespace love
+15
View File
@@ -48,6 +48,21 @@ public:
**/
virtual const char *getName() const = 0;
/**
* Add module to internal registry. To be used /only/ in
* runtime.cpp:luax_register_module()
* @param instance The module instance.
*/
static void registerInstance(Module *instance);
/**
* Retrieve module instance from internal registry. May return NULL
* if module not registered.
* @param name The full name of the module.
* @returns Module instance of NULL if the module is not registered.
*/
static Module *getInstance(const char *name);
}; // Module
} // love
+33
View File
@@ -0,0 +1,33 @@
#include "math.h"
#include <limits>
#include <cmath>
namespace
{
// The BoxMuller transform generates two random numbers, one of which we
// cache here. A value of +infinity is used to signal the cache is invalid
// and that new numbers have to be generated.
float last_randnormal = std::numeric_limits<float>::infinity();
}
namespace love
{
float random_normal(float o)
{
// number in cache?
if (last_randnormal != std::numeric_limits<float>::infinity())
{
float r = last_randnormal;
last_randnormal = std::numeric_limits<float>::infinity();
return r * o;
}
// else: generate numbers using the Box-Muller transform
float a = sqrt(-2.0f * log(random()));
float b = float(LOVE_M_PI) * 2.0f * random();
last_randnormal = a * cos(b);
return a * sin(b) * o;
}
} // namespace love
+37
View File
@@ -22,6 +22,7 @@
#define LOVE_MATH_H
#include <climits> // for CHAR_BIT
#include <cstdlib> // for rand() and RAND_MAX
/* Definitions of useful mathematical constants
* M_E - e
@@ -80,6 +81,42 @@ inline float next_p2(float x)
return static_cast<float>(next_p2(static_cast<int>(x)));
}
/**
* Draws a random number from a uniform distribution.
* @returns Uniformly distributed random number in [0:1).
*/
inline float random()
{
// to satisfy picky compilers...
return float(double(rand() % RAND_MAX) / double(RAND_MAX));
}
/**
* Draws a random number from a uniform distribution.
* @return Uniformly distributed random number in [0:max).
*/
inline float random(float max)
{
return random() * max;
}
/**
* Draws a random number from a uniform distribution.
* @return Uniformly distributed random number in [min:max).
*/
inline float random(float min, float max)
{
return random(max - min) + min;
}
/**
* Draws a random number from a normal/gaussian distribution.
* @param o Standard deviation of the distribution.
* @returns Normal distributed random number with mean 0 and variance o^2.
*/
float random_normal(float o = 1.);
#define random_gaussian random_normal
} // love
#endif // LOVE_MATH_H
+3
View File
@@ -196,6 +196,9 @@ int luax_register_module(lua_State *L, const WrappedModule &m)
lua_setfield(L, -3, m.name); // love.graphics = table
lua_remove(L, -2); // love
// Register module instance
Module::registerInstance(m.module);
return 1;
}
+16 -14
View File
@@ -91,22 +91,24 @@ Message *Event::convert(SDL_Event &e)
switch (e.type)
{
case SDL_KEYDOWN:
if (keys.find(e.key.keysym.sym, key) && love::event::Event::keys.find(key, txt))
{
arg1 = new Variant(txt, strlen(txt));
arg2 = new Variant((double) e.key.keysym.unicode);
msg = new Message("keypressed", arg1, arg2);
arg1->release();
arg2->release();
}
if (!keys.find(e.key.keysym.sym, key))
key = love::keyboard::Keyboard::KEY_UNKNOWN;
if (!love::event::Event::keys.find(key, txt))
txt = "unknown";
arg1 = new Variant(txt, strlen(txt));
arg2 = new Variant((double) e.key.keysym.unicode);
msg = new Message("keypressed", arg1, arg2);
arg1->release();
arg2->release();
break;
case SDL_KEYUP:
if (keys.find(e.key.keysym.sym, key) && love::event::Event::keys.find(key, txt))
{
arg1 = new Variant(txt, strlen(txt));
msg = new Message("keyreleased", arg1);
arg1->release();
}
if (!keys.find(e.key.keysym.sym, key))
key = love::keyboard::Keyboard::KEY_UNKNOWN;
if (!love::event::Event::keys.find(key, txt))
txt = "unknown";
arg1 = new Variant(txt, strlen(txt));
msg = new Message("keyreleased", arg1);
arg1->release();
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
+2 -1
View File
@@ -29,6 +29,7 @@
#include "common/Object.h"
#include "common/StringMap.h"
#include "common/int.h"
#include "FileData.h"
namespace love
{
@@ -93,7 +94,7 @@ public:
* @param size The number of bytes to attempt reading, or -1 for EOF.
* @return A newly allocated Data object.
**/
virtual Data *read(int64 size = ALL) = 0;
virtual FileData *read(int64 size = ALL) = 0;
/**
* Reads data into the destination buffer.
+1 -1
View File
@@ -111,7 +111,7 @@ int64 File::getSize()
}
Data *File::read(int64 size)
FileData *File::read(int64 size)
{
bool isOpen = (file != 0);
+1 -1
View File
@@ -69,7 +69,7 @@ public:
bool open(Mode mode);
bool close();
int64 getSize();
Data *read(int64 size = ALL);
FileData *read(int64 size = ALL);
int64 read(void *dst, int64 size);
bool write(const void *data, int64 size);
bool write(const Data *data, int64 size = ALL);
+10
View File
@@ -131,6 +131,16 @@ void ImageRasterizer::load()
widths[c] = (end - start);
}
// Find spacing of last glyph
if (length > 0)
{
start = end;
while (start < imgw && equal(pixels[start], spacer))
++start;
spacing[glyphs[length - 1]] = (start > end) ? (start - end) : 0;
}
// Replace spacer color with an empty pixel
for (unsigned int i = 0; i < imgs; ++i)
{
+1
View File
@@ -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 },
+1
View File
@@ -85,6 +85,7 @@ public:
enum Support
{
SUPPORT_CANVAS = 1,
SUPPORT_HDR_CANVAS,
SUPPORT_PIXELEFFECT,
SUPPORT_NPOT,
SUPPORT_SUBTRACTIVE,
-5
View File
@@ -50,11 +50,6 @@ public:
virtual void flip(bool x, bool y) = 0;
/**
* Mirror texture coordinates around 0.5
*/
virtual void mirror(bool x, bool y) = 0;
/**
* Gets a pointer to the vertices.
**/
+148 -39
View File
@@ -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;
@@ -75,27 +76,41 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, depth_stencil);
GL_RENDERBUFFER, depth_stencil);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depth_stencil);
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);
GL_TEXTURE_2D, img, 0);
// check status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
// unbind framebuffer
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)current_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
return status;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
@@ -111,10 +126,9 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
}
};
struct FramebufferStrategyEXT : 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, &current_fbo);
@@ -126,29 +140,45 @@ struct FramebufferStrategyEXT : public FramebufferStrategy
// create stencil buffer
glGenRenderbuffersEXT(1, &depth_stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, width, height);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, depth_stencil);
GL_RENDERBUFFER_EXT, depth_stencil);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, depth_stencil);
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);
GL_TEXTURE_2D, img, 0);
// check status
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
// unbind framebuffer
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint)current_fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) current_fbo);
return status;
}
@@ -165,45 +195,112 @@ struct FramebufferStrategyEXT : public FramebufferStrategy
}
};
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
{
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, &current_fbo);
// create framebuffer
glGenFramebuffersEXT(1, &framebuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
// create stencil buffer
glGenRenderbuffersEXT(1, &stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
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, internalFormat, width, height,
0, GL_RGBA, format, NULL);
bindTexture(0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, img, 0);
// check status
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
// unbind framebuffer
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) current_fbo);
return status;
}
bool isSupported()
{
GLuint fb, stencil, img;
GLenum status = createFBO(fb, stencil, img, 2, 2, Canvas::TYPE_NORMAL);
deleteFBO(fb, stencil, img);
return status == GL_FRAMEBUFFER_COMPLETE;
}
};
FramebufferStrategy *strategy = NULL;
FramebufferStrategy strategyNone;
FramebufferStrategyGL3 strategyGL3;
FramebufferStrategyPackedEXT strategyPackedEXT;
FramebufferStrategyEXT strategyEXT;
Canvas *Canvas::current = NULL;
static void loadStrategy()
static void getStrategy()
{
if (!strategy)
{
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
strategy = &strategyGL3;
else if (GLEE_EXT_framebuffer_object && GLEE_EXT_packed_depth_stencil)
strategy = &strategyPackedEXT;
else if (GLEE_EXT_framebuffer_object && strategyEXT.isSupported())
strategy = &strategyEXT;
else
strategy = &strategyNone;
}
}
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);
// world coordinates
vertices[0].x = 0;
vertices[0].y = 0;
vertices[0].y = h;
vertices[1].x = 0;
vertices[1].y = h;
vertices[1].y = 0;
vertices[2].x = w;
vertices[2].y = h;
vertices[2].y = 0;
vertices[3].x = w;
vertices[3].y = 0;
vertices[3].y = h;
// texture coordinates
vertices[0].s = 0;
@@ -215,7 +312,7 @@ Canvas::Canvas(int width, int height)
vertices[3].s = 1;
vertices[3].t = 1;
loadStrategy();
getStrategy();
loadVolatile();
}
@@ -231,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)
@@ -262,7 +364,7 @@ void Canvas::startGrab()
glLoadIdentity();
// Set up orthographic view (no depth)
glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
glOrtho(0.0, width, 0.0, height, -1.0, 1.0);
// Switch back to modelview matrix
glMatrixMode(GL_MODELVIEW);
@@ -312,12 +414,10 @@ void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, f
void Canvas::drawq(love::graphics::Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
static Matrix t;
quad->mirror(false, true);
const vertex *v = quad->getVertices();
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
drawv(t, v);
quad->mirror(false, true);
}
love::image::ImageData *Canvas::getImageData(love::image::Image *image)
@@ -325,7 +425,6 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
int row = 4 * width;
int size = row * height;
GLubyte *pixels = new GLubyte[size];
GLubyte *screenshot = new GLubyte[size];
strategy->bindFBO(fbo);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
@@ -334,15 +433,8 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
else
strategy->bindFBO(0);
GLubyte *src = pixels - row; // second line of source image
GLubyte *dst = screenshot + size; // last row of destination image
love::image::ImageData *img = image->newImageData(width, height, (void *)pixels);
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;
@@ -399,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;
@@ -447,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
+31 -12
View File
@@ -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
+10 -4
View File
@@ -400,9 +400,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)
@@ -543,7 +549,7 @@ void Graphics::setBlendMode(Graphics::BlendMode mode)
if (mode == BLEND_ALPHA)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
else if (mode == BLEND_MULTIPLICATIVE)
glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_DST_COLOR, GL_ZERO);
else if (mode == BLEND_PREMULTIPLIED)
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
else // mode == BLEND_ADDITIVE || mode == BLEND_SUBTRACTIVE
@@ -582,7 +588,7 @@ Graphics::BlendMode Graphics::getBlendMode()
return BLEND_ADDITIVE;
else if (src == GL_SRC_ALPHA && dst == GL_ONE_MINUS_SRC_ALPHA) // && equation == GL_FUNC_ADD
return BLEND_ALPHA;
else if (src == GL_DST_COLOR && dst == GL_ONE_MINUS_SRC_ALPHA) // && equation == GL_FUNC_ADD
else if (src == GL_DST_COLOR && dst == GL_ZERO) // && equation == GL_FUNC_ADD
return BLEND_MULTIPLICATIVE;
else if (src == GL_ONE && dst == GL_ONE_MINUS_SRC_ALPHA) // && equation == GL_FUNC_ADD
return BLEND_PREMULTIPLIED;
+1 -1
View File
@@ -267,7 +267,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);
+61 -10
View File
@@ -45,10 +45,18 @@ float calculate_variation(float inner, float outer, float var)
{
float low = inner - (outer/2.0f)*var;
float high = inner + (outer/2.0f)*var;
float r = (rand() / (float(RAND_MAX)+1));
float r = random();
return low*(1-r)+high*r;
}
StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM>::Entry ParticleSystem::distributionsEntries[] = {
{ "none", ParticleSystem::DISTRIBUTION_NONE },
{ "uniform", ParticleSystem::DISTRIBUTION_UNIFORM },
{ "normal", ParticleSystem::DISTRIBUTION_NORMAL },
};
StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM> ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries));
ParticleSystem::ParticleSystem(Image *sprite, unsigned int buffer)
: pStart(0)
@@ -57,6 +65,7 @@ ParticleSystem::ParticleSystem(Image *sprite, unsigned int buffer)
, active(true)
, emissionRate(0)
, emitCounter(0)
, areaSpreadDistribution(DISTRIBUTION_NONE)
, lifetime(-1)
, life(0)
, particleLifeMin(0)
@@ -108,45 +117,60 @@ void ParticleSystem::add()
if (min == max)
pLast->life = min;
else
pLast->life = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->life = random(min, max);
pLast->lifetime = pLast->life;
pLast->position[0] = position.getX();
pLast->position[1] = position.getY();
switch (areaSpreadDistribution)
{
case DISTRIBUTION_UNIFORM:
pLast->position[0] += random(-areaSpread.getX(), areaSpread.getX());
pLast->position[1] += random(-areaSpread.getY(), areaSpread.getY());
break;
case DISTRIBUTION_NORMAL:
pLast->position[0] += random_normal(areaSpread.getX());
pLast->position[1] += random_normal(areaSpread.getY());
break;
case DISTRIBUTION_NONE:
default:
break;
}
min = direction - spread/2.0f;
max = direction + spread/2.0f;
pLast->direction = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->direction = random(min, max);
pLast->origin = position;
min = speedMin;
max = speedMax;
float speed = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
float speed = random(min, max);
pLast->speed = love::Vector(cos(pLast->direction), sin(pLast->direction));
pLast->speed *= speed;
min = gravityMin;
max = gravityMax;
pLast->gravity = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->gravity = random(min, max);
min = radialAccelerationMin;
max = radialAccelerationMax;
pLast->radialAcceleration = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->radialAcceleration = random(min, max);
min = tangentialAccelerationMin;
max = tangentialAccelerationMax;
pLast->tangentialAcceleration = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;
pLast->tangentialAcceleration = random(min, max);
pLast->sizeOffset = (rand() / (float(RAND_MAX)+1)) * sizeVariation; // time offset for size change
pLast->sizeIntervalSize = (1.0f - (rand() / (float(RAND_MAX)+1)) * sizeVariation) - pLast->sizeOffset;
pLast->sizeOffset = random(sizeVariation); // time offset for size change
pLast->sizeIntervalSize = (1.0f - random(sizeVariation)) - pLast->sizeOffset;
pLast->size = sizes[(size_t)(pLast->sizeOffset - .5f) * (sizes.size() - 1)];
min = rotationMin;
max = rotationMax;
pLast->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
pLast->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
pLast->rotation = (rand() / (float(RAND_MAX)+1)) * (max - min) + min;;
pLast->rotation = random(min, max);
pLast->color = colors[0];
@@ -204,6 +228,13 @@ void ParticleSystem::setPosition(float x, float y)
position = love::Vector(x, y);
}
void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y)
{
areaSpread = love::Vector(x, y);
areaSpreadDistribution = distribution;
}
void ParticleSystem::setDirection(float direction)
{
this->direction = direction;
@@ -349,6 +380,16 @@ const love::Vector &ParticleSystem::getPosition() const
return position;
}
ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const
{
return areaSpreadDistribution;
}
const love::Vector &ParticleSystem::getAreaSpreadParameters() const
{
return areaSpread;
}
float ParticleSystem::getDirection() const
{
return direction;
@@ -543,6 +584,16 @@ void ParticleSystem::update(float dt)
} // while
}
bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
{
return distributions.find(in, out);
}
bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out)
{
return distributions.find(in, out);
}
} // opengl
} // graphics
} // love
@@ -72,6 +72,16 @@ struct particle
class ParticleSystem : public Drawable
{
public:
/**
* Type of distribution new particles are drawn from: None, uniform, normal.
*/
enum AreaSpreadDistribution
{
DISTRIBUTION_NONE,
DISTRIBUTION_UNIFORM,
DISTRIBUTION_NORMAL,
DISTRIBUTION_MAX_ENUM,
};
/**
* Creates a particle system with the specified buffersize and sprite.
@@ -122,6 +132,19 @@ public:
**/
void setPosition(float x, float y);
/**
* Sets the emission area spread parameters and distribution type. The interpretation of
* the parameters depends on the distribution type:
*
* * None: Parameters are ignored. No area spread.
* * Uniform: Parameters denote maximal (symmetric) displacement from emitter position.
* * Normal: Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated.
* @param x First parameter. Interpretation depends on distribution type.
* @param y Second parameter. Interpretation depends on distribution type.
* @param distribution Distribution type
* */
void setAreaSpread(AreaSpreadDistribution distribution, float x, float y);
/**
* Sets the direction and the spread of the particle emitter.
* @param direction The direction (in degrees).
@@ -285,6 +308,16 @@ public:
**/
const love::Vector &getPosition() const;
/**
* Returns area spread distribution type.
*/
AreaSpreadDistribution getAreaSpreadDistribution() const;
/**
* Returns area spread parameters.
*/
const love::Vector &getAreaSpreadParameters() const;
/**
* Returns the direction of the emitter (in degrees).
**/
@@ -357,6 +390,9 @@ public:
* @param dt Time since last update.
**/
void update(float dt);
static bool getConstant(const char *in, AreaSpreadDistribution &out);
static bool getConstant(AreaSpreadDistribution in, const char *&out);
protected:
// The max amount of particles.
@@ -386,6 +422,10 @@ protected:
// The relative position of the particle emitter.
love::Vector position;
// Emission area spread.
AreaSpreadDistribution areaSpreadDistribution;
love::Vector areaSpread;
// The lifetime of the particle emitter (-1 means infinite) and the life it has left.
float lifetime;
float life;
@@ -439,6 +479,9 @@ protected:
void add();
void remove(particle *p);
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
};
} // opengl
+9 -6
View File
@@ -20,6 +20,7 @@
#include "PixelEffect.h"
#include "GLee.h"
#include "Graphics.h"
namespace
{
@@ -134,6 +135,7 @@ bool PixelEffect::loadVolatile()
}
glDeleteShader(shader);
return true;
}
@@ -149,24 +151,25 @@ void PixelEffect::unloadVolatile()
std::string PixelEffect::getGLSLVersion()
{
// GL_SHADING_LANGUAGE_VERSION is not available in OpenGL < 2.1.
// Be very pessimistic about the GLSL version in that case.
if (!GLEE_VERSION_2_1)
// GL_SHADING_LANGUAGE_VERSION may not be available in OpenGL < 2.0.
const char *tmp = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
if (NULL == tmp)
return "0.0";
// the version string always begins with a version number of the format
// major_number.minor_number
// or
// major_number.minor_number.release_number
std::string versionString((const char *)glGetString(GL_SHADING_LANGUAGE_VERSION));
size_t minorEndPos = versionString.find(" ");
// we can keep release_number, since it does not affect the check below.
std::string versionString(tmp);
size_t minorEndPos = versionString.find(' ');
return versionString.substr(0, minorEndPos);
}
bool PixelEffect::isSupported()
{
return GLEE_VERSION_2_0 && GLEE_ARB_shader_objects && GLEE_ARB_fragment_shader && getGLSLVersion() >= "1.2";
return GLEE_VERSION_2_0 && getGLSLVersion() >= "1.2";
}
std::string PixelEffect::getWarnings() const
-11
View File
@@ -113,17 +113,6 @@ void Quad::flip(bool x, bool y)
}
}
void Quad::mirror(bool x, bool y)
{
for (size_t i = 0; i < NUM_VERTICES; ++i)
{
if (x)
vertices[i].s = 1.0f - vertices[i].s;
if (y)
vertices[i].t = 1.0f - vertices[i].t;
}
}
const vertex *Quad::getVertices() const
{
return vertices;
-1
View File
@@ -54,7 +54,6 @@ public:
Viewport getViewport() const;
void flip(bool x, bool y);
void mirror(bool x, bool y);
/**
* Gets a pointer to the vertices.
@@ -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
+19 -7
View File
@@ -361,6 +361,9 @@ int w_newImageFont(lua_State *L)
Image::Filter img_filter;
bool setFilter = false;
// For the filter modes..
int startIndex = 2;
// Convert to ImageData if necessary.
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || (luax_istype(L, 1, DATA_T) && !luax_istype(L, 1, IMAGE_IMAGE_DATA_T)))
luax_convobj(L, 1, "image", "newImageData");
@@ -379,16 +382,17 @@ int w_newImageFont(lua_State *L)
{
int idxs[] = {1, 2};
luax_convobj(L, idxs, 2, "font", "newRasterizer");
startIndex = 3; // There's a glyphs args in there, move up
}
love::font::Rasterizer *rasterizer = luax_checktype<love::font::Rasterizer>(L, 1, "Rasterizer", FONT_RASTERIZER_T);
if (lua_isstring(L, 2) && lua_isstring(L, 3))
if (lua_isstring(L, startIndex) && lua_isstring(L, startIndex+1))
{
Image::FilterMode min;
Image::FilterMode mag;
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_checkstring(L, 3);
const char *minstr = luaL_checkstring(L, startIndex);
const char *magstr = luaL_checkstring(L, startIndex+1);
if (!Image::getConstant(minstr, min))
return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, mag))
@@ -449,14 +453,18 @@ 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;
if (!Canvas::getConstant(str, texture_type))
return luaL_error(L, "Invalid canvas type: %s", str);
Canvas *canvas = NULL;
try
{
canvas = instance->newCanvas(width, height);
canvas = instance->newCanvas(width, height, texture_type);
}
catch(Exception &e)
{
@@ -874,6 +882,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;
@@ -86,6 +86,24 @@ int w_ParticleSystem_setPosition(lua_State *L)
return 0;
}
int w_ParticleSystem_setAreaSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::AreaSpreadDistribution distribution;
const char *str = luaL_checkstring(L, 2);
if (!ParticleSystem::getConstant(str, distribution))
return luaL_error(L, "Invalid distribution: '%s'", str);
float x = (float)luaL_checknumber(L, 3);
float y = (float)luaL_checknumber(L, 4);
if (x < 0.0f || y < 0.0f)
return luaL_error(L, "Invalid area spread parameters (must be >= 0)");
t->setAreaSpread(distribution, x, y);
return 0;
}
int w_ParticleSystem_setDirection(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
@@ -273,6 +291,21 @@ int w_ParticleSystem_getPosition(lua_State *L)
return 2;
}
int w_ParticleSystem_getAreaSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::AreaSpreadDistribution distribution = t-> getAreaSpreadDistribution();
const char *str;
ParticleSystem::getConstant(distribution, str);
const love::Vector &p = t->getAreaSpreadParameters();
lua_pushstring(L, str);
lua_pushnumber(L, p.x);
lua_pushnumber(L, p.y);
return 3;
}
int w_ParticleSystem_getDirection(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
@@ -373,6 +406,7 @@ static const luaL_Reg functions[] =
{ "setLifetime", w_ParticleSystem_setLifetime },
{ "setParticleLife", w_ParticleSystem_setParticleLife },
{ "setPosition", w_ParticleSystem_setPosition },
{ "setAreaSpread", w_ParticleSystem_setAreaSpread },
{ "setDirection", w_ParticleSystem_setDirection },
{ "setSpread", w_ParticleSystem_setSpread },
{ "setRelativeDirection", w_ParticleSystem_setRelativeDirection },
@@ -390,6 +424,7 @@ static const luaL_Reg functions[] =
{ "getX", w_ParticleSystem_getX },
{ "getY", w_ParticleSystem_getY },
{ "getPosition", w_ParticleSystem_getPosition },
{ "getAreaSpread", w_ParticleSystem_getAreaSpread },
{ "getDirection", w_ParticleSystem_getDirection },
{ "getSpread", w_ParticleSystem_getSpread },
{ "getOffsetX", w_ParticleSystem_getOffsetX },
@@ -143,19 +143,38 @@ int w_SpriteBatch_getImage(lua_State *L)
int w_SpriteBatch_setColor(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
Color c;
if (lua_gettop(L) <= 1)
{
t->setColor();
return 0;
}
else if (lua_istable(L, 2))
{
lua_rawgeti(L, 2, 1);
c.r = (unsigned char) luaL_checkint(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, 2, 2);
c.g = (unsigned char) luaL_checkint(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, 2, 3);
c.b = (unsigned char) luaL_checkint(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, 2, 4);
c.a = (unsigned char) luaL_optint(L, -1, 255);
lua_pop(L, 1);
}
else
{
Color c;
c.r = (unsigned char)luaL_checkint(L, 2);
c.g = (unsigned char)luaL_checkint(L, 3);
c.b = (unsigned char)luaL_checkint(L, 4);
c.a = (unsigned char)luaL_optint(L, 5, 255);
t->setColor(c);
}
t->setColor(c);
return 0;
}
+15 -1
View File
@@ -39,7 +39,21 @@ Contact::Contact(b2Contact *contact)
Contact::~Contact()
{
Memoizer::remove(contact);
invalidate();
}
void Contact::invalidate()
{
if (contact != NULL)
{
Memoizer::remove(contact);
contact = NULL;
}
}
bool Contact::isValid()
{
return contact != NULL ? true : false;
}
int Contact::getPositions(lua_State *L)
+12
View File
@@ -59,6 +59,18 @@ public:
virtual ~Contact();
/**
* Removes the b2Contact pointer from Memoizer and sets it
* to null on the Contact.
**/
void invalidate();
/**
* Returns if the Contact still points to a valid b2Contact.
* @return True if the contact is still valid or false if it has been destroyed.
**/
bool isValid();
/**
* Gets the position of each point of contact.
* @return The position along the x-axis.
+25 -14
View File
@@ -77,9 +77,13 @@ void World::ContactCallback::process(b2Contact *contact, const b2ContactImpulse
throw love::Exception("A fixture has escaped Memoizer!");
}
Contact *c = new Contact(contact);
Contact *cobj = (Contact *)Memoizer::find(contact);
if (!cobj)
cobj = new Contact(contact);
else
cobj->retain();
luax_newtype(L, "Contact", (PHYSICS_CONTACT_T), (void *)c);
luax_newtype(L, "Contact", (PHYSICS_CONTACT_T), (void *)cobj);
int args = 3;
if (impulse)
@@ -117,12 +121,12 @@ bool World::ContactFilter::process(Fixture *a, Fixture *b)
a->getFilterData(filterA);
b->getFilterData(filterB);
if (filterA[2] != 0 && // 0 is the default group, so this does not count
filterA[2] == filterB[2]) // if they are in the same group
if (filterA[2] != 0 && // 0 is the default group, so this does not count
filterA[2] == filterB[2]) // if they are in the same group
return filterA[2] > 0; // Negative indexes mean you don't collide
if ((filterA[1] & filterB[0]) == 0 ||
(filterB[1] & filterA[0]) == 0)
(filterB[1] & filterA[0]) == 0)
return false; // A and B aren't set to collide
if (ref != 0)
@@ -290,6 +294,11 @@ void World::BeginContact(b2Contact *contact)
void World::EndContact(b2Contact *contact)
{
end.process(contact);
// Letting the Contact know that the b2Contact will be destroyed any second.
Contact *c = (Contact *)Memoizer::find(contact);
if (c != NULL)
c->invalidate();
}
void World::PreSolve(b2Contact *contact, const b2Manifold *oldManifold)
@@ -332,19 +341,19 @@ int World::setCallbacks(lua_State *L)
case 4:
if (postsolve.ref)
delete postsolve.ref;
postsolve.ref = luax_refif (L, LUA_TFUNCTION);
postsolve.ref = luax_refif(L, LUA_TFUNCTION);
case 3:
if (presolve.ref)
delete presolve.ref;
presolve.ref = luax_refif (L, LUA_TFUNCTION);
presolve.ref = luax_refif(L, LUA_TFUNCTION);
case 2:
if (end.ref)
delete end.ref;
end.ref = luax_refif (L, LUA_TFUNCTION);
end.ref = luax_refif(L, LUA_TFUNCTION);
case 1:
if (begin.ref)
delete begin.ref;
begin.ref = luax_refif (L, LUA_TFUNCTION);
begin.ref = luax_refif(L, LUA_TFUNCTION);
}
return 0;
@@ -364,7 +373,7 @@ int World::setContactFilter(lua_State *L)
luax_assert_argc(L, 1);
if (filter.ref)
delete filter.ref;
filter.ref = luax_refif (L, LUA_TFUNCTION);
filter.ref = luax_refif(L, LUA_TFUNCTION);
return 0;
}
@@ -468,8 +477,10 @@ int World::getContactList(lua_State *L) const
{
if (!c) break;
Contact *contact = (Contact *)Memoizer::find(c);
if (!contact) throw love::Exception("A contact has escaped Memoizer!");
contact->retain();
if (!contact)
contact = new Contact(c);
else
contact->retain();
luax_newtype(L, "Contact", PHYSICS_CONTACT_T, (void *)contact);
lua_rawseti(L, -2, i);
i++;
@@ -494,7 +505,7 @@ int World::queryBoundingBox(lua_State *L)
box.lowerBound = Physics::scaleDown(b2Vec2(lx, ly));
box.upperBound = Physics::scaleDown(b2Vec2(ux, uy));
if (query.ref) delete query.ref;
query.ref = luax_refif (L, LUA_TFUNCTION);
query.ref = luax_refif(L, LUA_TFUNCTION);
world->QueryAABB(&query, box);
return 0;
}
@@ -510,7 +521,7 @@ int World::rayCast(lua_State *L)
b2Vec2 v2 = Physics::scaleDown(b2Vec2(x2, y2));
if (raycast.ref)
delete raycast.ref;
raycast.ref = luax_refif (L, LUA_TFUNCTION);
raycast.ref = luax_refif(L, LUA_TFUNCTION);
world->RayCast(&raycast, v1, v2);
return 0;
}
+4 -1
View File
@@ -29,7 +29,10 @@ namespace box2d
Contact *luax_checkcontact(lua_State *L, int idx)
{
return luax_checktype<Contact>(L, idx, "Contact", PHYSICS_CONTACT_T);
Contact *c = luax_checktype<Contact>(L, idx, "Contact", PHYSICS_CONTACT_T);
if (!c->isValid())
luaL_error(L, "Attempt to use destroyed contact.");
return c;
}
int w_Contact_getPositions(lua_State *L)
+1 -1
View File
@@ -39,7 +39,7 @@ int w_World_update(lua_State *L)
{
World *t = luax_checkworld(L, 1);
float dt = (float)luaL_checknumber(L, 2);
t->update(dt);
ASSERT_GUARD(t->update(dt);)
return 0;
}
+2 -2
View File
@@ -74,11 +74,11 @@ public:
* @param sampleRate Samples per second, or quality of the audio. 44100 is a good value.
* @return A Decoder object on success, or zero if no decoder could be found.
**/
virtual Decoder *newDecoder(filesystem::File *file, int bufferSize) = 0;
virtual Decoder *newDecoder(filesystem::FileData *file, int bufferSize) = 0;
}; // Sound
} // sound
} // love
#endif // LOVE_SOUND_SOUND_H
#endif // LOVE_SOUND_SOUND_H
+12 -4
View File
@@ -46,11 +46,19 @@ GmeDecoder::GmeDecoder(Data *data, const std::string &ext, int bufferSize)
num_tracks = gme_track_count(emu);
if (num_tracks <= 0)
throw love::Exception("Game music file has no tracks");
try
{
if (num_tracks <= 0)
throw love::Exception("Game music file has no tracks");
if (gme_start_track(emu, cur_track) != 0)
throw love::Exception("Could not start game music playback");
if (gme_start_track(emu, cur_track) != 0)
throw love::Exception("Could not start game music playback");
}
catch (love::Exception &)
{
gme_delete(emu);
throw;
}
}
GmeDecoder::~GmeDecoder()
+4
View File
@@ -27,7 +27,11 @@
#include "common/Data.h"
#include "Decoder.h"
#ifdef LOVE_MACOSX
#include <Game_Music_Emu/gme.h>
#else
#include <gme.h>
#endif
namespace love
{
+2 -5
View File
@@ -51,10 +51,9 @@ const char *Sound::getName() const
return "love.sound.lullaby";
}
sound::Decoder *Sound::newDecoder(love::filesystem::File *file, int bufferSize)
sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSize)
{
Data *data = file->read();
std::string ext = file->getExtension();
std::string ext = data->getExtension();
sound::Decoder *decoder = 0;
@@ -76,8 +75,6 @@ sound::Decoder *Sound::newDecoder(love::filesystem::File *file, int bufferSize)
// else if (OtherDecoder::accept(ext))
data->release();
return decoder;
}
+2 -2
View File
@@ -59,7 +59,7 @@ public:
const char *getName() const;
/// @copydoc love::sound::Sound::newDecoder
sound::Decoder *newDecoder(love::filesystem::File *file, int bufferSize);
sound::Decoder *newDecoder(love::filesystem::FileData *file, int bufferSize);
}; // Sound
@@ -67,4 +67,4 @@ public:
} // sound
} // love
#endif // LOVE_SOUND_LULLABY_SOUND_H
#endif // LOVE_SOUND_LULLABY_SOUND_H
+16 -3
View File
@@ -83,18 +83,31 @@ int w_newDecoder(lua_State *L)
if (lua_isstring(L, 1))
luax_convobj(L, 1, "filesystem", "newFile");
love::filesystem::File *file = luax_checktype<love::filesystem::File>(L, 1, "File", FILESYSTEM_FILE_T);
love::filesystem::FileData *data;
if (luax_istype(L, 1, FILESYSTEM_FILE_T))
{
love::filesystem::File *file = luax_checktype<love::filesystem::File>(L, 1, "File", FILESYSTEM_FILE_T);
data = file->read();
}
else
{
data = luax_checktype<love::filesystem::FileData>(L, 1, "FileData", FILESYSTEM_FILE_DATA_T);
data->retain();
}
int bufferSize = luaL_optint(L, 2, Decoder::DEFAULT_BUFFER_SIZE);
try
{
Decoder *t = instance->newDecoder(file, bufferSize);
Decoder *t = instance->newDecoder(data, bufferSize);
data->release();
if (t == 0)
return luaL_error(L, "Extension \"%s\" not supported.", file->getExtension().c_str());
return luaL_error(L, "Extension \"%s\" not supported.", data->getExtension().c_str());
luax_newtype(L, "Decoder", SOUND_DECODER_T, (void *)t);
}
catch(love::Exception &e)
{
data->release();
return luaL_error(L, e.what());
}
+1 -1
View File
@@ -430,8 +430,8 @@ function love.run()
if love.draw then love.draw() end
end
if love.timer then love.timer.sleep(0.001) end
if love.graphics then love.graphics.present() end
if love.timer then love.timer.sleep(0.001) end
end
+3 -3
View File
@@ -737,12 +737,12 @@ const unsigned char boot_lua[] =
0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x73, 0x6c, 0x65, 0x65,
0x70, 0x28, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63,
0x73, 0x2e, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x73, 0x6c, 0x65, 0x65,
0x70, 0x28, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a,
0x65, 0x6e, 0x64, 0x0a,
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
+15 -18
View File
@@ -1284,24 +1284,20 @@ do
end
-- PIXEL EFFECTS
local GLSL_HEADER_LINE_COUNT = 6
local GLSL_HEADER = [[#version 120
#define number float
#define Image sampler2D
#define extern uniform
#define Texel texture2D
uniform sampler2D _tex0_;]]
local GLSL_FOOTER = [[void main() {
// fix weird crashing issue in OSX when _tex0_ is unused within effect()
float dummy = texture2D(_tex0_, vec2(.5)).r;
gl_FragColor = effect(gl_Color, _tex0_, gl_TexCoord[0].xy, gl_FragCoord.xy);
}]]
function love.graphics._effectCodeToGLSL(code)
local header = [[#version 120
#define number float
#define Image sampler2D
#define extern uniform
#define Texel texture2D
uniform sampler2D _tex0_;]]
local footer = [[void main() {
// fix weird crashing issue in OSX when _tex0_ is unused within effect()
float dummy = texture2D(_tex0_, vec2(.5)).r;
gl_FragColor = effect(gl_Color, _tex0_, gl_TexCoord[0].xy, gl_FragCoord.xy);
}]]
local function include(quoted_path) return (love.filesystem.read(quoted_path:sub(2,-2))) end
code = code:gsub("#include (%b'')", include)
code = code:gsub('#include (%b"")', include)
return table.concat{header, "\n", code, footer}
return table.concat{GLSL_HEADER, "\n", code, GLSL_FOOTER}
end
function love.graphics._transformGLSLErrorMessages(message)
@@ -1317,7 +1313,8 @@ do
linenumber, what, message = l:match("^%w+: 0:(%d+):%s*(%w+)%([^%)]+%)%s*(.+)$")
end
if linenumber and what and message then
lines[#lines+1] = ("Line %d: %s: %s"):format(linenumber - 5, what, message)
linenumber = linenumber - GLSL_HEADER_LINE_COUNT
lines[#lines+1] = ("Line %d: %s: %s"):format(linenumber, what, message)
end
end
-- did not match any known error messages
+38 -44
View File
@@ -6260,52 +6260,43 @@ const unsigned char graphics_lua[] =
0x69, 0x6e, 0x74, 0x66, 0x28, 0x2e, 0x2e, 0x2e, 0x29, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x2d, 0x2d, 0x20, 0x50, 0x49, 0x58, 0x45, 0x4c, 0x20, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x53, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52,
0x5f, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x20, 0x3d, 0x20, 0x36, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52,
0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x23, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x32, 0x30, 0x0a,
0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x6c,
0x6f, 0x61, 0x74, 0x0a,
0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x20, 0x73, 0x61, 0x6d,
0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x0a,
0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x75, 0x6e,
0x69, 0x66, 0x6f, 0x72, 0x6d, 0x0a,
0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x54, 0x65, 0x78, 0x65, 0x6c, 0x20, 0x74, 0x65, 0x78,
0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x0a,
0x09, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44,
0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x3b, 0x5d, 0x5d, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52,
0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a,
0x09, 0x09, 0x2f, 0x2f, 0x20, 0x66, 0x69, 0x78, 0x20, 0x77, 0x65, 0x69, 0x72, 0x64, 0x20, 0x63, 0x72, 0x61,
0x73, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x4f, 0x53, 0x58,
0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x20, 0x69, 0x73, 0x20, 0x75, 0x6e,
0x75, 0x73, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74,
0x28, 0x29, 0x0a,
0x09, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x65,
0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x28, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x76, 0x65,
0x63, 0x32, 0x28, 0x2e, 0x35, 0x29, 0x29, 0x2e, 0x72, 0x3b, 0x0a,
0x09, 0x09, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x65,
0x66, 0x66, 0x65, 0x63, 0x74, 0x28, 0x67, 0x6c, 0x5f, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x5f, 0x74,
0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x67, 0x6c, 0x5f, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x5b,
0x30, 0x5d, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72,
0x64, 0x2e, 0x78, 0x79, 0x29, 0x3b, 0x0a,
0x09, 0x7d, 0x5d, 0x5d, 0x0a,
0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61,
0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x5f, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x54,
0x6f, 0x47, 0x4c, 0x53, 0x4c, 0x28, 0x63, 0x6f, 0x64, 0x65, 0x29, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x5b,
0x5b, 0x23, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x32, 0x30, 0x0a,
0x09, 0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66,
0x6c, 0x6f, 0x61, 0x74, 0x0a,
0x09, 0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x20, 0x73, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x0a,
0x09, 0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x75,
0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x0a,
0x09, 0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x54, 0x65, 0x78, 0x65, 0x6c, 0x20, 0x74, 0x65,
0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x0a,
0x09, 0x09, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32,
0x44, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x3b, 0x5d, 0x5d, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x5b,
0x5b, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a,
0x09, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x66, 0x69, 0x78, 0x20, 0x77, 0x65, 0x69, 0x72, 0x64, 0x20, 0x63, 0x72,
0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x4f, 0x53,
0x58, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x20, 0x69, 0x73, 0x20, 0x75,
0x6e, 0x75, 0x73, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x65, 0x66, 0x66, 0x65, 0x63,
0x74, 0x28, 0x29, 0x0a,
0x09, 0x09, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x3d, 0x20, 0x74,
0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x28, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x76,
0x65, 0x63, 0x32, 0x28, 0x2e, 0x35, 0x29, 0x29, 0x2e, 0x72, 0x3b, 0x0a,
0x09, 0x09, 0x09, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20,
0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x28, 0x67, 0x6c, 0x5f, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x5f,
0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x67, 0x6c, 0x5f, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64,
0x5b, 0x30, 0x5d, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f,
0x72, 0x64, 0x2e, 0x78, 0x79, 0x29, 0x3b, 0x0a,
0x09, 0x09, 0x7d, 0x5d, 0x5d, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69,
0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x28, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68,
0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6c,
0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x71, 0x75, 0x6f, 0x74, 0x65,
0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x3a, 0x73, 0x75, 0x62, 0x28, 0x32, 0x2c, 0x2d, 0x32, 0x29, 0x29, 0x29,
0x20, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62,
0x28, 0x22, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x28, 0x25, 0x62, 0x27, 0x27, 0x29, 0x22,
0x2c, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x29, 0x0a,
0x09, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x3a, 0x67, 0x73, 0x75, 0x62,
0x28, 0x27, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x28, 0x25, 0x62, 0x22, 0x22, 0x29, 0x27,
0x2c, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x29, 0x0a,
0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6e,
0x63, 0x61, 0x74, 0x7b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x22, 0x5c, 0x6e, 0x22, 0x2c, 0x20,
0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x66, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x7d, 0x0a,
0x63, 0x61, 0x74, 0x7b, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x2c, 0x20, 0x22,
0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x46, 0x4f,
0x4f, 0x54, 0x45, 0x52, 0x7d, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61,
0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x47, 0x4c,
@@ -6350,11 +6341,14 @@ const unsigned char graphics_lua[] =
0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x61,
0x6e, 0x64, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
0x09, 0x09, 0x09, 0x09, 0x6c, 0x69, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6c,
0x69, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x48,
0x45, 0x41, 0x44, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x4e, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x0a,
0x09, 0x09, 0x09, 0x09, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x5b, 0x23, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2b, 0x31,
0x5d, 0x20, 0x3d, 0x20, 0x28, 0x22, 0x4c, 0x69, 0x6e, 0x65, 0x20, 0x25, 0x64, 0x3a, 0x20, 0x25, 0x73, 0x3a,
0x20, 0x25, 0x73, 0x22, 0x29, 0x3a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x6c, 0x69, 0x6e, 0x65, 0x6e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x35, 0x2c, 0x20, 0x77, 0x68, 0x61, 0x74, 0x2c, 0x20, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x29, 0x0a,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x2c, 0x20, 0x77, 0x68, 0x61, 0x74, 0x2c, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x29, 0x0a,
0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
0x09, 0x09, 0x2d, 0x2d, 0x20, 0x64, 0x69, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68,