imported Löve GLES branch (changeset 1ba9037e558b)

This commit is contained in:
Martin Felis
2013-12-05 18:05:13 +01:00
parent 41b2db04a7
commit 2644d1ee18
615 changed files with 150300 additions and 0 deletions
+109
View File
@@ -0,0 +1,109 @@
/**
* Copyright (c) 2006-2011 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.
**/
#ifndef LOVE_GRAPHICS_COLOR_H
#define LOVE_GRAPHICS_COLOR_H
namespace love
{
namespace graphics
{
template <typename T>
struct ColorT
{
T r;
T g;
T b;
T a;
ColorT() : r(0), g(0), b(0), a(0) {}
ColorT(T r_, T g_, T b_, T a_) : r(r_), g(g_), b(b_), a(a_) {}
void set(T r_, T g_, T b_, T a_)
{
r = r_;
g = g_;
b = b_;
a = a_;
}
ColorT<T> operator+=(const ColorT<T> &other);
ColorT<T> operator*=(T s);
ColorT<T> operator/=(T s);
};
template <typename T>
ColorT<T> ColorT<T>::operator+=(const ColorT<T> &other)
{
r += other.r;
g += other.g;
b += other.b;
a += other.a;
return *this;
}
template <typename T>
ColorT<T> ColorT<T>::operator*=(T s)
{
r *= s;
g *= s;
b *= s;
a *= s;
return *this;
}
template <typename T>
ColorT<T> ColorT<T>::operator/=(T s)
{
r /= s;
g /= s;
b /= s;
a /= s;
return *this;
}
template <typename T>
ColorT<T> operator+(const ColorT<T> &a, const ColorT<T> &b)
{
ColorT<T> tmp(a);
return tmp += b;
}
template <typename T>
ColorT<T> operator*(const ColorT<T> &a, T s)
{
ColorT<T> tmp(a);
return tmp *= s;
}
template <typename T>
ColorT<T> operator/(const ColorT<T> &a, T s)
{
ColorT<T> tmp(a);
return tmp /= s;
}
typedef ColorT<unsigned char> Color;
typedef ColorT<float> Colorf;
} // graphics
} // love
#endif // LOVE_GRAPHICS_COLOR_H
+65
View File
@@ -0,0 +1,65 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_DRAWQABLE_H
#define LOVE_GRAPHICS_DRAWQABLE_H
// LOVE
#include "Drawable.h"
#include "Quad.h"
namespace love
{
namespace graphics
{
/**
* A DrawQable is anything that be drawn in part with a Quad object.
**/
class DrawQable : public Drawable
{
public:
/**
* Destructor.
**/
virtual ~DrawQable() {}
/**
* Draws the object with the specified transformation.
*
* @param quad The Quad object to use to draw the object.
* @param x The position of the object along the x-axis.
* @param y The position of the object along the y-axis.
* @param angle The angle of the object (in radians).
* @param sx The scale factor along the x-axis.
* @param sy The scale factor along the y-axis.
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0;
};
} // graphics
} // love
#endif // LOVE_GRAPHICS_DRAWQABLE_H
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "Drawable.h"
namespace love
{
namespace graphics
{
Drawable::~Drawable()
{
}
} // graphics
} // love
+64
View File
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_DRAWABLE_H
#define LOVE_GRAPHICS_DRAWABLE_H
// LOVE
#include "common/Object.h"
namespace love
{
namespace graphics
{
/**
* A Drawable is anything that can be drawn on screen with a
* position, scale and orientation.
**/
class Drawable : public Object
{
public:
/**
* Destructor.
**/
virtual ~Drawable();
/**
* Draws the object with the specified transformation.
*
* @param x The position of the object along the x-axis.
* @param y The position of the object along the y-axis.
* @param angle The angle of the object (in radians).
* @param sx The scale factor along the x-axis.
* @param sy The scale factor along the y-axis.
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0;
};
} // graphics
} // love
#endif // LOVE_GRAPHICS_DRAWABLE_H
+173
View File
@@ -0,0 +1,173 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "Graphics.h"
namespace love
{
namespace graphics
{
Graphics::~Graphics()
{
}
bool Graphics::getConstant(const char *in, DrawMode &out)
{
return drawModes.find(in, out);
}
bool Graphics::getConstant(DrawMode in, const char *&out)
{
return drawModes.find(in, out);
}
bool Graphics::getConstant(const char *in, AlignMode &out)
{
return alignModes.find(in, out);
}
bool Graphics::getConstant(AlignMode in, const char *&out)
{
return alignModes.find(in, out);
}
bool Graphics::getConstant(const char *in, BlendMode &out)
{
return blendModes.find(in, out);
}
bool Graphics::getConstant(BlendMode in, const char *&out)
{
return blendModes.find(in, out);
}
bool Graphics::getConstant(const char *in, LineStyle &out)
{
return lineStyles.find(in, out);
}
bool Graphics::getConstant(LineStyle in, const char *&out)
{
return lineStyles.find(in, out);
}
bool Graphics::getConstant(const char *in, LineJoin &out)
{
return lineJoins.find(in, out);
}
bool Graphics::getConstant(LineJoin in, const char *&out)
{
return lineJoins.find(in, out);
}
bool Graphics::getConstant(const char *in, PointStyle &out)
{
return pointStyles.find(in, out);
}
bool Graphics::getConstant(PointStyle in, const char *&out)
{
return pointStyles.find(in, out);
}
bool Graphics::getConstant(const char *in, Support &out)
{
return support.find(in, out);
}
bool Graphics::getConstant(Support in, const char *&out)
{
return support.find(in, out);
}
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
{
{ "line", Graphics::DRAW_LINE },
{ "fill", Graphics::DRAW_FILL },
};
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM> Graphics::drawModes(Graphics::drawModeEntries, sizeof(Graphics::drawModeEntries));
StringMap<Graphics::AlignMode, Graphics::ALIGN_MAX_ENUM>::Entry Graphics::alignModeEntries[] =
{
{ "left", Graphics::ALIGN_LEFT },
{ "right", Graphics::ALIGN_RIGHT },
{ "center", Graphics::ALIGN_CENTER },
{ "justify", Graphics::ALIGN_JUSTIFY },
};
StringMap<Graphics::AlignMode, Graphics::ALIGN_MAX_ENUM> Graphics::alignModes(Graphics::alignModeEntries, sizeof(Graphics::alignModeEntries));
StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM>::Entry Graphics::blendModeEntries[] =
{
{ "alpha", Graphics::BLEND_ALPHA },
{ "additive", Graphics::BLEND_ADDITIVE },
{ "subtractive", Graphics::BLEND_SUBTRACTIVE },
{ "multiplicative", Graphics::BLEND_MULTIPLICATIVE },
{ "premultiplied", Graphics::BLEND_PREMULTIPLIED },
{ "replace", Graphics::BLEND_REPLACE },
};
StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM> Graphics::blendModes(Graphics::blendModeEntries, sizeof(Graphics::blendModeEntries));
StringMap<Graphics::LineStyle, Graphics::LINE_MAX_ENUM>::Entry Graphics::lineStyleEntries[] =
{
{ "smooth", Graphics::LINE_SMOOTH },
{ "rough", Graphics::LINE_ROUGH }
};
StringMap<Graphics::LineStyle, Graphics::LINE_MAX_ENUM> Graphics::lineStyles(Graphics::lineStyleEntries, sizeof(Graphics::lineStyleEntries));
StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM>::Entry Graphics::lineJoinEntries[] =
{
{ "none", Graphics::LINE_JOIN_NONE },
{ "miter", Graphics::LINE_JOIN_MITER },
{ "bevel", Graphics::LINE_JOIN_BEVEL }
};
StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM> Graphics::lineJoins(Graphics::lineJoinEntries, sizeof(Graphics::lineJoinEntries));
StringMap<Graphics::PointStyle, Graphics::POINT_MAX_ENUM>::Entry Graphics::pointStyleEntries[] =
{
{ "smooth", Graphics::POINT_SMOOTH },
{ "rough", Graphics::POINT_ROUGH }
};
StringMap<Graphics::PointStyle, Graphics::POINT_MAX_ENUM> Graphics::pointStyles(Graphics::pointStyleEntries, sizeof(Graphics::pointStyleEntries));
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
{
{ "canvas", Graphics::SUPPORT_CANVAS },
{ "hdrcanvas", Graphics::SUPPORT_HDR_CANVAS },
{ "multicanvas", Graphics::SUPPORT_MULTI_CANVAS },
{ "shader", Graphics::SUPPORT_SHADER },
{ "npot", Graphics::SUPPORT_NPOT },
{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
{ "mipmap", Graphics::SUPPORT_MIPMAP },
{ "dxt", Graphics::SUPPORT_DXT },
{ "bc5", Graphics::SUPPORT_BC5 },
};
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
} // graphics
} // love
+185
View File
@@ -0,0 +1,185 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_GRAPHICS_H
#define LOVE_GRAPHICS_GRAPHICS_H
// LOVE
#include "common/Module.h"
#include "common/StringMap.h"
namespace love
{
namespace graphics
{
class Graphics : public Module
{
public:
enum DrawMode
{
DRAW_LINE = 1,
DRAW_FILL,
DRAW_MAX_ENUM
};
enum AlignMode
{
ALIGN_LEFT = 1,
ALIGN_CENTER,
ALIGN_RIGHT,
ALIGN_JUSTIFY,
ALIGN_MAX_ENUM
};
enum BlendMode
{
BLEND_ALPHA = 1,
BLEND_ADDITIVE,
BLEND_SUBTRACTIVE,
BLEND_MULTIPLICATIVE,
BLEND_PREMULTIPLIED,
BLEND_REPLACE,
BLEND_MAX_ENUM
};
enum LineStyle
{
LINE_ROUGH = 1,
LINE_SMOOTH,
LINE_MAX_ENUM
};
enum LineJoin
{
LINE_JOIN_NONE = 1,
LINE_JOIN_MITER,
LINE_JOIN_BEVEL,
LINE_JOIN_MAX_ENUM
};
enum PointStyle
{
POINT_ROUGH = 1,
POINT_SMOOTH,
POINT_MAX_ENUM
};
enum Support
{
SUPPORT_CANVAS = 1,
SUPPORT_HDR_CANVAS,
SUPPORT_MULTI_CANVAS,
SUPPORT_SHADER,
SUPPORT_NPOT,
SUPPORT_SUBTRACTIVE,
SUPPORT_MIPMAP,
SUPPORT_DXT,
SUPPORT_BC5,
SUPPORT_MAX_ENUM
};
enum Renderer
{
RENDERER_OPENGL = 0,
RENDERER_OPENGLES,
RENDERER_MAX_ENUM
};
enum RendererInfo
{
RENDERER_INFO_NAME = 1,
RENDERER_INFO_VERSION,
RENDERER_INFO_VENDOR,
RENDERER_INFO_DEVICE,
RENDERER_INFO_MAX_ENUM
};
virtual ~Graphics();
/**
* Sets the current graphics display viewport dimensions.
**/
virtual void setViewportSize(int width, int height) = 0;
/**
* Sets the current graphics display viewport and initializes the renderer.
* @param width The viewport width.
* @param height The viewport height.
**/
virtual bool setMode(int width, int height) = 0;
/**
* Un-sets the current graphics display mode (uninitializing objects if
* necessary.)
**/
virtual void unSetMode() = 0;
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
static bool getConstant(const char *in, AlignMode &out);
static bool getConstant(AlignMode in, const char *&out);
static bool getConstant(const char *in, BlendMode &out);
static bool getConstant(BlendMode in, const char *&out);
static bool getConstant(const char *in, LineStyle &out);
static bool getConstant(LineStyle in, const char *&out);
static bool getConstant(const char *in, LineJoin &out);
static bool getConstant(LineJoin in, const char *&out);
static bool getConstant(const char *in, PointStyle &out);
static bool getConstant(PointStyle in, const char *&out);
static bool getConstant(const char *in, Support &out);
static bool getConstant(Support in, const char *&out);
private:
static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
static StringMap<DrawMode, DRAW_MAX_ENUM> drawModes;
static StringMap<AlignMode, ALIGN_MAX_ENUM>::Entry alignModeEntries[];
static StringMap<AlignMode, ALIGN_MAX_ENUM> alignModes;
static StringMap<BlendMode, BLEND_MAX_ENUM>::Entry blendModeEntries[];
static StringMap<BlendMode, BLEND_MAX_ENUM> blendModes;
static StringMap<LineStyle, LINE_MAX_ENUM>::Entry lineStyleEntries[];
static StringMap<LineStyle, LINE_MAX_ENUM> lineStyles;
static StringMap<LineJoin, LINE_JOIN_MAX_ENUM>::Entry lineJoinEntries[];
static StringMap<LineJoin, LINE_JOIN_MAX_ENUM> lineJoins;
static StringMap<PointStyle, POINT_MAX_ENUM>::Entry pointStyleEntries[];
static StringMap<PointStyle, POINT_MAX_ENUM> pointStyles;
static StringMap<Support, SUPPORT_MAX_ENUM>::Entry supportEntries[];
static StringMap<Support, SUPPORT_MAX_ENUM> support;
}; // Graphics
} // graphics
} // love
#endif // LOVE_GRAPHICS_GRAPHICS_H
+96
View File
@@ -0,0 +1,96 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "Image.h"
namespace love
{
namespace graphics
{
Image::Filter Image::defaultFilter;
Image::Filter::Filter()
: min(FILTER_LINEAR)
, mag(FILTER_LINEAR)
, mipmap(FILTER_NONE)
, anisotropy(1.0f)
{
}
Image::Wrap::Wrap()
: s(WRAP_CLAMP)
, t(WRAP_CLAMP)
{
}
Image::~Image()
{
}
void Image::setDefaultFilter(const Filter &f)
{
defaultFilter = f;
}
const Image::Filter &Image::getDefaultFilter()
{
return defaultFilter;
}
bool Image::getConstant(const char *in, FilterMode &out)
{
return filterModes.find(in, out);
}
bool Image::getConstant(FilterMode in, const char *&out)
{
return filterModes.find(in, out);
}
bool Image::getConstant(const char *in, WrapMode &out)
{
return wrapModes.find(in, out);
}
bool Image::getConstant(WrapMode in, const char *&out)
{
return wrapModes.find(in, out);
}
StringMap<Image::FilterMode, Image::FILTER_MAX_ENUM>::Entry Image::filterModeEntries[] =
{
{ "linear", Image::FILTER_LINEAR },
{ "nearest", Image::FILTER_NEAREST },
};
StringMap<Image::FilterMode, Image::FILTER_MAX_ENUM> Image::filterModes(Image::filterModeEntries, sizeof(Image::filterModeEntries));
StringMap<Image::WrapMode, Image::WRAP_MAX_ENUM>::Entry Image::wrapModeEntries[] =
{
{ "clamp", Image::WRAP_CLAMP },
{ "repeat", Image::WRAP_REPEAT },
};
StringMap<Image::WrapMode, Image::WRAP_MAX_ENUM> Image::wrapModes(Image::wrapModeEntries, sizeof(Image::wrapModeEntries));
} // graphics
} // love
+95
View File
@@ -0,0 +1,95 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_IMAGE_H
#define LOVE_GRAPHICS_IMAGE_H
// LOVE
#include "graphics/Volatile.h"
#include "graphics/DrawQable.h"
#include "common/StringMap.h"
namespace love
{
namespace graphics
{
class Image : public DrawQable, public Volatile
{
public:
enum WrapMode
{
WRAP_CLAMP = 1,
WRAP_REPEAT,
WRAP_MAX_ENUM
};
enum FilterMode
{
FILTER_LINEAR = 1,
FILTER_NEAREST,
FILTER_NONE,
FILTER_MAX_ENUM
};
struct Filter
{
Filter();
FilterMode min;
FilterMode mag;
FilterMode mipmap;
float anisotropy;
};
struct Wrap
{
Wrap();
WrapMode s;
WrapMode t;
};
virtual ~Image();
// The default filter.
static void setDefaultFilter(const Filter &f);
static const Filter &getDefaultFilter();
static bool getConstant(const char *in, FilterMode &out);
static bool getConstant(FilterMode in, const char *&out);
static bool getConstant(const char *in, WrapMode &out);
static bool getConstant(WrapMode in, const char *&out);
private:
// The default texture filter.
static Filter defaultFilter;
static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
}; // Image
} // graphics
} // love
#endif // LOVE_GRAPHICS_IMAGE_H
+83
View File
@@ -0,0 +1,83 @@
/**
* Copyright (c) 2006-2013 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 "Quad.h"
// C
#include <cstring> // For memcpy
namespace love
{
namespace graphics
{
Quad::Quad(const Quad::Viewport &v, float sw, float sh)
: sw(sw)
, sh(sh)
{
memset(vertices, 255, sizeof(Vertex) * NUM_VERTICES);
refresh(v, sw, sh);
}
Quad::~Quad()
{
}
void Quad::refresh(const Quad::Viewport &v, float sw, float sh)
{
viewport = v;
vertices[0].x = 0;
vertices[0].y = 0;
vertices[1].x = 0;
vertices[1].y = v.h;
vertices[2].x = v.w;
vertices[2].y = v.h;
vertices[3].x = v.w;
vertices[3].y = 0;
vertices[0].s = v.x/sw;
vertices[0].t = v.y/sh;
vertices[1].s = v.x/sw;
vertices[1].t = (v.y+v.h)/sh;
vertices[2].s = (v.x+v.w)/sw;
vertices[2].t = (v.y+v.h)/sh;
vertices[3].s = (v.x+v.w)/sw;
vertices[3].t = v.y/sh;
}
void Quad::setViewport(const Quad::Viewport &v)
{
refresh(v, sw, sh);
}
Quad::Viewport Quad::getViewport() const
{
return viewport;
}
const Vertex *Quad::getVertices() const
{
return vertices;
}
} // graphics
} // love
+67
View File
@@ -0,0 +1,67 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_QUAD_H
#define LOVE_GRAPHICS_QUAD_H
// LOVE
#include "common/Object.h"
#include "common/math.h"
namespace love
{
namespace graphics
{
class Quad : public Object
{
public:
struct Viewport
{
float x, y;
float w, h;
};
static const size_t NUM_VERTICES = 4;
Quad(const Viewport &v, float sw, float sh);
virtual ~Quad();
void refresh(const Viewport &v, float sw, float sh);
void setViewport(const Viewport &v);
Viewport getViewport() const;
const Vertex *getVertices() const;
private:
Vertex vertices[NUM_VERTICES];
Viewport viewport;
float sw;
float sh;
}; // Quad
} // graphics
} // love
#endif // LOVE_GRAPHICS_QUAD_H
@@ -0,0 +1,69 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "Volatile.h"
namespace love
{
namespace graphics
{
// Static members.
std::list<Volatile *> Volatile::all;
Volatile::Volatile()
{
// Insert this object into "all".
all.push_back(this);
}
Volatile::~Volatile()
{
// Remove the pointer to this object.
all.remove(this);
}
bool Volatile::loadAll()
{
bool success = true;
std::list<Volatile *>::iterator i = all.begin();
while (i != all.end())
{
success = success && (*i)->loadVolatile();
i++;
}
return success;
}
void Volatile::unloadAll()
{
std::list<Volatile *>::iterator i = all.begin();
while (i != all.end())
{
(*i)->unloadVolatile();
i++;
}
}
} // graphics
} // love
+93
View File
@@ -0,0 +1,93 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_VOLATILE_H
#define LOVE_GRAPHICS_VOLATILE_H
// STL
#include <list>
namespace love
{
namespace graphics
{
/**
* This class is the superclass of all objects which must completely or
* partially reload when the user changes the display resolution. All
* volatile objects will be notified when the display mode changes.
*
* @author Anders Ruud
**/
class Volatile
{
private:
// A list of all Volatile object currently alive.
static std::list<Volatile *> all;
public:
/**
* Constructor. Automatically adds \c this into the list
* of volatile objects.
**/
Volatile();
/**
* Destructor. Removes \c this from the list of volatile
* objects.
**/
virtual ~Volatile();
/**
* Loads the part(s) of the object which is destroyed when
* the display mode is changed.
*
* @return True if successful, false on errors.
**/
virtual bool loadVolatile() = 0;
/**
* Unloads the part(s) of the objects which would be destroyed
* anyway when the display mode is changed.
**/
virtual void unloadVolatile() = 0;
// Static:
/**
* Calls \c loadVolatile() on each element in the list of volatiles.
*
* @return True if all elements succeeded, false if one or more failed.
**/
static bool loadAll();
/**
* Calls \c unloadVolatile() on each element in the list of volatiles.
**/
static void unloadAll();
}; // Volatile
} // graphics
} // love
#endif // LOVE_GRAPHICS_VOLATILE_H
@@ -0,0 +1,813 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "Canvas.h"
#include "Image.h"
#include "Graphics.h"
#include "common/Matrix.h"
#include "common/config.h"
#include <cstring> // For memcpy
#include <limits>
namespace love
{
namespace graphics
{
namespace opengl
{
// strategy for fbo creation, interchangable at runtime:
// none, opengl >= 3.0, extensions
struct FramebufferStrategy
{
virtual ~FramebufferStrategy() {}
/// create a new framebuffer and texture
/**
* @param[out] framebuffer Framebuffer name
* @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 &, int, int, Canvas::TextureType)
{
return GL_FRAMEBUFFER_UNSUPPORTED;
}
/// Create a stencil buffer and attach it to the active framebuffer object
/**
* @param[in] width Width of the stencil buffer
* @param[in] height Height of the stencil buffer
* @param[out] stencil Name for stencil buffer
* @return Whether the stencil buffer was successfully created
**/
virtual bool createStencil(int, int, GLuint &)
{
return false;
}
/// remove objects
/**
* @param[in] framebuffer Framebuffer name
* @param[in] depth_stencil Name for packed depth and stencil buffer
* @param[in] img Texture name
*/
virtual void deleteFBO(GLuint, GLuint, GLuint) {}
virtual void bindFBO(GLuint) {}
/// attach additional canvases to the active framebuffer for rendering
/**
* @param[in] canvases List of canvases to attach
**/
virtual void setAttachments(const std::vector<Canvas *> &) {}
/// stop using all additional attached canvases
virtual void setAttachments() {}
};
struct FramebufferStrategyCore : public FramebufferStrategy
{
virtual GLenum createFBO(GLuint &framebuffer, GLuint &img, int width, int height, Canvas::TextureType texture_type)
{
// get currently bound fbo to reset to it later
GLint current_fbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
// create framebuffer
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// 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_RGBA;
format = GL_UNSIGNED_BYTE;
}
glGenTextures(1, &img);
gl.bindTexture(img);
gl.setTextureFilter(Image::getDefaultFilter());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
0, GL_RGBA, format, NULL);
gl.bindTexture(0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, img, 0);
// check status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
// unbind framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
return status;
}
virtual bool createStencil(int width, int height, GLuint &stencil)
{
// create stencil buffer
glDeleteRenderbuffers(1, &stencil);
glGenRenderbuffers(1, &stencil);
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, stencil);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// check status
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
{
gl.deleteTexture(img);
if (depth_stencil != 0)
glDeleteRenderbuffers(1, &depth_stencil);
if (framebuffer != 0)
glDeleteFramebuffers(1, &framebuffer);
}
virtual void bindFBO(GLuint framebuffer)
{
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
virtual void setAttachments()
{
// set a single render target
glDrawBuffer(GL_COLOR_ATTACHMENT0);
}
virtual void setAttachments(const std::vector<Canvas *> &canvases)
{
if (canvases.size() == 0)
{
setAttachments();
return;
}
std::vector<GLenum> drawbuffers;
drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
// Attach the canvas textures to the currently bound framebuffer.
for (size_t i = 0; i < canvases.size(); i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
}
// set up multiple render targets
if (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0)
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
else if (GLAD_ARB_draw_buffers)
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
}
};
struct FramebufferStrategyCorePacked : public FramebufferStrategyCore
{
virtual bool createStencil(int width, int height, GLuint &stencil)
{
// create combined depth/stencil buffer
glDeleteRenderbuffers(1, &stencil);
glGenRenderbuffers(1, &stencil);
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, stencil);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// check status
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
}
};
struct FramebufferStrategyPackedEXT : public FramebufferStrategy
{
virtual GLenum createFBO(GLuint &framebuffer, 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);
// 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_RGBA;
format = GL_UNSIGNED_BYTE;
}
glGenTextures(1, &img);
gl.bindTexture(img);
gl.setTextureFilter(Image::getDefaultFilter());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
0, GL_RGBA, format, NULL);
gl.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
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) current_fbo);
return status;
}
virtual bool createStencil(int width, int height, GLuint &stencil)
{
// create combined depth/stencil buffer
glDeleteRenderbuffers(1, &stencil);
glGenRenderbuffersEXT(1, &stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
// check status
return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
{
gl.deleteTexture(img);
if (depth_stencil != 0)
glDeleteRenderbuffersEXT(1, &depth_stencil);
if (framebuffer != 0)
glDeleteFramebuffersEXT(1, &framebuffer);
}
virtual void bindFBO(GLuint framebuffer)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
}
virtual void setAttachments()
{
// set a single render target
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
}
virtual void setAttachments(const std::vector<Canvas *> &canvases)
{
if (canvases.size() == 0)
{
setAttachments();
return;
}
std::vector<GLenum> drawbuffers;
drawbuffers.push_back(GL_COLOR_ATTACHMENT0_EXT);
// Attach the canvas textures to the currently bound framebuffer.
for (size_t i = 0; i < canvases.size(); i++)
{
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
}
// set up multiple render targets
if (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0)
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
else if (GLAD_ARB_draw_buffers)
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
}
};
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
{
virtual bool createStencil(int width, int height, GLuint &stencil)
{
// create stencil buffer
glDeleteRenderbuffers(1, &stencil);
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);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
// check status
return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
}
bool isSupported()
{
GLuint fb = 0, stencil = 0, img = 0;
GLenum status = createFBO(fb, img, 2, 2, Canvas::TYPE_NORMAL);
deleteFBO(fb, stencil, img);
return status == GL_FRAMEBUFFER_COMPLETE;
}
};
FramebufferStrategy *strategy = NULL;
FramebufferStrategy strategyNone;
FramebufferStrategyCore strategyCore;
FramebufferStrategyCorePacked strategyCorePacked;
FramebufferStrategyPackedEXT strategyPackedEXT;
FramebufferStrategyEXT strategyEXT;
Canvas *Canvas::current = NULL;
static void getStrategy()
{
if (!strategy)
{
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
strategy = &strategyCorePacked;
else if (GLAD_ES_VERSION_2_0)
strategy = &strategyCore;
else if (GLAD_EXT_framebuffer_object && GLAD_EXT_packed_depth_stencil)
strategy = &strategyPackedEXT;
else if (GLAD_EXT_framebuffer_object && strategyEXT.isSupported())
strategy = &strategyEXT;
else
strategy = &strategyNone;
}
}
static int maxFBOColorAttachments = 0;
static int maxDrawBuffers = 0;
Canvas::Canvas(int width, int height, TextureType texture_type)
: width(width)
, height(height)
, fbo(0)
, depth_stencil(0)
, img(0)
, 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 = h;
vertices[1].x = w;
vertices[1].y = h;
vertices[2].x = w;
vertices[2].y = 0;
vertices[3].x = 0;
vertices[3].y = 0;
// texture coordinates
vertices[0].s = 0;
vertices[0].t = 0;
vertices[1].s = 1;
vertices[1].t = 0;
vertices[2].s = 1;
vertices[2].t = 1;
vertices[3].s = 0;
vertices[3].t = 1;
settings.filter = Image::getDefaultFilter();
getStrategy();
loadVolatile();
}
Canvas::~Canvas()
{
// reset framebuffer if still using this one
if (current == this)
stopGrab();
unloadVolatile();
}
bool Canvas::isSupported()
{
getStrategy();
return (strategy != &strategyNone);
}
bool Canvas::isHDRSupported()
{
return GLAD_VERSION_3_0 || (isSupported() && GLAD_ARB_texture_float);
}
bool Canvas::isMultiCanvasSupported()
{
if (!(isSupported() && (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_ARB_draw_buffers)))
return false;
if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
{
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
}
// system must support at least 4 simultanious active canvases
return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
}
void Canvas::bindDefaultCanvas()
{
if (current != NULL)
current->stopGrab();
}
void Canvas::setupGrab()
{
// already grabbing
if (current == this)
return;
// cleanup after previous fbo
if (current != NULL)
current->stopGrab();
// bind the framebuffer object.
glPushAttrib(GL_VIEWPORT_BIT | GL_TRANSFORM_BIT);
strategy->bindFBO(fbo);
gl.setViewport(OpenGL::Viewport(0, 0, width, height));
// Set up orthographic view (no depth)
gl.matrices.projection.push(Matrix::ortho(0.0, width, height, 0.0));
// indicate we are using this fbo
current = this;
}
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
{
// Whether the new canvas list is different from the old one.
// A more thorough check is done below.
bool canvaseschanged = canvases.size() != attachedCanvases.size();
if (canvases.size() > 0)
{
if (!isMultiCanvasSupported())
throw love::Exception("Multi-canvas rendering is not supported on this system.");
if (canvases.size()+1 > size_t(maxDrawBuffers) || canvases.size()+1 > size_t(maxFBOColorAttachments))
throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1);
}
for (size_t i = 0; i < canvases.size(); i++)
{
if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
throw love::Exception("All canvas arguments must have the same dimensions.");
if (canvases[i]->getTextureType() != texture_type)
throw love::Exception("All canvas arguments must have the same texture type.");
if (!canvaseschanged && canvases[i] != attachedCanvases[i])
canvaseschanged = true;
}
setupGrab();
// Don't attach anything if there's nothing to change.
if (!canvaseschanged)
return;
// Attach the canvas textures to the active FBO and set up MRTs.
strategy->setAttachments(canvases);
for (size_t i = 0; i < canvases.size(); i++)
canvases[i]->retain();
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases = canvases;
}
void Canvas::startGrab()
{
setupGrab();
if (attachedCanvases.size() == 0)
return;
// make sure the FBO is only using a single canvas
strategy->setAttachments();
// release any previously attached canvases
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases.clear();
}
void Canvas::stopGrab()
{
// i am not grabbing. leave me alone
if (current != this)
return;
// bind default
strategy->bindFBO(gl.getDefaultFBO());
gl.matrices.projection.pop();
glPopAttrib();
current = NULL;
}
void Canvas::clear(Color c)
{
if (strategy == &strategyNone)
return;
GLuint previous = gl.getDefaultFBO();
if (current != this)
{
if (current != NULL)
previous = current->fbo;
strategy->bindFBO(fbo);
}
GLfloat glcolor[] = {c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f};
// We don't need to worry about multiple FBO attachments or global clear
// color state when OpenGL 3.0+ is supported.
if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0)
{
glClearBufferfv(GL_COLOR, 0, glcolor);
if (depth_stencil != 0)
{
GLint stencilvalue = 0;
glClearBufferiv(GL_STENCIL, 0, &stencilvalue);
}
}
else
{
// glClear will clear all active draw buffers, so we need to temporarily
// detach any other canvases (when MRT is being used.)
if (attachedCanvases.size() > 0)
strategy->setAttachments();
// Don't use the state-shadowed gl.setClearColor because we want to save
// the previous clear color.
glClearColor(glcolor[0], glcolor[1], glcolor[2], glcolor[3]);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (attachedCanvases.size() > 0)
strategy->setAttachments(attachedCanvases);
// Restore the global clear color.
gl.setClearColor(gl.getClearColor());
}
if (current != this)
strategy->bindFBO(previous);
}
void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
static Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
drawv(t, vertices);
}
void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
static Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
const Vertex *v = quad->getVertices();
// flip texture coordinates vertically.
Vertex w[4];
memcpy(w, v, sizeof(Vertex) * 4);
for (size_t i = 0; i < 4; i++)
w[i].t = 1.0f - w[i].t;
drawv(t, w);
}
bool Canvas::checkCreateStencil()
{
// Do nothing if we've already created the stencil buffer.
if (depth_stencil != 0)
return true;
if (current != this)
strategy->bindFBO(fbo);
bool success = strategy->createStencil(width, height, depth_stencil);
if (current && current != this)
strategy->bindFBO(current->fbo);
else if (!current)
strategy->bindFBO(gl.getDefaultFBO());
return success;
}
love::image::ImageData *Canvas::getImageData(love::image::Image *image)
{
int row = 4 * width;
int size = row * height;
GLubyte *pixels = new GLubyte[size];
GLubyte *flipped = 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(gl.getDefaultFBO());
GLubyte *src = pixels, *dst = flipped + size - row;
for (int i = 0; i < height; ++i, dst -= row, src += row)
memcpy(dst, src, row);
love::image::ImageData *img = image->newImageData(width, height, (void *)flipped, true);
// The new ImageData now owns the flipped data, so we don't delete it here.
delete[] pixels;
return img;
}
void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
{
if (current != this)
strategy->bindFBO(fbo);
glReadPixels(x, height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
if (current && current != this)
strategy->bindFBO(current->fbo);
else if (!current)
strategy->bindFBO(gl.getDefaultFBO());
}
const std::vector<Canvas *> &Canvas::getAttachedCanvases() const
{
return attachedCanvases;
}
void Canvas::setFilter(const Image::Filter &f)
{
settings.filter = f;
gl.bindTexture(img);
settings.filter.anisotropy = gl.setTextureFilter(f);
}
Image::Filter Canvas::getFilter() const
{
gl.bindTexture(img);
return gl.getTextureFilter();
}
void Canvas::setWrap(const Image::Wrap &w)
{
settings.wrap = w;
gl.bindTexture(img);
gl.setTextureWrap(w);
}
Image::Wrap Canvas::getWrap() const
{
return settings.wrap;
}
bool Canvas::loadVolatile()
{
fbo = depth_stencil = img = 0;
// glTexImage2D is guaranteed to error in this case.
if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
{
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
return false;
}
status = strategy->createFBO(fbo, img, width, height, texture_type);
if (status != GL_FRAMEBUFFER_COMPLETE)
return false;
setFilter(settings.filter);
setWrap(settings.wrap);
clear(Color(0, 0, 0, 0));
return true;
}
void Canvas::unloadVolatile()
{
strategy->deleteFBO(fbo, depth_stencil, img);
fbo = depth_stencil = img = 0;
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases.clear();
}
int Canvas::getWidth()
{
return width;
}
int Canvas::getHeight()
{
return height;
}
void Canvas::drawv(const Matrix &t, const Vertex *v) const
{
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
gl.prepareDraw();
gl.bindTexture(img);
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &v[0].x);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &v[0].s);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.matrices.transform.pop();
}
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
@@ -0,0 +1,155 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_CANVAS_H
#define LOVE_GRAPHICS_OPENGL_CANVAS_H
#include "graphics/DrawQable.h"
#include "graphics/Volatile.h"
#include "graphics/Image.h"
#include "graphics/Color.h"
#include "image/Image.h"
#include "image/ImageData.h"
#include "common/math.h"
#include "common/Matrix.h"
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
class Canvas : public DrawQable, public Volatile
{
public:
enum TextureType
{
TYPE_NORMAL,
TYPE_HDR,
TYPE_MAX_ENUM
};
Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
virtual ~Canvas();
/**
* @param canvases A list of other canvases to temporarily attach to this one,
* to allow drawing to multiple canvases at once.
**/
void startGrab(const std::vector<Canvas *> &canvases);
void startGrab();
void stopGrab();
void clear(Color c);
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* @copydoc DrawQable::drawq()
**/
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* Create and attach a stencil buffer to this Canvas' framebuffer, if necessary.
**/
bool checkCreateStencil();
love::image::ImageData *getImageData(love::image::Image *image);
void getPixel(unsigned char* pixel_rgba, int x, int y);
const std::vector<Canvas *> &getAttachedCanvases() const;
void setFilter(const Image::Filter &f);
Image::Filter getFilter() const;
void setWrap(const Image::Wrap &w);
Image::Wrap getWrap() const;
int getWidth();
int getHeight();
inline GLenum getStatus() const
{
return status;
}
inline TextureType getTextureType() const
{
return texture_type;
}
bool loadVolatile();
void unloadVolatile();
static bool isSupported();
static bool isHDRSupported();
static bool isMultiCanvasSupported();
static bool getConstant(const char *in, TextureType &out);
static bool getConstant(TextureType in, const char *&out);
static Canvas *current;
static void bindDefaultCanvas();
GLuint getTextureName() const
{
return img;
}
private:
friend class Shader;
GLsizei width;
GLsizei height;
GLuint fbo;
GLuint depth_stencil;
GLuint img;
TextureType texture_type;
Vertex vertices[4];
GLenum status;
struct
{
Image::Filter filter;
Image::Wrap wrap;
} settings;
std::vector<Canvas *> attachedCanvases;
void setupGrab();
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
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_CANVAS_H
@@ -0,0 +1,593 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "common/config.h"
#include "Font.h"
#include "font/GlyphData.h"
#include "Image.h"
#include "libraries/utf8/utf8.h"
#include "common/math.h"
#include "common/Matrix.h"
#include <math.h>
#include <sstream>
#include <algorithm> // for max
namespace love
{
namespace graphics
{
namespace opengl
{
const int Font::TEXTURE_WIDTHS[] = {128, 256, 256, 512, 512, 1024, 1024};
const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512, 1024};
Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
: rasterizer(r)
, height(r->getHeight())
, lineHeight(1)
, mSpacing(1)
, filter(filter)
{
this->filter.mipmap = Image::FILTER_NONE;
// Try to find the best texture size match for the font size. default to the
// largest texture size if no rough match is found.
textureSizeIndex = NUM_TEXTURE_SIZES - 1;
for (int i = 0; i < NUM_TEXTURE_SIZES; i++)
{
// Make a rough estimate of the total used texture size, based on glyph
// height. The estimated size is likely larger than the actual total
// size, which is good because texture switching is expensive.
if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i])
{
textureSizeIndex = i;
break;
}
}
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
love::font::GlyphData *gd = 0;
try
{
gd = r->getGlyphData(32);
type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA) ? FONT_TRUETYPE : FONT_IMAGE;
loadVolatile();
}
catch (love::Exception &)
{
delete gd;
throw;
}
delete gd;
rasterizer->retain();
}
Font::~Font()
{
rasterizer->release();
unloadVolatile();
}
bool Font::initializeTexture(GLint format)
{
GLint internalformat = (format == GL_LUMINANCE_ALPHA) ? GL_LUMINANCE_ALPHA : GL_RGBA;
// clear errors before initializing
while (glGetError() != GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_2D,
0,
internalformat,
(GLsizei)textureWidth,
(GLsizei)textureHeight,
0,
format,
GL_UNSIGNED_BYTE,
NULL);
return glGetError() == GL_NO_ERROR;
}
void Font::createTexture()
{
textureX = textureY = rowHeight = TEXTURE_PADDING;
GLuint t;
glGenTextures(1, &t);
textures.push_back(t);
gl.bindTexture(t);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
// Initialize the texture, attempting smaller sizes if initialization fails.
bool initialized = false;
while (textureSizeIndex >= 0)
{
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
initialized = initializeTexture(format);
if (initialized || textureSizeIndex <= 0)
break;
--textureSizeIndex;
}
if (!initialized)
{
// Clean up before throwing.
gl.deleteTexture(t);
gl.bindTexture(0);
textures.pop_back();
throw love::Exception("Could not create font texture!");
}
// Fill the texture with transparent black.
std::vector<GLubyte> emptyData(textureWidth * textureHeight * (type == FONT_TRUETYPE ? 2 : 4), 0);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0, 0,
(GLsizei)textureWidth,
(GLsizei)textureHeight,
format,
GL_UNSIGNED_BYTE,
&emptyData[0]);
setFilter(filter);
}
Font::Glyph *Font::addGlyph(uint32 glyph)
{
love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
int w = gd->getWidth();
int h = gd->getHeight();
if (textureX + w + TEXTURE_PADDING > textureWidth)
{
// out of space - new row!
textureX = TEXTURE_PADDING;
textureY += rowHeight;
rowHeight = TEXTURE_PADDING;
}
if (textureY + h + TEXTURE_PADDING > textureHeight)
{
// totally out of space - new texture!
createTexture();
}
Glyph *g = new Glyph;
g->texture = 0;
g->spacing = gd->getAdvance();
memset(g->vertices, 0, sizeof(GlyphVertex) * 4);
// don't waste space for empty glyphs. also fixes a division by zero bug with ati drivers
if (w > 0 && h > 0)
{
const GLuint t = textures.back();
gl.bindTexture(t);
glTexSubImage2D(GL_TEXTURE_2D,
0,
textureX,
textureY,
w, h,
(type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA),
GL_UNSIGNED_BYTE,
gd->getData());
g->texture = t;
const GlyphVertex verts[4] = {
{ 0.0f, 0.0f, float(textureX)/float(textureWidth), float(textureY)/float(textureHeight)},
{ 0.0f, float(h), float(textureX)/float(textureWidth), float(textureY+h)/float(textureHeight)},
{float(w), float(h), float(textureX+w)/float(textureWidth), float(textureY+h)/float(textureHeight)},
{float(w), 0.0f, float(textureX+w)/float(textureWidth), float(textureY)/float(textureHeight)},
};
// copy vertex data to the glyph and set proper bearing
for (int i = 0; i < 4; i++)
{
g->vertices[i] = verts[i];
g->vertices[i].x += gd->getBearingX();
g->vertices[i].y -= gd->getBearingY();
}
}
if (w > 0)
textureX += (w + TEXTURE_PADDING);
if (h > 0)
rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
delete gd;
glyphs[glyph] = g;
return g;
}
Font::Glyph *Font::findGlyph(uint32 glyph)
{
auto it = glyphs.find(glyph);
if (it != glyphs.end())
return it->second;
else
return addGlyph(glyph);
}
float Font::getHeight() const
{
return static_cast<float>(height);
}
void Font::print(const std::string &text, float x, float y, float extra_spacing, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
// Spacing counter and newline handling.
float dx = 0.0f;
float dy = 0.0f;
float lineheight = getBaseline();
// Keeps track of when we need to switch textures in our vertex array.
std::vector<GlyphArrayDrawInfo> glyphinfolist;
// Pre-allocate space for the maximum possible number of vertices.
std::vector<GlyphVertex> glyphverts;
glyphverts.reserve(text.length() * 4);
int vertexcount = 0;
try
{
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
while (i != end)
{
uint32 g = *i++;
if (g == '\n')
{
// Wrap newline, but do not print it.
dy += floorf(getHeight() * getLineHeight() + 0.5f);
dx = 0.0f;
continue;
}
Glyph *glyph = findGlyph(g);
if (glyph->texture != 0)
{
// Copy the vertices and set their proper relative positions.
for (int j = 0; j < 4; j++)
{
glyphverts.push_back(glyph->vertices[j]);
glyphverts.back().x += dx;
glyphverts.back().y += dy + lineheight;
}
// Check if glyph texture has changed since the last iteration.
if (glyphinfolist.size() == 0 || glyphinfolist.back().texture != glyph->texture)
{
// keep track of each sub-section of the string whose glyphs use different textures than the previous section
GlyphArrayDrawInfo gdrawinfo;
gdrawinfo.startvertex = vertexcount;
gdrawinfo.vertexcount = 0;
gdrawinfo.texture = glyph->texture;
glyphinfolist.push_back(gdrawinfo);
}
vertexcount += 4;
glyphinfolist.back().vertexcount += 4;
}
// Advance the x position for the next glyph.
dx += glyph->spacing;
// Account for extra spacing given to space characters.
if (g == ' ' && extra_spacing != 0.0f)
dx = floorf(dx + extra_spacing);
}
}
catch (utf8::exception &e)
{
throw love::Exception("Decoding error: %s", e.what());
}
if (vertexcount <= 0 || glyphinfolist.size() == 0)
return;
// Sort glyph draw info list by texture first, and quad position in memory
// second (using the struct's < operator).
std::sort(glyphinfolist.begin(), glyphinfolist.end());
std::vector<uint16> indices;
int indicescount = 0;
for (auto it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
{
if ((it->vertexcount / 4) * 6 > indicescount)
indicescount = (it->vertexcount / 4) * 6;
}
indices.reserve(indicescount);
for (int i = 0; i < indicescount / 6; i++)
{
// First triangle.
indices.push_back(i * 4 + 0);
indices.push_back(i * 4 + 1);
indices.push_back(i * 4 + 2);
// Second triangle.
indices.push_back(i * 4 + 0);
indices.push_back(i * 4 + 2);
indices.push_back(i * 4 + 3);
}
gl.matrices.transform.push(gl.matrices.transform.top());
Matrix t;
t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.top() *= t;
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.prepareDraw();
// We need to draw a new vertex array for every section of the string which
// uses a different texture than the previous section.
for (auto it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
{
gl.bindTexture(it->texture);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[it->startvertex].x);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[it->startvertex].s);
glDrawElements(GL_TRIANGLES, (it->vertexcount / 4) * 6, GL_UNSIGNED_SHORT, &indices[0]);
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.matrices.transform.pop();
}
int Font::getWidth(const std::string &str)
{
if (str.size() == 0) return 0;
std::istringstream iss(str);
std::string line;
Glyph *g;
int max_width = 0;
while (getline(iss, line, '\n'))
{
int width = 0;
try
{
utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
while (i != end)
{
uint32 c = *i++;
g = findGlyph(c);
width += static_cast<int>(g->spacing * mSpacing);
}
}
catch(utf8::exception &e)
{
throw love::Exception("Decoding error: %s", e.what());
}
if (width > max_width)
max_width = width;
}
return max_width;
}
int Font::getWidth(char character)
{
Glyph *g = findGlyph(character);
return g->spacing;
}
std::vector<std::string> Font::getWrap(const std::string &text, float wrap, int *max_width, std::vector<bool> *wrappedlines)
{
using namespace std;
const float width_space = static_cast<float>(getWidth(' '));
vector<string> lines_to_draw;
int maxw = 0;
//split text at newlines
istringstream iss(text);
string line;
ostringstream string_builder;
while (getline(iss, line, '\n'))
{
// split line into words
vector<string> words;
istringstream word_iss(line);
copy(istream_iterator<string>(word_iss), istream_iterator<string>(),
back_inserter< vector<string> >(words));
// put words back together until a wrap occurs
float width = 0.0f;
float oldwidth = 0.0f;
string_builder.str("");
vector<string>::const_iterator word_iter, wend = words.end();
for (word_iter = words.begin(); word_iter != wend; ++word_iter)
{
const string &word = *word_iter;
width += getWidth(word);
// on wordwrap, push line to line buffer and clear string builder
if (width > wrap && oldwidth > 0)
{
int realw = (int) width;
// remove trailing space
string tmp = string_builder.str();
lines_to_draw.push_back(tmp.substr(0,tmp.size()-1));
string_builder.str("");
width = static_cast<float>(getWidth(word));
realw -= (int) width;
if (realw > maxw)
maxw = realw;
// Indicate that this line was automatically wrapped.
if (wrappedlines)
wrappedlines->push_back(true);
}
string_builder << word << " ";
width += width_space;
oldwidth = width;
}
// push last line
if (width > maxw)
maxw = (int) width;
string tmp = string_builder.str();
lines_to_draw.push_back(tmp.substr(0,tmp.size()-1));
// Indicate that this line was not automatically wrapped.
if (wrappedlines)
wrappedlines->push_back(false);
}
if (max_width)
*max_width = maxw;
return lines_to_draw;
}
void Font::setLineHeight(float height)
{
this->lineHeight = height;
}
float Font::getLineHeight() const
{
return lineHeight;
}
void Font::setSpacing(float amount)
{
mSpacing = amount;
}
float Font::getSpacing() const
{
return mSpacing;
}
void Font::setFilter(const Image::Filter &f)
{
filter = f;
for (auto it = textures.begin(); it != textures.end(); ++it)
{
gl.bindTexture(*it);
filter.anisotropy = gl.setTextureFilter(f);
}
}
const Image::Filter &Font::getFilter()
{
return filter;
}
bool Font::loadVolatile()
{
createTexture();
return true;
}
void Font::unloadVolatile()
{
// nuke everything from orbit
std::map<uint32, Glyph *>::iterator it = glyphs.begin();
Glyph *g;
while (it != glyphs.end())
{
g = it->second;
delete g;
glyphs.erase(it++);
}
std::vector<GLuint>::iterator iter = textures.begin();
while (iter != textures.end())
{
gl.deleteTexture(*iter);
iter++;
}
textures.clear();
}
int Font::getAscent() const
{
return rasterizer->getAscent();
}
int Font::getDescent() const
{
return rasterizer->getDescent();
}
float Font::getBaseline() const
{
// 1.25 is magic line height for true type fonts
return (type == FONT_TRUETYPE) ? floorf(getHeight() / 1.25f + 0.5f) : 0.0f;
}
bool Font::hasGlyph(uint32 glyph) const
{
return rasterizer->hasGlyph(glyph);
}
bool Font::hasGlyphs(const std::string &text) const
{
return rasterizer->hasGlyphs(text);
}
} // opengl
} // graphics
} // love
+219
View File
@@ -0,0 +1,219 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_FONT_H
#define LOVE_GRAPHICS_OPENGL_FONT_H
// STD
#include <map>
#include <string>
#include <vector>
// LOVE
#include "common/Object.h"
#include "font/Rasterizer.h"
#include "graphics/Image.h"
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
class Font : public Object, public Volatile
{
public:
Font(love::font::Rasterizer *r, const Image::Filter &filter = Image::getDefaultFilter());
virtual ~Font();
/**
* Prints the text at the designated position with rotation and scaling.
*
* @param text A string.
* @param x The x-coordinate.
* @param y The y-coordinate.
* @param extra_spacing Additional spacing added to spaces (" ").
* @param angle The amount of rotation.
* @param sx Scale along the x axis.
* @param sy Scale along the y axis.
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @param kx Shear along the x axis.
* @param ky Shear along the y axis.
**/
void print(const std::string &text, float x, float y, float extra_spacing = 0.0f, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
/**
* Returns the height of the font.
**/
float getHeight() const;
/**
* Returns the width of the passed string.
*
* @param str A string of text.
**/
int getWidth(const std::string &str);
/**
* Returns the width of the passed character.
*
* @param character A character.
**/
int getWidth(char character);
/**
* Returns the maximal width of a wrapped string
* and optionally the number of lines
*
* @param text The input text
* @param wrap The number of pixels to wrap at
* @param max_width Optional output of the maximum width
* @param wrapped_lines Optional output indicating which lines were
* auto-wrapped. Indices correspond to indices of the returned value.
* Returns a vector with the lines.
**/
std::vector<std::string> getWrap(const std::string &text, float wrap, int *max_width = 0, std::vector<bool> *wrapped_lines = 0);
/**
* Sets the line height (which should be a number to multiply the font size by,
* example: line height = 1.2 and size = 12 means that rendered line height = 12*1.2)
* @param height The new line height.
**/
void setLineHeight(float height);
/**
* Returns the line height.
**/
float getLineHeight() const;
/**
* Sets the spacing modifier (changes the spacing between the characters the
* same way that the line height does [multiplication]).
* Note: The spacing must be set BEFORE the font is loaded to have any effect.
* @param amount The amount of modification.
**/
void setSpacing(float amount);
/**
* Returns the spacing modifier.
**/
float getSpacing() const;
void setFilter(const Image::Filter &f);
const Image::Filter &getFilter();
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
// Extra font metrics
int getAscent() const;
int getDescent() const;
float getBaseline() const;
bool hasGlyph(uint32 glyph) const;
bool hasGlyphs(const std::string &text) const;
private:
enum FontType
{
FONT_TRUETYPE = 1,
FONT_IMAGE,
FONT_UNKNOWN
};
struct GlyphVertex
{
float x, y;
float s, t;
};
struct Glyph
{
GLuint texture;
int spacing;
GlyphVertex vertices[4];
};
// used to determine when to change textures in the vertex array generated when printing text
struct GlyphArrayDrawInfo
{
GLuint texture;
int startvertex;
int vertexcount;
// used when sorting with std::sort
// sorts by texture first (binding textures is expensive) and relative position in memory second
bool operator < (const GlyphArrayDrawInfo &other) const
{
if (texture != other.texture)
return texture < other.texture;
else
return startvertex < other.startvertex;
};
};
bool initializeTexture(GLint format);
void createTexture();
Glyph *addGlyph(uint32 glyph);
Glyph *findGlyph(uint32 glyph);
love::font::Rasterizer *rasterizer;
int height;
float lineHeight;
float mSpacing; // modifies the spacing by multiplying it with this value
int textureSizeIndex;
int textureWidth;
int textureHeight;
// vector of packed textures
std::vector<GLuint> textures;
// maps glyphs to glyph texture information
std::map<uint32, Glyph *> glyphs;
FontType type;
Image::Filter filter;
static const int NUM_TEXTURE_SIZES = 7;
static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
static const int TEXTURE_PADDING = 1;
int textureX, textureY;
int rowHeight;
}; // Font
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_FONT_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,479 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_GRAPHICS_H
#define LOVE_GRAPHICS_OPENGL_GRAPHICS_H
// STD
#include <iostream>
#include <stack>
#include <vector>
// OpenGL
#include "OpenGL.h"
// LOVE
#include "graphics/Graphics.h"
#include "graphics/Color.h"
#include "image/Image.h"
#include "image/ImageData.h"
#include "window/Window.h"
#include "Font.h"
#include "Image.h"
#include "graphics/Quad.h"
#include "SpriteBatch.h"
#include "ParticleSystem.h"
#include "Canvas.h"
#include "Shader.h"
#include "Mesh.h"
namespace love
{
namespace graphics
{
namespace opengl
{
// During display mode changing, certain
// variables about the OpenGL context are
// lost.
struct DisplayState
{
// Colors.
Color color;
Color backgroundColor;
// Blend mode.
Graphics::BlendMode blendMode;
// Line.
Graphics::LineStyle lineStyle;
Graphics::LineJoin lineJoin;
// Point.
float pointSize;
Graphics::PointStyle pointStyle;
// Scissor.
bool scissor;
OpenGL::Viewport scissorBox;
// Color mask.
bool colorMask[4];
// Default values.
DisplayState()
{
color.set(255,255,255,255);
backgroundColor.set(0, 0, 0, 255);
blendMode = Graphics::BLEND_ALPHA;
lineStyle = Graphics::LINE_SMOOTH;
lineJoin = Graphics::LINE_JOIN_MITER;
pointSize = 1.0f;
pointStyle = Graphics::POINT_SMOOTH;
scissor = false;
colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true;
}
};
class Graphics : public love::graphics::Graphics
{
public:
Graphics();
virtual ~Graphics();
// Implements Module.
const char *getName() const;
DisplayState saveState();
void restoreState(const DisplayState &s);
virtual void setViewportSize(int width, int height);
virtual bool setMode(int width, int height);
virtual void unSetMode();
/**
* Resets the current color, background color,
* line style, and so forth. (This will be called
* when the game reloads.
**/
void reset();
/**
* Clears the screen.
**/
void clear();
/**
* Flips buffers. (Rendered geometry is presented on screen).
**/
void present();
/**
* Gets the width of the current graphics viewport.
**/
int getWidth() const;
/**
* Gets the height of the current graphics viewport.
**/
int getHeight() const;
/**
* True if a graphics viewport is set.
**/
bool isCreated() const;
/**
* Scissor defines a box such that everything outside that box is discarded and not drawn.
* Scissoring is automatically enabled.
* @param x The x-coordinate of the top-left corner.
* @param y The y-coordinate of the top-left corner.
* @param width The width of the box.
* @param height The height of the box.
**/
void setScissor(int x, int y, int width, int height);
/**
* Clears any scissor that has been created.
**/
void setScissor();
/**
* This native Lua function gets the current scissor box in the order of:
* x, y, width, height
**/
int getScissor(lua_State *L) const;
/**
* Enables the stencil buffer and set stencil function to fill it
*/
void defineStencil();
/**
* Set stencil function to mask the following drawing calls using
* the current stencil buffer
* @param invert Invert the mask, i.e. draw everywhere expect where
* the mask is defined.
*/
void useStencil(bool invert = false);
/**
* Disables the stencil buffer
*/
void discardStencil();
/**
* Gets the maximum supported width or height of Images and Canvases on this
* system.
**/
int getMaxImageSize() const;
/**
* Creates an Image object with padding and/or optimization.
**/
Image *newImage(love::image::ImageData *data);
Image *newImage(love::image::CompressedData *cdata);
Quad *newQuad(Quad::Viewport v, float sw, float sh);
/**
* Creates a Font object.
**/
Font *newFont(love::font::Rasterizer *data, const Image::Filter &filter = Image::Filter());
SpriteBatch *newSpriteBatch(Image *image, int size, int usage);
ParticleSystem *newParticleSystem(Image *image, int size);
Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL);
Shader *newShader(const Shader::ShaderSources &sources);
Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
/**
* Sets the foreground color.
* @param c The new foreground color.
**/
void setColor(const Color &c);
/**
* Gets current color.
**/
Color getColor() const;
/**
* Sets the background Color.
**/
void setBackgroundColor(const Color &c);
/**
* Gets the current background color.
**/
Color getBackgroundColor() const;
/**
* Sets the current font.
* @param font A Font object.
**/
void setFont(Font *font);
/**
* Gets the current Font, or nil if none.
**/
Font *getFont() const;
/**
* Sets the enabled color components when rendering.
**/
void setColorMask(bool r, bool g, bool b, bool a);
/**
* Gets the current color mask.
* Returns an array of 4 booleans representing the mask.
**/
const bool *getColorMask() const;
/**
* Sets the current blend mode.
**/
void setBlendMode(BlendMode mode);
/**
* Gets the current blend mode.
**/
BlendMode getBlendMode() const;
/**
* Sets the default filter for images, canvases, and fonts.
**/
void setDefaultFilter(const Image::Filter &f);
/**
* Gets the default filter for images, canvases, and fonts.
**/
const Image::Filter &getDefaultFilter() const;
/**
* Default Image mipmap filter mode and sharpness values.
**/
void setDefaultMipmapFilter(Image::FilterMode filter, float sharpness);
void getDefaultMipmapFilter(Image::FilterMode *filter, float *sharpness) const;
/**
* Sets the line width.
* @param width The new width of the line.
**/
void setLineWidth(float width);
/**
* Sets the line style.
* @param style LINE_ROUGH or LINE_SMOOTH.
**/
void setLineStyle(LineStyle style);
/**
* Sets the line style.
* @param style LINE_ROUGH or LINE_SMOOTH.
**/
void setLineJoin(LineJoin style);
/**
* Gets the line width.
**/
float getLineWidth() const;
/**
* Gets the line style.
**/
LineStyle getLineStyle() const;
/**
* Gets the line style.
**/
LineJoin getLineJoin() const;
/**
* Sets the size of points.
**/
void setPointSize(float size);
/**
* Sets the style of points.
* @param style POINT_SMOOTH or POINT_ROUGH.
**/
void setPointStyle(PointStyle style);
/**
* Gets the point size.
**/
float getPointSize() const;
/**
* Gets the point style.
**/
PointStyle getPointStyle() const;
/**
* Gets the maximum point size supported.
* This may vary from computer to computer.
**/
int getMaxPointSize() const;
/**
* Draws text at the specified coordinates, with rotation and
* scaling along both axes.
* @param x The x-coordinate.
* @param y The y-coordinate.
* @param angle The amount of rotation.
* @param sx The scale factor along the x-axis. (1 = normal).
* @param sy The scale factor along the y-axis. (1 = normal).
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
void print(const std::string &str, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* Draw formatted text on screen at the specified coordinates.
*
* @param str A string of text.
* @param x The x-coordinate.
* @param y The y-coordinate.
* @param wrap The maximum width of the text area.
* @param align Where to align the text.
* @param angle The amount of rotation.
* @param sx The scale factor along the x-axis. (1 = normal).
* @param sy The scale factor along the y-axis. (1 = normal).
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
void printf(const std::string &str, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* Draws a point at (x,y).
* @param x Point along x-axis.
* @param y Point along y-axis.
**/
void point(float x, float y);
/**
* Draws a series of lines connecting the given vertices.
* @param coords Vertex components (x1, y1, ..., xn, yn). If x1,y1 == xn,yn the line will be drawn closed.
* @param count Number of items in the array, i.e. count = 2 * n
**/
void polyline(const float *coords, size_t count);
/**
* Draws a rectangle.
* @param x Position along x-axis for top-left corner.
* @param y Position along y-axis for top-left corner.
* @param w The width of the rectangle.
* @param h The height of the rectangle.
**/
void rectangle(DrawMode mode, float x, float y, float w, float h);
/**
* Draws a circle using the specified arguments.
* @param mode The mode of drawing (line/filled).
* @param x X-coordinate.
* @param y Y-coordinate.
* @param radius Radius of the circle.
* @param points Number of points to use to draw the circle.
**/
void circle(DrawMode mode, float x, float y, float radius, int points = 10);
/**
* Draws an arc using the specified arguments.
* @param mode The mode of drawing (line/filled).
* @param x X-coordinate.
* @param y Y-coordinate.
* @param radius Radius of the arc.
* @param angle1 The angle at which the arc begins.
* @param angle2 The angle at which the arc terminates.
* @param points Number of points to use to draw the arc.
**/
void arc(DrawMode mode, float x, float y, float radius, float angle1, float angle2, int points = 10);
/**
* Draws a polygon with an arbitrary number of vertices.
* @param mode The type of drawing (line/filled).
* @param coords Vertex components (x1, y1, x2, y2, etc.)
* @param count Coord array size
**/
void polygon(DrawMode mode, const float *coords, size_t count);
/**
* Creates a screenshot of the view and saves it to the default folder.
* @param image The love.image module.
* @param copyAlpha If the alpha channel should be copied or set to full opacity (255).
**/
love::image::ImageData *newScreenshot(love::image::Image *image, bool copyAlpha = true);
/**
* Returns a string containing system-dependent renderer information.
* Returned string can vary greatly between systems! Do not rely on it for
* anything!
* @param infotype The type of information to return.
**/
std::string getRendererInfo(Graphics::RendererInfo infotype) const;
void push();
void pop();
void rotate(float r);
void scale(float x, float y = 1.0f);
void translate(float x, float y);
void shear(float kx, float ky);
void origin();
private:
Font *currentFont;
love::window::Window *currentWindow;
std::vector<double> pixel_size_stack; // stores current size of a pixel (needed for line drawing)
LineStyle lineStyle;
LineJoin lineJoin;
float lineWidth;
size_t matrixLimit;
bool colorMask[4];
int width;
int height;
bool created;
DisplayState savedState;
}; // Graphics
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_GRAPHICS_H
@@ -0,0 +1,648 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "Image.h"
// STD
#include <cstring> // For memcpy
#include <algorithm> // for min/max
namespace love
{
namespace graphics
{
namespace opengl
{
float Image::maxMipmapSharpness = 0.0f;
Image::FilterMode Image::defaultMipmapFilter = Image::FILTER_NONE;
float Image::defaultMipmapSharpness = 0.0f;
Image::Image(love::image::ImageData *data)
: data(data)
, cdata(0)
, width(data->getWidth())
, height(data->getHeight())
, paddedWidth(width)
, paddedHeight(height)
, texture(0)
, mipmapSharpness(defaultMipmapSharpness)
, mipmapsCreated(false)
, compressed(false)
, usingDefaultTexture(false)
{
data->retain();
preload();
}
Image::Image(love::image::CompressedData *cdata)
: data(0)
, cdata(cdata)
, width(cdata->getWidth(0))
, height(cdata->getHeight(0))
, paddedWidth(width)
, paddedHeight(height)
, texture(0)
, mipmapSharpness(defaultMipmapSharpness)
, mipmapsCreated(false)
, compressed(true)
, usingDefaultTexture(false)
{
cdata->retain();
preload();
}
Image::~Image()
{
if (data != 0)
data->release();
if (cdata != 0)
cdata->release();
unload();
}
int Image::getWidth() const
{
return width;
}
int Image::getHeight() const
{
return height;
}
const Vertex *Image::getVertices() const
{
return vertices;
}
love::image::ImageData *Image::getImageData() const
{
return data;
}
love::image::CompressedData *Image::getCompressedData() const
{
return cdata;
}
void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
drawv(t, vertices);
}
void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
drawv(t, quad->getVertices());
}
void Image::predraw() const
{
bind();
if (width != paddedWidth || height != paddedHeight)
{
// NPOT image padded to POT size, so the texcoords should be scaled.
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glScalef(float(width) / float(paddedWidth), float(height) / float(paddedHeight), 0.0f);
glMatrixMode(GL_MODELVIEW);
}
}
void Image::postdraw() const
{
if (width != paddedWidth || height != paddedHeight)
{
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
}
void Image::uploadCompressedMipmaps()
{
if (!isCompressed() || !cdata || !hasCompressedTextureSupport(cdata->getFormat()))
return;
bind();
int count = cdata->getMipmapCount();
// We have to inform OpenGL if the image doesn't have all mipmap levels.
if (GLAD_VERSION_1_2 || GLAD_ES_VERSION_3_0 || GLAD_APPLE_texture_max_level)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
}
else if (cdata->getWidth(count-1) > 1 || cdata->getHeight(count-1) > 1)
{
// Telling OpenGL to ignore certain levels isn't always supported.
throw love::Exception("Cannot load mipmaps: "
"compressed image does not have all required levels.");
}
for (int i = 1; i < count; i++)
{
glCompressedTexImage2D(GL_TEXTURE_2D,
i,
getCompressedFormat(cdata->getFormat()),
cdata->getWidth(i),
cdata->getHeight(i),
0,
GLsizei(cdata->getSize(i)),
cdata->getData(i));
}
}
void Image::createMipmaps()
{
// Only valid for Images created with ImageData.
if (!data || isCompressed())
return;
if (!hasMipmapSupport())
throw love::Exception("Mipmap filtering is not supported on this system.");
// Some old drivers claim support for NPOT textures, but fail when creating
// mipmaps. We can't detect which systems will do this, so we fail gracefully
// for all NPOT images.
int w = int(width), h = int(height);
if (w != next_p2(w) || h != next_p2(h))
{
throw love::Exception("Cannot create mipmaps: "
"image does not have power of two dimensions.");
}
bind();
// Prevent other threads from changing the ImageData while we upload it.
love::thread::Lock lock(data->getMutex());
if (hasNpot() && (GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
{
if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD)
{
// AMD/ATI drivers have several bugs when generating mipmaps,
// re-uploading the entire base image seems to be required.
uploadTexture();
// More bugs: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
glEnable(GL_TEXTURE_2D);
}
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0,
0,
(GLsizei)width,
(GLsizei)height,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
}
}
void Image::checkMipmapsCreated()
{
if (mipmapsCreated || filter.mipmap == FILTER_NONE || usingDefaultTexture)
return;
if (isCompressed() && cdata && hasCompressedTextureSupport(cdata->getFormat()))
uploadCompressedMipmaps();
else if (data)
createMipmaps();
else
return;
mipmapsCreated = true;
}
void Image::setFilter(const Image::Filter &f)
{
filter = f;
// We don't want filtering or (attempted) mipmaps on the default texture.
if (usingDefaultTexture)
{
filter.mipmap = FILTER_NONE;
filter.min = filter.mag = FILTER_NEAREST;
}
bind();
filter.anisotropy = gl.setTextureFilter(filter);
checkMipmapsCreated();
}
const Image::Filter &Image::getFilter() const
{
return filter;
}
void Image::setWrap(const Image::Wrap &w)
{
wrap = w;
bind();
gl.setTextureWrap(w);
}
const Image::Wrap &Image::getWrap() const
{
return wrap;
}
void Image::setMipmapSharpness(float sharpness)
{
if (hasMipmapSharpnessSupport())
{
// LOD bias has the range (-maxbias, maxbias)
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
bind();
// negative bias is sharper
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
}
else
mipmapSharpness = 0.0f;
}
float Image::getMipmapSharpness() const
{
return mipmapSharpness;
}
void Image::bind() const
{
if (texture == 0)
return;
gl.bindTexture(texture);
}
void Image::preload()
{
memset(vertices, 255, sizeof(Vertex)*4);
vertices[0].x = 0;
vertices[0].y = 0;
vertices[1].x = 0;
vertices[1].y = (float) height;
vertices[2].x = (float) width;
vertices[2].y = (float) height;
vertices[3].x = (float) width;
vertices[3].y = 0;
vertices[0].s = 0;
vertices[0].t = 0;
vertices[1].s = 0;
vertices[1].t = 1;
vertices[2].s = 1;
vertices[2].t = 1;
vertices[3].s = 1;
vertices[3].t = 0;
filter = getDefaultFilter();
filter.mipmap = defaultMipmapFilter;
}
bool Image::load()
{
return loadVolatile();
}
void Image::unload()
{
return unloadVolatile();
}
bool Image::loadVolatile()
{
if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat()))
{
const char *str;
if (image::CompressedData::getConstant(cdata->getFormat(), str))
{
throw love::Exception("Cannot create image: "
"%s compressed images are not supported on this system.", str);
}
else
throw love::Exception("cannot create image: format is not supported on this system.");
}
if (hasMipmapSharpnessSupport() && maxMipmapSharpness == 0.0f)
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
glGenTextures(1, &texture);
gl.bindTexture(texture);
filter.anisotropy = gl.setTextureFilter(filter);
gl.setTextureWrap(wrap);
setMipmapSharpness(mipmapSharpness);
paddedWidth = width;
paddedHeight = height;
if (!hasNpot())
{
// NPOT textures will be padded to POT dimensions if necessary.
paddedWidth = next_p2(width);
paddedHeight = next_p2(height);
}
// Use a default texture if the size is too big for the system.
if (paddedWidth > gl.getMaxTextureSize() || paddedHeight > gl.getMaxTextureSize())
{
uploadDefaultTexture();
return true;
}
// Mutex lock will potentially cover texture loading and mipmap creation.
love::thread::EmptyLock lock;
if (data)
lock.setLock(data->getMutex());
while (glGetError() != GL_NO_ERROR); // Clear errors.
if (hasNpot() || (width == paddedWidth && height == paddedHeight))
uploadTexture();
else
uploadTexturePadded();
GLenum glerr = glGetError();
if (glerr != GL_NO_ERROR)
throw love::Exception("Cannot create image (error code 0x%x)", glerr);
usingDefaultTexture = false;
mipmapsCreated = false;
checkMipmapsCreated();
return true;
}
void Image::uploadTexturePadded()
{
if (isCompressed() && cdata)
{
// Padded textures don't really work if they're compressed...
throw love::Exception("Cannot create image: "
"compressed NPOT images are not supported on this system.");
}
else if (data)
{
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
(GLsizei)paddedWidth,
(GLsizei)paddedHeight,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
0);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0, 0,
(GLsizei)width,
(GLsizei)height,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
}
}
void Image::uploadTexture()
{
if (isCompressed() && cdata)
{
GLenum format = getCompressedFormat(cdata->getFormat());
glCompressedTexImage2D(GL_TEXTURE_2D,
0,
format,
cdata->getWidth(0),
cdata->getHeight(0),
0,
GLsizei(cdata->getSize(0)),
cdata->getData(0));
}
else if (data)
{
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
(GLsizei)width,
(GLsizei)height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
}
}
void Image::unloadVolatile()
{
// Delete the hardware texture.
if (texture != 0)
{
gl.deleteTexture(texture);
texture = 0;
}
}
bool Image::refresh()
{
// No effect if the texture hasn't been created yet.
if (texture == 0)
return false;
if (usingDefaultTexture)
{
uploadDefaultTexture();
return true;
}
// We want this lock to potentially cover mipmap creation as well.
love::thread::EmptyLock lock;
bind();
if (data && !isCompressed())
lock.setLock(data->getMutex());
while (glGetError() != GL_NO_ERROR); // Clear errors.
if (hasNpot() || (width == paddedWidth && height == paddedHeight))
uploadTexture();
else
uploadTexturePadded();
if (glGetError() != GL_NO_ERROR)
uploadDefaultTexture();
else
usingDefaultTexture = false;
mipmapsCreated = false;
checkMipmapsCreated();
return true;
}
void Image::uploadDefaultTexture()
{
usingDefaultTexture = true;
bind();
setFilter(filter);
// A nice friendly checkerboard to signify invalid textures...
GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xC0,0xC0,0xC0,0xFF,
0xC0,0xC0,0xC0,0xFF, 0xFF,0xFF,0xFF,0xFF};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px);
}
void Image::drawv(const Matrix &t, const Vertex *v) const
{
predraw();
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
gl.prepareDraw();
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].x);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.matrices.transform.pop();
postdraw();
}
void Image::setDefaultMipmapSharpness(float sharpness)
{
defaultMipmapSharpness = sharpness;
}
float Image::getDefaultMipmapSharpness()
{
return defaultMipmapSharpness;
}
void Image::setDefaultMipmapFilter(Image::FilterMode f)
{
defaultMipmapFilter = f;
}
Image::FilterMode Image::getDefaultMipmapFilter()
{
return defaultMipmapFilter;
}
bool Image::isCompressed() const
{
return compressed;
}
GLenum Image::getCompressedFormat(image::CompressedData::Format format) const
{
switch (format)
{
case image::CompressedData::FORMAT_DXT1:
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
case image::CompressedData::FORMAT_DXT3:
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
case image::CompressedData::FORMAT_DXT5:
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case image::CompressedData::FORMAT_BC4:
return GL_COMPRESSED_RED_RGTC1;
case image::CompressedData::FORMAT_BC4s:
return GL_COMPRESSED_SIGNED_RED_RGTC1;
case image::CompressedData::FORMAT_BC5:
return GL_COMPRESSED_RG_RGTC2;
case image::CompressedData::FORMAT_BC5s:
return GL_COMPRESSED_SIGNED_RG_RGTC2;
default:
return GL_RGBA;
}
}
bool Image::hasNpot()
{
return GLAD_ES_VERSION_2_0 || GLAD_VERSION_2_0 || GLAD_ARB_texture_non_power_of_two;
}
bool Image::hasAnisotropicFilteringSupport()
{
return GLAD_EXT_texture_filter_anisotropic;
}
bool Image::hasMipmapSupport()
{
return GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_4 || GLAD_SGIS_generate_mipmap;
}
bool Image::hasMipmapSharpnessSupport()
{
return GLAD_VERSION_1_4;
}
bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
{
switch (format)
{
case image::CompressedData::FORMAT_DXT1:
case image::CompressedData::FORMAT_DXT3:
case image::CompressedData::FORMAT_DXT5:
return GLAD_EXT_texture_compression_s3tc;
case image::CompressedData::FORMAT_BC4:
case image::CompressedData::FORMAT_BC4s:
case image::CompressedData::FORMAT_BC5:
case image::CompressedData::FORMAT_BC5s:
return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
default:
break;
}
return false;
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,220 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_IMAGE_H
#define LOVE_GRAPHICS_OPENGL_IMAGE_H
// LOVE
#include "common/config.h"
#include "common/Matrix.h"
#include "common/Vector.h"
#include "common/math.h"
#include "image/ImageData.h"
#include "image/CompressedData.h"
#include "graphics/Image.h"
// OpenGL
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* A drawable image based on OpenGL-textures. This class takes ImageData
* objects and create textures on the GPU for fast drawing.
*
* @author Anders Ruud
**/
class Image : public love::graphics::Image
{
public:
/**
* Creates a new Image. Not that anything is ready to use
* before load is called.
*
* @param data The data from which to load the image.
**/
Image(love::image::ImageData *data);
/**
* Creates a new Image with compressed image data.
*
* @param cdata The compressed data from which to load the image.
**/
Image(love::image::CompressedData *cdata);
/**
* Destructor. Deletes the hardware texture and other resources.
**/
virtual ~Image();
int getWidth() const;
int getHeight() const;
const Vertex *getVertices() const;
love::image::ImageData *getImageData() const;
love::image::CompressedData *getCompressedData() const;
/**
* @copydoc Drawable::draw()
**/
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* @copydoc DrawQable::drawq()
**/
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* Call before using this Image's texture to draw. Binds the texture,
* globally scales texture coordinates if the Image has NPOT dimensions and
* NPOT isn't supported, etc.
**/
void predraw() const;
void postdraw() const;
/**
* Sets the filter mode.
* @param f The filter mode.
**/
void setFilter(const Image::Filter &f);
const Image::Filter &getFilter() const;
void setWrap(const Image::Wrap &w);
const Image::Wrap &getWrap() const;
void setMipmapSharpness(float sharpness);
float getMipmapSharpness() const;
/**
* Whether this Image is using a compressed texture (via CompressedData).
**/
bool isCompressed() const;
void bind() const;
bool load();
void unload();
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
/**
* Re-uploads the ImageData or CompressedData associated with this Image to
* the GPU, allowing situations where lovers modify an ImageData after image
* creation from the ImageData, and apply the changes with Image:refresh().
**/
bool refresh();
static void setDefaultMipmapSharpness(float sharpness);
static float getDefaultMipmapSharpness();
static void setDefaultMipmapFilter(FilterMode f);
static FilterMode getDefaultMipmapFilter();
static bool hasNpot();
static bool hasAnisotropicFilteringSupport();
static bool hasMipmapSupport();
static bool hasMipmapSharpnessSupport();
static bool hasCompressedTextureSupport(image::CompressedData::Format format);
private:
void uploadDefaultTexture();
void drawv(const Matrix &t, const Vertex *v) const;
friend class Shader;
GLuint getTextureName() const
{
return texture;
}
// The ImageData from which the texture is created. May be null if
// Compressed image data was used to create the texture.
love::image::ImageData *data;
// Or the Compressed Image Data from which the texture is created. May be
// null if raw ImageData was used to create the texture.
love::image::CompressedData *cdata;
// Width and height of the hardware texture.
int width, height;
// Real dimensions of the texture, if it was auto-padded to POT size.
int paddedWidth, paddedHeight;
// OpenGL texture identifier.
GLuint texture;
// The source vertices of the image.
Vertex vertices[4];
// Mipmap texture LOD bias (sharpness) value.
float mipmapSharpness;
// True if mipmaps have been created for this Image.
bool mipmapsCreated;
// Whether this Image is using a compressed texture.
bool compressed;
// True if the image wasn't able to be properly created and it had to fall
// back to a default texture.
bool usingDefaultTexture;
// The image's filter mode
Image::Filter filter;
// The image's wrap mode
Image::Wrap wrap;
void preload();
void uploadTexturePadded();
void uploadTexture();
void uploadCompressedMipmaps();
void createMipmaps();
void checkMipmapsCreated();
static float maxMipmapSharpness;
static FilterMode defaultMipmapFilter;
static float defaultMipmapSharpness;
GLenum getCompressedFormat(image::CompressedData::Format format) const;
}; // Image
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_IMAGE_H
@@ -0,0 +1,330 @@
/**
* Copyright (c) 2006-2013 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 "Mesh.h"
#include "common/Matrix.h"
#include "common/Exception.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
: vbo(nullptr)
, vertex_count(0)
, ibo(nullptr)
, element_count(0)
, draw_mode(mode)
, image(nullptr)
, colors_enabled(false)
{
setVertices(verts);
}
Mesh::~Mesh()
{
delete vbo;
delete ibo;
}
void Mesh::setVertices(const std::vector<Vertex> &verts)
{
if (verts.size() < 3)
throw love::Exception("At least 3 vertices are required.");
size_t size = sizeof(Vertex) * verts.size();
if (vbo && size > vbo->getSize())
{
delete vbo;
vbo = nullptr;
}
if (!vbo)
{
// Full memory backing because we might access the data at any time.
vbo = VertexBuffer::Create(size, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, VertexBuffer::BACKING_FULL);
}
vertex_count = verts.size();
VertexBuffer::Bind vbo_bind(*vbo);
VertexBuffer::Mapper vbo_mapper(*vbo);
// Fill the buffer with the vertices.
memcpy(vbo_mapper.get(), &verts[0], size);
}
const Vertex *Mesh::getVertices() const
{
if (vbo)
{
VertexBuffer::Bind vbo_bind(*vbo);
return (Vertex *) vbo->map();
}
return nullptr;
}
void Mesh::setVertex(size_t index, const Vertex &v)
{
if (index >= vertex_count)
throw love::Exception("Invalid vertex index: %ld", index + 1);
VertexBuffer::Bind vbo_bind(*vbo);
// We unmap the vertex buffer in Mesh::draw. This lets us coalesce the
// buffer transfer calls into just one.
Vertex *vertices = (Vertex *) vbo->map();
vertices[index] = v;
}
Vertex Mesh::getVertex(size_t index) const
{
if (index >= vertex_count)
throw love::Exception("Invalid vertex index: %ld", index + 1);
VertexBuffer::Bind vbo_bind(*vbo);
// We unmap the vertex buffer in Mesh::draw.
Vertex *vertices = (Vertex *) vbo->map();
return vertices[index];
}
size_t Mesh::getVertexCount() const
{
return vertex_count;
}
void Mesh::setVertexMap(const std::vector<uint32> &map)
{
for (size_t i = 0; i < map.size(); i++)
{
if (map[i] >= vertex_count)
throw love::Exception("Invalid vertex map value: %d", map[i] + 1);
}
size_t size = sizeof(uint32) * map.size();
if (ibo && size > ibo->getSize())
{
delete ibo;
ibo = nullptr;
}
if (!ibo && size > 0)
{
// Full memory backing because we might access the data at any time.
ibo = VertexBuffer::Create(size, GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW, VertexBuffer::BACKING_FULL);
}
element_count = map.size();
if (ibo && element_count > 0)
{
VertexBuffer::Bind ibo_bind(*ibo);
VertexBuffer::Mapper ibo_map(*ibo);
// Fill the buffer.
memcpy(ibo_map.get(), &map[0], size);
}
}
const uint32 *Mesh::getVertexMap() const
{
if (ibo && element_count > 0)
{
VertexBuffer::Bind ibo_bind(*ibo);
// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
return (uint32 *) ibo->map();
}
return 0;
}
size_t Mesh::getVertexMapCount() const
{
return element_count;
}
void Mesh::setImage(Image *img)
{
img->retain();
if (image)
image->release();
image = img;
}
void Mesh::setImage()
{
if (image)
image->release();
image = nullptr;
}
Image *Mesh::getImage() const
{
return image;
}
void Mesh::setDrawMode(Mesh::DrawMode mode)
{
draw_mode = mode;
}
Mesh::DrawMode Mesh::getDrawMode() const
{
return draw_mode;
}
void Mesh::setVertexColors(bool enable)
{
colors_enabled = enable;
}
bool Mesh::hasVertexColors() const
{
return colors_enabled;
}
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
const size_t pos_offset = offsetof(Vertex, x);
const size_t tex_offset = offsetof(Vertex, s);
const size_t color_offset = offsetof(Vertex, r);
if (vertex_count == 0)
return;
if (image)
image->predraw();
else
gl.bindTexture(0);
Matrix m;
m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= m;
VertexBuffer::Bind vbo_bind(*vbo);
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
vbo->unmap();
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), vbo->getPointer(pos_offset));
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), vbo->getPointer(tex_offset));
if (hasVertexColors())
{
// Per-vertex colors.
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, sizeof(Vertex), vbo->getPointer(color_offset));
}
gl.prepareDraw();
GLenum mode = getGLDrawMode(draw_mode);
if (ibo && element_count > 0)
{
VertexBuffer::Bind ibo_bind(*ibo);
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
ibo->unmap();
// Use the custom vertex map to draw the vertices.
glDrawElements(mode, element_count, GL_UNSIGNED_INT, ibo->getPointer(0));
}
else
{
// Normal non-indexed drawing (no custom vertex map.)
glDrawArrays(mode, 0, vertex_count);
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
if (hasVertexColors())
{
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
// Using the color array leaves the GL constant color undefined.
gl.setColor(gl.getColor());
}
gl.matrices.transform.pop();
if (image)
image->postdraw();
}
GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
{
switch (mode)
{
case DRAW_MODE_FAN:
return GL_TRIANGLE_FAN;
case DRAW_MODE_STRIP:
return GL_TRIANGLE_STRIP;
case DRAW_MODE_TRIANGLES:
return GL_TRIANGLES;
case DRAW_MODE_POINTS:
return GL_POINTS;
default:
break;
}
return GL_TRIANGLES;
}
bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
{
return drawModes.find(in, out);
}
bool Mesh::getConstant(Mesh::DrawMode in, const char *&out)
{
return drawModes.find(in, out);
}
StringMap<Mesh::DrawMode, Mesh::DRAW_MODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
{
{"fan", Mesh::DRAW_MODE_FAN},
{"strip", Mesh::DRAW_MODE_STRIP},
{"triangles", Mesh::DRAW_MODE_TRIANGLES},
{"points", Mesh::DRAW_MODE_POINTS},
};
StringMap<Mesh::DrawMode, Mesh::DRAW_MODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
} // opengl
} // graphics
} // love
+175
View File
@@ -0,0 +1,175 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_MESH_H
#define LOVE_GRAPHICS_OPENGL_MESH_H
// LOVE
#include "common/int.h"
#include "common/math.h"
#include "common/StringMap.h"
#include "graphics/Drawable.h"
#include "Image.h"
#include "VertexBuffer.h"
// C++
#include <vector>
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* Holds and draws arbitrary vertex geometry.
* Each vertex in the Mesh has a position, texture coordinate, and color.
**/
class Mesh : public Drawable
{
public:
// How the Mesh's vertices are used when drawing.
// http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png
enum DrawMode
{
DRAW_MODE_FAN,
DRAW_MODE_STRIP,
DRAW_MODE_TRIANGLES,
DRAW_MODE_POINTS,
DRAW_MODE_MAX_ENUM
};
/**
* Constructor.
* @param verts The vertices to use in the Mesh.
* @param mode The draw mode to use when drawing the Mesh.
**/
Mesh(const std::vector<Vertex> &verts, DrawMode mode = DRAW_MODE_FAN);
virtual ~Mesh();
/**
* Replaces all the vertices in the Mesh with a new set of vertices.
**/
void setVertices(const std::vector<Vertex> &verts);
/**
* Gets all of the vertices in the Mesh as an array.
**/
const Vertex *getVertices() const;
/**
* Sets an individual vertex in the Mesh.
* @param index The index into the list of vertices to use.
* @param v The new vertex.
**/
void setVertex(size_t index, const Vertex &v);
Vertex getVertex(size_t index) const;
/**
* Gets the total number of vertices in the Mesh.
**/
size_t getVertexCount() const;
/**
* Sets the vertex map to use when drawing the Mesh. The vertex map
* determines the order in which vertices are used by the draw mode.
* A 0-element vector is equivalent to the default vertex map:
* {0, 1, 2, 3, 4, ...}
**/
void setVertexMap(const std::vector<uint32> &map);
/**
* Gets a pointer to the vertex map array. The pointer is only valid until
* the next function call in the graphics module.
* May return null if the vertex map is empty.
**/
const uint32 *getVertexMap() const;
/**
* Gets the total number of elements in the vertex map array.
**/
size_t getVertexMapCount() const;
/**
* Sets the Image used when drawing the Mesh.
**/
void setImage(Image *img);
/**
* Disables any Image from being used when drawing the Mesh.
**/
void setImage();
/**
* Gets the Image used when drawing the Mesh. May return null if no Image is
* set.
**/
Image *getImage() const;
/**
* Sets the draw mode used when drawing the Mesh.
**/
void setDrawMode(DrawMode mode);
DrawMode getDrawMode() const;
/**
* Sets whether per-vertex colors are enabled. If this is disabled, the
* global color (love.graphics.setColor) will be used for the entire Mesh.
**/
void setVertexColors(bool enable);
bool hasVertexColors() const;
// Implements Drawable.
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
private:
GLenum getGLDrawMode(DrawMode mode) const;
// Vertex buffer.
VertexBuffer *vbo;
size_t vertex_count;
// Element (vertex index) buffer, for the vertex map.
VertexBuffer *ibo;
size_t element_count;
DrawMode draw_mode;
Image *image;
// Whether the per-vertex colors are used when drawing.
bool colors_enabled;
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM>::Entry drawModeEntries[];
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM> drawModes;
}; // Mesh
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_MESH_H
@@ -0,0 +1,645 @@
/**
* Copyright (c) 2006-2013 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 "common/config.h"
#include "OpenGL.h"
#include "Shader.h"
#include "common/Exception.h"
// C++
#include <algorithm>
// C
#include <cstring>
namespace love
{
namespace graphics
{
namespace opengl
{
OpenGL::OpenGL()
: contextInitialized(false)
, maxAnisotropy(1.0f)
, maxTextureSize(0)
, vendor(VENDOR_UNKNOWN)
, state()
{
}
bool OpenGL::initContext()
{
if (contextInitialized)
return true;
if (!gladLoadGL())
return false;
initOpenGLFunctions();
initVendor();
initMatrices();
// Store the current color so we don't have to get it through GL later.
GLfloat glcolor[4];
if (GLAD_ES_VERSION_2_0)
glGetVertexAttribfv(GLuint(ATTRIB_COLOR), GL_CURRENT_VERTEX_ATTRIB, glcolor);
else
glGetFloatv(GL_CURRENT_COLOR, glcolor);
state.color.r = glcolor[0] * 255;
state.color.g = glcolor[1] * 255;
state.color.b = glcolor[2] * 255;
state.color.a = glcolor[3] * 255;
// Same with the current clear color.
glGetFloatv(GL_COLOR_CLEAR_VALUE, glcolor);
state.clearColor.r = glcolor[0] * 255;
state.clearColor.g = glcolor[1] * 255;
state.clearColor.b = glcolor[2] * 255;
state.clearColor.a = glcolor[3] * 255;
// Get the current viewport.
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
// And the current scissor - but we need to compensate for GL scissors
// starting at the bottom left instead of top left.
glGetIntegerv(GL_SCISSOR_BOX, (GLint *) &state.scissor.x);
state.scissor.y = state.viewport.h - (state.scissor.y + state.scissor.h);
if (GLAD_VERSION_1_0)
glGetFloatv(GL_POINT_SIZE, &state.pointSize);
else
state.pointSize = 1.0f;
// Initialize multiple texture unit support for shaders, if available.
state.textureUnits.clear();
if (Shader::isSupported())
{
GLint maxtextureunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits);
state.textureUnits.resize(maxtextureunits, 0);
GLenum curgltextureunit;
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
state.curTextureUnit = curgltextureunit - GL_TEXTURE0;
// Retrieve currently bound textures for each texture unit.
for (size_t i = 0; i < state.textureUnits.size(); i++)
{
glActiveTexture(GL_TEXTURE0 + i);
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[i]);
}
glActiveTexture(curgltextureunit);
}
else
{
// Multitexturing not supported, so we only have 1 texture unit.
state.textureUnits.resize(1, 0);
state.curTextureUnit = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[0]);
}
// This will be non-zero on some platforms.
if (Canvas::isSupported())
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint *) &state.defaultFBO);
initMaxValues();
createDefaultTexture();
contextInitialized = true;
return true;
}
void OpenGL::deInitContext()
{
if (!contextInitialized)
return;
contextInitialized = false;
}
void OpenGL::initVendor()
{
const char *vstr = (const char *) glGetString(GL_VENDOR);
if (!vstr)
{
vendor = VENDOR_UNKNOWN;
return;
}
// http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR
if (strstr(vstr, "ATI Technologies"))
vendor = VENDOR_ATI_AMD;
else if (strstr(vstr, "NVIDIA"))
vendor = VENDOR_NVIDIA;
else if (strstr(vstr, "Intel"))
vendor = VENDOR_INTEL;
else if (strstr(vstr, "Mesa"))
vendor = VENDOR_MESA_SOFT;
else if (strstr(vstr, "Apple Computer"))
vendor = VENDOR_APPLE;
else if (strstr(vstr, "Microsoft"))
vendor = VENDOR_MICROSOFT;
else
vendor = VENDOR_UNKNOWN;
}
void OpenGL::initOpenGLFunctions()
{
// The functionality of the core and ARB VBOs are identical, so we can
// assign the pointers of the ARB functions to the names of the core
// functions, if the latter isn't supported but the former is.
if (GLAD_ARB_vertex_buffer_object && !GLAD_VERSION_1_5)
{
fp_glBindBuffer = (pfn_glBindBuffer) fp_glBindBufferARB;
fp_glBufferData = (pfn_glBufferData) fp_glBufferDataARB;
fp_glBufferSubData = (pfn_glBufferSubData) fp_glBufferSubDataARB;
fp_glDeleteBuffers = (pfn_glDeleteBuffers) fp_glDeleteBuffersARB;
fp_glGenBuffers = (pfn_glGenBuffers) fp_glGenBuffersARB;
fp_glGetBufferParameteriv = (pfn_glGetBufferParameteriv) fp_glGetBufferParameterivARB;
fp_glGetBufferPointerv = (pfn_glGetBufferPointerv) fp_glGetBufferPointervARB;
fp_glGetBufferSubData = (pfn_glGetBufferSubData) fp_glGetBufferSubDataARB;
fp_glIsBuffer = (pfn_glIsBuffer) fp_glIsBufferARB;
fp_glMapBuffer = (pfn_glMapBuffer) fp_glMapBufferARB;
fp_glUnmapBuffer = (pfn_glUnmapBuffer) fp_glUnmapBufferARB;
}
}
void OpenGL::initMaxValues()
{
// We'll need this value to clamp anisotropy.
if (GLAD_EXT_texture_filter_anisotropic)
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
else
maxAnisotropy = 1.0f;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
void OpenGL::initMatrices()
{
while (matrices.transform.size() > 0)
matrices.transform.pop();
while (matrices.projection.size() > 0)
matrices.projection.pop();
matrices.transform.push(Matrix());
matrices.projection.push(Matrix());
}
void OpenGL::createDefaultTexture()
{
// Set the 'default' texture (id 0) as a repeating white pixel. Otherwise,
// texture2D calls inside a shader would return black when drawing graphics
// primitives, which would create the need to use different "passthrough"
// shaders for untextured primitives vs images.
GLuint curtexture = state.textureUnits[state.curTextureUnit];
bindTexture(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLubyte pix = 255;
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, &pix);
bindTexture(curtexture);
}
void OpenGL::prepareDraw()
{
const Matrix &transform = matrices.transform.top();
const Matrix &proj = matrices.projection.top();
Shader *shader = Shader::current;
if (GLAD_ES_VERSION_2_0 && shader)
{
// Send built-in uniforms to the current shader.
shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_MATRIX, 4, transform.getElements(), 1);
shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_MATRIX, 4, proj.getElements(), 1);
Matrix tp_matrix(proj * transform);
shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX, 4, tp_matrix.getElements(), 1);
shader->sendBuiltinFloat(Shader::BUILTIN_POINT_SIZE, 1, &state.pointSize, 1);
}
else if (GLAD_VERSION_1_0)
{
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(proj.getElements());
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(transform.getElements());
}
}
void OpenGL::setColor(const Color &c)
{
if (GLAD_ES_VERSION_2_0)
glVertexAttrib4f(GLuint(ATTRIB_COLOR), c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f);
else
glColor4ubv(&c.r);
state.color = c;
}
Color OpenGL::getColor() const
{
return state.color;
}
void OpenGL::setClearColor(const Color &c)
{
glClearColor(c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f);
state.clearColor = c;
}
Color OpenGL::getClearColor() const
{
return state.clearColor;
}
GLint OpenGL::getGLAttrib(OpenGL::VertexAttrib attrib)
{
if (GLAD_ES_VERSION_2_0)
{
// The enum value maps to a generic vertex attribute index.
return GLint(attrib);
}
else
{
switch (attrib)
{
case ATTRIB_POS:
return GL_VERTEX_ARRAY;
case ATTRIB_TEXCOORD:
return GL_TEXTURE_COORD_ARRAY;
case ATTRIB_COLOR:
return GL_COLOR_ARRAY;
default:
return GLint(attrib);
}
}
return -1;
}
void OpenGL::enableVertexAttribArray(OpenGL::VertexAttrib attrib)
{
GLint glattrib = getGLAttrib(attrib);
if (GLAD_ES_VERSION_2_0)
glEnableVertexAttribArray((GLuint) glattrib);
else
glEnableClientState((GLenum) glattrib);
}
void OpenGL::disableVertexAttribArray(OpenGL::VertexAttrib attrib)
{
GLint glattrib = getGLAttrib(attrib);
if (GLAD_ES_VERSION_2_0)
glDisableVertexAttribArray((GLuint) glattrib);
else
glDisableClientState((GLenum) glattrib);
}
void OpenGL::setVertexAttribArray(OpenGL::VertexAttrib attrib, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
if (GLAD_ES_VERSION_2_0)
{
GLboolean normalized = (type == GL_UNSIGNED_BYTE) ? GL_TRUE : GL_FALSE;
glVertexAttribPointer(GLuint(attrib), size, type, normalized, stride, pointer);
}
else
{
switch (attrib)
{
case ATTRIB_POS:
glVertexPointer(size, type, stride, pointer);
break;
case ATTRIB_TEXCOORD:
glTexCoordPointer(size, type, stride, pointer);
break;
case ATTRIB_COLOR:
glColorPointer(size, type, stride, pointer);
break;
default:
break;
}
}
}
void OpenGL::setViewport(const OpenGL::Viewport &v)
{
glViewport(v.x, v.y, v.w, v.h);
state.viewport = v;
// glScissor starts from the lower left, so we compensate when setting the
// scissor. When the viewport is changed, we need to manually update the
// scissor again.
if (v.h != state.viewport.h)
setScissor(state.scissor);
}
OpenGL::Viewport OpenGL::getViewport() const
{
return state.viewport;
}
void OpenGL::setScissor(const OpenGL::Viewport &v)
{
// We need to compensate for glScissor starting from the lower left of the
// viewport instead of the top left.
glScissor(v.x, state.viewport.h - (v.y + v.h), v.w, v.h);
state.scissor = v;
}
OpenGL::Viewport OpenGL::getScissor() const
{
return state.scissor;
}
void OpenGL::setPointSize(float size)
{
if (GLAD_VERSION_1_0)
glPointSize(size);
state.pointSize = size;
}
float OpenGL::getPointSize() const
{
return state.pointSize;
}
GLuint OpenGL::getDefaultFBO() const
{
return state.defaultFBO;
}
void OpenGL::setTextureUnit(int textureunit)
{
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
throw love::Exception("Invalid texture unit index (%d).", textureunit);
if (textureunit != state.curTextureUnit)
{
if (state.textureUnits.size() > 1)
glActiveTexture(GL_TEXTURE0 + textureunit);
else
throw love::Exception("Multitexturing not supported.");
}
state.curTextureUnit = textureunit;
}
void OpenGL::bindTexture(GLuint texture)
{
if (texture != state.textureUnits[state.curTextureUnit])
{
state.textureUnits[state.curTextureUnit] = texture;
glBindTexture(GL_TEXTURE_2D, texture);
}
}
void OpenGL::bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev)
{
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
throw love::Exception("Invalid texture unit index.");
if (texture != state.textureUnits[textureunit])
{
int oldtextureunit = state.curTextureUnit;
setTextureUnit(textureunit);
state.textureUnits[textureunit] = texture;
glBindTexture(GL_TEXTURE_2D, texture);
if (restoreprev)
setTextureUnit(oldtextureunit);
}
}
void OpenGL::deleteTexture(GLuint texture)
{
// glDeleteTextures binds texture 0 to all texture units the deleted texture
// was bound to before deletion.
std::vector<GLuint>::iterator it;
for (it = state.textureUnits.begin(); it != state.textureUnits.end(); ++it)
{
if (*it == texture)
*it = 0;
}
glDeleteTextures(1, &texture);
}
float OpenGL::setTextureFilter(const graphics::Image::Filter &f)
{
GLint gmin, gmag;
if (f.mipmap == Image::FILTER_NONE)
{
if (f.min == Image::FILTER_NEAREST)
gmin = GL_NEAREST;
else // f.min == Image::FILTER_LINEAR
gmin = GL_LINEAR;
}
else
{
if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_NEAREST)
gmin = GL_NEAREST_MIPMAP_NEAREST;
else if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_LINEAR)
gmin = GL_NEAREST_MIPMAP_LINEAR;
else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_NEAREST)
gmin = GL_LINEAR_MIPMAP_NEAREST;
else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_LINEAR)
gmin = GL_LINEAR_MIPMAP_LINEAR;
else
gmin = GL_LINEAR;
}
switch (f.mag)
{
case Image::FILTER_NEAREST:
gmag = GL_NEAREST;
break;
case Image::FILTER_LINEAR:
default:
gmag = GL_LINEAR;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
float anisotropy = 1.0f;
if (GLAD_EXT_texture_filter_anisotropic)
{
anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
}
return anisotropy;
}
graphics::Image::Filter OpenGL::getTextureFilter()
{
GLint gmin, gmag;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
Image::Filter f;
switch (gmin)
{
case GL_NEAREST:
f.min = Image::FILTER_NEAREST;
f.mipmap = Image::FILTER_NONE;
break;
case GL_NEAREST_MIPMAP_NEAREST:
f.min = f.mipmap = Image::FILTER_NEAREST;
break;
case GL_NEAREST_MIPMAP_LINEAR:
f.min = Image::FILTER_NEAREST;
f.mipmap = Image::FILTER_LINEAR;
break;
case GL_LINEAR_MIPMAP_NEAREST:
f.min = Image::FILTER_LINEAR;
f.mipmap = Image::FILTER_NEAREST;
break;
case GL_LINEAR_MIPMAP_LINEAR:
f.min = f.mipmap = Image::FILTER_LINEAR;
break;
case GL_LINEAR:
default:
f.min = Image::FILTER_LINEAR;
f.mipmap = Image::FILTER_NONE;
break;
}
switch (gmag)
{
case GL_NEAREST:
f.mag = Image::FILTER_NEAREST;
break;
case GL_LINEAR:
default:
f.mag = Image::FILTER_LINEAR;
break;
}
if (GLAD_EXT_texture_filter_anisotropic)
glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &f.anisotropy);
return f;
}
void OpenGL::setTextureWrap(const graphics::Image::Wrap &w)
{
GLint gs, gt;
switch (w.s)
{
case Image::WRAP_CLAMP:
gs = GL_CLAMP_TO_EDGE;
break;
case Image::WRAP_REPEAT:
default:
gs = GL_REPEAT;
break;
}
switch (w.t)
{
case Image::WRAP_CLAMP:
gt = GL_CLAMP_TO_EDGE;
break;
case Image::WRAP_REPEAT:
default:
gt = GL_REPEAT;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
}
graphics::Image::Wrap OpenGL::getTextureWrap()
{
GLint gs, gt;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &gt);
Image::Wrap w;
switch (gs)
{
case GL_CLAMP_TO_EDGE:
w.s = Image::WRAP_CLAMP;
break;
case GL_REPEAT:
default:
w.s = Image::WRAP_REPEAT;
break;
}
switch (gt)
{
case GL_CLAMP_TO_EDGE:
w.t = Image::WRAP_CLAMP;
break;
case GL_REPEAT:
default:
w.t = Image::WRAP_REPEAT;
break;
}
return w;
}
int OpenGL::getMaxTextureSize() const
{
return maxTextureSize;
}
OpenGL::Vendor OpenGL::getVendor() const
{
return vendor;
}
// OpenGL class instance singleton.
OpenGL gl;
} // opengl
} // graphics
} // love
@@ -0,0 +1,311 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_OPENGL_H
#define LOVE_GRAPHICS_OPENGL_OPENGL_H
// LOVE
#include "graphics/Color.h"
#include "graphics/Image.h"
#include "common/Matrix.h"
// GLAD
#include "libraries/glad/gladfuncs.hpp"
// C++
#include <vector>
#include <stack>
// The last argument to AttribPointer takes a buffer offset casted to a pointer.
#define BUFFER_OFFSET(i) ((char *) NULL + (i))
namespace love
{
namespace graphics
{
namespace opengl
{
// Awful, but the library uses the namespace in order to use the functions sanely
// with proper autocomplete in IDEs while having name mangling safety -
// no clashes with other GL libraries when linking, etc.
using namespace glad;
/**
* Thin layer between OpenGL and the rest of the program.
* Internally shadows some OpenGL context state for improved efficiency and
* accuracy (compared to glGet etc.)
* A class is more convenient and readable than plain namespaced functions, but
* typically only one OpenGL object should be used (singleton.)
**/
class OpenGL
{
public:
// OpenGL GPU vendors.
enum Vendor
{
VENDOR_ATI_AMD,
VENDOR_NVIDIA,
VENDOR_INTEL,
VENDOR_MESA_SOFT, // Software renderer.
VENDOR_APPLE, // Software renderer.
VENDOR_MICROSOFT, // Software renderer.
VENDOR_UNKNOWN
};
// Vertex attributes. The values map to OpenGL generic vertex attribute
// indices, when applicable (GLES2.)
enum VertexAttrib
{
ATTRIB_POS = 0,
ATTRIB_TEXCOORD = 1,
ATTRIB_COLOR = 2,
ATTRIB_MAX_ENUM
};
// A rectangle representing an OpenGL viewport or a scissor box.
struct Viewport
{
int x, y;
int w, h;
Viewport()
: x(0), y(0), w(0), h(0)
{}
Viewport(int _x, int _y, int _w, int _h)
: x(_x), y(_y), w(_w), h(_h)
{}
};
// Transformation matrix stacks.
struct
{
std::stack<Matrix> transform;
std::stack<Matrix> projection;
} matrices;
OpenGL();
virtual ~OpenGL() {}
/**
* Initializes some required context state based on current and default
* OpenGL state. Call this directly after creating an OpenGL context!
**/
bool initContext();
/**
* Marks current context state as invalid and deletes OpenGL objects owned
* by this class instance. Call this directly before potentially deleting
* an OpenGL context!
**/
void deInitContext();
/**
* Set up necessary state (matrices etc.) for drawing. This *must* be called
* directly before GL draws.
**/
void prepareDraw();
/**
* Sets the current constant color.
**/
void setColor(const Color &c);
/**
* Gets the current constant color.
**/
Color getColor() const;
/**
* Sets the current clear color for all framebuffer objects.
**/
void setClearColor(const Color &c);
/**
* Gets the current clear color.
**/
Color getClearColor() const;
/**
* Enables usage of an array for a vertex attribute when drawing.
* See http://www.opengl.org/sdk/docs/man/xhtml/glEnableVertexAttribArray.xml
**/
void enableVertexAttribArray(VertexAttrib attrib);
/**
* Disables usage of an array for a vertex attribute when drawing.
* See http://www.opengl.org/sdk/docs/man/xhtml/glDisableVertexAttribArray.xml
**/
void disableVertexAttribArray(VertexAttrib attrib);
/**
* Sets the parameters for an array of data for a vertex attribute.
* See http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribPointer.xml
**/
void setVertexAttribArray(VertexAttrib attrib, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
/**
* Sets the OpenGL rendering viewport to the specified rectangle.
* The y-coordinate starts at the top.
**/
void setViewport(const Viewport &v);
/**
* Gets the current OpenGL rendering viewport rectangle.
**/
Viewport getViewport() const;
/**
* Sets the scissor box to the specified rectangle.
* The y-coordinate starts at the top and is flipped internally.
**/
void setScissor(const Viewport &v);
/**
* Gets the current scissor box (regardless of whether scissoring is enabled.)
**/
Viewport getScissor() const;
/**
* Sets the global point size.
**/
void setPointSize(float size);
/**
* Gets the global point size.
**/
float getPointSize() const;
/**
* This will usually be 0 (system drawable), but some platforms require a
* non-zero FBO for rendering.
**/
GLuint getDefaultFBO() const;
/**
* Helper for setting the active texture unit.
*
* @param textureunit Index in the range of [0, maxtextureunits-1]
**/
void setTextureUnit(int textureunit);
/**
* Helper for binding an OpenGL texture.
* Makes sure we aren't redundantly binding textures.
**/
void bindTexture(GLuint texture);
/**
* Helper for binding a texture to a specific texture unit.
*
* @param textureunit Index in the range of [0, maxtextureunits-1]
* @param restoreprev Restore previously bound texture unit when done.
**/
void bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev);
/**
* Helper for deleting an OpenGL texture.
* Cleans up if the texture is currently bound.
**/
void deleteTexture(GLuint texture);
/**
* Sets the image filter mode for the currently bound texture.
* Returns the actual amount of anisotropic filtering set.
**/
float setTextureFilter(const graphics::Image::Filter &f);
/**
* Returns the image filter mode for the currently bound texture.
**/
graphics::Image::Filter getTextureFilter();
/**
* Sets the image wrap mode for the currently bound texture.
**/
void setTextureWrap(const graphics::Image::Wrap &w);
/**
* Returns the image wrap mode for the currently bound texture.
**/
graphics::Image::Wrap getTextureWrap();
/**
* Returns the maximum supported width or height of a texture.
**/
int getMaxTextureSize() const;
/**
* Get the GPU vendor of this OpenGL context.
**/
Vendor getVendor() const;
private:
void initVendor();
void initOpenGLFunctions();
void initMaxValues();
void initMatrices();
void createDefaultTexture();
GLint getGLAttrib(VertexAttrib attrib);
bool contextInitialized;
float maxAnisotropy;
int maxTextureSize;
Vendor vendor;
// Tracked OpenGL state.
struct
{
// Current constant color.
Color color;
Color clearColor;
// Texture unit state (currently bound texture for each texture unit.)
std::vector<GLuint> textureUnits;
// Currently active texture unit.
int curTextureUnit;
Viewport viewport;
Viewport scissor;
float pointSize;
GLuint defaultFBO;
} state;
}; // OpenGL
// OpenGL class instance singleton.
extern OpenGL gl;
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_OPENGL_H
@@ -0,0 +1,942 @@
/**
* Copyright (c) 2006-2013 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 "common/config.h"
#include "ParticleSystem.h"
#include "common/math.h"
#include "modules/math/RandomGenerator.h"
#include "OpenGL.h"
// STD
#include <algorithm>
#include <cmath>
#include <cstdlib>
namespace love
{
namespace graphics
{
namespace opengl
{
namespace
{
love::math::RandomGenerator rng;
Colorf colorToFloat(const Color &c)
{
return Colorf((float)c.r/255.0f, (float)c.g/255.0f, (float)c.b/255.0f, (float)c.a/255.0f);
}
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 = (float) rng.random();
return low*(1-r)+high*r;
}
} // anonymous namespace
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));
StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM>::Entry ParticleSystem::insertModesEntries[] =
{
{ "top", ParticleSystem::INSERT_MODE_TOP },
{ "bottom", ParticleSystem::INSERT_MODE_BOTTOM },
{ "random", ParticleSystem::INSERT_MODE_RANDOM },
};
StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM> ParticleSystem::insertModes(ParticleSystem::insertModesEntries, sizeof(ParticleSystem::insertModesEntries));
ParticleSystem::ParticleSystem(Image *image, uint32 size)
: pMem(NULL)
, pFree(NULL)
, pHead(NULL)
, pTail(NULL)
, particleVerts(NULL)
, ibo(NULL)
, image(image)
, active(true)
, insertMode(INSERT_MODE_TOP)
, maxParticles(0)
, activeParticles(0)
, emissionRate(0)
, emitCounter(0)
, areaSpreadDistribution(DISTRIBUTION_NONE)
, lifetime(-1)
, life(0)
, particleLifeMin(0)
, particleLifeMax(0)
, direction(0)
, spread(0)
, speedMin(0)
, speedMax(0)
, linearAccelerationMin(0, 0)
, linearAccelerationMax(0, 0)
, radialAccelerationMin(0)
, radialAccelerationMax(0)
, tangentialAccelerationMin(0)
, tangentialAccelerationMax(0)
, sizeVariation(0)
, rotationMin(0)
, rotationMax(0)
, spinStart(0)
, spinEnd(0)
, spinVariation(0)
, offsetX(float(image->getWidth())*0.5f)
, offsetY(float(image->getHeight())*0.5f)
{
if (size == 0 || size > MAX_PARTICLES)
throw love::Exception("Invalid ParticleSystem size.");
sizes.push_back(1.0f);
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
setBufferSize(size);
image->retain();
}
ParticleSystem::~ParticleSystem()
{
if (this->image != 0)
this->image->release();
deleteBuffers();
}
void ParticleSystem::createBuffers(size_t size)
{
try
{
pFree = pMem = new particle[size];
particleVerts = new love::Vertex[size * 4];
ibo = new VertexIndex(size);
maxParticles = (uint32) size;
}
catch (love::Exception &)
{
deleteBuffers();
throw;
}
catch (std::bad_alloc &)
{
deleteBuffers();
throw love::Exception("Out of memory");
}
}
void ParticleSystem::deleteBuffers()
{
// Clean up for great gracefulness!
delete[] pMem;
delete[] particleVerts;
delete ibo;
pMem = NULL;
particleVerts = NULL;
ibo = NULL;
maxParticles = 0;
activeParticles = 0;
}
void ParticleSystem::setBufferSize(uint32 size)
{
if (size == 0 || size > MAX_PARTICLES)
throw love::Exception("Invalid buffer size");
deleteBuffers();
createBuffers(size);
reset();
}
uint32 ParticleSystem::getBufferSize() const
{
return maxParticles;
}
void ParticleSystem::addParticle()
{
if (isFull())
return;
// Gets a free particle and updates the allocation pointer.
particle *p = pFree++;
initParticle(p);
switch (insertMode)
{
default:
case INSERT_MODE_TOP:
insertTop(p);
break;
case INSERT_MODE_BOTTOM:
insertBottom(p);
break;
case INSERT_MODE_RANDOM:
insertRandom(p);
break;
}
activeParticles++;
}
void ParticleSystem::initParticle(particle *p)
{
float min,max;
min = particleLifeMin;
max = particleLifeMax;
if (min == max)
p->life = min;
else
p->life = (float) rng.random(min, max);
p->lifetime = p->life;
p->position[0] = position.getX();
p->position[1] = position.getY();
switch (areaSpreadDistribution)
{
case DISTRIBUTION_UNIFORM:
p->position[0] += (float) rng.random(-areaSpread.getX(), areaSpread.getX());
p->position[1] += (float) rng.random(-areaSpread.getY(), areaSpread.getY());
break;
case DISTRIBUTION_NORMAL:
p->position[0] += (float) rng.randomNormal(areaSpread.getX());
p->position[1] += (float) rng.randomNormal(areaSpread.getY());
break;
case DISTRIBUTION_NONE:
default:
break;
}
min = direction - spread/2.0f;
max = direction + spread/2.0f;
p->direction = (float) rng.random(min, max);
p->origin = position;
min = speedMin;
max = speedMax;
float speed = (float) rng.random(min, max);
p->speed = love::Vector(cosf(p->direction), sinf(p->direction));
p->speed *= speed;
p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x);
p->linearAcceleration.y = (float) rng.random(linearAccelerationMin.y, linearAccelerationMax.y);
min = radialAccelerationMin;
max = radialAccelerationMax;
p->radialAcceleration = (float) rng.random(min, max);
min = tangentialAccelerationMin;
max = tangentialAccelerationMax;
p->tangentialAcceleration = (float) rng.random(min, max);
p->sizeOffset = (float) rng.random(sizeVariation); // time offset for size change
p->sizeIntervalSize = (1.0f - (float) rng.random(sizeVariation)) - p->sizeOffset;
p->size = sizes[(size_t)(p->sizeOffset - .5f) * (sizes.size() - 1)];
min = rotationMin;
max = rotationMax;
p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
p->rotation = (float) rng.random(min, max);
p->color = colors[0];
}
void ParticleSystem::insertTop(particle *p)
{
if (pHead == NULL)
{
pHead = p;
p->prev = NULL;
}
else
{
pTail->next = p;
p->prev = pTail;
}
p->next = NULL;
pTail = p;
}
void ParticleSystem::insertBottom(particle *p)
{
if (pTail == NULL)
{
pTail = p;
p->next = NULL;
}
else
{
pHead->prev = p;
p->next = pHead;
}
p->prev = NULL;
pHead = p;
}
void ParticleSystem::insertRandom(particle *p)
{
// Nonuniform, but 64-bit is so large nobody will notice. Hopefully.
uint64 pos = rng.rand() % ((int64) activeParticles + 1);
// Special case where the particle gets inserted before the head.
if (pos == activeParticles)
{
particle *pA = pHead;
if (pA)
pA->prev = p;
p->prev = NULL;
p->next = pA;
pHead = p;
return;
}
// Inserts the particle after the randomly selected particle.
particle *pA = pMem + pos;
particle *pB = pA->next;
pA->next = p;
if (pB)
pB->prev = p;
else
pTail = p;
p->prev = pA;
p->next = pB;
}
ParticleSystem::particle *ParticleSystem::removeParticle(particle *p)
{
// The linked list is updated in this function and old pointers may be
// invalidated. The returned pointer will inform the caller of the new
// pointer to the next particle.
particle *pNext = NULL;
// Removes the particle from the linked list.
if (p->prev)
p->prev->next = p->next;
else
pHead = p->next;
if (p->next)
{
p->next->prev = p->prev;
pNext = p->next;
}
else
pTail = p->prev;
// The (in memory) last particle can now be moved into the free slot.
// It will skip the moving if it happens to be the removed particle.
pFree--;
if (p != pFree)
{
*p = *pFree;
if (pNext == pFree)
pNext = p;
if (p->prev)
p->prev->next = p;
else
pHead = p;
if (p->next)
p->next->prev = p;
else
pTail = p;
}
activeParticles--;
return pNext;
}
void ParticleSystem::setImage(Image *image)
{
Object::AutoRelease imagerelease(this->image);
this->image = image;
this->image->retain();
}
Image *ParticleSystem::getImage() const
{
return image;
}
void ParticleSystem::setInsertMode(InsertMode mode)
{
insertMode = mode;
}
ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
{
return insertMode;
}
void ParticleSystem::setEmissionRate(int rate)
{
if (rate < 0)
throw love::Exception("Invalid emission rate");
emissionRate = rate;
}
int ParticleSystem::getEmissionRate() const
{
return emissionRate;
}
void ParticleSystem::setEmitterLifetime(float life)
{
this->life = lifetime = life;
}
float ParticleSystem::getEmitterLifetime() const
{
return lifetime;
}
void ParticleSystem::setParticleLifetime(float min, float max)
{
particleLifeMin = min;
if (max == 0)
particleLifeMax = min;
else
particleLifeMax = max;
}
void ParticleSystem::getParticleLifetime(float *min, float *max) const
{
if (min)
*min = particleLifeMin;
if (max)
*max = particleLifeMax;
}
void ParticleSystem::setPosition(float x, float y)
{
position = love::Vector(x, y);
}
const love::Vector &ParticleSystem::getPosition() const
{
return position;
}
void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y)
{
areaSpread = love::Vector(x, y);
areaSpreadDistribution = distribution;
}
ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const
{
return areaSpreadDistribution;
}
const love::Vector &ParticleSystem::getAreaSpreadParameters() const
{
return areaSpread;
}
void ParticleSystem::setDirection(float direction)
{
this->direction = direction;
}
float ParticleSystem::getDirection() const
{
return direction;
}
void ParticleSystem::setSpread(float spread)
{
this->spread = spread;
}
float ParticleSystem::getSpread() const
{
return spread;
}
void ParticleSystem::setSpeed(float speed)
{
speedMin = speedMax = speed;
}
void ParticleSystem::setSpeed(float min, float max)
{
speedMin = min;
speedMax = max;
}
void ParticleSystem::getSpeed(float *min, float *max) const
{
if (min)
*min = speedMin;
if (max)
*max = speedMax;
}
void ParticleSystem::setLinearAcceleration(float x, float y)
{
linearAccelerationMin.x = linearAccelerationMax.x = x;
linearAccelerationMin.y = linearAccelerationMax.y = y;
}
void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, float ymax)
{
linearAccelerationMin = love::Vector(xmin, ymin);
linearAccelerationMax = love::Vector(xmax, ymax);
}
void ParticleSystem::getLinearAcceleration(love::Vector *min, love::Vector *max) const
{
if (min)
*min = linearAccelerationMin;
if (max)
*max = linearAccelerationMax;
}
void ParticleSystem::setRadialAcceleration(float acceleration)
{
radialAccelerationMin = radialAccelerationMax = acceleration;
}
void ParticleSystem::setRadialAcceleration(float min, float max)
{
radialAccelerationMin = min;
radialAccelerationMax = max;
}
void ParticleSystem::getRadialAcceleration(float *min, float *max) const
{
if (min)
*min = radialAccelerationMin;
if (max)
*max = radialAccelerationMax;
}
void ParticleSystem::setTangentialAcceleration(float acceleration)
{
tangentialAccelerationMin = tangentialAccelerationMax = acceleration;
}
void ParticleSystem::setTangentialAcceleration(float min, float max)
{
tangentialAccelerationMin = min;
tangentialAccelerationMax = max;
}
void ParticleSystem::getTangentialAcceleration(float *min, float *max) const
{
if (min)
*min = tangentialAccelerationMin;
if (max)
*max = tangentialAccelerationMax;
}
void ParticleSystem::setSize(float size)
{
sizes.resize(1);
sizes[0] = size;
}
void ParticleSystem::setSizes(const std::vector<float> &newSizes)
{
sizes = newSizes;
}
const std::vector<float> &ParticleSystem::getSizes() const
{
return sizes;
}
void ParticleSystem::setSizeVariation(float variation)
{
sizeVariation = variation;
}
float ParticleSystem::getSizeVariation() const
{
return sizeVariation;
}
void ParticleSystem::setRotation(float rotation)
{
rotationMin = rotationMax = rotation;
}
void ParticleSystem::setRotation(float min, float max)
{
rotationMin = min;
rotationMax = max;
}
void ParticleSystem::getRotation(float *min, float *max) const
{
if (min)
*min = rotationMin;
if (max)
*max = rotationMax;
}
void ParticleSystem::setSpin(float spin)
{
spinStart = spin;
spinEnd = spin;
}
void ParticleSystem::setSpin(float start, float end)
{
spinStart = start;
spinEnd = end;
}
void ParticleSystem::getSpin(float *start, float *end) const
{
if (start)
*start = spinStart;
if (end)
*end = spinEnd;
}
void ParticleSystem::setSpinVariation(float variation)
{
spinVariation = variation;
}
float ParticleSystem::getSpinVariation() const
{
return spinVariation;
}
void ParticleSystem::setOffset(float x, float y)
{
offsetX = x;
offsetY = y;
}
love::Vector ParticleSystem::getOffset() const
{
return love::Vector(offsetX, offsetY);
}
void ParticleSystem::setColor(const Color &color)
{
colors.resize(1);
colors[0] = colorToFloat(color);
}
void ParticleSystem::setColor(const std::vector<Color> &newColors)
{
colors.resize(newColors.size());
for (size_t i = 0; i < newColors.size(); ++i)
colors[i] = colorToFloat(newColors[i]);
}
std::vector<Color> ParticleSystem::getColor() const
{
// The particle system stores colors as floats...
std::vector<Color> ncolors(colors.size());
for (size_t i = 0; i < colors.size(); ++i)
{
ncolors[i].r = (unsigned char) (colors[i].r * 255);
ncolors[i].g = (unsigned char) (colors[i].g * 255);
ncolors[i].b = (unsigned char) (colors[i].b * 255);
ncolors[i].a = (unsigned char) (colors[i].a * 255);
}
return ncolors;
}
uint32 ParticleSystem::getCount() const
{
return activeParticles;
}
void ParticleSystem::start()
{
active = true;
}
void ParticleSystem::stop()
{
active = false;
life = lifetime;
emitCounter = 0;
}
void ParticleSystem::pause()
{
active = false;
}
void ParticleSystem::reset()
{
if (pMem == NULL)
return;
pFree = pMem;
pHead = NULL;
pTail = NULL;
activeParticles = 0;
life = lifetime;
emitCounter = 0;
}
void ParticleSystem::emit(uint32 num)
{
if (!active)
return;
num = std::min(num, maxParticles - activeParticles);
while(num--)
addParticle();
}
bool ParticleSystem::isActive() const
{
return active;
}
bool ParticleSystem::isPaused() const
{
return !active && life < lifetime;
}
bool ParticleSystem::isStopped() const
{
return !active && life >= lifetime;
}
bool ParticleSystem::isEmpty() const
{
return activeParticles == 0;
}
bool ParticleSystem::isFull() const
{
return activeParticles == maxParticles;
}
void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
uint32 pCount = getCount();
if (pCount == 0 || image == NULL || pMem == NULL || particleVerts == NULL)
return;
Color curcolor = gl.getColor();
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
const Vertex *imageVerts = image->getVertices();
Vertex *pVerts = particleVerts;
particle *p = pHead;
// set the vertex data for each particle (transformation, texcoords, color)
while (p)
{
// particle vertices are image vertices transformed by particle information
t.setTransformation(p->position[0], p->position[1], p->rotation, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
t.transform(pVerts, imageVerts, 4);
// set the texture coordinate and color data for particle vertices
for (int v = 0; v < 4; v++)
{
pVerts[v].s = imageVerts[v].s;
pVerts[v].t = imageVerts[v].t;
// particle colors are stored as floats (0-1) but vertex colors are stored as unsigned bytes (0-255)
pVerts[v].r = (unsigned char) (p->color.r*255);
pVerts[v].g = (unsigned char) (p->color.g*255);
pVerts[v].b = (unsigned char) (p->color.b*255);
pVerts[v].a = (unsigned char) (p->color.a*255);
}
pVerts += 4;
p = p->next;
}
image->predraw();
gl.prepareDraw();
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), &particleVerts[0].x);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), &particleVerts[0].s);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, sizeof(Vertex), &particleVerts[0].r);
{
VertexBuffer::Bind ibo_bind(*ibo->getVertexBuffer());
glDrawElements(GL_TRIANGLES, ibo->getIndexCount(pCount), ibo->getType(), ibo->getPointer(0));
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
image->postdraw();
gl.matrices.transform.pop();
gl.setColor(curcolor);
}
void ParticleSystem::update(float dt)
{
if (pMem == NULL || dt == 0.0f)
return;
// Make some more particles.
if (active)
{
float rate = 1.0f / emissionRate; // the amount of time between each particle emit
emitCounter += dt;
while (emitCounter > rate)
{
addParticle();
emitCounter -= rate;
}
/*int particles = (int)(emissionRate * dt);
for (int i = 0; i != particles; i++)
add();*/
life -= dt;
if (lifetime != -1 && life < 0)
stop();
}
// Traverse all particles and update.
particle *p = pHead;
while (p)
{
// Decrease lifespan.
p->life -= dt;
if (p->life <= 0)
p = removeParticle(p);
else
{
// Temp variables.
love::Vector radial, tangential;
love::Vector ppos(p->position[0], p->position[1]);
// Get vector from particle center to particle.
radial = ppos - p->origin;
radial.normalize();
tangential = radial;
// Resize radial acceleration.
radial *= p->radialAcceleration;
// Calculate tangential acceleration.
{
float a = tangential.getX();
tangential.setX(-tangential.getY());
tangential.setY(a);
}
// Resize tangential.
tangential *= p->tangentialAcceleration;
// Update position.
p->speed += (radial+tangential+p->linearAcceleration)*dt;
// Modify position.
ppos += p->speed * dt;
p->position[0] = ppos.getX();
p->position[1] = ppos.getY();
const float t = 1.0f - p->life / p->lifetime;
// Rotate.
p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t)*dt;
// Change size according to given intervals:
// i = 0 1 2 3 n-1
// |-------|-------|------|--- ... ---|
// t = 0 1/(n-1) 3/(n-1) 1
//
// `s' is the interpolation variable scaled to the current
// interval width, e.g. if n = 5 and t = 0.3, then the current
// indices are 1,2 and s = 0.3 - 0.25 = 0.05
float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation
s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size()
size_t i = (size_t)s;
size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f)
s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1
p->size = sizes[i] * (1.0f - s) + sizes[k] * s;
// Update color according to given intervals (as above)
s = t * (float)(colors.size() - 1);
i = (size_t)s;
k = (i == colors.size() - 1) ? i : i + 1;
s -= (float)i; // 0 <= s <= 1
p->color = colors[i] * (1.0f - s) + colors[k] * s;
// Next particle.
p = p->next;
}
}
}
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);
}
bool ParticleSystem::getConstant(const char *in, InsertMode &out)
{
return insertModes.find(in, out);
}
bool ParticleSystem::getConstant(InsertMode in, const char *&out)
{
return insertModes.find(in, out);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,632 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
#define LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
// LOVE
#include "common/int.h"
#include "common/math.h"
#include "common/Vector.h"
#include "graphics/Drawable.h"
#include "graphics/Color.h"
#include "Image.h"
#include "VertexBuffer.h"
// STL
#include <vector>
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* A class for creating, moving and drawing particles.
* A big thanks to bobthebloke.org
**/
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
};
/**
* Insertion modes of new particles in the list: top, bottom, random.
*/
enum InsertMode
{
INSERT_MODE_TOP,
INSERT_MODE_BOTTOM,
INSERT_MODE_RANDOM,
INSERT_MODE_MAX_ENUM,
};
/**
* Maximum numbers of particles in a ParticleSystem.
* This limit comes from the fact that a quad requires four vertices and the
* OpenGL API where GLsizei is a signed int.
**/
static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4;
/**
* Creates a particle system with the specified buffersize and image.
**/
ParticleSystem(Image *image, uint32 buffer);
/**
* Deletes any allocated memory.
**/
virtual ~ParticleSystem();
/**
* Sets the image used in the particle system.
* @param image The new image.
**/
void setImage(Image *image);
/**
* Returns the image used when drawing the particle system.
**/
Image *getImage() const;
/**
* Clears the current buffer and allocates the appropriate amount of space for the buffer.
* @param size The new buffer size.
**/
void setBufferSize(uint32 size);
/**
* Returns the total amount of particles this ParticleSystem can have active
* at any given point in time.
**/
uint32 getBufferSize() const;
/**
* Sets the insert mode for new particles.
* @param mode The new insert mode.
*/
void setInsertMode(InsertMode mode);
/**
* Returns the current insert mode.
*/
InsertMode getInsertMode() const;
/**
* Sets the emission rate.
* @param rate The amount of particles per second.
**/
void setEmissionRate(int rate);
/**
* Returns the number of particles created per second.
**/
int getEmissionRate() const;
/**
* Sets the lifetime of the particle emitter (-1 means eternal)
* @param life The lifetime (in seconds).
**/
void setEmitterLifetime(float life);
/**
* Returns the lifetime of the particle emitter.
**/
float getEmitterLifetime() const;
/**
* Sets the life range of the particles.
* @param min The minimum life.
* @param max The maximum life (if 0, then becomes the same as minimum life).
**/
void setParticleLifetime(float min, float max = 0);
/**
* Gets the lifetime of a particle.
* @param[out] min The minimum life.
* @param[out] max The maximum life.
**/
void getParticleLifetime(float *min, float *max) const;
/**
* Sets the position of the center of the emitter.
* Used to move the emitter without changing the position of already existing particles.
* @param x The x-coordinate.
* @param y The y-coordinate.
**/
void setPosition(float x, float y);
/**
* Returns the position of the emitter.
**/
const love::Vector &getPosition() const;
/**
* 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);
/**
* Returns area spread distribution type.
**/
AreaSpreadDistribution getAreaSpreadDistribution() const;
/**
* Returns area spread parameters.
**/
const love::Vector &getAreaSpreadParameters() const;
/**
* Sets the direction of the particle emitter.
* @param direction The direction (in degrees).
**/
void setDirection(float direction);
/**
* Returns the direction of the particle emitter (in radians).
**/
float getDirection() const;
/**
* Sets the spread of the particle emitter.
* @param spread The spread (in radians).
**/
void setSpread(float spread);
/**
* Returns the directional spread of the emitter (in radians).
**/
float getSpread() const;
/**
* Sets the speed of the particles.
* @param speed The speed.
**/
void setSpeed(float speed);
/**
* Sets the speed of the particles.
* @param min The minimum speed.
* @param max The maximum speed.
**/
void setSpeed(float min, float max);
/**
* Gets the speed of the particles.
* @param[out] min The minimum speed.
* @param[out] max The maximum speed.
**/
void getSpeed(float *min, float *max) const;
/**
* Sets the linear acceleration (the acceleration along the x and y axes).
* @param x The acceleration along the x-axis.
* @param y The acceleration along the y-axis.
**/
void setLinearAcceleration(float x, float y);
/**
* Sets the linear acceleration (the acceleration along the x and y axes).
* @param xmin The minimum amount of acceleration along the x-axis.
* @param ymin The minimum amount of acceleration along the y-axis.
* @param xmax The maximum amount of acceleration along the x-axis.
* @param ymax The maximum amount of acceleration along the y-axis.
**/
void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax);
/**
* Gets the linear acceleration of the particles.
* @param[out] min The minimum acceleration.
* @param[out] max The maximum acceleration.
**/
void getLinearAcceleration(love::Vector *min, love::Vector *max) const;
/**
* Sets the radial acceleration (the acceleration towards the particle emitter).
* @param acceleration The amount of acceleration.
**/
void setRadialAcceleration(float acceleration);
/**
* Sets the radial acceleration (the acceleration towards the particle emitter).
* @param min The minimum acceleration.
* @param max The maximum acceleration.
**/
void setRadialAcceleration(float min, float max);
/**
* Gets the radial acceleration.
* @param[out] min The minimum amount of radial acceleration.
* @param[out] max The maximum amount of radial acceleration.
**/
void getRadialAcceleration(float *min, float *max) const;
/**
* Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
* @param acceleration The amount of acceleration.
**/
void setTangentialAcceleration(float acceleration);
/**
* Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
* @param min The minimum acceleration.
* @param max The maximum acceleration.
**/
void setTangentialAcceleration(float min, float max);
/**
* Gets the tangential acceleration.
* @param[out] min The minimum tangential acceleration.
* @param[out] max The maximum tangential acceleration.
**/
void getTangentialAcceleration(float *min, float *max) const;
/**
* Sets the size of the sprite (1.0 being the default size).
* @param size The size of the sprite.
**/
void setSize(float size);
/**
* Sets the sizes of the sprite upon creation and upon death (1.0 being the default size).
* @param newSizes Array of sizes
**/
void setSizes(const std::vector<float> &newSizes);
/**
* Returns the sizes of the particle sprites.
**/
const std::vector<float> &getSizes() const;
/**
* Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end).
* @param variation The amount of variation.
**/
void setSizeVariation(float variation);
/**
* Returns the amount of initial size variation between particles.
**/
float getSizeVariation() const;
/**
* Sets the amount of rotation a sprite starts out with.
* @param rotation The amount of rotation.
**/
void setRotation(float rotation);
/**
* Sets the amount of rotation a sprite starts out with (a random value between min and max).
* @param min The minimum amount of rotation.
* @param max The maximum amount of rotation.
**/
void setRotation(float min, float max);
/**
* Gets the initial amount of rotation of a particle, in radians.
* @param[out] min The minimum initial rotation.
* @param[out] max The maximum initial rotation.
**/
void getRotation(float *min, float *max) const;
/**
* Sets the spin of the sprite.
* @param spin The spin of the sprite (in degrees).
**/
void setSpin(float spin);
/**
* Sets the spin of the sprite upon particle creation and death.
* @param start The spin of the sprite upon creation (in radians / second).
* @param end The spin of the sprite upon death (in radians / second).
**/
void setSpin(float start, float end);
/**
* Gets the amount of spin of a particle during its lifetime.
* @param[out] start The initial spin, in radians / s.
* @param[out] end The final spin, in radians / s.
**/
void getSpin(float *start, float *end) const;
/**
* Sets the variation of the start spin (0 being no variation and 1 being a random spin between start and end).
* @param variation The variation.
**/
void setSpinVariation(float variation);
/**
* Returns the amount of variation of the start spin of a particle.
**/
float getSpinVariation() const;
/**
* Sets the particles' offsets for rotation.
* @param x The x offset.
* @param y The y offset.
**/
void setOffset(float x, float y);
/**
* Returns of the particle offset.
**/
love::Vector getOffset() const;
/**
* Sets the color of the particles.
* @param color The color.
**/
void setColor(const Color &color);
/**
* Sets the color of the particles.
* @param newColors Array of colors
**/
void setColor(const std::vector<Color> &newColors);
/**
* Returns the color of the particles.
**/
std::vector<Color> getColor() const;
/**
* Returns the amount of particles that are currently active in the system.
**/
uint32 getCount() const;
/**
* Starts/resumes the particle emitter.
**/
void start();
/**
* Stops the particle emitter and resets.
**/
void stop();
/**
* Pauses the particle emitter.
**/
void pause();
/**
* Resets the particle emitter.
**/
void reset();
/**
* Instantly emits a number of particles.
* @param num The number of particles to emit.
**/
void emit(uint32 num);
/**
* Returns whether the particle emitter is active.
**/
bool isActive() const;
/**
* Returns whether the particle emitter is paused.
**/
bool isPaused() const;
bool isStopped() const;
/**
* Returns whether the particle system is empty of particles or not.
**/
bool isEmpty() const;
/**
* Returns whether the amount of particles has reached the buffer limit or not.
**/
bool isFull() const;
/**
* Draws the particle emitter at the specified position.
* @param x The x-coordinate.
* @param y The y-coordinate.
**/
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* Updates the particle system.
* @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);
static bool getConstant(const char *in, InsertMode &out);
static bool getConstant(InsertMode in, const char *&out);
protected:
// Represents a single particle.
struct particle
{
particle *prev;
particle *next;
float lifetime;
float life;
float position[2];
float direction;
// Particles gravitate towards this point.
love::Vector origin;
love::Vector speed;
love::Vector linearAcceleration;
float radialAcceleration;
float tangentialAcceleration;
float size;
float sizeOffset;
float sizeIntervalSize;
float rotation;
float spinStart;
float spinEnd;
Colorf color;
};
// The max amount of particles.
int bufferSize;
// Pointer to the beginning of the allocated memory.
particle *pMem;
// Pointer to a free particle.
particle *pFree;
// Pointer to the start of the linked list.
particle *pHead;
// Pointer to the end of the linked list.
particle *pTail;
// array of transformed vertex data for all particles, for drawing
Vertex *particleVerts;
// Vertex index buffer.
VertexIndex *ibo;
// The image to be drawn.
Image *image;
// Whether the particle emitter is active.
bool active;
// Insert mode of new particles.
InsertMode insertMode;
// The maximum number of particles.
uint32 maxParticles;
// The number of active particles.
uint32 activeParticles;
// The emission rate (particles/sec).
int emissionRate;
// Used to determine when a particle should be emitted.
float emitCounter;
// 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;
// The particle life.
float particleLifeMin;
float particleLifeMax;
// The direction (and spread) the particles will be emitted in. Measured in radians.
float direction;
float spread;
// The speed.
float speedMin;
float speedMax;
// Acceleration along the x and y axes.
love::Vector linearAccelerationMin;
love::Vector linearAccelerationMax;
// Acceleration towards the emitter's center
float radialAccelerationMin;
float radialAccelerationMax;
// Acceleration perpendicular to the particle's direction.
float tangentialAccelerationMin;
float tangentialAccelerationMax;
// Size.
std::vector<float> sizes;
float sizeVariation;
// Rotation
float rotationMin;
float rotationMax;
// Spin.
float spinStart;
float spinEnd;
float spinVariation;
// Offsets
float offsetX;
float offsetY;
// Color.
std::vector<Colorf> colors;
void createBuffers(size_t size);
void deleteBuffers();
void addParticle();
particle *removeParticle(particle *p);
// Called by addParticle.
void initParticle(particle *p);
void insertTop(particle *p);
void insertBottom(particle *p);
void insertRandom(particle *p);
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
static StringMap<InsertMode, INSERT_MODE_MAX_ENUM>::Entry insertModesEntries[];
static StringMap<InsertMode, INSERT_MODE_MAX_ENUM> insertModes;
};
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
@@ -0,0 +1,383 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include <algorithm>
// LOVE
#include "Polyline.h"
// OpenGL
#include "OpenGL.h"
// treat adjacent segments with angles between their directions <5 degree as straight
static const float LINES_PARALLEL_EPS = 0.05f;
namespace love
{
namespace graphics
{
namespace opengl
{
void Polyline::render(const float *coords, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw)
{
static std::vector<Vector> anchors;
anchors.clear();
anchors.reserve(size_hint);
static std::vector<Vector> normals;
normals.clear();
normals.reserve(size_hint);
// prepare vertex arrays
if (draw_overdraw)
halfwidth -= pixel_size * .3;
// compute sleeve
bool is_looping = (coords[0] == coords[count - 2]) && (coords[1] == coords[count - 1]);
Vector s;
if (!is_looping) // virtual starting point at second point mirrored on first point
s = Vector(coords[2] - coords[0], coords[3] - coords[1]);
else // virtual starting point at last vertex
s = Vector(coords[0] - coords[count - 4], coords[1] - coords[count - 3]);
float len_s = s.getLength();
Vector ns = s.getNormal(halfwidth / len_s);
Vector q, r(coords[0], coords[1]);
for (size_t i = 0; i + 3 < count; i += 2)
{
q = r;
r = Vector(coords[i + 2], coords[i + 3]);
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
}
q = r;
r = is_looping ? Vector(coords[2], coords[3]) : r + s;
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
vertex_count = normals.size();
vertices = new Vector[vertex_count];
for (size_t i = 0; i < vertex_count; ++i)
vertices[i] = anchors[i] + normals[i];
if (draw_overdraw)
render_overdraw(normals, pixel_size, is_looping);
}
void NoneJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw)
{
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(ns);
normals.push_back(-ns);
s = (r - q);
len_s = s.getLength();
ns = s.getNormal(hw / len_s);
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(-ns);
normals.push_back(ns);
}
/** Calculate line boundary points.
*
* Sketch:
*
* u1
* -------------+---...___
* | ```'''-- ---
* p- - - - - - q- - . _ _ | w/2
* | ` ' ' r +
* -------------+---...___ | w/2
* u2 ```'''-- ---
*
* u1 and u2 depend on four things:
* - the half line width w/2
* - the previous line vertex p
* - the current line vertex q
* - the next line vertex r
*
* u1/u2 are the intersection points of the parallel lines to p-q and q-r,
* i.e. the point where
*
* (q + w/2 * ns) + lambda * (q - p) = (q + w/2 * nt) + mu * (r - q) (u1)
* (q - w/2 * ns) + lambda * (q - p) = (q - w/2 * nt) + mu * (r - q) (u2)
*
* with nt,nt being the normals on the segments s = p-q and t = q-r,
*
* ns = perp(s) / |s|
* nt = perp(t) / |t|.
*
* Using the linear equation system (similar for u2)
*
* q + w/2 * ns + lambda * s - (q + w/2 * nt + mu * t) = 0 (u1)
* <=> q-q + lambda * s - mu * t = (nt - ns) * w/2
* <=> lambda * s - mu * t = (nt - ns) * w/2
*
* the intersection points can be efficiently calculated using Cramer's rule.
*/
void MiterJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw)
{
Vector t = (r - q);
float len_t = t.getLength();
Vector nt = t.getNormal(hw / len_t);
anchors.push_back(q);
anchors.push_back(q);
float det = s ^ t;
if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && s * t > 0)
{
// lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
normals.push_back(ns);
normals.push_back(-ns);
}
else
{
// cramers rule
float lambda = ((nt - ns) ^ t) / det;
Vector d = ns + s * lambda;
normals.push_back(d);
normals.push_back(-d);
}
s = t;
ns = nt;
len_s = len_t;
}
/** Calculate line boundary points.
*
* Sketch:
*
* uh1___uh2
* .' '.
* .' q '.
* .' ' ' '.
*.' ' .'. ' '.
* ' .' ul'. '
* p .' '. r
*
*
* ul can be found as above, uh1 and uh2 are much simpler:
*
* uh1 = q + ns * w/2, uh2 = q + nt * w/2
*/
void BevelJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw)
{
Vector t = (r - q);
float len_t = t.getLength();
float det = s ^ t;
if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && s * t > 0)
{
// lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
Vector n = t.getNormal(hw / len_t);
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(n);
normals.push_back(-n);
s = t;
len_s = len_t;
return; // early out
}
// cramers rule
Vector nt= t.getNormal(hw / len_t);
float lambda = ((nt - ns) ^ t) / det;
Vector d = ns + s * lambda;
anchors.push_back(q);
anchors.push_back(q);
anchors.push_back(q);
anchors.push_back(q);
if (det > 0) // 'left' turn -> intersection on the top
{
normals.push_back(d);
normals.push_back(-ns);
normals.push_back(d);
normals.push_back(-nt);
}
else
{
normals.push_back(ns);
normals.push_back(-d);
normals.push_back(nt);
normals.push_back(-d);
}
s = t;
len_s = len_t;
ns = nt;
}
void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping)
{
overdraw_vertex_count = 2 * vertex_count + (is_looping ? 0 : 2);
overdraw = new Vector[overdraw_vertex_count];
// upper segment
for (size_t i = 0; i + 1 < vertex_count; i += 2)
{
overdraw[i] = vertices[i];
overdraw[i+1] = vertices[i] + normals[i] * (pixel_size / normals[i].getLength());
}
// lower segment
for (size_t i = 0; i + 1 < vertex_count; i += 2)
{
size_t k = vertex_count - i - 1;
overdraw[vertex_count + i] = vertices[k];
overdraw[vertex_count + i+1] = vertices[k] + normals[k] * (pixel_size / normals[i].getLength());
}
// if not looping, the outer overdraw vertices need to be displaced
// to cover the line endings, i.e.:
// +- - - - //- - + +- - - - - //- - - +
// +-------//-----+ : +-------//-----+ :
// | core // line | --> : | core // line | :
// +-----//-------+ : +-----//-------+ :
// +- - //- - - - + +- - - //- - - - - +
if (!is_looping)
{
// left edge
Vector spacer = (overdraw[1] - overdraw[3]);
spacer.normalize(pixel_size);
overdraw[1] += spacer;
overdraw[overdraw_vertex_count - 3] += spacer;
// right edge
spacer = (overdraw[vertex_count-1] - overdraw[vertex_count-3]);
spacer.normalize(pixel_size);
overdraw[vertex_count-1] += spacer;
overdraw[vertex_count+1] += spacer;
// we need to draw two more triangles to close the
// overdraw at the line start.
overdraw[overdraw_vertex_count-2] = overdraw[0];
overdraw[overdraw_vertex_count-1] = overdraw[1];
}
}
void NoneJoinPolyline::render_overdraw(const std::vector<Vector> &/*normals*/, float pixel_size, bool /*is_looping*/)
{
overdraw_vertex_count = 4 * (vertex_count-2); // less than ideal
overdraw = new Vector[overdraw_vertex_count];
for (size_t i = 2; i + 3 < vertex_count; i += 4)
{
Vector s = vertices[i] - vertices[i+3];
Vector t = vertices[i] - vertices[i+1];
s.normalize(pixel_size);
t.normalize(pixel_size);
const size_t k = 4 * (i - 2);
overdraw[k ] = vertices[i];
overdraw[k+1] = vertices[i] + s + t;
overdraw[k+2] = vertices[i+1] + s - t;
overdraw[k+3] = vertices[i+1];
overdraw[k+4] = vertices[i+1];
overdraw[k+5] = vertices[i+1] + s - t;
overdraw[k+6] = vertices[i+2] - s - t;
overdraw[k+7] = vertices[i+2];
overdraw[k+8] = vertices[i+2];
overdraw[k+9] = vertices[i+2] - s - t;
overdraw[k+10] = vertices[i+3] - s + t;
overdraw[k+11] = vertices[i+3];
overdraw[k+12] = vertices[i+3];
overdraw[k+13] = vertices[i+3] - s + t;
overdraw[k+14] = vertices[i] + s + t;
overdraw[k+15] = vertices[i];
}
}
Polyline::~Polyline()
{
if (vertices)
delete[] vertices;
if (overdraw)
delete[] overdraw;
}
void Polyline::draw()
{
gl.prepareDraw();
// draw the core line
gl.bindTexture(0);
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) vertices);
glDrawArrays(draw_mode, 0, vertex_count);
if (overdraw)
{
// prepare colors:
Color c = gl.getColor();
Color *colors = new Color[overdraw_vertex_count];
fill_color_array(colors, c);
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) overdraw);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colors);
glDrawArrays(draw_mode, 0, overdraw_vertex_count);
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setColor(c);
delete[] colors;
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
}
void Polyline::fill_color_array(Color *colors, const Color &c)
{
for (size_t i = 0; i < overdraw_vertex_count; ++i)
{
colors[i] = c;
// avoids branching. equiv to if (i%2 == 1) colors[i].a = 0;
colors[i].a *= GLubyte((i+1) % 2);
}
}
void NoneJoinPolyline::fill_color_array(Color *colors, const Color &c)
{
for (size_t i = 0; i < overdraw_vertex_count; ++i)
{
colors[i] = c;
// if (i % 4 == 1 || i % 4 == 2) colors[i].a = 0
colors[i].a *= GLubyte((i+1) % 4 < 2);
}
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,168 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_POLYLINE_H
#define LOVE_GRAPHICS_OPENGL_POLYLINE_H
#include <vector>
// LOVE
#include "common/Vector.h"
// OpenGL
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* Abstract base class for a chain of segments.
* @author Matthias Richter
**/
class Polyline
{
public:
Polyline(GLenum mode = GL_TRIANGLE_STRIP)
: vertices(NULL)
, overdraw(NULL)
, vertex_count(0)
, overdraw_vertex_count(0)
, draw_mode(mode)
{}
virtual ~Polyline();
/**
* @param vertices Vertices defining the core line segments
* @param count Number of coordinates (= size of the array vertices)
* @param size_hint Expected number of vertices of the rendering sleeve around the core line.
* @param halfwidth linewidth / 2.
* @param pixel_size Dimension of one pixel on the screen in world coordinates.
* @param draw_overdraw Fake antialias the line.
*/
void render(const float *vertices, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw);
/** Draws the line on the screen
*/
void draw();
protected:
virtual void render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping);
virtual void fill_color_array(Color *colors, const Color &c);
/** Calculate line boundary points.
*
* @param[out] anchors Anchor points defining the core line.
* @param[out] normals Normals defining the edge of the sleeve.
* @param[in,out] s Direction of segment pq (updated to the segment qr).
* @param[in,out] len_s Length of segment pq (updated to the segment qr).
* @param[in,out] ns Normal on the segment pq (updated to the segment qr).
* @param[in] q Current point on the line.
* @param[in] r Next point on the line.
* @param[in] hw Half line width (see Polyline.render()).
*/
virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw) = 0;
Vector *vertices;
Vector *overdraw;
size_t vertex_count;
size_t overdraw_vertex_count;
GLenum draw_mode;
}; // Polyline
/**
* A Polyline whose segments are not connected.
* @author Matthias Richter
*/
class NoneJoinPolyline : public Polyline
{
public:
NoneJoinPolyline()
// TODO: replace GL_QUADS (indexed triangles?)
: Polyline(GL_QUADS)
{}
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, 2 * count - 4, halfwidth, pixel_size, draw_overdraw);
// discard the first and last two vertices. (these are redundant)
for (size_t i = 0; i < vertex_count - 2; ++i)
this->vertices[i] = this->vertices[i+2];
vertex_count -= 2;
}
protected:
virtual void render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping);
virtual void fill_color_array(Color *colors, const Color &c);
virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw);
};
/**
* A Polyline whose segments are connected by a sharp edge.
* @author Matthias Richter
*/
class MiterJoinPolyline : public Polyline
{
public:
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, count, halfwidth, pixel_size, draw_overdraw);
}
protected:
virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw);
};
/**
* A Polyline whose segments are connected by a flat edge.
* @author Matthias Richter
*/
class BevelJoinPolyline : public Polyline
{
public:
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, 2 * count - 4, halfwidth, pixel_size, draw_overdraw);
}
protected:
virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw);
};
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_POLYLINE_H
@@ -0,0 +1,803 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "common/config.h"
#include "Shader.h"
#include "Graphics.h"
#include <algorithm>
namespace love
{
namespace graphics
{
namespace opengl
{
namespace
{
// temporarily attaches a shader program (for setting uniforms, etc)
// reattaches the originally active program when destroyed
struct TemporaryAttacher
{
TemporaryAttacher(Shader *shader)
: curShader(shader)
, prevShader(Shader::current)
{
curShader->attach(true);
}
~TemporaryAttacher()
{
if (prevShader != nullptr)
prevShader->attach();
else
curShader->detach();
}
Shader *curShader;
Shader *prevShader;
};
} // anonymous namespace
Shader *Shader::current = nullptr;
Shader *Shader::defaultShader = nullptr;
Shader::ShaderSources Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
GLint Shader::maxTextureUnits = 0;
std::vector<int> Shader::textureCounters;
Shader::Shader(const ShaderSources &sources)
: shaderSources(sources)
, program(0)
, builtinUniforms()
{
if (shaderSources.empty())
throw love::Exception("Cannot create shader: no source code!");
if (maxTextureUnits <= 0)
{
GLint maxtexunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtexunits);
maxTextureUnits = std::max(maxtexunits - 1, 0);
}
// initialize global texture id counters if needed
if (textureCounters.size() < (size_t) maxTextureUnits)
textureCounters.resize(maxTextureUnits, 0);
// load shader source and create program object
loadVolatile();
}
Shader::~Shader()
{
if (current == this)
detach();
for (auto it = boundRetainables.begin(); it != boundRetainables.end(); ++it)
{
it->second->release();
boundRetainables.erase(it);
}
unloadVolatile();
}
GLuint Shader::compileCode(ShaderType type, const std::string &code)
{
GLenum glshadertype;
const char *typestr;
if (!typeNames.find(type, typestr))
typestr = "";
switch (type)
{
case TYPE_VERTEX:
glshadertype = GL_VERTEX_SHADER;
break;
case TYPE_PIXEL:
glshadertype = GL_FRAGMENT_SHADER;
break;
default:
throw love::Exception("Cannot create shader object: unknown shader type.");
break;
}
// clear existing errors
while (glGetError() != GL_NO_ERROR);
GLuint shaderid = glCreateShader(glshadertype);
if (shaderid == 0) // oh no!
{
GLenum err = glGetError();
if (err == GL_INVALID_ENUM)
throw love::Exception("Cannot create %s shader object: %s shaders not supported.", typestr, typestr);
else
throw love::Exception("Cannot create %s shader object.", typestr);
}
const char *src = code.c_str();
size_t srclen = code.length();
glShaderSource(shaderid, 1, (const GLchar **)&src, (GLint *)&srclen);
glCompileShader(shaderid);
// Get any warnings the shader compiler may have produced.
GLint infologlen;
glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen);
GLchar *infolog = new GLchar[infologlen + 1];
glGetShaderInfoLog(shaderid, infologlen, nullptr, infolog);
// Save any warnings for later querying.
if (infologlen > 0)
shaderWarnings[type] = infolog;
delete[] infolog;
GLint status;
glGetShaderiv(shaderid, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
throw love::Exception("Cannot compile %s shader code:\n%s",
typestr, shaderWarnings[type].c_str());
}
return shaderid;
}
void Shader::createProgram(const std::vector<GLuint> &shaderids)
{
program = glCreateProgram();
if (program == 0)
throw love::Exception("Cannot create shader program object.");
std::vector<GLuint>::const_iterator it;
for (it = shaderids.begin(); it != shaderids.end(); ++it)
glAttachShader(program, *it);
// We use generic vertex attributes in OpenGL ES 2, so we have to bind the
// attribute indices to names in the shader.
if (GLAD_ES_VERSION_2_0)
{
const char *name = nullptr;
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
{
if (attribNames.find(OpenGL::VertexAttrib(i), name))
glBindAttribLocation(program, i, (const GLchar *) name);
}
}
glLinkProgram(program);
// flag shaders for auto-deletion when the program object is deleted.
for (it = shaderids.begin(); it != shaderids.end(); ++it)
glDeleteShader(*it);
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
std::string warnings = getProgramWarnings();
glDeleteProgram(program);
program = 0;
throw love::Exception("Cannot link shader program object:\n%s", warnings.c_str());
}
}
void Shader::mapActiveUniforms()
{
uniforms.clear();
GLint numuniforms;
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numuniforms);
GLsizei bufsize;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, (GLint *) &bufsize);
if (bufsize <= 0)
return;
for (int i = 0; i < numuniforms; i++)
{
GLchar *cname = new GLchar[bufsize];
GLsizei namelength;
Uniform u;
glGetActiveUniform(program, (GLuint) i, bufsize, &namelength, &u.count, &u.type, cname);
u.name = std::string(cname, (size_t) namelength);
u.location = glGetUniformLocation(program, u.name.c_str());
u.baseType = getUniformBaseType(u.type);
delete[] cname;
// glGetActiveUniform appends "[0]" to the end of array uniform names...
if (u.name.length() > 3)
{
size_t findpos = u.name.find("[0]");
if (findpos != std::string::npos && findpos == u.name.length() - 3)
u.name.erase(u.name.length() - 3);
}
// Store the uniform locations for any built-in extern variables, in ES.
if (GLAD_ES_VERSION_2_0)
{
BuiltinExtern builtin;
if (builtinNames.find(u.name.c_str(), builtin))
builtinUniforms[int(builtin)] = u.location;
}
if (u.location != -1)
uniforms[u.name] = u;
}
}
bool Shader::loadVolatile()
{
// zero out active texture list
activeTextureUnits.clear();
activeTextureUnits.insert(activeTextureUnits.begin(), maxTextureUnits, 0);
// Built-in uniform locations default to -1 (nonexistant.)
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
std::vector<GLuint> shaderids;
ShaderSources::const_iterator source;
for (source = shaderSources.begin(); source != shaderSources.end(); ++source)
{
GLuint shaderid = compileCode(source->first, source->second);
shaderids.push_back(shaderid);
}
// All shader programs in ES2 must have a vertex and pixel shader.
if (GLAD_ES_VERSION_2_0)
{
ShaderSources &defaults = defaultCode[Graphics::RENDERER_OPENGLES];
source = shaderSources.find(TYPE_VERTEX);
if (source == shaderSources.end())
shaderids.push_back(compileCode(TYPE_VERTEX, defaults[TYPE_VERTEX]));
source = shaderSources.find(TYPE_PIXEL);
if (source == shaderSources.end())
shaderids.push_back(compileCode(TYPE_PIXEL, defaults[TYPE_PIXEL]));
}
if (shaderids.empty())
throw love::Exception("Cannot create shader: no valid source code!");
createProgram(shaderids);
// Retrieve all active uniform variables in this shader from OpenGL.
mapActiveUniforms();
if (current == this)
{
// make sure glUseProgram gets called.
current = nullptr;
attach();
}
return true;
}
void Shader::unloadVolatile()
{
if (current == this)
glUseProgram(0);
if (program != 0)
{
glDeleteProgram(program);
program = 0;
}
// decrement global texture id counters for texture units which had textures bound from this shader
for (size_t i = 0; i < activeTextureUnits.size(); ++i)
{
if (activeTextureUnits[i] > 0)
textureCounters[i] = std::max(textureCounters[i] - 1, 0);
}
// active texture list is probably invalid, clear it
activeTextureUnits.clear();
activeTextureUnits.insert(activeTextureUnits.begin(), maxTextureUnits, 0);
// same with uniform location list
uniforms.clear();
// And the locations of any built-in uniform variables.
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
shaderWarnings.clear();
}
std::string Shader::getProgramWarnings() const
{
GLint strlen, nullpos;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &strlen);
char *tempstr = new char[strlen+1];
// be extra sure that the error string will be 0-terminated
memset(tempstr, '\0', strlen+1);
glGetProgramInfoLog(program, strlen, &nullpos, tempstr);
tempstr[nullpos] = '\0';
std::string warnings(tempstr);
delete[] tempstr;
return warnings;
}
std::string Shader::getWarnings() const
{
std::string warnings;
const char *typestr;
// Get the individual shader stage warnings
std::map<ShaderType, std::string>::const_iterator it;
for (it = shaderWarnings.begin(); it != shaderWarnings.end(); ++it)
{
if (typeNames.find(it->first, typestr))
warnings += std::string(typestr) + std::string(" shader:\n") + it->second;
}
warnings += getProgramWarnings();
return warnings;
}
void Shader::attach(bool temporary)
{
Shader *oldshader = current;
if (oldshader != this)
{
glUseProgram(program);
current = this;
current->retain();
if (oldshader != nullptr)
oldshader->release();
}
if (!temporary)
{
// make sure all sent textures are properly bound to their respective texture units
// note: list potentially contains texture ids of deleted/invalid textures!
for (size_t i = 0; i < activeTextureUnits.size(); ++i)
{
if (activeTextureUnits[i] > 0)
gl.bindTextureToUnit(activeTextureUnits[i], i + 1, false);
}
// We always want to use texture unit 0 for everyhing else.
gl.setTextureUnit(0);
}
}
void Shader::detach()
{
// We always need a shader set in ES2.
if (GLAD_ES_VERSION_2_0)
{
if (defaultShader && current != defaultShader)
defaultShader->attach();
}
else
{
if (current != nullptr)
{
glUseProgram(0);
current = nullptr;
current->release();
}
}
}
const Shader::Uniform &Shader::getUniform(const std::string &name) const
{
std::map<std::string, Uniform>::const_iterator it = uniforms.find(name);
if (it == uniforms.end())
throw love::Exception("Variable '%s' does not exist.\n"
"A common error is to define but not use the variable.", name.c_str());
return it->second;
}
int Shader::getUniformTypeSize(GLenum type) const
{
switch (type)
{
case GL_INT:
case GL_FLOAT:
case GL_BOOL:
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
return 1;
case GL_INT_VEC2:
case GL_FLOAT_VEC2:
case GL_FLOAT_MAT2:
case GL_BOOL_VEC2:
return 2;
case GL_INT_VEC3:
case GL_FLOAT_VEC3:
case GL_FLOAT_MAT3:
case GL_BOOL_VEC3:
return 3;
case GL_INT_VEC4:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT4:
case GL_BOOL_VEC4:
return 4;
default:
break;
}
return 1;
}
Shader::UniformType Shader::getUniformBaseType(GLenum type) const
{
switch (type)
{
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
return UNIFORM_INT;
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
return UNIFORM_FLOAT;
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
return UNIFORM_BOOL;
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
return UNIFORM_SAMPLER;
default:
break;
}
return UNIFORM_UNKNOWN;
}
void Shader::checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const
{
if (!program)
throw love::Exception("No active shader program.");
int realsize = getUniformTypeSize(u.type);
if (size != realsize)
throw love::Exception("Value size of %d does not match variable size of %d.", size, realsize);
if ((u.count == 1 && count > 1) || count < 0)
throw love::Exception("Invalid number of values (expected %d, got %d).", u.count, count);
if (u.baseType == UNIFORM_SAMPLER && sendtype != u.baseType)
throw love::Exception("Cannot send a value of this type to an Image variable.");
if ((sendtype == UNIFORM_FLOAT && u.baseType == UNIFORM_INT) || (sendtype == UNIFORM_INT && u.baseType == UNIFORM_FLOAT))
throw love::Exception("Cannot convert between float and int.");
}
void Shader::sendInt(const std::string &name, int size, const GLint *vec, int count)
{
TemporaryAttacher attacher(this);
const Uniform &u = getUniform(name);
checkSetUniformError(u, size, count, UNIFORM_INT);
switch (size)
{
case 4:
glUniform4iv(u.location, count, vec);
break;
case 3:
glUniform3iv(u.location, count, vec);
break;
case 2:
glUniform2iv(u.location, count, vec);
break;
case 1:
default:
glUniform1iv(u.location, count, vec);
break;
}
}
void Shader::sendFloat(const std::string &name, int size, const GLfloat *vec, int count)
{
TemporaryAttacher attacher(this);
const Uniform &u = getUniform(name);
checkSetUniformError(u, size, count, UNIFORM_FLOAT);
switch (size)
{
case 4:
glUniform4fv(u.location, count, vec);
break;
case 3:
glUniform3fv(u.location, count, vec);
break;
case 2:
glUniform2fv(u.location, count, vec);
break;
case 1:
default:
glUniform1fv(u.location, count, vec);
break;
}
}
void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int count)
{
TemporaryAttacher attacher(this);
if (size < 2 || size > 4)
{
throw love::Exception("Invalid matrix size: %dx%d "
"(can only set 2x2, 3x3 or 4x4 matrices.)", size,size);
}
const Uniform &u = getUniform(name);
checkSetUniformError(u, size, count, UNIFORM_FLOAT);
switch (size)
{
case 4:
glUniformMatrix4fv(u.location, count, GL_FALSE, m);
break;
case 3:
glUniformMatrix3fv(u.location, count, GL_FALSE, m);
break;
case 2:
default:
glUniformMatrix2fv(u.location, count, GL_FALSE, m);
break;
}
}
void Shader::sendTexture(const std::string &name, GLuint texture)
{
TemporaryAttacher attacher(this);
int textureunit = getTextureUnit(name);
const Uniform &u = getUniform(name);
checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
// bind texture to assigned texture unit and send uniform to shader program
gl.bindTextureToUnit(texture, textureunit, false);
glUniform1i(u.location, textureunit);
// reset texture unit
gl.setTextureUnit(0);
// increment global shader texture id counter for this texture unit, if we haven't already
if (activeTextureUnits[textureunit-1] == 0)
++textureCounters[textureunit-1];
// store texture id so it can be re-bound to the proper texture unit later
activeTextureUnits[textureunit-1] = texture;
}
void Shader::retainTexture(const std::string &name, Object *texture)
{
auto it = boundRetainables.find(name);
if (it != boundRetainables.end())
it->second->release();
texture->retain();
boundRetainables[name] = texture;
}
void Shader::sendImage(const std::string &name, Image &image)
{
sendTexture(name, image.getTextureName());
retainTexture(name, &image);
}
void Shader::sendCanvas(const std::string &name, Canvas &canvas)
{
sendTexture(name, canvas.getTextureName());
retainTexture(name, &canvas);
}
int Shader::getTextureUnit(const std::string &name)
{
auto it = textureUnitPool.find(name);
if (it != textureUnitPool.end())
return it->second;
int textureunit = 1;
// prefer texture units which are unused by all other shaders
auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0);
if (freeunit_it != textureCounters.end())
{
// we don't want to use unit 0
textureunit = std::distance(textureCounters.begin(), freeunit_it) + 1;
}
else
{
// no completely unused texture units exist, try to use next free slot in our own list
auto nextunit_it = std::find(activeTextureUnits.begin(), activeTextureUnits.end(), 0);
if (nextunit_it == activeTextureUnits.end())
throw love::Exception("No more texture units available for shader.");
// we don't want to use unit 0
textureunit = std::distance(activeTextureUnits.begin(), nextunit_it) + 1;
}
textureUnitPool[name] = textureunit;
return textureunit;
}
bool Shader::hasBuiltinUniform(love::graphics::opengl::Shader::BuiltinExtern builtin) const
{
return builtinUniforms[int(builtin)] != -1;
}
bool Shader::sendBuiltinMatrix(BuiltinExtern builtin, int size, const GLfloat *m, int count)
{
if (!hasBuiltinUniform(builtin))
return false;
GLint location = builtinUniforms[GLint(builtin)];
TemporaryAttacher attacher(this);
switch (size)
{
case 2:
glUniformMatrix2fv(location, count, GL_FALSE, m);
break;
case 3:
glUniformMatrix3fv(location, count, GL_FALSE, m);
break;
case 4:
glUniformMatrix4fv(location, count, GL_FALSE, m);
break;
default:
return false;
}
return true;
}
bool Shader::sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *vec, int count)
{
if (!hasBuiltinUniform(builtin))
return false;
GLint location = builtinUniforms[GLint(builtin)];
TemporaryAttacher attacher(this);
switch (size)
{
case 1:
glUniform1fv(location, count, vec);
break;
case 2:
glUniform2fv(location, count, vec);
break;
case 3:
glUniform3fv(location, count, vec);
break;
case 4:
glUniform4fv(location, count, vec);
break;
default:
return false;
}
return true;
}
std::string Shader::getGLSLVersion()
{
const char *tmp = nullptr;
// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
if (GLAD_ES_VERSION_2_0 || GLAD_VERSION_2_0 || GLAD_ARB_shading_language_100)
tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
if (tmp == nullptr)
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
// 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 Shader::isSupported()
{
return GLAD_ES_VERSION_2_0 || (GLAD_VERSION_2_0 && getGLSLVersion() >= "1.2");
}
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM>::Entry Shader::typeNameEntries[] =
{
{"vertex", Shader::TYPE_VERTEX},
{"pixel", Shader::TYPE_PIXEL},
};
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM> Shader::typeNames(Shader::typeNameEntries, sizeof(Shader::typeNameEntries));
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
{
{"VertexPosition", OpenGL::ATTRIB_POS},
{"VertexTexCoord", OpenGL::ATTRIB_TEXCOORD},
{"VertexColor", OpenGL::ATTRIB_COLOR},
};
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
{
{"TransformMatrix", Shader::BUILTIN_TRANSFORM_MATRIX},
{"ProjectionMatrix", Shader::BUILTIN_PROJECTION_MATRIX},
{"TransformProjectionMatrix", Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX},
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
};
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
} // opengl
} // graphics
} // love
@@ -0,0 +1,252 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_SHADER_H
#define LOVE_GRAPHICS_SHADER_H
// LOVE
#include "common/Object.h"
#include "common/StringMap.h"
#include "graphics/Graphics.h"
#include "OpenGL.h"
#include "Image.h"
#include "Canvas.h"
// STL
#include <string>
#include <map>
#include <vector>
namespace love
{
namespace graphics
{
namespace opengl
{
// A GLSL shader
class Shader : public Object, public Volatile
{
public:
// Pointer to currently active Shader.
static Shader *current;
// Pointer to the current default Shader.
static Shader *defaultShader;
enum ShaderType
{
TYPE_VERTEX,
TYPE_PIXEL,
TYPE_MAX_ENUM
};
// Built-in extern (uniform) variables.
enum BuiltinExtern
{
BUILTIN_TRANSFORM_MATRIX = 0,
BUILTIN_PROJECTION_MATRIX,
BUILTIN_TRANSFORM_PROJECTION_MATRIX,
BUILTIN_POINT_SIZE,
BUILTIN_MAX_ENUM
};
// Type for a list of shader source codes in the form of sources[shadertype] = code
typedef std::map<ShaderType, std::string> ShaderSources;
/**
* Creates a new Shader using a list of source codes.
* Sources must contain either vertex or pixel shader code, or both.
**/
Shader(const ShaderSources &sources);
virtual ~Shader();
// Implements Volatile
virtual bool loadVolatile();
virtual void unloadVolatile();
/**
* Binds this Shader's program to be used when rendering.
*
* @param temporary True if we just want to send values to the shader with no intention of rendering.
**/
void attach(bool temporary = false);
/**
* Detach the currently bound Shader.
* Causes the GPU rendering pipeline to use fixed functionality in place of shader programs.
**/
static void detach();
/**
* Returns any warnings this Shader may have generated.
**/
std::string getWarnings() const;
/**
* Send at least one integer or int-vector value to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
* @param size Number of elements in each vector to send.
* A value of 1 indicates a single-component vector (an int).
* @param vec Pointer to the integer or int-vector values.
* @param count Number of integer or int-vector values.
**/
void sendInt(const std::string &name, int size, const GLint *vec, int count);
/**
* Send at least one float or vector value to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
* @param size Number of elements in each vector to send.
* A value of 1 indicates a single-component vector (a float).
* @param vec Pointer to the float or float-vector values.
* @param count Number of float or float-vector values.
**/
void sendFloat(const std::string &name, int size, const GLfloat *vec, int count);
/**
* Send at least one matrix to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
* @param size Number of rows/columns in the matrix.
* @param m Pointer to the first element of the first matrix.
* @param count Number of matrices to send.
**/
void sendMatrix(const std::string &name, int size, const GLfloat *m, int count);
/**
* Send an image to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
**/
void sendImage(const std::string &name, Image &image);
/**
* Send a canvas to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
**/
void sendCanvas(const std::string &name, Canvas &canvas);
/**
* Internal use only.
**/
bool hasBuiltinUniform(BuiltinExtern builtin) const;
bool sendBuiltinMatrix(BuiltinExtern builtin, int size, const GLfloat *m, int count);
bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count);
static std::string getGLSLVersion();
static bool isSupported();
// Default code used when renderers require code for a shader stage.
static ShaderSources defaultCode[Graphics::RENDERER_MAX_ENUM];
private:
// Types of potential uniform variables used in love's shaders.
enum UniformType
{
UNIFORM_FLOAT,
UNIFORM_INT,
UNIFORM_BOOL,
UNIFORM_SAMPLER,
UNIFORM_UNKNOWN
};
// Represents a single uniform/extern shader variable.
struct Uniform
{
GLint location;
GLint count;
GLenum type;
UniformType baseType;
std::string name;
};
// Map active uniform names to their locations.
void mapActiveUniforms();
const Uniform &getUniform(const std::string &name) const;
int getUniformTypeSize(GLenum type) const;
UniformType getUniformBaseType(GLenum type) const;
void checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const;
GLuint compileCode(ShaderType type, const std::string &code);
void createProgram(const std::vector<GLuint> &shaderids);
int getTextureUnit(const std::string &name);
void sendTexture(const std::string &name, GLuint texture);
void retainTexture(const std::string &name, Object *texture);
// Get any warnings or errors generated only by the shader program object.
std::string getProgramWarnings() const;
// List of all shader code attached to this Shader
ShaderSources shaderSources;
// Shader compiler warning strings for individual shader stages.
std::map<ShaderType, std::string> shaderWarnings;
// volatile
GLuint program;
// Locations for any built-in uniform variables.
GLint builtinUniforms[BUILTIN_MAX_ENUM];
// Uniform location buffer map
std::map<std::string, Uniform> uniforms;
// Texture unit pool for setting images
std::map<std::string, GLint> textureUnitPool; // textureUnitPool[name] = textureunit
std::vector<GLuint> activeTextureUnits; // activeTextureUnits[textureunit-1] = textureid
// Uniform name to retainable objects
std::map<std::string, Object*> boundRetainables;
// Max GPU texture units available for sent images
static GLint maxTextureUnits;
// Counts total number of textures bound to each texture unit in all shaders
static std::vector<int> textureCounters;
static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
static StringMap<ShaderType, TYPE_MAX_ENUM> typeNames;
// Names for the generic vertex attributes used in OpenGL ES 2.
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> attribNames;
// Names for the uniform matrices used in OpenGL ES 2.
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM> builtinNames;
}; // Shader
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_SHADER_H
@@ -0,0 +1,358 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "common/config.h"
#include "SpriteBatch.h"
// OpenGL
#include "OpenGL.h"
// LOVE
#include "Image.h"
#include "VertexBuffer.h"
// C++
#include <algorithm>
// C
#include <stddef.h>
namespace love
{
namespace graphics
{
namespace opengl
{
SpriteBatch::SpriteBatch(Image *image, int size, int usage)
: image(image)
, size(size)
, next(0)
, color(0)
, array_buf(0)
, element_buf(0)
{
if (size <= 0)
throw love::Exception("Invalid SpriteBatch size.");
GLenum gl_usage;
switch (usage)
{
default:
case USAGE_DYNAMIC:
gl_usage = GL_DYNAMIC_DRAW;
break;
case USAGE_STATIC:
gl_usage = GL_STATIC_DRAW;
break;
case USAGE_STREAM:
gl_usage = GL_STREAM_DRAW;
break;
}
const size_t vertex_size = sizeof(Vertex) * 4 * size;
try
{
array_buf = VertexBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
element_buf = new VertexIndex(size);
}
catch (love::Exception &)
{
delete array_buf;
delete element_buf;
throw;
}
catch (std::bad_alloc &)
{
delete array_buf;
delete element_buf;
throw love::Exception("Out of memory.");
}
image->retain();
}
SpriteBatch::~SpriteBatch()
{
image->release();
delete color;
delete array_buf;
delete element_buf;
}
int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index /*= -1*/)
{
// Only do this if there's a free slot.
if ((index == -1 && next >= size) || index < -1 || index >= size)
return -1;
// Needed for colors.
memcpy(sprite, image->getVertices(), sizeof(Vertex)*4);
// Transform.
static Matrix t;
t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
t.transform(sprite, sprite, 4);
if (color)
setColorv(sprite, *color);
addv(sprite, (index == -1) ? next : index);
// Increment counter.
if (index == -1)
return next++;
return index;
}
int SpriteBatch::addq(Quad *quad, float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index /*= -1*/)
{
// Only do this if there's a free slot.
if ((index == -1 && next >= size) || index < -1 || index >= next)
return -1;
// Needed for colors.
memcpy(sprite, quad->getVertices(), sizeof(Vertex) * 4);
static Matrix t;
t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
t.transform(sprite, sprite, 4);
if (color)
setColorv(sprite, *color);
addv(sprite, (index == -1) ? next : index);
// Increment counter.
if (index == -1)
return next++;
return index;
}
void SpriteBatch::clear()
{
// Reset the position of the next index.
next = 0;
}
void *SpriteBatch::lock()
{
VertexBuffer::Bind bind(*array_buf);
return array_buf->map();
}
void SpriteBatch::unlock()
{
VertexBuffer::Bind bind(*array_buf);
array_buf->unmap();
}
void SpriteBatch::setImage(Image *newimage)
{
Object::AutoRelease imagerelease(image);
newimage->retain();
image = newimage;
}
Image *SpriteBatch::getImage()
{
return image;
}
void SpriteBatch::setColor(const Color &color)
{
if (!this->color)
this->color = new Color(color);
else
*(this->color) = color;
}
void SpriteBatch::setColor()
{
delete color;
color = 0;
}
const Color *SpriteBatch::getColor() const
{
return color;
}
int SpriteBatch::getCount() const
{
return next;
}
void SpriteBatch::setBufferSize(int newsize)
{
if (newsize <= 0)
throw love::Exception("Invalid SpriteBatch size.");
if (newsize == size)
return;
// Map (lock) the old VertexBuffer to get a pointer to its data.
void *old_data = lock();
size_t vertex_size = sizeof(Vertex) * 4 * newsize;
VertexBuffer *new_array_buf = 0;
VertexIndex *new_element_buf = 0;
void *new_data = 0;
try
{
new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
new_element_buf = new VertexIndex(newsize);
// VBO::map can throw an exception. Also we want to scope the bind.
VertexBuffer::Bind bind(*new_array_buf);
new_data = new_array_buf->map();
}
catch (love::Exception &)
{
delete new_array_buf;
delete new_element_buf;
unlock();
throw;
}
// Copy as much of the old data into the new VertexBuffer as can fit.
memcpy(new_data, old_data, sizeof(Vertex) * 4 * std::min(newsize, size));
// We don't need to unmap the old VertexBuffer since we're deleting it.
delete array_buf;
delete element_buf;
array_buf = new_array_buf;
element_buf = new_element_buf;
size = newsize;
next = std::min(next, newsize);
// But we should unmap (unlock) the new one!
unlock();
}
int SpriteBatch::getBufferSize() const
{
return size;
}
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
const size_t vertex_offset = offsetof(Vertex, x);
const size_t texel_offset = offsetof(Vertex, s);
const size_t color_offset = offsetof(Vertex, r);
if (next == 0)
return;
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
image->predraw();
gl.prepareDraw();
VertexBuffer::Bind array_bind(*array_buf);
VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(vertex_offset));
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
Color curcolor = gl.getColor();
// Apply per-sprite color, if a color is set.
if (color)
{
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, sizeof(Vertex), array_buf->getPointer(color_offset));
}
glDrawElements(GL_TRIANGLES, element_buf->getIndexCount(next), element_buf->getType(), element_buf->getPointer(0));
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
if (color)
{
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setColor(curcolor);
}
image->postdraw();
gl.matrices.transform.pop();
}
void SpriteBatch::addv(const Vertex *v, int index)
{
static const int sprite_size = 4 * sizeof(Vertex); // bytecount
VertexBuffer::Bind bind(*array_buf);
array_buf->fill(index * sprite_size, sprite_size, v);
}
void SpriteBatch::setColorv(Vertex *v, const Color &color)
{
for (size_t i = 0; i < 4; ++i)
{
v[i].r = color.r;
v[i].g = color.g;
v[i].b = color.b;
v[i].a = color.a;
}
}
bool SpriteBatch::getConstant(const char *in, UsageHint &out)
{
return usageHints.find(in, out);
}
bool SpriteBatch::getConstant(UsageHint in, const char *&out)
{
return usageHints.find(in, out);
}
StringMap<SpriteBatch::UsageHint, SpriteBatch::USAGE_MAX_ENUM>::Entry SpriteBatch::usageHintEntries[] =
{
{"dynamic", SpriteBatch::USAGE_DYNAMIC},
{"static", SpriteBatch::USAGE_STATIC},
{"stream", SpriteBatch::USAGE_STREAM},
};
StringMap<SpriteBatch::UsageHint, SpriteBatch::USAGE_MAX_ENUM> SpriteBatch::usageHints(usageHintEntries, sizeof(usageHintEntries));
} // opengl
} // graphics
} // love
@@ -0,0 +1,156 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
#define LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
// C
#include <cstring>
// LOVE
#include "common/math.h"
#include "common/Object.h"
#include "common/Matrix.h"
#include "common/StringMap.h"
#include "graphics/Drawable.h"
#include "graphics/Volatile.h"
#include "graphics/Color.h"
#include "graphics/Quad.h"
namespace love
{
namespace graphics
{
namespace opengl
{
// Forward declarations.
class Image;
class VertexBuffer;
class VertexIndex;
class SpriteBatch : public Drawable
{
public:
enum UsageHint
{
USAGE_DYNAMIC = 1,
USAGE_STATIC,
USAGE_STREAM,
USAGE_MAX_ENUM
};
SpriteBatch(Image *image, int size, int usage);
virtual ~SpriteBatch();
int add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index = -1);
int addq(Quad *quad, float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index = -1);
void clear();
void *lock();
void unlock();
void setImage(Image *newimage);
Image *getImage();
/**
* Set the current color for this SpriteBatch. The sprites added
* after this call will use this color. Note that global color
* will not longer apply to the SpriteBatch if this is used.
*
* @param color The color to use for the following sprites.
*/
void setColor(const Color &color);
/**
* Disable per-sprite colors for this SpriteBatch. The next call to
* draw will use the global color for all sprites.
*/
void setColor();
/**
* Get the current color for this SpriteBatch. Returns NULL if no color is
* set.
**/
const Color *getColor() const;
/**
* Get the number of sprites currently in this SpriteBatch.
**/
int getCount() const;
/**
* Sets the total number of sprites this SpriteBatch can hold.
* Leaves existing sprite data intact when possible.
**/
void setBufferSize(int newsize);
/**
* Get the total number of sprites this SpriteBatch can hold.
**/
int getBufferSize() const;
// Implements Drawable.
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
static bool getConstant(const char *in, UsageHint &out);
static bool getConstant(UsageHint in, const char *&out);
private:
void addv(const Vertex *v, int index);
/**
* Set the color for vertices.
*
* @param v The vertices to set the color for. Must be an array of
* of size 4.
* @param color The color to assign to each vertex.
*/
void setColorv(Vertex *v, const Color &color);
Image *image;
// Max number of sprites in the batch.
int size;
// The next free element.
int next;
Vertex sprite[4];
// Current color. This color, if present, will be applied to the next
// added sprite.
Color *color;
VertexBuffer *array_buf;
VertexIndex *element_buf;
static StringMap<UsageHint, USAGE_MAX_ENUM>::Entry usageHintEntries[];
static StringMap<UsageHint, USAGE_MAX_ENUM> usageHints;
}; // SpriteBatch
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
@@ -0,0 +1,468 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "VertexBuffer.h"
#include "common/Exception.h"
#include "common/config.h"
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <limits>
// Conflicts with std::numeric_limits<GLushort>::max() (Windows).
#ifdef max
# undef max
#endif
namespace love
{
namespace graphics
{
namespace opengl
{
// VertexBuffer
VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
{
try
{
// Try to create a VBO.
return new VBO(size, target, usage, backing);
}
catch(const love::Exception &)
{
// VBO not supported ... create regular array.
return new VertexArray(size, target, usage, backing);
}
}
VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: is_bound(false)
, is_mapped(false)
, size(size)
, target(target)
, usage(usage)
, backing(backing)
{
}
VertexBuffer::~VertexBuffer()
{
}
// VertexArray
VertexArray::VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: VertexBuffer(size, target, usage, backing)
, buf(new char[size])
{
}
VertexArray::~VertexArray()
{
delete [] buf;
}
void *VertexArray::map()
{
is_mapped = true;
return buf;
}
void VertexArray::unmap()
{
is_mapped = false;
}
void VertexArray::bind()
{
is_bound = true;
}
void VertexArray::unbind()
{
is_bound = false;
}
void VertexArray::fill(size_t offset, size_t size, const void *data)
{
memcpy(buf + offset, data, size);
}
const void *VertexArray::getPointer(size_t offset) const
{
return buf + offset;
}
// VBO
VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: VertexBuffer(size, target, usage, backing)
, vbo(0)
, memory_map(0)
, is_dirty(false)
{
if (!(GLAD_ARB_vertex_buffer_object || GLAD_VERSION_1_5 || GLAD_ES_VERSION_2_0))
throw love::Exception("Not supported");
// FIXME:
// ES2 can't do glGetBufferSubData.
if (GLAD_ES_VERSION_2_0)
backing = BACKING_FULL;
if (getMemoryBacking() == BACKING_FULL)
memory_map = malloc(getSize());
bool ok = load(false);
if (!ok)
{
free(memory_map);
throw love::Exception("Could not load VBO.");
}
}
VBO::~VBO()
{
if (vbo != 0)
unload(false);
if (memory_map)
free(memory_map);
}
void *VBO::map()
{
if (is_mapped)
return memory_map;
if (!memory_map)
{
memory_map = malloc(getSize());
if (!memory_map)
throw love::Exception("Out of memory (oh the humanity!)");
}
if (is_dirty)
{
glGetBufferSubData(getTarget(), 0, getSize(), memory_map);
is_dirty = false;
}
is_mapped = true;
return memory_map;
}
void VBO::unmap()
{
if (!is_mapped)
return;
// VBO::bind is a no-op when the VBO is mapped, so we have to make sure it's
// bound here.
if (!is_bound)
{
glBindBuffer(getTarget(), vbo);
is_bound = true;
}
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
glBufferData(getTarget(), getSize(), NULL, getUsage());
glBufferData(getTarget(), getSize(), memory_map, getUsage());
is_mapped = false;
}
void VBO::bind()
{
if (!is_mapped)
{
glBindBuffer(getTarget(), vbo);
is_bound = true;
}
}
void VBO::unbind()
{
if (is_bound)
glBindBuffer(getTarget(), 0);
is_bound = false;
}
void VBO::fill(size_t offset, size_t size, const void *data)
{
if (is_mapped || getMemoryBacking() == BACKING_FULL)
memcpy(static_cast<char *>(memory_map) + offset, data, size);
if (!is_mapped)
{
// Not all systems have access to some faster paths...
if (GLAD_APPLE_flush_buffer_range)
{
void *mapdata = glMapBuffer(getTarget(), GL_WRITE_ONLY);
if (mapdata)
{
// We specified in VBO::load that we'll do manual flushing.
// Now we tell the driver it only needs to deal with the data
// we changed.
memcpy(static_cast<char *>(mapdata) + offset, data, size);
glFlushMappedBufferRangeAPPLE(getTarget(), offset, size);
}
glUnmapBuffer(getTarget());
}
else
{
// Fall back to a possibly slower SubData (more chance of syncing.)
glBufferSubData(getTarget(), offset, size, data);
}
if (getMemoryBacking() != BACKING_FULL)
is_dirty = true;
}
}
const void *VBO::getPointer(size_t offset) const
{
return BUFFER_OFFSET(offset);
}
bool VBO::loadVolatile()
{
return load(true);
}
void VBO::unloadVolatile()
{
unload(true);
}
bool VBO::load(bool restore)
{
glGenBuffers(1, &vbo);
VertexBuffer::Bind bind(*this);
// Copy the old buffer only if 'restore' was requested.
const GLvoid *src = restore ? memory_map : 0;
while (GL_NO_ERROR != glGetError())
/* clear error messages */;
// We don't want to flush the entire buffer when we just modify a small
// portion of it (VBO::fill without VBO::map), so we'll handle the flushing
// ourselves when we can.
if (GLAD_APPLE_flush_buffer_range)
glBufferParameteriAPPLE(getTarget(), GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
// Note that if 'src' is '0', no data will be copied.
glBufferData(getTarget(), getSize(), src, getUsage());
GLenum err = glGetError();
return (GL_NO_ERROR == err);
}
void VBO::unload(bool save)
{
// Save data before unloading, if we need to.
if (save && getMemoryBacking() == BACKING_PARTIAL)
{
VertexBuffer::Bind bind(*this);
bool mapped = is_mapped;
map(); // saves buffer content to memory_map.
is_mapped = mapped;
}
glDeleteBuffers(1, &vbo);
vbo = 0;
}
// VertexIndex
size_t VertexIndex::maxSize = 0;
size_t VertexIndex::elementSize = 0;
std::list<size_t> VertexIndex::sizeRefs;
VertexBuffer *VertexIndex::element_array = NULL;
VertexIndex::VertexIndex(size_t size)
: size(size)
{
// The upper limit is the maximum of GLuint divided by six (the number
// of indices per size) and divided by the size of GLuint. This guarantees
// no overflows when calculating the array size in bytes.
// Memory issues will be handled by other exceptions.
if (size == 0 || size > ((GLuint) -1) / 6 / sizeof(GLuint))
throw love::Exception("Invalid size.");
addSize(size);
}
VertexIndex::~VertexIndex()
{
removeSize(size);
}
size_t VertexIndex::getSize() const
{
return size;
}
size_t VertexIndex::getIndexCount(size_t elements) const
{
return elements * 6;
}
GLenum VertexIndex::getType(size_t s) const
{
// Calculates if unsigned short is big enough to hold all the vertex indices.
static const GLenum type_table[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT};
return type_table[s * 4 > std::numeric_limits<GLushort>::max()];
// if buffer-size > max(GLushort) then GL_UNSIGNED_INT else GL_UNSIGNED_SHORT
}
size_t VertexIndex::getElementSize()
{
return elementSize;
}
VertexBuffer *VertexIndex::getVertexBuffer() const
{
return element_array;
}
const void *VertexIndex::getPointer(size_t offset) const
{
return element_array->getPointer(offset);
}
void VertexIndex::addSize(size_t newSize)
{
if (newSize <= maxSize)
{
// Current size is bigger. Append the size to list and sort.
sizeRefs.push_back(newSize);
sizeRefs.sort();
return;
}
// Try to resize before adding it to the list because resize may throw.
resize(newSize);
sizeRefs.push_back(newSize);
}
void VertexIndex::removeSize(size_t oldSize)
{
// TODO: For debugging purposes, this should check if the size was actually found.
sizeRefs.erase(std::find(sizeRefs.begin(), sizeRefs.end(), oldSize));
if (sizeRefs.size() == 0)
{
resize(0);
return;
}
if (oldSize == maxSize)
{
// Shrink if there's a smaller size.
size_t newSize = sizeRefs.back();
if (newSize < maxSize)
resize(newSize);
}
}
void VertexIndex::resize(size_t size)
{
if (size == 0)
{
delete element_array;
element_array = NULL;
maxSize = 0;
return;
}
VertexBuffer *new_element_array;
// Depending on the size, a switch to int and more memory is needed.
GLenum target_type = getType(size);
size_t elem_size = (target_type == GL_UNSIGNED_SHORT) ? sizeof(GLushort) : sizeof(GLuint);
size_t array_size = elem_size * 6 * size;
// Create may throw out-of-memory exceptions.
// VertexIndex will propagate the exception and keep the old VertexBuffer.
try
{
new_element_array = VertexBuffer::Create(array_size, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
// Allocation of the new VertexBuffer succeeded.
// The old VertexBuffer can now be deleted.
delete element_array;
element_array = new_element_array;
maxSize = size;
elementSize = elem_size;
switch (target_type)
{
case GL_UNSIGNED_SHORT:
fill<GLushort>();
break;
case GL_UNSIGNED_INT:
fill<GLuint>();
break;
}
}
template <typename T>
void VertexIndex::fill()
{
VertexBuffer::Bind bind(*element_array);
VertexBuffer::Mapper mapper(*element_array);
T *indices = (T *) mapper.get();
for (size_t i = 0; i < maxSize; ++i)
{
indices[i*6+0] = i * 4 + 0;
indices[i*6+1] = i * 4 + 1;
indices[i*6+2] = i * 4 + 2;
indices[i*6+3] = i * 4 + 0;
indices[i*6+4] = i * 4 + 2;
indices[i*6+5] = i * 4 + 3;
}
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,519 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_VERTEX_BUFFER_H
#define LOVE_GRAPHICS_OPENGL_VERTEX_BUFFER_H
// LOVE
#include "graphics/Volatile.h"
// OpenGL
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* VertexBuffer is an abstraction over VBOs (Vertex Buffer Objects), which
* falls back to regular vertex arrays if VBOs are not supported.
*
* This allows code to take advantage of VBOs where available, but still
* work on older systems where it's *not* available. Everyone's happy.
*
* The class is (for now) meant for internal use.
*/
class VertexBuffer
{
public:
// Different guarantees for VertexBuffer data storage.
enum MemoryBacking
{
// The VertexBuffer is will have a valid copy of its data in main memory
// at all times.
BACKING_FULL,
// The VertexBuffer will have a valid copy of its data in main memory
// when it needs to be reloaded and when it's mapped.
BACKING_PARTIAL
};
/**
* Create a new VertexBuffer (either a plain vertex array, or a VBO),
* based on what's supported on the system.
*
* If VBOs are not supported, a plain vertex array will automatically
* be created and returned instead.
*
* @param size The size of the VertexBuffer (in bytes).
* @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER.
* @param usage GL_DYNAMIC_DRAW, etc.
* @param backing Determines what guarantees are placed on the data.
* @return A new VertexBuffer.
*/
static VertexBuffer *Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
/**
* Constructor.
*
* @param size The size of the VertexBuffer in bytes.
* @param target The target VertexBuffer object, e.g. GL_ARRAY_BUFFER.
* @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
* @param backing Determines what guarantees are placed on the data.
*/
VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
/**
* Destructor. Does nothing, but must be declared virtual.
*/
virtual ~VertexBuffer();
/**
* Get the size of the VertexBuffer, in bytes.
*
* @return The size of the VertexBuffer.
*/
size_t getSize() const
{
return size;
}
/**
* Get the target buffer object.
*
* @return The target buffer object, e.g. GL_ARRAY_BUFFER.
*/
GLenum getTarget() const
{
return target;
}
/**
* Get the usage hint for this VertexBuffer.
*
* @return The usage hint, e.g. GL_DYNAMIC_DRAW.
*/
GLenum getUsage() const
{
return usage;
}
bool isBound() const
{
return is_bound;
}
bool isMapped() const
{
return is_mapped;
}
MemoryBacking getMemoryBacking() const
{
return backing;
}
/**
* Map the VertexBuffer to client memory.
*
* This can be faster for large changes to the buffer. For smaller
* changes, see fill().
*
* The VertexBuffer must be bound to use this function.
*
* @return A pointer to memory which represents the buffer.
*/
virtual void *map() = 0;
/**
* Unmap a previously mapped VertexBuffer. The buffer must be unmapped
* when used to draw elements.
*
* The VertexBuffer must be bound to use this function.
*/
virtual void unmap() = 0;
/**
* Bind the VertexBuffer to its specified target.
* (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc).
*/
virtual void bind() = 0;
/**
* Unbind a prevously bound VertexBuffer.
*/
virtual void unbind() = 0;
/**
* Fill a portion of the buffer with data.
*
* The VertexBuffer must be bound to use this function.
*
* @param offset The offset in the VertexBuffer to store the data.
* @param size The size of the incoming data.
* @param data Pointer to memory to copy data from.
*/
virtual void fill(size_t offset, size_t size, const void *data) = 0;
/**
* Get a pointer which represents the specified byte offset.
*
* @param offset The byte offset. (0 is first byte).
* @return A pointer which represents the offset.
*/
virtual const void *getPointer(size_t offset) const = 0;
/**
* This helper class can bind a VertexArray temporarily, and
* automatically un-bind when it's destroyed.
*/
class Bind
{
public:
/**
* Bind a VertexBuffer.
*/
Bind(VertexBuffer &buf)
: buf(buf)
{
buf.bind();
}
/**
* Unbinds a VertexBuffer.
*/
~Bind()
{
buf.unbind();
}
private:
// VertexBuffer to work on.
VertexBuffer &buf;
};
class Mapper
{
public:
/**
* Memory-maps a VertexBuffer.
*/
Mapper(VertexBuffer &buffer)
: buf(buffer)
{
elems = buf.map();
}
/**
* unmaps the buffer
*/
~Mapper()
{
buf.unmap();
}
/**
* Get pointer to memory mapped region
*/
void *get()
{
return elems;
}
private:
VertexBuffer &buf;
void *elems;
};
protected:
// Whether the buffer is currently bound.
bool is_bound;
// Whether the buffer is currently mapped to main memory.
bool is_mapped;
private:
// The size of the buffer, in bytes.
size_t size;
// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
GLenum target;
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
GLenum usage;
//
MemoryBacking backing;
};
/**
* Implementation of VertexBuffer which uses plain arrays to store the data.
*
* This implementation should be supported everywhere, and acts as a fallback
* on systems which do not support VBOs.
*/
class VertexArray : public VertexBuffer
{
public:
/**
* @copydoc VertexBuffer(int, GLenum, GLenum, Backing)
*/
VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
/**
* Frees the data we've allocated.
*/
virtual ~VertexArray();
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
virtual const void *getPointer(size_t offset) const ;
private:
// Holds the data.
char *buf;
};
/**
* Vertex Buffer Object (VBO) implementation of VertexBuffer.
*
* This will be used on all systems that support it. It's in general
* faster than vertex arrays, but especially in use-cases where there
* is no need to update the data every frame.
**/
class VBO : public VertexBuffer, public Volatile
{
public:
/**
* @copydoc VertexBuffer(size_t, GLenum, GLenum, Backing)
**/
VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
/**
* Deletes the VBOs from OpenGL.
**/
virtual ~VBO();
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
virtual const void *getPointer(size_t offset) const ;
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
private:
/**
* Creates the VBO, and optionally restores data we saved earlier.
*
* @param restore True to restore data previously saved with 'unload'.
* @return True on success, false otherwise.
*/
bool load(bool restore);
/**
* Optionally save the data in the VBO, then delete it.
*
* @param save True to save the data before deleting.
*/
void unload(bool save);
// The VBO identifier. Assigned by OpenGL.
GLuint vbo;
// A pointer to mapped memory. Will be inialized on the first
// call to map().
void *memory_map;
// Set if the buffer was modified while operating on gpu memory
// and needs to be synchronized.
bool is_dirty;
};
/**
* VertexIndex manages one shared VertexBuffer that stores the indices for an
* element array. Vertex arrays using the vertex structure (or anything else
* that can use the pattern below) can request a size and use it for the
* drawElements call.
*
* indices[i*6 + 0] = i*4 + 0;
* indices[i*6 + 1] = i*4 + 1;
* indices[i*6 + 2] = i*4 + 2;
*
* indices[i*6 + 3] = i*4 + 0;
* indices[i*6 + 4] = i*4 + 2;
* indices[i*6 + 5] = i*4 + 3;
*
* There will always be a large enough VertexBuffer around until all
* VertexIndex instances have been deleted.
*
* Q: Why have something like VertexIndex?
* A: The indices for the SpriteBatch do not change, only the array size
* varies. Using one VertexBuffer for all element arrays removes this
* duplicated data and saves some memory.
*/
class VertexIndex
{
public:
/**
* Adds an entry to the list of sizes and resizes the VertexBuffer
* if needed. A size of 1 allocates a group of 6 indices for 4 vertices
* creating 1 face.
*
* @param size The requested size in groups of 6 indices.
*/
VertexIndex(size_t size);
/**
* Removes an entry from the list of sizes and resizes the VertexBuffer
* if needed.
*/
~VertexIndex();
/**
* Returns the number of index groups.
* This can be used for getIndexCount to get the full count of indices.
*
* @return The number of index groups.
*/
size_t getSize() const;
/**
* Returns the number of indices that the passed element count will have.
* Use VertexIndex::getSize to get the full index count for that
* VertexIndex instance.
*
* @param elements The number of elements to calculate the index count for.
* @return The index count.
*/
size_t getIndexCount(size_t elements) const;
/**
* Returns the integer type of the element array.
* If an optional nonzero size argument is passed, the function returns
* the integer type of the element array of that size.
*
* @param s The size of the array to calculated the integer type of.
* @return The element array integer type.
*/
GLenum getType(size_t s) const;
inline GLenum getType() const
{
return getType(maxSize);
}
/**
* Returns the size in bytes of an element in the element array.
* Can be used with getPointer to calculate an offset into the array based
* on a number of elements.
*
* @return The size of an element in bytes.
**/
size_t getElementSize();
/**
* Returns the pointer to the VertexBuffer.
* The pointer will change if a new size request or removal causes
* a VertexBuffer resize. It is recommended to retrieve the pointer
* value directly before the drawing call.
*
* @return The pointer to the VertexBuffer.
*/
VertexBuffer *getVertexBuffer() const;
/**
* Returns a pointer which represents the specified byte offset.
*
* @param offset The offset in bytes.
* @return A pointer which represents the offset.
*/
const void *getPointer(size_t offset) const;
private:
/**
* Adds a new size to the size list, then sorts and resizes it if needed.
*
* @param newSize The new size to be added.
*/
void addSize(size_t newSize);
/**
* Removes a size from the size list, then sorts and resizes it if needed.
*
* @param oldSize The old size to be removed.
*/
void removeSize(size_t oldSize);
/**
* Resizes the VertexBuffer to the requested size.
* This function takes care of choosing the correct integer type and
* allocating and deleting the VertexBuffer instance. It also has some
* fallback logic in case the memory ran out.
*
* @param size The requested VertexBuffer size. Passing 0 deletes the VertexBuffer without allocating a new one.
*/
void resize(size_t size);
/**
* Adds all indices to the array with the type T.
* There are no checks for the correct types or overflows. The calling
* function should check for that.
*/
template <typename T> void fill();
// The size of the array requested by this instance.
size_t size;
// The size in bytes of an element in the element array.
static size_t elementSize;
// The current VertexBuffer size. 0 means no VertexBuffer.
static size_t maxSize;
// The list of sizes. Needs to be kept sorted in ascending order.
static std::list<size_t> sizeRefs;
// The VertexBuffer for the element array. Can be NULL.
static VertexBuffer *element_array;
};
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
@@ -0,0 +1,245 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "Graphics.h"
#include "wrap_Canvas.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Canvas *luax_checkcanvas(lua_State *L, int idx)
{
return luax_checktype<Canvas>(L, idx, "Canvas", GRAPHICS_CANVAS_T);
}
int w_Canvas_renderTo(lua_State *L)
{
// As startGrab() clears the framebuffer, better not allow
// grabbing inside another grabbing
if (Canvas::current != NULL)
{
Canvas::bindDefaultCanvas();
return luaL_error(L, "Current render target not the default canvas!");
}
Canvas *canvas = luax_checkcanvas(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
EXCEPT_GUARD(canvas->startGrab();)
lua_settop(L, 2); // make sure the function is on top of the stack
lua_call(L, 0, 0);
canvas->stopGrab();
return 0;
}
int w_Canvas_getImageData(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
love::image::Image *image = luax_getmodule<love::image::Image>(L, "image", MODULE_IMAGE_T);
love::image::ImageData *img = canvas->getImageData(image);
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, img);
return 1;
}
int w_Canvas_getPixel(lua_State * L)
{
Canvas * canvas = luax_checkcanvas(L, 1);
int x = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3);
unsigned char c[4];
EXCEPT_GUARD(canvas->getPixel(c, x, y);)
lua_pushnumber(L, c[0]);
lua_pushnumber(L, c[1]);
lua_pushnumber(L, c[2]);
lua_pushnumber(L, c[3]);
return 4;
}
int w_Canvas_setFilter(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
Image::Filter f;
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid filter mode: %s", magstr);
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
canvas->setFilter(f);
return 0;
}
int w_Canvas_getFilter(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
const Image::Filter f = canvas->getFilter();
const char *minstr;
const char *magstr;
Image::getConstant(f.min, minstr);
Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr);
lua_pushstring(L, magstr);
lua_pushnumber(L, f.anisotropy);
return 3;
}
int w_Canvas_setWrap(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
Image::Wrap w;
const char *sstr = luaL_checkstring(L, 2);
const char *tstr = luaL_optstring(L, 3, sstr);
if (!Image::getConstant(sstr, w.s))
return luaL_error(L, "Invalid wrap mode: %s", sstr);
if (!Image::getConstant(tstr, w.t))
return luaL_error(L, "Invalid wrap mode, %s", tstr);
canvas->setWrap(w);
return 0;
}
int w_Canvas_getWrap(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
const Image::Wrap w = canvas->getWrap();
const char *wrap_s;
const char *wrap_t;
Image::getConstant(w.s, wrap_s);
Image::getConstant(w.t, wrap_t);
lua_pushstring(L, wrap_s);
lua_pushstring(L, wrap_t);
return 2;
}
int w_Canvas_clear(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
Color c;
if (lua_isnoneornil(L, 2))
{
c.set(0, 0, 0, 0);
}
else if (lua_istable(L, 2))
{
for (int i = 1; i <= 4; i++)
lua_rawgeti(L, 2, i);
c.r = (unsigned char)luaL_checkinteger(L, -4);
c.g = (unsigned char)luaL_checkinteger(L, -3);
c.b = (unsigned char)luaL_checkinteger(L, -2);
c.a = (unsigned char)luaL_optinteger(L, -1, 255);
lua_pop(L, 4);
}
else
{
c.r = (unsigned char)luaL_checkinteger(L, 2);
c.g = (unsigned char)luaL_checkinteger(L, 3);
c.b = (unsigned char)luaL_checkinteger(L, 4);
c.a = (unsigned char)luaL_optinteger(L, 5, 255);
}
canvas->clear(c);
return 0;
}
int w_Canvas_getWidth(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
lua_pushnumber(L, canvas->getWidth());
return 1;
}
int w_Canvas_getHeight(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
lua_pushnumber(L, canvas->getHeight());
return 1;
}
int w_Canvas_getDimensions(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
lua_pushnumber(L, canvas->getWidth());
lua_pushnumber(L, canvas->getHeight());
return 2;
}
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 },
{ "getImageData", w_Canvas_getImageData },
{ "getPixel", w_Canvas_getPixel },
{ "setFilter", w_Canvas_setFilter },
{ "getFilter", w_Canvas_getFilter },
{ "setWrap", w_Canvas_setWrap },
{ "getWrap", w_Canvas_getWrap },
{ "clear", w_Canvas_clear },
{ "getWidth", w_Canvas_getWidth },
{ "getHeight", w_Canvas_getHeight },
{ "getDimensions", w_Canvas_getDimensions },
{ "getType", w_Canvas_getType },
{ 0, 0 }
};
extern "C" int luaopen_canvas(lua_State *L)
{
return luax_register_type(L, "Canvas", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_CANVAS_H
#define LOVE_GRAPHICS_OPENGL_WRAP_CANVAS_H
// LOVE
#include "common/runtime.h"
#include "Canvas.h"
namespace love
{
namespace graphics
{
namespace opengl
{
//see Canvas.h
Canvas *luax_checkcanvas(lua_State *L, int idx);
int w_Canvas_renderTo(lua_State *L);
int w_Canvas_getImageData(lua_State *L);
int w_Canvas_getPixel(lua_State * L);
int w_Canvas_setFilter(lua_State *L);
int w_Canvas_getFilter(lua_State *L);
int w_Canvas_setWrap(lua_State *L);
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_getDimensions(lua_State *L);
int w_Canvas_getType(lua_State *L);
extern "C" int luaopen_canvas(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_CANVAS_H
@@ -0,0 +1,186 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Font.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Font *luax_checkfont(lua_State *L, int idx)
{
return luax_checktype<Font>(L, idx, "Font", GRAPHICS_FONT_T);
}
int w_Font_getHeight(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getHeight());
return 1;
}
int w_Font_getWidth(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
const char *str = luaL_checkstring(L, 2);
EXCEPT_GUARD(lua_pushinteger(L, t->getWidth(str));)
return 1;
}
int w_Font_getWrap(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
const char *str = luaL_checkstring(L, 2);
float wrap = (float) luaL_checknumber(L, 3);
int max_width = 0, numlines = 0;
EXCEPT_GUARD(
std::vector<std::string> lines = t->getWrap(str, wrap, &max_width);
numlines = lines.size();
)
lua_pushinteger(L, max_width);
lua_pushinteger(L, numlines);
return 2;
}
int w_Font_setLineHeight(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
float h = (float)luaL_checknumber(L, 2);
t->setLineHeight(h);
return 0;
}
int w_Font_getLineHeight(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getLineHeight());
return 1;
}
int w_Font_setFilter(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
Image::Filter f = t->getFilter();
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid filter mode: %s", magstr);
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
EXCEPT_GUARD(t->setFilter(f);)
return 0;
}
int w_Font_getFilter(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
const Image::Filter f = t->getFilter();
const char *minstr;
const char *magstr;
Image::getConstant(f.min, minstr);
Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr);
lua_pushstring(L, magstr);
lua_pushnumber(L, f.anisotropy);
return 3;
}
int w_Font_getAscent(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getAscent());
return 1;
}
int w_Font_getDescent(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getDescent());
return 1;
}
int w_Font_getBaseline(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getBaseline());
return 1;
}
int w_Font_hasGlyphs(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
bool hasglyph = false;
int count = lua_gettop(L) - 1;
count = count < 1 ? 1 : count;
EXCEPT_GUARD(
for (int i = 2; i < count + 2; i++)
{
if (lua_type(L, i) == LUA_TSTRING)
hasglyph = t->hasGlyphs(luax_checkstring(L, i));
else
hasglyph = t->hasGlyph((uint32) luaL_checknumber(L, i));
if (!hasglyph)
break;
}
)
luax_pushboolean(L, hasglyph);
return 1;
}
static const luaL_Reg functions[] =
{
{ "getHeight", w_Font_getHeight },
{ "getWidth", w_Font_getWidth },
{ "getWrap", w_Font_getWrap },
{ "setLineHeight", w_Font_setLineHeight },
{ "getLineHeight", w_Font_getLineHeight },
{ "setFilter", w_Font_setFilter },
{ "getFilter", w_Font_getFilter },
{ "getAscent", w_Font_getAscent },
{ "getDescent", w_Font_getDescent },
{ "getBaseline", w_Font_getBaseline },
{ "hasGlyphs", w_Font_hasGlyphs },
{ 0, 0 }
};
extern "C" int luaopen_font(lua_State *L)
{
return luax_register_type(L, "Font", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,53 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_FONT_H
#define LOVE_GRAPHICS_OPENGL_WRAP_FONT_H
// LOVE
#include "common/runtime.h"
#include "Font.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Font *luax_checkfont(lua_State *L, int idx);
int w_Font_getHeight(lua_State *L);
int w_Font_getWidth(lua_State *L);
int w_Font_getWrap(lua_State *L);
int w_Font_setLineHeight(lua_State *L);
int w_Font_getLineHeight(lua_State *L);
int w_Font_setFilter(lua_State *L);
int w_Font_getFilter(lua_State *L);
int w_Font_getAscent(lua_State *L);
int w_Font_getDescent(lua_State *L);
int w_Font_getBaseline(lua_State *L);
int w_Font_hasGlyphs(lua_State *L);
extern "C" int luaopen_font(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_FONT_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_GRAPHICS_H
#define LOVE_GRAPHICS_OPENGL_WRAP_GRAPHICS_H
// LOVE
#include "wrap_Font.h"
#include "wrap_Image.h"
#include "wrap_Quad.h"
#include "wrap_SpriteBatch.h"
#include "wrap_ParticleSystem.h"
#include "wrap_Canvas.h"
#include "wrap_Shader.h"
#include "wrap_Mesh.h"
#include "Graphics.h"
namespace love
{
namespace graphics
{
namespace opengl
{
int w_reset(lua_State *L);
int w_clear(lua_State *L);
int w_present(lua_State *L);
int w_isCreated(lua_State *L);
int w_getWidth(lua_State *L);
int w_getHeight(lua_State *L);
int w_getDimensions(lua_State *L);
int w_setScissor(lua_State *L);
int w_getScissor(lua_State *L);
int w_setStencil(lua_State *L);
int w_setInvertedStencil(lua_State *L);
int w_getMaxImageSize(lua_State *L);
int w_newImage(lua_State *L);
int w_newQuad(lua_State *L);
int w_newFont(lua_State *L);
int w_newImageFont(lua_State *L);
int w_newSpriteBatch(lua_State *L);
int w_newParticleSystem(lua_State *L);
int w_newCanvas(lua_State *L); // comments in function
int w_newShader(lua_State *L);
int w_newMesh(lua_State *L);
int w_setColor(lua_State *L);
int w_getColor(lua_State *L);
int w_setBackgroundColor(lua_State *L);
int w_getBackgroundColor(lua_State *L);
int w_setFont(lua_State *L);
int w_getFont(lua_State *L);
int w_setColorMask(lua_State *L);
int w_getColorMask(lua_State *L);
int w_setBlendMode(lua_State *L);
int w_getBlendMode(lua_State *L);
int w_setDefaultFilter(lua_State *L);
int w_getDefaultFilter(lua_State *L);
int w_setDefaultMipmapFilter(lua_State *L);
int w_getDefaultMipmapFilter(lua_State *L);
int w_setLineWidth(lua_State *L);
int w_setLineStyle(lua_State *L);
int w_setLineJoin(lua_State *L);
int w_getLineWidth(lua_State *L);
int w_getLineStyle(lua_State *L);
int w_getLineJoin(lua_State *L);
int w_setPointSize(lua_State *L);
int w_setPointStyle(lua_State *L);
int w_getPointSize(lua_State *L);
int w_getPointStyle(lua_State *L);
int w_getMaxPointSize(lua_State *L);
int w_newScreenshot(lua_State *L);
int w_setCanvas(lua_State *L);
int w_getCanvas(lua_State *L);
int w_setShader(lua_State *L);
int w_getShader(lua_State *L);
int w_setDefaultShaderCode(lua_State *L);
int w_isSupported(lua_State *L);
int w_getRendererInfo(lua_State *L);
int w_draw(lua_State *L);
int w_print(lua_State *L);
int w_printf(lua_State *L);
int w_point(lua_State *L);
int w_line(lua_State *L);
int w_rectangle(lua_State *L);
int w_circle(lua_State *L);
int w_arc(lua_State *L);
int w_polygon(lua_State *L);
int w_push(lua_State *L);
int w_pop(lua_State *L);
int w_rotate(lua_State *L);
int w_scale(lua_State *L);
int w_translate(lua_State *L);
int w_shear(lua_State *L);
int w_origin(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_graphics(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_GRAPHICS_H
@@ -0,0 +1,229 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Image.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Image *luax_checkimage(lua_State *L, int idx)
{
return luax_checktype<Image>(L, idx, "Image", GRAPHICS_IMAGE_T);
}
int w_Image_getWidth(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
lua_pushnumber(L, t->getWidth());
return 1;
}
int w_Image_getHeight(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
lua_pushnumber(L, t->getHeight());
return 1;
}
int w_Image_getDimensions(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
lua_pushnumber(L, t->getWidth());
lua_pushnumber(L, t->getHeight());
return 2;
}
int w_Image_setFilter(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
Image::Filter f = t->getFilter();
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid filter mode: %s", magstr);
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
EXCEPT_GUARD(t->setFilter(f);)
return 0;
}
int w_Image_getFilter(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
const Image::Filter f = t->getFilter();
const char *minstr;
const char *magstr;
Image::getConstant(f.min, minstr);
Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr);
lua_pushstring(L, magstr);
lua_pushnumber(L, f.anisotropy);
return 3;
}
int w_Image_setMipmapFilter(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
Image::Filter f = t->getFilter();
if (lua_isnoneornil(L, 2))
f.mipmap = Image::FILTER_NONE; // mipmapping is disabled if no argument is given
else
{
const char *mipmapstr = luaL_checkstring(L, 2);
if (!Image::getConstant(mipmapstr, f.mipmap))
return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
}
EXCEPT_GUARD(t->setFilter(f);)
float sharpness = (float) luaL_optnumber(L, 3, 0);
t->setMipmapSharpness(sharpness);
return 0;
}
int w_Image_getMipmapFilter(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
const Image::Filter &f = t->getFilter();
const char *mipmapstr;
if (Image::getConstant(f.mipmap, mipmapstr))
lua_pushstring(L, mipmapstr);
else
lua_pushnil(L); // only return a mipmap filter if mipmapping is enabled
lua_pushnumber(L, t->getMipmapSharpness());
return 2;
}
int w_Image_setWrap(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
Image::Wrap w;
const char *sstr = luaL_checkstring(L, 2);
const char *tstr = luaL_optstring(L, 3, sstr);
if (!Image::getConstant(sstr, w.s))
return luaL_error(L, "Invalid wrap mode: %s", sstr);
if (!Image::getConstant(tstr, w.t))
return luaL_error(L, "Invalid wrap mode, %s", tstr);
i->setWrap(w);
return 0;
}
int w_Image_getWrap(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
const Image::Wrap w = i->getWrap();
const char *sstr;
const char *tstr;
Image::getConstant(w.s, sstr);
Image::getConstant(w.t, tstr);
lua_pushstring(L, sstr);
lua_pushstring(L, tstr);
return 2;
}
int w_Image_isCompressed(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
luax_pushboolean(L, i->isCompressed());
return 1;
}
int w_Image_refresh(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
EXCEPT_GUARD(i->refresh();)
return 0;
}
int w_Image_getData(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
if (i->isCompressed())
{
love::image::CompressedData *t = i->getCompressedData();
if (t)
{
t->retain();
luax_pushtype(L, "CompressedData", IMAGE_COMPRESSED_DATA_T, t);
}
else
lua_pushnil(L);
}
else
{
love::image::ImageData *t = i->getImageData();
if (t)
{
t->retain();
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, t);
}
else
lua_pushnil(L);
}
return 1;
}
static const luaL_Reg functions[] =
{
{ "getWidth", w_Image_getWidth },
{ "getHeight", w_Image_getHeight },
{ "getDimensions", w_Image_getDimensions },
{ "setFilter", w_Image_setFilter },
{ "getFilter", w_Image_getFilter },
{ "setWrap", w_Image_setWrap },
{ "getWrap", w_Image_getWrap },
{ "setMipmapFilter", w_Image_setMipmapFilter },
{ "getMipmapFilter", w_Image_getMipmapFilter },
{ "isCompressed", w_Image_isCompressed },
{ "refresh", w_Image_refresh },
{ "getData", w_Image_getData },
{ 0, 0 }
};
extern "C" int luaopen_image(lua_State *L)
{
return luax_register_type(L, "Image", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_IMAGE_H
#define LOVE_GRAPHICS_OPENGL_WRAP_IMAGE_H
// LOVE
#include "common/runtime.h"
#include "Image.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Image *luax_checkimage(lua_State *L, int idx);
int w_Image_getWidth(lua_State *L);
int w_Image_getHeight(lua_State *L);
int w_Image_getDimensions(lua_State *L);
int w_Image_setFilter(lua_State *L);
int w_Image_getFilter(lua_State *L);
int w_Image_setMipmapFilter(lua_State *L);
int w_Image_getMipmapFilter(lua_State *L);
int w_Image_setWrap(lua_State *L);
int w_Image_getWrap(lua_State *L);
int w_Image_isCompressed(lua_State *L);
int w_Image_refresh(lua_State *L);
int w_Image_getData(lua_State *L);
extern "C" int luaopen_image(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_IMAGE_H
@@ -0,0 +1,327 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Mesh.h"
#include "wrap_Image.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Mesh *luax_checkmesh(lua_State *L, int idx)
{
return luax_checktype<Mesh>(L, idx, "Mesh", GRAPHICS_MESH_T);
}
int w_Mesh_setVertex(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
size_t i = size_t(luaL_checkinteger(L, 2) - 1);
Vertex v;
if (lua_istable(L, 3))
{
for (int i = 1; i <= 8; i++)
lua_rawgeti(L, 3, i);
v.x = luaL_checknumber(L, -8);
v.y = luaL_checknumber(L, -7);
v.s = luaL_checknumber(L, -6);
v.t = luaL_checknumber(L, -5);
v.r = luaL_optinteger(L, -4, 255);
v.g = luaL_optinteger(L, -3, 255);
v.b = luaL_optinteger(L, -2, 255);
v.a = luaL_optinteger(L, -1, 255);
lua_pop(L, 8);
}
else
{
v.x = luaL_checknumber(L, 3);
v.y = luaL_checknumber(L, 4);
v.s = luaL_checknumber(L, 5);
v.t = luaL_checknumber(L, 6);
v.r = luaL_optinteger(L, 7, 255);
v.g = luaL_optinteger(L, 8, 255);
v.b = luaL_optinteger(L, 9, 255);
v.a = luaL_optinteger(L, 10, 255);
}
EXCEPT_GUARD(t->setVertex(i, v);)
return 0;
}
int w_Mesh_getVertex(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
size_t i = (size_t) (luaL_checkinteger(L, 2) - 1);
Vertex v;
EXCEPT_GUARD(v = t->getVertex(i);)
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
lua_pushnumber(L, v.s);
lua_pushnumber(L, v.t);
lua_pushnumber(L, v.r);
lua_pushnumber(L, v.g);
lua_pushnumber(L, v.b);
lua_pushnumber(L, v.a);
return 8;
}
int w_Mesh_setVertices(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
size_t vertex_count = lua_objlen(L, 2);
std::vector<Vertex> vertices;
vertices.reserve(vertex_count);
// Get the vertices from the table.
for (size_t i = 1; i <= vertex_count; i++)
{
lua_rawgeti(L, 2, i);
if (lua_type(L, -1) != LUA_TTABLE)
return luax_typerror(L, 2, "table of tables");
for (int j = 1; j <= 8; j++)
lua_rawgeti(L, -j, j);
Vertex v;
v.x = (float) luaL_checknumber(L, -8);
v.y = (float) luaL_checknumber(L, -7);
v.s = (float) luaL_checknumber(L, -6);
v.t = (float) luaL_checknumber(L, -5);
v.r = (unsigned char) luaL_optinteger(L, -4, 255);
v.g = (unsigned char) luaL_optinteger(L, -3, 255);
v.b = (unsigned char) luaL_optinteger(L, -2, 255);
v.a = (unsigned char) luaL_optinteger(L, -1, 255);
lua_pop(L, 9);
vertices.push_back(v);
}
EXCEPT_GUARD(t->setVertices(vertices);)
return 0;
}
int w_Mesh_getVertices(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const Vertex *vertices = t->getVertices();
size_t count = t->getVertexCount();
lua_createtable(L, count, 0);
for (size_t i = 0; i < count; i++)
{
// Create vertex table.
lua_createtable(L, 8, 0);
lua_pushnumber(L, vertices[i].x);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, vertices[i].y);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, vertices[i].s);
lua_rawseti(L, -2, 3);
lua_pushnumber(L, vertices[i].t);
lua_rawseti(L, -2, 4);
lua_pushnumber(L, vertices[i].r);
lua_rawseti(L, -2, 5);
lua_pushnumber(L, vertices[i].g);
lua_rawseti(L, -2, 6);
lua_pushnumber(L, vertices[i].b);
lua_rawseti(L, -2, 7);
lua_pushnumber(L, vertices[i].a);
lua_rawseti(L, -2, 8);
// Insert vertex table into vertices table.
lua_rawseti(L, -2, i + 1);
}
// Return vertices table.
return 1;
}
int w_Mesh_getVertexCount(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
lua_pushinteger(L, t->getVertexCount());
return 1;
}
int w_Mesh_setVertexMap(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
bool is_table = lua_istable(L, 2);
int nargs = is_table ? lua_objlen(L, 2) : lua_gettop(L) - 1;
std::vector<uint32> vertexmap;
vertexmap.reserve(nargs);
for (int i = 0; i < nargs; i++)
{
if (is_table)
{
lua_rawgeti(L, 2, i + 1);
vertexmap.push_back(uint32(luaL_checkinteger(L, -1) - 1));
lua_pop(L, 1);
}
else
vertexmap.push_back(uint32(luaL_checkinteger(L, i + 2) - 1));
}
EXCEPT_GUARD(t->setVertexMap(vertexmap);)
return 0;
}
int w_Mesh_getVertexMap(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const uint32 *vertex_map = 0;
EXCEPT_GUARD(vertex_map = t->getVertexMap();)
size_t elements = t->getVertexMapCount();
lua_createtable(L, elements, 0);
for (size_t i = 0; i < elements; i++)
{
lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
int w_Mesh_setImage(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
if (lua_isnoneornil(L, 2))
t->setImage();
else
{
Image *img = luax_checkimage(L, 2);
t->setImage(img);
}
return 0;
}
int w_Mesh_getImage(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
Image *img = t->getImage();
if (img == NULL)
return 0;
img->retain();
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, img);
return 1;
}
int w_Mesh_setDrawMode(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const char *str = luaL_checkstring(L, 2);
Mesh::DrawMode mode;
if (!Mesh::getConstant(str, mode))
return luaL_error(L, "Invalid mesh draw mode: %s", str);
t->setDrawMode(mode);
return 0;
}
int w_Mesh_getDrawMode(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
Mesh::DrawMode mode = t->getDrawMode();
const char *str;
if (!Mesh::getConstant(mode, str))
return luaL_error(L, "Unknown mesh draw mode.");
lua_pushstring(L, str);
return 1;
}
int w_Mesh_setVertexColors(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
t->setVertexColors(luax_toboolean(L, 2));
return 0;
}
int w_Mesh_hasVertexColors(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
luax_pushboolean(L, t->hasVertexColors());
return 1;
}
static const luaL_Reg functions[] =
{
{ "setVertex", w_Mesh_setVertex },
{ "getVertex", w_Mesh_getVertex },
{ "setVertices", w_Mesh_setVertices },
{ "getVertices", w_Mesh_getVertices },
{ "getVertexCount", w_Mesh_getVertexCount },
{ "setVertexMap", w_Mesh_setVertexMap },
{ "getVertexMap", w_Mesh_getVertexMap },
{ "setImage", w_Mesh_setImage },
{ "getImage", w_Mesh_getImage },
{ "setDrawMode", w_Mesh_setDrawMode },
{ "getDrawMode", w_Mesh_getDrawMode },
{ "setVertexColors", w_Mesh_setVertexColors },
{ "hasVertexColors", w_Mesh_hasVertexColors },
{ 0, 0 }
};
extern "C" int luaopen_mesh(lua_State *L)
{
return luax_register_type(L, "Mesh", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
#define LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
// LOVE
#include "common/runtime.h"
#include "Mesh.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Mesh *luax_checkmesh(lua_State *L, int idx);
int w_Mesh_setVertex(lua_State *L);
int w_Mesh_getVertex(lua_State *L);
int w_Mesh_setVertices(lua_State *L);
int w_Mesh_getVertices(lua_State *L);
int w_Mesh_getVertexCount(lua_State *L);
int w_Mesh_setVertexMap(lua_State *L);
int w_Mesh_getVertexMap(lua_State *L);
int w_Mesh_setImage(lua_State *L);
int w_Mesh_getImage(lua_State *L);
int w_Mesh_setDrawMode(lua_State *L);
int w_Mesh_getDrawMode(lua_State *L);
int w_Mesh_setVertexColors(lua_State *L);
int w_Mesh_hasVertexColors(lua_State *L);
extern "C" int luaopen_mesh(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
@@ -0,0 +1,669 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "wrap_ParticleSystem.h"
#include "common/Vector.h"
#include <cstring>
namespace love
{
namespace graphics
{
namespace opengl
{
ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx)
{
return luax_checktype<ParticleSystem>(L, idx, "ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_T);
}
int w_ParticleSystem_setImage(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
Image *i = luax_checkimage(L, 2);
t->setImage(i);
return 0;
}
int w_ParticleSystem_getImage(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
Image *i = t->getImage();
i->retain();
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, i);
return 1;
}
int w_ParticleSystem_setBufferSize(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_Number arg1 = luaL_checknumber(L, 2);
if (arg1 < 1.0 || arg1 > ParticleSystem::MAX_PARTICLES)
return luaL_error(L, "Invalid buffer size");
EXCEPT_GUARD(t->setBufferSize((uint32) arg1);)
return 0;
}
int w_ParticleSystem_getBufferSize(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushinteger(L, t->getBufferSize());
return 1;
}
int w_ParticleSystem_setInsertMode(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::InsertMode mode;
const char *str = luaL_checkstring(L, 2);
if (!ParticleSystem::getConstant(str, mode))
return luaL_error(L, "Invalid insert mode: '%s'", str);
t->setInsertMode(mode);
return 0;
}
int w_ParticleSystem_getInsertMode(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::InsertMode mode;
mode = t->getInsertMode();
const char *str;
if (!ParticleSystem::getConstant(mode, str))
return luaL_error(L, "Unknown insert mode");
lua_pushstring(L, str);
return 1;
}
int w_ParticleSystem_setEmissionRate(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
int arg1 = luaL_checkint(L, 2);
EXCEPT_GUARD(t->setEmissionRate(arg1);)
return 0;
}
int w_ParticleSystem_getEmissionRate(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushinteger(L, t->getEmissionRate());
return 1;
}
int w_ParticleSystem_setEmitterLifetime(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setEmitterLifetime(arg1);
return 0;
}
int w_ParticleSystem_getEmitterLifetime(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getEmitterLifetime());
return 1;
}
int w_ParticleSystem_setParticleLifetime(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setParticleLifetime(arg1, arg2);
return 0;
}
int w_ParticleSystem_getParticleLifetime(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getParticleLifetime(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setPosition(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_checknumber(L, 3);
t->setPosition(arg1, arg2);
return 0;
}
int w_ParticleSystem_getPosition(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
love::Vector pos = t->getPosition();
lua_pushnumber(L, pos.getX());
lua_pushnumber(L, pos.getY());
return 2;
}
int w_ParticleSystem_setAreaSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::AreaSpreadDistribution distribution = ParticleSystem::DISTRIBUTION_NONE;
float x = 0.f, y = 0.f;
const char *str = lua_isnoneornil(L, 2) ? 0 : luaL_checkstring(L, 2);
if (str && !ParticleSystem::getConstant(str, distribution))
return luaL_error(L, "Invalid particle distribution: %s", str);
if (distribution != ParticleSystem::DISTRIBUTION_NONE)
{
x = (float) luaL_checknumber(L, 3);
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_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_setDirection(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setDirection(arg1);
return 0;
}
int w_ParticleSystem_getDirection(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getDirection());
return 1;
}
int w_ParticleSystem_setSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setSpread(arg1);
return 0;
}
int w_ParticleSystem_getSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getSpread());
return 1;
}
int w_ParticleSystem_setSpeed(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setSpeed(arg1, arg2);
return 0;
}
int w_ParticleSystem_getSpeed(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getSpeed(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setLinearAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float xmin = (float) luaL_checknumber(L, 2);
float ymin = (float) luaL_checknumber(L, 3);
float xmax = (float) luaL_optnumber(L, 4, xmin);
float ymax = (float) luaL_optnumber(L, 5, ymin);
t->setLinearAcceleration(xmin, ymin, xmax, ymax);
return 0;
}
int w_ParticleSystem_getLinearAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
love::Vector min, max;
t->getLinearAcceleration(&min, &max);
lua_pushnumber(L, min.x);
lua_pushnumber(L, min.y);
lua_pushnumber(L, max.x);
lua_pushnumber(L, max.y);
return 4;
}
int w_ParticleSystem_setRadialAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setRadialAcceleration(arg1, arg2);
return 0;
}
int w_ParticleSystem_getRadialAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getRadialAcceleration(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setTangentialAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setTangentialAcceleration(arg1, arg2);
return 0;
}
int w_ParticleSystem_getTangentialAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getTangentialAcceleration(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setSizes(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
size_t nSizes = lua_gettop(L) - 1;
if (nSizes > 8)
return luaL_error(L, "At most eight (8) sizes may be used.");
if (nSizes <= 1)
{
float size = luax_checkfloat(L, 2);
t->setSize(size);
}
else
{
std::vector<float> sizes(nSizes);
for (size_t i = 0; i < nSizes; ++i)
sizes[i] = luax_checkfloat(L, 1 + i + 1);
t->setSizes(sizes);
}
return 0;
}
int w_ParticleSystem_getSizes(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
const std::vector<float> &sizes = t->getSizes();
for (size_t i = 0; i < sizes.size(); i++)
lua_pushnumber(L, sizes[i]);
return sizes.size();
}
int w_ParticleSystem_setSizeVariation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
if (arg1 < 0.0f || arg1 > 1.0f)
return luaL_error(L, "Size variation has to be between 0 and 1, inclusive.");
t->setSizeVariation(arg1);
return 0;
}
int w_ParticleSystem_getSizeVariation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getSizeVariation());
return 1;
}
int w_ParticleSystem_setRotation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setRotation(arg1, arg2);
return 0;
}
int w_ParticleSystem_getRotation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getRotation(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setSpin(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setSpin(arg1, arg2);
return 0;
}
int w_ParticleSystem_getSpin(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float start, end;
t->getSpin(&start, &end);
lua_pushnumber(L, start);
lua_pushnumber(L, end);
return 2;
}
int w_ParticleSystem_setSpinVariation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setSpinVariation(arg1);
return 0;
}
int w_ParticleSystem_getSpinVariation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getSpinVariation());
return 1;
}
int w_ParticleSystem_setOffset(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
t->setOffset(x, y);
return 0;
}
int w_ParticleSystem_getOffset(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
love::Vector offset = t->getOffset();
lua_pushnumber(L, offset.getX());
lua_pushnumber(L, offset.getY());
return 2;
}
int w_ParticleSystem_setColors(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
if (lua_istable(L, 2)) // setColors({r,g,b,a}, {r,g,b,a}, ...)
{
size_t nColors = lua_gettop(L) - 1;
if (nColors > 8)
return luaL_error(L, "At most eight (8) colors may be used.");
std::vector<Color> colors(nColors);
for (size_t i = 0; i < nColors; i++)
{
luaL_checktype(L, i + 2, LUA_TTABLE);
if (lua_objlen(L, i + 2) < 3)
return luaL_argerror(L, i + 2, "expected 4 color components");
for (int j = 0; j < 4; j++)
// push args[i+2][j+1] onto the stack
lua_rawgeti(L, i + 2, j + 1);
unsigned char r = (unsigned char) luaL_checkinteger(L, -4);
unsigned char g = (unsigned char) luaL_checkinteger(L, -3);
unsigned char b = (unsigned char) luaL_checkinteger(L, -2);
unsigned char a = (unsigned char) luaL_optinteger(L, -1, 255);
// pop the color components from the stack
lua_pop(L, 4);
colors[i] = Color(r, g, b, a);
}
t->setColor(colors);
}
else // setColors(r,g,b,a, r,g,b,a, ...)
{
int cargs = lua_gettop(L) - 1;
size_t nColors = (cargs + 3) / 4; // nColors = ceil(color_args / 4)
if (cargs != 3 && (cargs % 4 != 0 || cargs == 0))
return luaL_error(L, "Expected red, green, blue, and alpha. Only got %d of 4 components.", cargs % 4);
if (nColors > 8)
return luaL_error(L, "At most eight (8) colors may be used.");
if (nColors == 1)
{
unsigned char r = (unsigned char) luaL_checkinteger(L, 2);
unsigned char g = (unsigned char) luaL_checkinteger(L, 3);
unsigned char b = (unsigned char) luaL_checkinteger(L, 4);
unsigned char a = (unsigned char) luaL_optinteger(L, 5, 255);
t->setColor(Color(r,g,b,a));
}
else
{
std::vector<Color> colors(nColors);
for (size_t i = 0; i < nColors; ++i)
{
unsigned char r = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 1);
unsigned char g = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 2);
unsigned char b = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 3);
unsigned char a = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 4);
colors[i] = Color(r,g,b,a);
}
t->setColor(colors);
}
}
return 0;
}
int w_ParticleSystem_getColors(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
const std::vector<Color> &colors =t->getColor();
for (size_t i = 0; i < colors.size(); i++)
{
lua_createtable(L, 4, 0);
lua_pushinteger(L, colors[i].r);
lua_rawseti(L, -2, 1);
lua_pushinteger(L, colors[i].g);
lua_rawseti(L, -2, 2);
lua_pushinteger(L, colors[i].b);
lua_rawseti(L, -2, 3);
lua_pushinteger(L, colors[i].a);
lua_rawseti(L, -2, 4);
}
return colors.size();
}
int w_ParticleSystem_getCount(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getCount());
return 1;
}
int w_ParticleSystem_start(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->start();
return 0;
}
int w_ParticleSystem_stop(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->stop();
return 0;
}
int w_ParticleSystem_pause(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->pause();
return 0;
}
int w_ParticleSystem_reset(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->reset();
return 0;
}
int w_ParticleSystem_emit(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
int num = luaL_checkint(L, 2);
t->emit(num);
return 0;
}
int w_ParticleSystem_isActive(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
luax_pushboolean(L, t->isActive());
return 1;
}
int w_ParticleSystem_isPaused(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
luax_pushboolean(L, t->isPaused());
return 1;
}
int w_ParticleSystem_isStopped(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
luax_pushboolean(L, t->isStopped());
return 1;
}
int w_ParticleSystem_update(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float dt = (float)luaL_checknumber(L, 2);
t->update(dt);
return 0;
}
static const luaL_Reg functions[] =
{
{ "setImage", w_ParticleSystem_setImage },
{ "getImage", w_ParticleSystem_getImage },
{ "setBufferSize", w_ParticleSystem_setBufferSize },
{ "getBufferSize", w_ParticleSystem_getBufferSize },
{ "setInsertMode", w_ParticleSystem_setInsertMode },
{ "getInsertMode", w_ParticleSystem_getInsertMode },
{ "setEmissionRate", w_ParticleSystem_setEmissionRate },
{ "getEmissionRate", w_ParticleSystem_getEmissionRate },
{ "setEmitterLifetime", w_ParticleSystem_setEmitterLifetime },
{ "getEmitterLifetime", w_ParticleSystem_getEmitterLifetime },
{ "setParticleLifetime", w_ParticleSystem_setParticleLifetime },
{ "getParticleLifetime", w_ParticleSystem_getParticleLifetime },
{ "setPosition", w_ParticleSystem_setPosition },
{ "getPosition", w_ParticleSystem_getPosition },
{ "setAreaSpread", w_ParticleSystem_setAreaSpread },
{ "getAreaSpread", w_ParticleSystem_getAreaSpread },
{ "setDirection", w_ParticleSystem_setDirection },
{ "getDirection", w_ParticleSystem_getDirection },
{ "setSpread", w_ParticleSystem_setSpread },
{ "getSpread", w_ParticleSystem_getSpread },
{ "setSpeed", w_ParticleSystem_setSpeed },
{ "getSpeed", w_ParticleSystem_getSpeed },
{ "setLinearAcceleration", w_ParticleSystem_setLinearAcceleration },
{ "getLinearAcceleration", w_ParticleSystem_getLinearAcceleration },
{ "setRadialAcceleration", w_ParticleSystem_setRadialAcceleration },
{ "getRadialAcceleration", w_ParticleSystem_getRadialAcceleration },
{ "setTangentialAcceleration", w_ParticleSystem_setTangentialAcceleration },
{ "getTangentialAcceleration", w_ParticleSystem_getTangentialAcceleration },
{ "setSizes", w_ParticleSystem_setSizes },
{ "getSizes", w_ParticleSystem_getSizes },
{ "setSizeVariation", w_ParticleSystem_setSizeVariation },
{ "getSizeVariation", w_ParticleSystem_getSizeVariation },
{ "setRotation", w_ParticleSystem_setRotation },
{ "getRotation", w_ParticleSystem_getRotation },
{ "setSpin", w_ParticleSystem_setSpin },
{ "getSpin", w_ParticleSystem_getSpin },
{ "setSpinVariation", w_ParticleSystem_setSpinVariation },
{ "getSpinVariation", w_ParticleSystem_getSpinVariation },
{ "setColors", w_ParticleSystem_setColors },
{ "getColors", w_ParticleSystem_getColors },
{ "setOffset", w_ParticleSystem_setOffset },
{ "getOffset", w_ParticleSystem_getOffset },
{ "getCount", w_ParticleSystem_getCount },
{ "start", w_ParticleSystem_start },
{ "stop", w_ParticleSystem_stop },
{ "pause", w_ParticleSystem_pause },
{ "reset", w_ParticleSystem_reset },
{ "emit", w_ParticleSystem_emit },
{ "isActive", w_ParticleSystem_isActive },
{ "isPaused", w_ParticleSystem_isPaused },
{ "isStopped", w_ParticleSystem_isStopped },
{ "update", w_ParticleSystem_update },
{ 0, 0 }
};
extern "C" int luaopen_particlesystem(lua_State *L)
{
return luax_register_type(L, "ParticleSystem", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,95 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H
#define LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H
// LOVE
#include "common/runtime.h"
#include "wrap_Image.h"
#include "ParticleSystem.h"
namespace love
{
namespace graphics
{
namespace opengl
{
ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx);
int w_ParticleSystem_setImage(lua_State *L);
int w_ParticleSystem_getImage(lua_State *L);
int w_ParticleSystem_setBufferSize(lua_State *L);
int w_ParticleSystem_getBufferSize(lua_State *L);
int w_ParticleSystem_setInsertMode(lua_State *L);
int w_ParticleSystem_getInsertMode(lua_State *L);
int w_ParticleSystem_setEmissionRate(lua_State *L);
int w_ParticleSystem_getEmissionRate(lua_State *L);
int w_ParticleSystem_setEmitterLifetime(lua_State *L);
int w_ParticleSystem_getEmitterLifetime(lua_State *L);
int w_ParticleSystem_setParticleLifetime(lua_State *L);
int w_ParticleSystem_getParticleLifetime(lua_State *L);
int w_ParticleSystem_setPosition(lua_State *L);
int w_ParticleSystem_getPosition(lua_State *L);
int w_ParticleSystem_setAreaSpread(lua_State *L);
int w_ParticleSystem_getAreaSpread(lua_State *L);
int w_ParticleSystem_setDirection(lua_State *L);
int w_ParticleSystem_getDirection(lua_State *L);
int w_ParticleSystem_setSpread(lua_State *L);
int w_ParticleSystem_getSpread(lua_State *L);
int w_ParticleSystem_setSpeed(lua_State *L);
int w_ParticleSystem_getSpeed(lua_State *L);
int w_ParticleSystem_setLinearAcceleration(lua_State *L);
int w_ParticleSystem_getLinearAcceleration(lua_State *L);
int w_ParticleSystem_setRadialAcceleration(lua_State *L);
int w_ParticleSystem_getRadialAcceleration(lua_State *L);
int w_ParticleSystem_setTangentialAcceleration(lua_State *L);
int w_ParticleSystem_getTangentialAcceleration(lua_State *L);
int w_ParticleSystem_setSizes(lua_State *L);
int w_ParticleSystem_getSizes(lua_State *L);
int w_ParticleSystem_setSizeVariation(lua_State *L);
int w_ParticleSystem_getSizeVariation(lua_State *L);
int w_ParticleSystem_setRotation(lua_State *L);
int w_ParticleSystem_getRotation(lua_State *L);
int w_ParticleSystem_setSpin(lua_State *L);
int w_ParticleSystem_getSpin(lua_State *L);
int w_ParticleSystem_setSpinVariation(lua_State *L);
int w_ParticleSystem_getSpinVariation(lua_State *L);
int w_ParticleSystem_setColors(lua_State *L);
int w_ParticleSystem_getColors(lua_State *L);
int w_ParticleSystem_setOffset(lua_State *L);
int w_ParticleSystem_getOffset(lua_State *L);
int w_ParticleSystem_getCount(lua_State *L);
int w_ParticleSystem_start(lua_State *L);
int w_ParticleSystem_stop(lua_State *L);
int w_ParticleSystem_pause(lua_State *L);
int w_ParticleSystem_reset(lua_State *L);
int w_ParticleSystem_emit(lua_State *L);
int w_ParticleSystem_isActive(lua_State *L);
int w_ParticleSystem_isPaused(lua_State *L);
int w_ParticleSystem_isStopped(lua_State *L);
int w_ParticleSystem_update(lua_State *L);
extern "C" int luaopen_particlesystem(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H
@@ -0,0 +1,83 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Quad.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Quad *luax_checkquad(lua_State *L, int idx)
{
return luax_checktype<Quad>(L, idx, "Quad", GRAPHICS_QUAD_T);
}
int w_Quad_setViewport(lua_State *L)
{
Quad *quad = luax_checkquad(L, 1);
Quad::Viewport v;
v.x = (float) luaL_checknumber(L, 2);
v.y = (float) luaL_checknumber(L, 3);
v.w = (float) luaL_checknumber(L, 4);
v.h = (float) luaL_checknumber(L, 5);
if (lua_isnoneornil(L, 6))
quad->setViewport(v);
else
{
float sw = (float) luaL_checknumber(L, 6);
float sh = (float) luaL_checknumber(L, 7);
quad->refresh(v, sw, sh);
}
return 0;
}
int w_Quad_getViewport(lua_State *L)
{
Quad *quad = luax_checkquad(L, 1);
Quad::Viewport v = quad->getViewport();
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
lua_pushnumber(L, v.w);
lua_pushnumber(L, v.h);
return 4;
}
static const luaL_Reg functions[] =
{
{ "setViewport", w_Quad_setViewport },
{ "getViewport", w_Quad_getViewport },
{ 0, 0 }
};
extern "C" int luaopen_quad(lua_State *L)
{
return luax_register_type(L, "Quad", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_QUAD_H
#define LOVE_GRAPHICS_OPENGL_WRAP_QUAD_H
// LOVE
#include "common/runtime.h"
#include "graphics/Quad.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Quad *luax_checkquad(lua_State *L, int idx);
int w_Quad_setViewport(lua_State *L);
int w_Quad_getViewport(lua_State *L);
extern "C" int luaopen_quad(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_QUAD_H
@@ -0,0 +1,383 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "wrap_Shader.h"
#include "wrap_Image.h"
#include "wrap_Canvas.h"
#include <string>
#include <iostream>
namespace love
{
namespace graphics
{
namespace opengl
{
Shader *luax_checkshader(lua_State *L, int idx)
{
return luax_checktype<Shader>(L, idx, "Shader", GRAPHICS_SHADER_T);
}
int w_Shader_getWarnings(lua_State *L)
{
Shader *shader = luax_checkshader(L, 1);
lua_pushstring(L, shader->getWarnings().c_str());
return 1;
}
template <typename T>
static T *_getScalars(lua_State *L, int count, size_t &dimension)
{
dimension = 1;
T *values = new T[count];
for (int i = 0; i < count; ++i)
{
if (lua_isnumber(L, 3 + i))
values[i] = static_cast<T>(lua_tonumber(L, 3 + i));
else if (lua_isboolean(L, 3 + i))
values[i] = static_cast<T>(lua_toboolean(L, 3 + i));
else
{
delete[] values;
luax_typerror(L, 3 + i, "number or boolean");
return 0;
}
}
return values;
}
template <typename T>
static T *_getVectors(lua_State *L, int count, size_t &dimension)
{
dimension = lua_objlen(L, 3);
T *values = new T[count * dimension];
for (int i = 0; i < count; ++i)
{
if (!lua_istable(L, 3 + i))
{
delete[] values;
luax_typerror(L, 3 + i, "table");
return 0;
}
if (lua_objlen(L, 3 + i) != dimension)
{
delete[] values;
luaL_error(L, "Error in argument %d: Expected table size %d, got %d.",
3+i, dimension, lua_objlen(L, 3+i));
return 0;
}
for (size_t k = 1; k <= dimension; ++k)
{
lua_rawgeti(L, 3 + i, k);
if (lua_isnumber(L, -1))
values[i * dimension + k - 1] = static_cast<T>(lua_tonumber(L, -1));
else if (lua_isboolean(L, -1))
values[i * dimension + k - 1] = static_cast<T>(lua_toboolean(L, -1));
else
{
delete[] values;
luax_typerror(L, -1, "number or boolean");
return 0;
}
}
lua_pop(L, int(dimension));
}
return values;
}
int w_Shader_sendInt(lua_State *L)
{
Shader *shader = luax_checkshader(L, 1);
const char *name = luaL_checkstring(L, 2);
int count = lua_gettop(L) - 2;
if (count < 1)
return luaL_error(L, "No variable to send.");
int *values = 0;
size_t dimension = 1;
if (lua_isnumber(L, 3) || lua_isboolean(L, 3))
values = _getScalars<int>(L, count, dimension);
else if (lua_istable(L, 3))
values = _getVectors<int>(L, count, dimension);
else
return luax_typerror(L, 3, "number, boolean, or table");
if (!values)
return luaL_error(L, "Error in arguments.");
bool should_error = false;
try
{
shader->sendInt(name, dimension, values, count);
}
catch (love::Exception &e)
{
should_error = true;
lua_pushstring(L, e.what());
}
delete[] values;
if (should_error)
return luaL_error(L, "%s", lua_tostring(L, -1));
return 0;
}
int w_Shader_sendFloat(lua_State *L)
{
Shader *shader = luax_checkshader(L, 1);
const char *name = luaL_checkstring(L, 2);
int count = lua_gettop(L) - 2;
if (count < 1)
return luaL_error(L, "No variable to send.");
float *values = 0;
size_t dimension = 1;
if (lua_isnumber(L, 3) || lua_isboolean(L, 3))
values = _getScalars<float>(L, count, dimension);
else if (lua_istable(L, 3))
values = _getVectors<float>(L, count, dimension);
else
return luax_typerror(L, 3, "number, boolean, or table");
if (!values)
return luaL_error(L, "Error in arguments.");
bool should_error = false;
try
{
shader->sendFloat(name, dimension, values, count);
}
catch (love::Exception &e)
{
should_error = true;
lua_pushstring(L, e.what());
}
delete[] values;
if (should_error)
return luaL_error(L, "%s", lua_tostring(L, -1));
return 0;
}
int w_Shader_sendMatrix(lua_State *L)
{
int count = lua_gettop(L) - 2;
Shader *shader = luax_checkshader(L, 1);
const char *name = luaL_checkstring(L, 2);
if (!lua_istable(L, 3))
return luax_typerror(L, 3, "matrix table");
lua_getfield(L, 3, "dimension");
int dimension = lua_tointeger(L, -1);
lua_pop(L, 1);
if (dimension < 2 || dimension > 4)
return luaL_error(L, "Invalid matrix size: %dx%d (only 2x2, 3x3 and 4x4 matrices are supported).",
dimension, dimension);
float *values = new float[dimension * dimension * count];
for (int i = 0; i < count; ++i)
{
lua_getfield(L, 3+i, "dimension");
if (lua_tointeger(L, -1) != dimension)
{
// You unlock this door with the key of imagination. Beyond it is
// another dimension: a dimension of sound, a dimension of sight,
// a dimension of mind. You're moving into a land of both shadow
// and substance, of things and ideas. You've just crossed over
// into... the Twilight Zone.
int other_dimension = lua_tointeger(L, -1);
delete[] values;
return luaL_error(L, "Invalid matrix size at argument %d: Expected size %dx%d, got %dx%d.",
3+i, dimension, dimension, other_dimension, other_dimension);
}
for (int k = 1; k <= dimension*dimension; ++k)
{
lua_rawgeti(L, 3+i, k);
values[i * dimension * dimension + k - 1] = (float)lua_tonumber(L, -1);
}
lua_pop(L, 1 + dimension);
}
bool should_error = false;
try
{
shader->sendMatrix(name, dimension, values, count);
}
catch(love::Exception &e)
{
should_error = true;
lua_pushstring(L, e.what());
}
delete[] values;
if (should_error)
return luaL_error(L, "%s", lua_tostring(L, -1));
return 0;
}
int w_Shader_sendImage(lua_State *L)
{
Shader *shader = luax_checkshader(L, 1);
const char *name = luaL_checkstring(L, 2);
Image *img = luax_checkimage(L, 3);
EXCEPT_GUARD(shader->sendImage(name, *img);)
return 0;
}
int w_Shader_sendCanvas(lua_State *L)
{
Shader *shader = luax_checkshader(L, 1);
const char *name = luaL_checkstring(L, 2);
Canvas *canvas = luax_checkcanvas(L, 3);
EXCEPT_GUARD(shader->sendCanvas(name, *canvas);)
return 0;
}
// Convert matrices on the stack for use with sendMatrix.
static void w_convertMatrices(lua_State *L, int idx)
{
int matrixcount = lua_gettop(L) - (idx - 1);
for (int matrix = idx; matrix < idx + matrixcount; matrix++)
{
luaL_checktype(L, matrix, LUA_TTABLE);
int dimension = lua_objlen(L, matrix);
int newi = 1;
lua_createtable(L, dimension * dimension, 0);
// Collapse {{a,b,c}, {d,e,f}, ...} to {a,b,c, d,e,f, ...}
for (size_t i = 1; i <= lua_objlen(L, matrix); i++)
{
// Push args[matrix][i] onto the stack.
lua_rawgeti(L, matrix, i);
luaL_checktype(L, -1, LUA_TTABLE);
for (size_t j = 1; j <= lua_objlen(L, -1); j++)
{
// Push args[matrix[i][j] onto the stack.
lua_rawgeti(L, -1, j);
luaL_checktype(L, -1, LUA_TNUMBER);
// newtable[newi] = args[matrix][i][j]
lua_rawseti(L, -3, newi++);
}
lua_pop(L, 1);
}
// newtable.dimension = #args[matrix]
lua_pushinteger(L, dimension);
lua_setfield(L, -2, "dimension");
// Replace args[i] with the new table
lua_replace(L, matrix);
}
}
int w_Shader_send(lua_State *L)
{
int ttype = lua_type(L, 3);
Proxy *p = 0;
switch (ttype)
{
case LUA_TNUMBER:
case LUA_TBOOLEAN:
// Scalar float/boolean.
return w_Shader_sendFloat(L);
break;
case LUA_TUSERDATA:
// Image or Canvas.
p = (Proxy *) lua_touserdata(L, 3);
if (p->flags[GRAPHICS_IMAGE_ID])
return w_Shader_sendImage(L);
else if (p->flags[GRAPHICS_CANVAS_ID])
return w_Shader_sendCanvas(L);
break;
case LUA_TTABLE:
// Vector or Matrix.
lua_rawgeti(L, 3, 1);
ttype = lua_type(L, -1);
lua_pop(L, 1);
if (ttype == LUA_TNUMBER || ttype == LUA_TBOOLEAN)
return w_Shader_sendFloat(L);
else if (ttype == LUA_TTABLE)
{
w_convertMatrices(L, 3);
return w_Shader_sendMatrix(L);
}
break;
default:
break;
}
return luaL_argerror(L, 3, "number, boolean, table, image, or canvas expected");
}
static const luaL_Reg functions[] =
{
{ "getWarnings", w_Shader_getWarnings },
{ "sendInt", w_Shader_sendInt },
{ "sendBoolean", w_Shader_sendInt },
{ "sendFloat", w_Shader_sendFloat },
{ "sendMatrix", w_Shader_sendMatrix },
{ "sendImage", w_Shader_sendImage },
{ "sendCanvas", w_Shader_sendCanvas },
{ "send", w_Shader_send },
{ 0, 0 }
};
extern "C" int luaopen_shader(lua_State *L)
{
return luax_register_type(L, "Shader", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_PROGRAM_H
#define LOVE_GRAPHICS_OPENGL_WRAP_PROGRAM_H
#include "common/runtime.h"
#include "Shader.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Shader *luax_checkshader(lua_State *L, int idx);
int w_Shader_getWarnings(lua_State *L);
int w_Shader_sendInt(lua_State *L);
int w_Shader_sendFloat(lua_State *L);
int w_Shader_sendMatrix(lua_State *L);
int w_Shader_sendImage(lua_State *L);
int w_Shader_sendCanvas(lua_State *L);
int w_Shader_send(lua_State *L);
extern "C" int luaopen_shader(lua_State *L);
} // opengl
} // graphics
} // love
#endif
@@ -0,0 +1,244 @@
/**
* Copyright (c) 2006-2013 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.
**/
#include "Image.h"
#include "wrap_SpriteBatch.h"
namespace love
{
namespace graphics
{
namespace opengl
{
SpriteBatch *luax_checkspritebatch(lua_State *L, int idx)
{
return luax_checktype<SpriteBatch>(L, idx, "SpriteBatch", GRAPHICS_SPRITE_BATCH_T);
}
int w_SpriteBatch_add(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
Quad *quad = 0;
int startidx = 2;
if (luax_istype(L, 2, GRAPHICS_QUAD_T))
{
quad = luax_totype<Quad>(L, 2, "Quad", GRAPHICS_QUAD_T);
startidx = 3;
}
else if (lua_isnil(L, 2) && !lua_isnoneornil(L, 3))
return luax_typerror(L, 2, "Quad");
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
int id = 0;
EXCEPT_GUARD(
if (quad)
id = t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky);
else
id = t->add(x, y, a, sx, sy, ox, oy, kx, ky);
)
lua_pushinteger(L, id);
return 1;
}
int w_SpriteBatch_set(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
int id = luaL_checkinteger(L, 2);
Quad *quad = 0;
int startidx = 3;
if (luax_istype(L, 3, GRAPHICS_QUAD_T))
{
quad = luax_totype<Quad>(L, 3, "Quad", GRAPHICS_QUAD_T);
startidx = 4;
}
else if (lua_isnil(L, 3) && !lua_isnoneornil(L, 4))
return luax_typerror(L, 3, "Quad");
float x = (float) luaL_optnumber(L, startidx + 0, 0.0);
float y = (float) luaL_optnumber(L, startidx + 1, 0.0);
float a = (float) luaL_optnumber(L, startidx + 2, 0.0);
float sx = (float) luaL_optnumber(L, startidx + 3, 1.0);
float sy = (float) luaL_optnumber(L, startidx + 4, sx);
float ox = (float) luaL_optnumber(L, startidx + 5, 0.0);
float oy = (float) luaL_optnumber(L, startidx + 6, 0.0);
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
EXCEPT_GUARD(
if (quad)
t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky, id);
else
t->add(x, y, a, sx, sy, ox, oy, kx, ky, id);
)
return 0;
}
int w_SpriteBatch_clear(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
t->clear();
return 0;
}
int w_SpriteBatch_bind(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
EXCEPT_GUARD(t->lock();)
return 0;
}
int w_SpriteBatch_unbind(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
t->unlock();
return 0;
}
int w_SpriteBatch_setImage(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
Image *image = luax_checktype<Image>(L, 2, "Image", GRAPHICS_IMAGE_T);
t->setImage(image);
return 0;
}
int w_SpriteBatch_getImage(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
Image *image = t->getImage();
image->retain();
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, image);
return 1;
}
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))
{
for (int i = 1; i <= 4; i++)
lua_rawgeti(L, 2, i);
c.r = (unsigned char) luaL_checkinteger(L, -4);
c.g = (unsigned char) luaL_checkinteger(L, -3);
c.b = (unsigned char) luaL_checkinteger(L, -2);
c.a = (unsigned char) luaL_optinteger(L, -1, 255);
lua_pop(L, 4);
}
else
{
c.r = (unsigned char)luaL_checkinteger(L, 2);
c.g = (unsigned char)luaL_checkinteger(L, 3);
c.b = (unsigned char)luaL_checkinteger(L, 4);
c.a = (unsigned char)luaL_optinteger(L, 5, 255);
}
t->setColor(c);
return 0;
}
int w_SpriteBatch_getColor(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
const Color *color = t->getColor();
// getColor returns NULL if no color is set.
if (!color)
return 0;
lua_pushinteger(L, (lua_Integer) color->r);
lua_pushinteger(L, (lua_Integer) color->g);
lua_pushinteger(L, (lua_Integer) color->b);
lua_pushinteger(L, (lua_Integer) color->a);
return 4;
}
int w_SpriteBatch_getCount(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
lua_pushinteger(L, t->getCount());
return 1;
}
int w_SpriteBatch_setBufferSize(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
int size = luaL_checkint(L, 2);
EXCEPT_GUARD(t->setBufferSize(size);)
return 0;
}
int w_SpriteBatch_getBufferSize(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
lua_pushinteger(L, t->getBufferSize());
return 1;
}
static const luaL_Reg functions[] =
{
{ "add", w_SpriteBatch_add },
{ "set", w_SpriteBatch_set },
{ "clear", w_SpriteBatch_clear },
{ "bind", w_SpriteBatch_bind },
{ "unbind", w_SpriteBatch_unbind },
{ "setImage", w_SpriteBatch_setImage },
{ "getImage", w_SpriteBatch_getImage },
{ "setColor", w_SpriteBatch_setColor },
{ "getColor", w_SpriteBatch_getColor },
{ "getCount", w_SpriteBatch_getCount },
{ "setBufferSize", w_SpriteBatch_setBufferSize },
{ "getBufferSize", w_SpriteBatch_getBufferSize },
{ 0, 0 }
};
extern "C" int luaopen_spritebatch(lua_State *L)
{
return luax_register_type(L, "SpriteBatch", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2006-2013 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.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H
#define LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H
#include "common/runtime.h"
#include "SpriteBatch.h"
namespace love
{
namespace graphics
{
namespace opengl
{
SpriteBatch *luax_checkspritebatch(lua_State *L, int idx);
int w_SpriteBatch_add(lua_State *L);
int w_SpriteBatch_addg(lua_State *L);
int w_SpriteBatch_set(lua_State *L);
int w_SpriteBatch_setg(lua_State *L);
int w_SpriteBatch_clear(lua_State *L);
int w_SpriteBatch_bind(lua_State *L);
int w_SpriteBatch_unbind(lua_State *L);
int w_SpriteBatch_setImage(lua_State *L);
int w_SpriteBatch_getImage(lua_State *L);
int w_SpriteBatch_setColor(lua_State *L);
int w_SpriteBatch_getColor(lua_State *L);
int w_SpriteBatch_getCount(lua_State *L);
int w_SpriteBatch_setBufferSize(lua_State *L);
int w_SpriteBatch_getBufferSize(lua_State *L);
extern "C" int luaopen_spritebatch(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H