mirror of
https://github.com/love2d/love-android.git
synced 2026-08-19 04:05:22 +02:00
imported Löve GLES branch (changeset 1ba9037e558b)
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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, ¤t_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, ¤t_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
|
||||
@@ -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
|
||||
@@ -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, >);
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user