mirror of
https://github.com/love2d/love-android.git
synced 2026-08-20 04:31:26 +02:00
Using latest android branch of LÖVE (6d14516b05c9) that now syncs with default (woooo!)
This commit is contained in:
@@ -34,27 +34,17 @@ bool Graphics::getConstant(const char *in, DrawMode &out)
|
||||
return drawModes.find(in, out);
|
||||
}
|
||||
|
||||
bool Graphics::getConstant(DrawMode in, const char *&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)
|
||||
bool Graphics::getConstant(BlendMode in, const char *&out)
|
||||
{
|
||||
return blendModes.find(in, out);
|
||||
}
|
||||
@@ -64,7 +54,7 @@ bool Graphics::getConstant(const char *in, LineStyle &out)
|
||||
return lineStyles.find(in, out);
|
||||
}
|
||||
|
||||
bool Graphics::getConstant(LineStyle in, const char *&out)
|
||||
bool Graphics::getConstant(LineStyle in, const char *&out)
|
||||
{
|
||||
return lineStyles.find(in, out);
|
||||
}
|
||||
@@ -74,27 +64,17 @@ bool Graphics::getConstant(const char *in, LineJoin &out)
|
||||
return lineJoins.find(in, out);
|
||||
}
|
||||
|
||||
bool Graphics::getConstant(LineJoin in, const char *&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)
|
||||
bool Graphics::getConstant(Support in, const char *&out)
|
||||
{
|
||||
return support.find(in, out);
|
||||
}
|
||||
@@ -131,105 +111,76 @@ bool Graphics::getConstant(StatType in, const char *&out)
|
||||
|
||||
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
|
||||
{
|
||||
{ "line", Graphics::DRAW_LINE },
|
||||
{ "fill", Graphics::DRAW_FILL },
|
||||
{ "line", DRAW_LINE },
|
||||
{ "fill", 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 },
|
||||
{ "screen", Graphics::BLEND_SCREEN },
|
||||
{ "replace", Graphics::BLEND_REPLACE },
|
||||
{ "alpha", BLEND_ALPHA },
|
||||
{ "add", BLEND_ADD },
|
||||
{ "subtract", BLEND_SUBTRACT },
|
||||
{ "multiply", BLEND_MULTIPLY },
|
||||
{ "screen", BLEND_SCREEN },
|
||||
{ "replace", 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 }
|
||||
{ "smooth", LINE_SMOOTH },
|
||||
{ "rough", 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 }
|
||||
{ "none", LINE_JOIN_NONE },
|
||||
{ "miter", LINE_JOIN_MITER },
|
||||
{ "bevel", 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 },
|
||||
{ "etc1", Graphics::SUPPORT_ETC1 },
|
||||
{ "pvrtc1", Graphics::SUPPORT_PVRTC1 },
|
||||
{ "srgb", Graphics::SUPPORT_SRGB },
|
||||
{ "multicanvas", SUPPORT_MULTI_CANVAS },
|
||||
{ "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS },
|
||||
{ "srgb", SUPPORT_SRGB },
|
||||
};
|
||||
|
||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
|
||||
|
||||
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::systemLimitEntries[] =
|
||||
{
|
||||
{"pointsize", Graphics::LIMIT_POINT_SIZE},
|
||||
{"texturesize", Graphics::LIMIT_TEXTURE_SIZE},
|
||||
{"multicanvas", Graphics::LIMIT_MULTI_CANVAS},
|
||||
{"canvasfsaa", Graphics::LIMIT_CANVAS_FSAA},
|
||||
{"canvasmsaa", Graphics::LIMIT_CANVAS_MSAA},
|
||||
{ "pointsize", LIMIT_POINT_SIZE },
|
||||
{ "texturesize", LIMIT_TEXTURE_SIZE },
|
||||
{ "multicanvas", LIMIT_MULTI_CANVAS },
|
||||
{ "canvasmsaa", LIMIT_CANVAS_MSAA },
|
||||
};
|
||||
|
||||
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
|
||||
|
||||
StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM>::Entry Graphics::stackTypeEntries[] =
|
||||
{
|
||||
{"all", Graphics::STACK_ALL},
|
||||
{"transform", Graphics::STACK_TRANSFORM},
|
||||
{ "all", STACK_ALL },
|
||||
{ "transform", STACK_TRANSFORM },
|
||||
};
|
||||
|
||||
StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM> Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries));
|
||||
|
||||
StringMap<Graphics::StatType, Graphics::STAT_MAX_ENUM>::Entry Graphics::statTypeEntries[] =
|
||||
{
|
||||
{"drawcalls", Graphics::STAT_DRAW_CALLS},
|
||||
{"canvasswitches", Graphics::STAT_CANVAS_SWITCHES},
|
||||
{"canvases", Graphics::STAT_CANVASES},
|
||||
{"images", Graphics::STAT_IMAGES},
|
||||
{"fonts", Graphics::STAT_FONTS},
|
||||
{"texturememory", Graphics::STAT_TEXTURE_MEMORY},
|
||||
{ "drawcalls", STAT_DRAW_CALLS },
|
||||
{ "canvasswitches", STAT_CANVAS_SWITCHES },
|
||||
{ "canvases", STAT_CANVASES },
|
||||
{ "images", STAT_IMAGES },
|
||||
{ "fonts", STAT_FONTS },
|
||||
{ "texturememory", STAT_TEXTURE_MEMORY },
|
||||
};
|
||||
|
||||
StringMap<Graphics::StatType, Graphics::STAT_MAX_ENUM> Graphics::statTypes(Graphics::statTypeEntries, sizeof(Graphics::statTypeEntries));
|
||||
|
||||
@@ -44,22 +44,12 @@ public:
|
||||
DRAW_MAX_ENUM
|
||||
};
|
||||
|
||||
enum AlignMode
|
||||
{
|
||||
ALIGN_LEFT,
|
||||
ALIGN_CENTER,
|
||||
ALIGN_RIGHT,
|
||||
ALIGN_JUSTIFY,
|
||||
ALIGN_MAX_ENUM
|
||||
};
|
||||
|
||||
enum BlendMode
|
||||
{
|
||||
BLEND_ALPHA,
|
||||
BLEND_ADDITIVE,
|
||||
BLEND_SUBTRACTIVE,
|
||||
BLEND_MULTIPLICATIVE,
|
||||
BLEND_PREMULTIPLIED,
|
||||
BLEND_ADD,
|
||||
BLEND_SUBTRACT,
|
||||
BLEND_MULTIPLY,
|
||||
BLEND_SCREEN,
|
||||
BLEND_REPLACE,
|
||||
BLEND_MAX_ENUM
|
||||
@@ -80,26 +70,10 @@ public:
|
||||
LINE_JOIN_MAX_ENUM
|
||||
};
|
||||
|
||||
enum PointStyle
|
||||
{
|
||||
POINT_ROUGH,
|
||||
POINT_SMOOTH,
|
||||
POINT_MAX_ENUM
|
||||
};
|
||||
|
||||
enum Support
|
||||
{
|
||||
SUPPORT_CANVAS,
|
||||
SUPPORT_HDR_CANVAS,
|
||||
SUPPORT_MULTI_CANVAS,
|
||||
SUPPORT_SHADER,
|
||||
SUPPORT_NPOT,
|
||||
SUPPORT_SUBTRACTIVE,
|
||||
SUPPORT_MIPMAP,
|
||||
SUPPORT_DXT,
|
||||
SUPPORT_BC5,
|
||||
SUPPORT_ETC1,
|
||||
SUPPORT_PVRTC1,
|
||||
SUPPORT_MULTI_CANVAS_FORMATS,
|
||||
SUPPORT_SRGB,
|
||||
SUPPORT_MAX_ENUM
|
||||
};
|
||||
@@ -116,7 +90,6 @@ public:
|
||||
LIMIT_POINT_SIZE,
|
||||
LIMIT_TEXTURE_SIZE,
|
||||
LIMIT_MULTI_CANVAS,
|
||||
LIMIT_CANVAS_FSAA, // For backward-compatibility. TODO: remove!
|
||||
LIMIT_CANVAS_MSAA,
|
||||
LIMIT_MAX_ENUM
|
||||
};
|
||||
@@ -159,10 +132,7 @@ public:
|
||||
|
||||
struct ColorMask
|
||||
{
|
||||
bool r;
|
||||
bool g;
|
||||
bool b;
|
||||
bool a;
|
||||
bool r, g, b, a;
|
||||
|
||||
bool operator == (const ColorMask &m) const
|
||||
{
|
||||
@@ -213,25 +183,19 @@ public:
|
||||
virtual bool isActive() const = 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(DrawMode in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, BlendMode &out);
|
||||
static bool getConstant(BlendMode in, const char *&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(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(LineJoin in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, Support &out);
|
||||
static bool getConstant(Support in, const char *&out);
|
||||
static bool getConstant(Support in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, SystemLimit &out);
|
||||
static bool getConstant(SystemLimit in, const char *&out);
|
||||
@@ -247,9 +211,6 @@ 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;
|
||||
|
||||
@@ -259,9 +220,6 @@ private:
|
||||
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;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ Quad::Quad(const Quad::Viewport &v, float sw, float sh)
|
||||
: sw(sw)
|
||||
, sh(sh)
|
||||
{
|
||||
memset(vertices, 255, sizeof(Vertex) * NUM_VERTICES);
|
||||
memset(vertices, 255, sizeof(Vertex) * 4);
|
||||
refresh(v, sw, sh);
|
||||
}
|
||||
|
||||
@@ -45,23 +45,28 @@ 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 are ordered for use with triangle strips:
|
||||
// 0----2
|
||||
// | / |
|
||||
// | / |
|
||||
// 1----3
|
||||
vertices[0].x = 0.0f;
|
||||
vertices[0].y = 0.0f;
|
||||
vertices[1].x = 0.0f;
|
||||
vertices[1].y = v.h;
|
||||
vertices[2].x = v.w;
|
||||
vertices[2].y = v.h;
|
||||
vertices[2].y = 0.0f;
|
||||
vertices[3].x = v.w;
|
||||
vertices[3].y = 0;
|
||||
vertices[3].y = v.h;
|
||||
|
||||
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[2].t = v.y/sh;
|
||||
vertices[3].s = (v.x+v.w)/sw;
|
||||
vertices[3].t = v.y/sh;
|
||||
vertices[3].t = (v.y+v.h)/sh;
|
||||
}
|
||||
|
||||
void Quad::setViewport(const Quad::Viewport &v)
|
||||
|
||||
@@ -40,8 +40,6 @@ public:
|
||||
float w, h;
|
||||
};
|
||||
|
||||
static const size_t NUM_VERTICES = 4;
|
||||
|
||||
Quad(const Viewport &v, float sw, float sh);
|
||||
virtual ~Quad();
|
||||
|
||||
@@ -53,7 +51,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
Vertex vertices[NUM_VERTICES];
|
||||
Vertex vertices[4];
|
||||
|
||||
Viewport viewport;
|
||||
float sw;
|
||||
|
||||
@@ -27,20 +27,6 @@ namespace graphics
|
||||
|
||||
Texture::Filter Texture::defaultFilter;
|
||||
|
||||
Texture::Filter::Filter()
|
||||
: min(FILTER_LINEAR)
|
||||
, mag(FILTER_LINEAR)
|
||||
, mipmap(FILTER_NONE)
|
||||
, anisotropy(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
Texture::Wrap::Wrap()
|
||||
: s(WRAP_CLAMP)
|
||||
, t(WRAP_CLAMP)
|
||||
{
|
||||
}
|
||||
|
||||
Texture::Texture()
|
||||
: width(0)
|
||||
, height(0)
|
||||
@@ -89,6 +75,23 @@ const Texture::Filter &Texture::getDefaultFilter()
|
||||
return defaultFilter;
|
||||
}
|
||||
|
||||
bool Texture::validateFilter(const Filter &f, bool mipmapsAllowed)
|
||||
{
|
||||
if (!mipmapsAllowed && f.mipmap != FILTER_NONE)
|
||||
return false;
|
||||
|
||||
if (f.mag != FILTER_LINEAR && f.mag != FILTER_NEAREST)
|
||||
return false;
|
||||
|
||||
if (f.min != FILTER_LINEAR && f.min != FILTER_NEAREST)
|
||||
return false;
|
||||
|
||||
if (f.mipmap != FILTER_LINEAR && f.mipmap != FILTER_NEAREST && f.mipmap != FILTER_NONE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::getConstant(const char *in, FilterMode &out)
|
||||
{
|
||||
return filterModes.find(in, out);
|
||||
@@ -113,6 +116,7 @@ StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterM
|
||||
{
|
||||
{ "linear", Texture::FILTER_LINEAR },
|
||||
{ "nearest", Texture::FILTER_NEAREST },
|
||||
{ "none", Texture::FILTER_NONE },
|
||||
};
|
||||
|
||||
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM> Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries));
|
||||
|
||||
@@ -58,18 +58,16 @@ public:
|
||||
|
||||
struct Filter
|
||||
{
|
||||
Filter();
|
||||
FilterMode min;
|
||||
FilterMode mag;
|
||||
FilterMode mipmap;
|
||||
float anisotropy;
|
||||
FilterMode min = FILTER_LINEAR;
|
||||
FilterMode mag = FILTER_LINEAR;
|
||||
FilterMode mipmap = FILTER_NONE;
|
||||
float anisotropy = 1.0f;
|
||||
};
|
||||
|
||||
struct Wrap
|
||||
{
|
||||
Wrap();
|
||||
WrapMode s;
|
||||
WrapMode t;
|
||||
WrapMode s = WRAP_CLAMP;
|
||||
WrapMode t = WRAP_CLAMP;
|
||||
};
|
||||
|
||||
Texture();
|
||||
@@ -102,10 +100,14 @@ public:
|
||||
|
||||
virtual const Vertex *getVertices() const;
|
||||
|
||||
virtual const void *getHandle() const = 0;
|
||||
|
||||
// The default filter.
|
||||
static void setDefaultFilter(const Filter &f);
|
||||
static const Filter &getDefaultFilter();
|
||||
|
||||
static bool validateFilter(const Filter &f, bool mipmapsAllowed);
|
||||
|
||||
static bool getConstant(const char *in, FilterMode &out);
|
||||
static bool getConstant(FilterMode in, const char *&out);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,13 +21,15 @@
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_CANVAS_H
|
||||
#define LOVE_GRAPHICS_OPENGL_CANVAS_H
|
||||
|
||||
#include "common/config.h"
|
||||
#include "graphics/Color.h"
|
||||
#include "image/Image.h"
|
||||
#include "image/ImageData.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "common/int.h"
|
||||
#include "Texture.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "OpenGL.h"
|
||||
|
||||
namespace love
|
||||
@@ -37,7 +39,7 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class Canvas : public Texture
|
||||
class Canvas : public Texture, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -46,15 +48,21 @@ public:
|
||||
{
|
||||
FORMAT_NORMAL, // Usually RGBA8 or a similar fallback. Always supported.
|
||||
FORMAT_HDR, // Usually RGBA16F. Not always supported.
|
||||
FORMAT_RGBA8, // RGBA with 8 bits per component.
|
||||
FORMAT_RGBA4, // RGBA with 4 bits per component.
|
||||
FORMAT_RGB5A1, // RGB with 5 bits per component, and A with 1 bit.
|
||||
FORMAT_RGBA4, // RGBA with 4 bits per channel.
|
||||
FORMAT_RGB5A1, // RGB with 5 bits per channel, and A with 1 bit.
|
||||
FORMAT_RGB565, // RGB with 5, 6, and 5 bits each, respectively.
|
||||
FORMAT_R8, // Single (red) 8-bit channel.
|
||||
FORMAT_RG8, // Two-channel (red and green) with 8 bits per channel.
|
||||
FORMAT_RGBA8, // RGBA with 8 bits per channel.
|
||||
FORMAT_RGB10A2, // RGB with 10 bits each, and A with 2 bits.
|
||||
FORMAT_RG11B10F, // Floating point [0, +inf]. RG with 11 FP bits each, and B with 10 FP bits.
|
||||
FORMAT_RGBA16F, // Floating point [-inf, +inf]. RGBA with 16 FP bits per component.
|
||||
FORMAT_RGBA32F, // Floating point [-inf, +inf]. RGBA with 32 FP bits per component.
|
||||
FORMAT_SRGB, // sRGB with 8 bits per component, plus 8 bit linear A.
|
||||
FORMAT_R16F, // Floating point [-inf, +inf]. R with 16 FP bits.
|
||||
FORMAT_RG16F, // Floating point [-inf, +inf]. RG with 16 FP bits per channel.
|
||||
FORMAT_RGBA16F, // Floating point [-inf, +inf]. RGBA with 16 FP bits per channel.
|
||||
FORMAT_R32F, // Floating point [-inf, +inf]. R with 32 FP bits.
|
||||
FORMAT_RG32F, // Floating point [-inf, +inf]. RG with 32 FP bits per channel.
|
||||
FORMAT_RGBA32F, // Floating point [-inf, +inf]. RGBA with 32 FP bits per channel.
|
||||
FORMAT_SRGB, // sRGB with 8 bits per channel, plus 8 bit linear A.
|
||||
FORMAT_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -72,8 +80,7 @@ public:
|
||||
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
virtual void setFilter(const Texture::Filter &f);
|
||||
virtual bool setWrap(const Texture::Wrap &w);
|
||||
virtual GLuint getGLTexture() const;
|
||||
virtual void predraw();
|
||||
virtual const void *getHandle() const;
|
||||
|
||||
/**
|
||||
* @param canvases A list of other canvases to temporarily attach to this one,
|
||||
@@ -83,16 +90,12 @@ public:
|
||||
void startGrab();
|
||||
void stopGrab(bool switchingToOtherCanvas = false);
|
||||
|
||||
void clear(Color c);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
love::image::ImageData *newImageData(love::image::Image *image, int x, int y, int w, int h);
|
||||
|
||||
inline const std::vector<Canvas *> &getAttachedCanvases() const
|
||||
{
|
||||
@@ -111,13 +114,12 @@ public:
|
||||
|
||||
inline int getMSAA() const
|
||||
{
|
||||
return msaa_samples;
|
||||
return actual_samples;
|
||||
}
|
||||
|
||||
bool resolveMSAA();
|
||||
|
||||
static bool isSupported();
|
||||
static bool isMultiCanvasSupported();
|
||||
static bool isMultiFormatMultiCanvasSupported();
|
||||
static bool isFormatSupported(Format format);
|
||||
|
||||
static Canvas *current;
|
||||
@@ -128,7 +130,6 @@ public:
|
||||
// Whether the main screen should have linear -> sRGB conversions enabled.
|
||||
static bool screenHasSRGB;
|
||||
|
||||
static int switchCount;
|
||||
static int canvasCount;
|
||||
|
||||
static bool getConstant(const char *in, Format &out);
|
||||
@@ -136,7 +137,12 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void setupGrab();
|
||||
|
||||
bool createMSAAFBO(GLenum internalformat);
|
||||
bool resolveMSAA(bool restoreprev);
|
||||
|
||||
void drawv(const Matrix4 &t, const Vertex *v);
|
||||
|
||||
static Format getSizedFormat(Format format);
|
||||
static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
|
||||
@@ -155,14 +161,11 @@ private:
|
||||
|
||||
std::vector<Canvas *> attachedCanvases;
|
||||
|
||||
int msaa_samples;
|
||||
bool msaa_dirty;
|
||||
int requested_samples;
|
||||
int actual_samples;
|
||||
|
||||
size_t texture_memory;
|
||||
|
||||
void setupGrab();
|
||||
void drawv(const Matrix &t, const Vertex *v);
|
||||
|
||||
static bool supportedFormats[FORMAT_MAX_ENUM];
|
||||
static bool checkedFormats[FORMAT_MAX_ENUM];
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,16 +22,20 @@
|
||||
#define LOVE_GRAPHICS_OPENGL_FONT_H
|
||||
|
||||
// STD
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/Object.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "common/Vector.h"
|
||||
|
||||
#include "font/Rasterizer.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "VertexBuffer.h"
|
||||
#include "GLBuffer.h"
|
||||
|
||||
#include "OpenGL.h"
|
||||
|
||||
@@ -46,17 +50,60 @@ class Font : public Object, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
enum AlignMode
|
||||
{
|
||||
ALIGN_LEFT,
|
||||
ALIGN_CENTER,
|
||||
ALIGN_RIGHT,
|
||||
ALIGN_JUSTIFY,
|
||||
ALIGN_MAX_ENUM
|
||||
};
|
||||
|
||||
struct GlyphVertex
|
||||
{
|
||||
float x, y;
|
||||
float s, t;
|
||||
};
|
||||
|
||||
struct TextInfo
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
// Used to determine when to change textures in the generated vertex array.
|
||||
struct DrawCommand
|
||||
{
|
||||
GLuint texture;
|
||||
int startvertex;
|
||||
int vertexcount;
|
||||
|
||||
// used when sorting with std::sort.
|
||||
bool operator < (const DrawCommand &other) const
|
||||
{
|
||||
// Texture binds are expensive, so we should sort by that first.
|
||||
if (texture != other.texture)
|
||||
return texture < other.texture;
|
||||
else
|
||||
return startvertex < other.startvertex;
|
||||
}
|
||||
};
|
||||
|
||||
Font(love::font::Rasterizer *r, const Texture::Filter &filter = Texture::getDefaultFilter());
|
||||
|
||||
virtual ~Font();
|
||||
|
||||
std::vector<DrawCommand> generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing = 0.0f, Vector offset = Vector(), TextInfo *info = nullptr);
|
||||
std::vector<DrawCommand> generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
|
||||
|
||||
void drawVertices(const std::vector<DrawCommand> &drawcommands);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -65,7 +112,9 @@ public:
|
||||
* @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);
|
||||
void print(const std::string &text, float x, float y, 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);
|
||||
|
||||
void printf(const std::string &text, float x, float y, float wrap, AlignMode align, 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.
|
||||
@@ -97,7 +146,7 @@ public:
|
||||
* 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);
|
||||
void getWrap(const std::string &text, float wrap, std::vector<std::string> &lines, std::vector<int> *line_widths = 0, std::vector<bool> *wrapped_lines = 0);
|
||||
|
||||
/**
|
||||
* Sets the line height (which should be a number to multiply the font size by,
|
||||
@@ -111,19 +160,6 @@ public:
|
||||
**/
|
||||
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 Texture::Filter &f);
|
||||
const Texture::Filter &getFilter();
|
||||
|
||||
@@ -139,6 +175,13 @@ public:
|
||||
bool hasGlyph(uint32 glyph) const;
|
||||
bool hasGlyphs(const std::string &text) const;
|
||||
|
||||
void setFallbacks(const std::vector<Font *> &fallbacks);
|
||||
|
||||
uint32 getTextureCacheID() const;
|
||||
|
||||
static bool getConstant(const char *in, AlignMode &out);
|
||||
static bool getConstant(AlignMode in, const char *&out);
|
||||
|
||||
static int fontCount;
|
||||
|
||||
private:
|
||||
@@ -150,12 +193,6 @@ private:
|
||||
FONT_UNKNOWN
|
||||
};
|
||||
|
||||
struct GlyphVertex
|
||||
{
|
||||
float x, y;
|
||||
float s, t;
|
||||
};
|
||||
|
||||
struct Glyph
|
||||
{
|
||||
GLuint texture;
|
||||
@@ -163,38 +200,25 @@ private:
|
||||
GlyphVertex vertices[4];
|
||||
};
|
||||
|
||||
// used to determine when to change textures in the vertex array generated when printing text
|
||||
struct GlyphArrayDrawInfo
|
||||
struct TextureSize
|
||||
{
|
||||
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;
|
||||
}
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
bool initializeTexture(GLenum format);
|
||||
TextureSize getNextTextureSize() const;
|
||||
void createTexture();
|
||||
Glyph *addGlyph(uint32 glyph);
|
||||
Glyph *findGlyph(uint32 glyph);
|
||||
love::font::GlyphData *getRasterizerGlyphData(uint32 glyph);
|
||||
const Glyph &addGlyph(uint32 glyph);
|
||||
const Glyph &findGlyph(uint32 glyph);
|
||||
float getKerning(uint32 leftglyph, uint32 rightglyph);
|
||||
void printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
|
||||
|
||||
StrongRef<love::font::Rasterizer> rasterizer;
|
||||
|
||||
VertexIndex *indexBuffer;
|
||||
std::vector<StrongRef<love::font::Rasterizer>> rasterizers;
|
||||
|
||||
int height;
|
||||
float lineHeight;
|
||||
float mSpacing; // modifies the spacing by multiplying it with this value
|
||||
|
||||
int textureSizeIndex;
|
||||
int textureWidth;
|
||||
int textureHeight;
|
||||
|
||||
@@ -202,7 +226,10 @@ private:
|
||||
std::vector<GLuint> textures;
|
||||
|
||||
// maps glyphs to glyph texture information
|
||||
std::map<uint32, Glyph *> glyphs;
|
||||
std::unordered_map<uint32, Glyph> glyphs;
|
||||
|
||||
// map of left/right glyph pairs to horizontal kerning.
|
||||
std::unordered_map<uint64, float> kerning;
|
||||
|
||||
FontType type;
|
||||
Texture::Filter filter;
|
||||
@@ -212,17 +239,22 @@ private:
|
||||
|
||||
bool useSpacesAsTab;
|
||||
|
||||
size_t textureMemorySize;
|
||||
// Index buffer used for drawing quads with GL_TRIANGLES.
|
||||
QuadIndices quadIndices;
|
||||
|
||||
static const int NUM_TEXTURE_SIZES = 7;
|
||||
static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
|
||||
static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
|
||||
// ID which is incremented when the texture cache is invalidated.
|
||||
uint32 textureCacheID;
|
||||
|
||||
size_t textureMemorySize;
|
||||
|
||||
static const int TEXTURE_PADDING = 1;
|
||||
|
||||
// This will be used if the Rasterizer doesn't have a tab character itself.
|
||||
static const int SPACES_PER_TAB = 4;
|
||||
|
||||
static StringMap<AlignMode, ALIGN_MAX_ENUM>::Entry alignModeEntries[];
|
||||
static StringMap<AlignMode, ALIGN_MAX_ENUM> alignModes;
|
||||
|
||||
}; // Font
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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 "GLBuffer.h"
|
||||
|
||||
#include "common/Exception.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
GLBuffer::GLBuffer(size_t size, const void *data, GLenum target, GLenum usage)
|
||||
: is_bound(false)
|
||||
, is_mapped(false)
|
||||
, size(size)
|
||||
, target(target)
|
||||
, usage(usage)
|
||||
, vbo(0)
|
||||
, memory_map(nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
memory_map = new char[size];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
if (data != nullptr)
|
||||
memcpy(memory_map, data, size);
|
||||
|
||||
bool ok = load(data != nullptr);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
delete[] memory_map;
|
||||
throw love::Exception("Could not load VBO.");
|
||||
}
|
||||
}
|
||||
|
||||
GLBuffer::~GLBuffer()
|
||||
{
|
||||
if (vbo != 0)
|
||||
unload();
|
||||
|
||||
delete[] memory_map;
|
||||
}
|
||||
|
||||
void *GLBuffer::map()
|
||||
{
|
||||
if (is_mapped)
|
||||
return memory_map;
|
||||
|
||||
is_mapped = true;
|
||||
|
||||
return memory_map;
|
||||
}
|
||||
|
||||
void GLBuffer::unmapStatic(size_t offset, size_t size)
|
||||
{
|
||||
// Upload the mapped data to the buffer.
|
||||
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
|
||||
}
|
||||
|
||||
void GLBuffer::unmapStream()
|
||||
{
|
||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), nullptr, getUsage());
|
||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
||||
}
|
||||
|
||||
void GLBuffer::unmap(size_t usedOffset, size_t usedSize)
|
||||
{
|
||||
if (!is_mapped)
|
||||
return;
|
||||
|
||||
usedOffset = std::min(usedOffset, getSize());
|
||||
usedSize = std::min(usedSize, getSize() - usedOffset);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
switch (getUsage())
|
||||
{
|
||||
case GL_STATIC_DRAW:
|
||||
unmapStatic(usedOffset, usedSize);
|
||||
break;
|
||||
case GL_STREAM_DRAW:
|
||||
unmapStream();
|
||||
break;
|
||||
case GL_DYNAMIC_DRAW:
|
||||
default:
|
||||
// It's probably more efficient to treat it like a streaming buffer if
|
||||
// more than a third of its contents have been modified during the map().
|
||||
if (usedSize >= getSize() / 3)
|
||||
unmapStream();
|
||||
else
|
||||
unmapStatic(usedOffset, usedSize);
|
||||
break;
|
||||
}
|
||||
|
||||
is_mapped = false;
|
||||
}
|
||||
|
||||
void GLBuffer::bind()
|
||||
{
|
||||
if (!is_mapped)
|
||||
{
|
||||
glBindBuffer(getTarget(), vbo);
|
||||
is_bound = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GLBuffer::unbind()
|
||||
{
|
||||
if (is_bound)
|
||||
glBindBuffer(getTarget(), 0);
|
||||
|
||||
is_bound = false;
|
||||
}
|
||||
|
||||
void GLBuffer::fill(size_t offset, size_t size, const void *data)
|
||||
{
|
||||
memcpy(memory_map + offset, data, size);
|
||||
|
||||
if (!is_mapped)
|
||||
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
|
||||
}
|
||||
|
||||
const void *GLBuffer::getPointer(size_t offset) const
|
||||
{
|
||||
return BUFFER_OFFSET(offset);
|
||||
}
|
||||
|
||||
bool GLBuffer::loadVolatile()
|
||||
{
|
||||
return load(true);
|
||||
}
|
||||
|
||||
void GLBuffer::unloadVolatile()
|
||||
{
|
||||
unload();
|
||||
}
|
||||
|
||||
bool GLBuffer::load(bool restore)
|
||||
{
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
GLBuffer::Bind bind(*this);
|
||||
|
||||
// Copy the old buffer only if 'restore' was requested.
|
||||
const GLvoid *src = restore ? memory_map : nullptr;
|
||||
|
||||
// Note that if 'src' is '0', no data will be copied.
|
||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLBuffer::unload()
|
||||
{
|
||||
is_mapped = false;
|
||||
|
||||
glDeleteBuffers(1, &vbo);
|
||||
vbo = 0;
|
||||
}
|
||||
|
||||
|
||||
// QuadIndices
|
||||
|
||||
size_t QuadIndices::maxSize = 0;
|
||||
size_t QuadIndices::elementSize = 0;
|
||||
size_t QuadIndices::objectCount = 0;
|
||||
GLBuffer *QuadIndices::indexBuffer = nullptr;
|
||||
|
||||
QuadIndices::QuadIndices(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.
|
||||
if (size == 0 || size > ((GLuint) -1) / 6 / sizeof(GLuint))
|
||||
throw love::Exception("Invalid number of quads.");
|
||||
|
||||
// Create a new / larger buffer if needed.
|
||||
if (indexBuffer == nullptr || size > maxSize)
|
||||
{
|
||||
GLBuffer *newbuffer = nullptr;
|
||||
|
||||
// Depending on the size, a switch to int and more memory is needed.
|
||||
GLenum targettype = getType(size);
|
||||
size_t elemsize = (targettype == GL_UNSIGNED_SHORT) ? sizeof(GLushort) : sizeof(GLuint);
|
||||
|
||||
size_t buffersize = elemsize * 6 * size;
|
||||
|
||||
// Create may throw out-of-memory exceptions.
|
||||
// QuadIndices will propagate the exception and keep the old GLBuffer.
|
||||
try
|
||||
{
|
||||
newbuffer = new GLBuffer(buffersize, nullptr, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
// Allocation of the new GLBuffer succeeded.
|
||||
// The old GLBuffer can now be deleted.
|
||||
delete indexBuffer;
|
||||
indexBuffer = newbuffer;
|
||||
maxSize = size;
|
||||
elementSize = elemsize;
|
||||
|
||||
switch (targettype)
|
||||
{
|
||||
case GL_UNSIGNED_SHORT:
|
||||
fill<GLushort>();
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
fill<GLuint>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
objectCount++;
|
||||
}
|
||||
|
||||
QuadIndices::QuadIndices(const QuadIndices &other)
|
||||
: size(other.size)
|
||||
{
|
||||
objectCount++;
|
||||
}
|
||||
|
||||
QuadIndices &QuadIndices::operator = (const QuadIndices &other)
|
||||
{
|
||||
size = other.size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuadIndices::~QuadIndices()
|
||||
{
|
||||
--objectCount;
|
||||
|
||||
// Delete the buffer if we were the last living QuadIndices object.
|
||||
if (objectCount <= 0)
|
||||
{
|
||||
delete indexBuffer;
|
||||
indexBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
size_t QuadIndices::getSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t QuadIndices::getIndexCount(size_t elements) const
|
||||
{
|
||||
return elements * 6;
|
||||
}
|
||||
|
||||
GLenum QuadIndices::getType(size_t s) const
|
||||
{
|
||||
// Calculates if unsigned short is big enough to hold all the vertex indices.
|
||||
static const GLenum types[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT};
|
||||
return types[s * 4 > std::numeric_limits<GLushort>::max()];
|
||||
// if buffer-size > max(GLushort) then GL_UNSIGNED_INT else GL_UNSIGNED_SHORT
|
||||
}
|
||||
|
||||
size_t QuadIndices::getElementSize()
|
||||
{
|
||||
return elementSize;
|
||||
}
|
||||
|
||||
GLBuffer *QuadIndices::getBuffer() const
|
||||
{
|
||||
return indexBuffer;
|
||||
}
|
||||
|
||||
const void *QuadIndices::getPointer(size_t offset) const
|
||||
{
|
||||
return indexBuffer->getPointer(offset);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void QuadIndices::fill()
|
||||
{
|
||||
GLBuffer::Bind bind(*indexBuffer);
|
||||
GLBuffer::Mapper mapper(*indexBuffer);
|
||||
|
||||
T *indices = (T *) mapper.get();
|
||||
|
||||
// 0----2
|
||||
// | / |
|
||||
// | / |
|
||||
// 1----3
|
||||
for (size_t i = 0; i < maxSize; ++i)
|
||||
{
|
||||
indices[i*6+0] = T(i * 4 + 0);
|
||||
indices[i*6+1] = T(i * 4 + 1);
|
||||
indices[i*6+2] = T(i * 4 + 2);
|
||||
|
||||
indices[i*6+3] = T(i * 4 + 2);
|
||||
indices[i*6+4] = T(i * 4 + 1);
|
||||
indices[i*6+5] = T(i * 4 + 3);
|
||||
}
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,398 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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_GL_BUFFER_H
|
||||
#define LOVE_GRAPHICS_OPENGL_GL_BUFFER_H
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "graphics/Volatile.h"
|
||||
|
||||
// OpenGL
|
||||
#include "OpenGL.h"
|
||||
|
||||
// C
|
||||
#include <stddef.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
/**
|
||||
* GLBuffer is a thin abstraction over OpenGL's Buffer Objects.
|
||||
* The class is meant for internal use.
|
||||
*/
|
||||
class GLBuffer : public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param size The size of the GLBuffer in bytes.
|
||||
* @param target The target GLBuffer object, e.g. GL_ARRAY_BUFFER.
|
||||
* @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
|
||||
*/
|
||||
GLBuffer(size_t size, const void *data, GLenum target, GLenum usage);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~GLBuffer();
|
||||
|
||||
/**
|
||||
* Get the size of the GLBuffer, in bytes.
|
||||
*
|
||||
* @return The size of the GLBuffer.
|
||||
*/
|
||||
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 GLBuffer.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the GLBuffer to client memory.
|
||||
*
|
||||
* This can be faster for large changes to the buffer. For smaller
|
||||
* changes, see fill().
|
||||
*
|
||||
* The GLBuffer must be bound to use this function.
|
||||
*
|
||||
* @return A pointer to memory which represents the buffer.
|
||||
*/
|
||||
void *map();
|
||||
|
||||
/**
|
||||
* Unmap a previously mapped GLBuffer. The buffer must be unmapped
|
||||
* when used to draw elements.
|
||||
*
|
||||
* The GLBuffer must be bound to use this function.
|
||||
*
|
||||
* @param usedOffset The offset into the mapped buffer indicating the
|
||||
* sub-range of data modified. Optional.
|
||||
* @param usedSize The size of the sub-range of modified data. Optional.
|
||||
*/
|
||||
void unmap(size_t usedOffset = 0, size_t usedSize = -1);
|
||||
|
||||
/**
|
||||
* Bind the GLBuffer to its specified target.
|
||||
* (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc).
|
||||
*/
|
||||
void bind();
|
||||
|
||||
/**
|
||||
* Unbind a prevously bound GLBuffer.
|
||||
*/
|
||||
void unbind();
|
||||
|
||||
/**
|
||||
* Fill a portion of the buffer with data.
|
||||
*
|
||||
* The GLBuffer must be bound to use this function.
|
||||
*
|
||||
* @param offset The offset in the GLBuffer to store the data.
|
||||
* @param size The size of the incoming data.
|
||||
* @param data Pointer to memory to copy data from.
|
||||
*/
|
||||
void fill(size_t offset, size_t size, const void *data);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
const void *getPointer(size_t offset) const;
|
||||
|
||||
// Implements Volatile.
|
||||
bool loadVolatile() override;
|
||||
void unloadVolatile() override;
|
||||
|
||||
/**
|
||||
* This helper class can bind a GLBuffer temporarily, and
|
||||
* automatically un-bind when it's destroyed.
|
||||
*/
|
||||
class Bind
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Bind a GLBuffer.
|
||||
*/
|
||||
Bind(GLBuffer &buf)
|
||||
: buf(buf)
|
||||
{
|
||||
buf.bind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds a GLBuffer.
|
||||
*/
|
||||
~Bind()
|
||||
{
|
||||
buf.unbind();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// GLBuffer to work on.
|
||||
GLBuffer &buf;
|
||||
|
||||
}; // Bind
|
||||
|
||||
class Mapper
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Memory-maps a GLBuffer.
|
||||
*/
|
||||
Mapper(GLBuffer &buffer)
|
||||
: buf(buffer)
|
||||
{
|
||||
elems = buf.map();
|
||||
}
|
||||
|
||||
/**
|
||||
* unmaps the buffer
|
||||
*/
|
||||
~Mapper()
|
||||
{
|
||||
buf.unmap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pointer to memory mapped region
|
||||
*/
|
||||
void *get()
|
||||
{
|
||||
return elems;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
GLBuffer &buf;
|
||||
void *elems;
|
||||
|
||||
}; // Mapper
|
||||
|
||||
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);
|
||||
void unload();
|
||||
|
||||
void unmapStatic(size_t offset, size_t size);
|
||||
void unmapStream();
|
||||
|
||||
// Whether the buffer is currently bound.
|
||||
bool is_bound;
|
||||
|
||||
// Whether the buffer is currently mapped to main memory.
|
||||
bool is_mapped;
|
||||
|
||||
// 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;
|
||||
|
||||
// The VBO identifier. Assigned by OpenGL.
|
||||
GLuint vbo;
|
||||
|
||||
// A pointer to mapped memory.
|
||||
char *memory_map;
|
||||
|
||||
}; // GLBuffer
|
||||
|
||||
|
||||
/**
|
||||
* QuadIndices manages one shared GLBuffer 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 + 2;
|
||||
* indices[i*6 + 4] = i*4 + 1;
|
||||
* indices[i*6 + 5] = i*4 + 3;
|
||||
*
|
||||
* There will always be a large enough GLBuffer around until all
|
||||
* QuadIndices instances have been deleted.
|
||||
*
|
||||
* Q: Why have something like QuadIndices?
|
||||
* A: The indices for the SpriteBatch do not change, only the array size
|
||||
* varies. Using one GLBuffer for all element arrays removes this
|
||||
* duplicated data and saves some memory.
|
||||
*/
|
||||
class QuadIndices
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Adds an entry to the list of sizes and resizes the GLBuffer
|
||||
* 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.
|
||||
*/
|
||||
QuadIndices(size_t size);
|
||||
|
||||
QuadIndices(const QuadIndices &other);
|
||||
QuadIndices &operator = (const QuadIndices &other);
|
||||
|
||||
/**
|
||||
* Removes an entry from the list of sizes and resizes the GLBuffer
|
||||
* if needed.
|
||||
*/
|
||||
~QuadIndices();
|
||||
|
||||
/**
|
||||
* 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 QuadIndices::getSize to get the full index count for that
|
||||
* QuadIndices 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 GLBuffer.
|
||||
* The pointer will change if a new size request or removal causes
|
||||
* a GLBuffer resize. It is recommended to retrieve the pointer
|
||||
* value directly before the drawing call.
|
||||
*
|
||||
* @return The pointer to the GLBuffer.
|
||||
*/
|
||||
GLBuffer *getBuffer() 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 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 GLBuffer size. 0 means no GLBuffer.
|
||||
static size_t maxSize;
|
||||
|
||||
static size_t objectCount;
|
||||
|
||||
// The GLBuffer for the element array. Can be null.
|
||||
static GLBuffer *indexBuffer;
|
||||
};
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_GL_BUFFER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,11 +40,13 @@
|
||||
#include "Font.h"
|
||||
#include "Image.h"
|
||||
#include "graphics/Quad.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "SpriteBatch.h"
|
||||
#include "ParticleSystem.h"
|
||||
#include "Canvas.h"
|
||||
#include "Shader.h"
|
||||
#include "Mesh.h"
|
||||
#include "Text.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -53,11 +55,6 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
// During display mode changing, certain
|
||||
// variables about the OpenGL context are
|
||||
// lost.
|
||||
|
||||
|
||||
class Graphics : public love::graphics::Graphics
|
||||
{
|
||||
public:
|
||||
@@ -85,9 +82,19 @@ public:
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Clears the screen.
|
||||
* Clears the screen to a specific color.
|
||||
**/
|
||||
void clear();
|
||||
void clear(Color c);
|
||||
|
||||
/**
|
||||
* Clears each active canvas to a different color.
|
||||
**/
|
||||
void clear(const std::vector<Color> &colors);
|
||||
|
||||
/**
|
||||
* Discards the contents of the screen.
|
||||
**/
|
||||
void discard(const std::vector<bool> &colorbuffers, bool stencil);
|
||||
|
||||
/**
|
||||
* Flips buffers. (Rendered geometry is presented on screen).
|
||||
@@ -131,28 +138,27 @@ public:
|
||||
bool getScissor(int &x, int &y, int &width, int &height) const;
|
||||
|
||||
/**
|
||||
* Enables the stencil buffer and set stencil function to fill it
|
||||
*/
|
||||
void defineStencil();
|
||||
* Enables or disables drawing to the stencil buffer. When enabled, the
|
||||
* color buffer is disabled.
|
||||
**/
|
||||
void drawToStencilBuffer(bool enable);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
* Sets whether stencil testing is enabled.
|
||||
**/
|
||||
void setStencilTest(bool enable, bool invert);
|
||||
void getStencilTest(bool &enable, bool &invert);
|
||||
|
||||
/**
|
||||
* Disables the stencil buffer
|
||||
*/
|
||||
void discardStencil();
|
||||
* Clear the stencil buffer in the active Canvas(es.)
|
||||
**/
|
||||
void clearStencil();
|
||||
|
||||
/**
|
||||
* Creates an Image object with padding and/or optimization.
|
||||
**/
|
||||
Image *newImage(love::image::ImageData *data, Image::Format format = Image::FORMAT_NORMAL);
|
||||
Image *newImage(love::image::CompressedData *cdata, Image::Format format = Image::FORMAT_NORMAL);
|
||||
Image *newImage(love::image::ImageData *data, const Image::Flags &flags);
|
||||
Image *newImage(love::image::CompressedImageData *cdata, const Image::Flags &flags);
|
||||
|
||||
Quad *newQuad(Quad::Viewport v, float sw, float sh);
|
||||
|
||||
@@ -161,7 +167,7 @@ public:
|
||||
**/
|
||||
Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::getDefaultFilter());
|
||||
|
||||
SpriteBatch *newSpriteBatch(Texture *texture, int size, int usage);
|
||||
SpriteBatch *newSpriteBatch(Texture *texture, int size, Mesh::Usage usage);
|
||||
|
||||
ParticleSystem *newParticleSystem(Texture *texture, int size);
|
||||
|
||||
@@ -169,14 +175,19 @@ public:
|
||||
|
||||
Shader *newShader(const Shader::ShaderSource &source);
|
||||
|
||||
Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
|
||||
Mesh *newMesh(int vertexcount, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
|
||||
Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, Mesh::Usage usage);
|
||||
Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage);
|
||||
|
||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage);
|
||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, Mesh::Usage usage);
|
||||
|
||||
Text *newText(Font *font, const std::string &text = "");
|
||||
|
||||
/**
|
||||
* Sets the foreground color.
|
||||
* @param c The new foreground color.
|
||||
**/
|
||||
void setColor(const Color &c);
|
||||
void setColor(Color c);
|
||||
|
||||
/**
|
||||
* Gets current color.
|
||||
@@ -186,7 +197,7 @@ public:
|
||||
/**
|
||||
* Sets the background Color.
|
||||
**/
|
||||
void setBackgroundColor(const Color &c);
|
||||
void setBackgroundColor(Color c);
|
||||
|
||||
/**
|
||||
* Gets the current background color.
|
||||
@@ -221,12 +232,12 @@ public:
|
||||
/**
|
||||
* Sets the current blend mode.
|
||||
**/
|
||||
void setBlendMode(BlendMode mode);
|
||||
void setBlendMode(BlendMode mode, bool multiplyalpha);
|
||||
|
||||
/**
|
||||
* Gets the current blend mode.
|
||||
**/
|
||||
BlendMode getBlendMode() const;
|
||||
BlendMode getBlendMode(bool &multiplyalpha) const;
|
||||
|
||||
/**
|
||||
* Sets the default filter for images, canvases, and fonts.
|
||||
@@ -282,22 +293,11 @@ public:
|
||||
**/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Sets whether graphics will be drawn as wireframe lines instead of filled
|
||||
* triangles (has no effect for drawn points.)
|
||||
@@ -342,7 +342,7 @@ public:
|
||||
* @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);
|
||||
void printf(const std::string &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
/**
|
||||
* Draws a point at (x,y).
|
||||
@@ -367,6 +367,19 @@ public:
|
||||
**/
|
||||
void rectangle(DrawMode mode, float x, float y, float w, float h);
|
||||
|
||||
/**
|
||||
* Variant of rectangle that draws a rounded rectangle.
|
||||
* @param mode The mode of drawing (line/filled).
|
||||
* @param x X-coordinate of top-left corner
|
||||
* @param y Y-coordinate of top-left corner
|
||||
* @param w The width of the rectangle.
|
||||
* @param h The height of the rectangle.
|
||||
* @param rx The radius of the corners on the x axis
|
||||
* @param ry The radius of the corners on the y axis
|
||||
* @param points The number of points to use per corner
|
||||
**/
|
||||
void rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry, int points = 10);
|
||||
|
||||
/**
|
||||
* Draws a circle using the specified arguments.
|
||||
* @param mode The mode of drawing (line/filled).
|
||||
@@ -377,6 +390,17 @@ public:
|
||||
**/
|
||||
void circle(DrawMode mode, float x, float y, float radius, int points = 10);
|
||||
|
||||
/**
|
||||
* Draws an ellipse using the specified arguments.
|
||||
* @param mode The mode of drawing (line/filled).
|
||||
* @param x X-coordinate of center
|
||||
* @param y Y-coordinate of center
|
||||
* @param a Radius in x-direction
|
||||
* @param b Radius in y-direction
|
||||
* @param points Number of points to use to draw the circle.
|
||||
**/
|
||||
void ellipse(DrawMode mode, float x, float y, float a, float b, int points = 10);
|
||||
|
||||
/**
|
||||
* Draws an arc using the specified arguments.
|
||||
* @param mode The mode of drawing (line/filled).
|
||||
@@ -443,17 +467,21 @@ private:
|
||||
Color backgroundColor;
|
||||
|
||||
BlendMode blendMode;
|
||||
bool blendMultiplyAlpha;
|
||||
|
||||
float lineWidth;
|
||||
LineStyle lineStyle;
|
||||
LineJoin lineJoin;
|
||||
|
||||
float pointSize;
|
||||
PointStyle pointStyle;
|
||||
|
||||
bool scissor;
|
||||
OpenGL::Viewport scissorBox;
|
||||
|
||||
// Stencil.
|
||||
bool stencilTest;
|
||||
bool stencilInvert;
|
||||
|
||||
StrongRef<Font> font;
|
||||
StrongRef<Shader> shader;
|
||||
|
||||
@@ -484,14 +512,16 @@ private:
|
||||
|
||||
StrongRef<Font> defaultFont;
|
||||
|
||||
std::vector<double> pixel_size_stack; // stores current size of a pixel (needed for line drawing)
|
||||
std::vector<double> pixelSizeStack; // stores current size of a pixel (needed for line drawing)
|
||||
|
||||
QuadIndices *quadIndices;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
bool created;
|
||||
bool active;
|
||||
|
||||
bool activeStencil;
|
||||
bool writingToStencil;
|
||||
|
||||
std::vector<DisplayState> states;
|
||||
std::vector<StackType> stackTypes; // Keeps track of the pushed stack types.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,8 +28,9 @@
|
||||
#include "common/StringMap.h"
|
||||
#include "common/math.h"
|
||||
#include "image/ImageData.h"
|
||||
#include "image/CompressedData.h"
|
||||
#include "Texture.h"
|
||||
#include "image/CompressedImageData.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/Volatile.h"
|
||||
|
||||
// OpenGL
|
||||
#include "OpenGL.h"
|
||||
@@ -47,15 +48,21 @@ namespace opengl
|
||||
*
|
||||
* @author Anders Ruud
|
||||
**/
|
||||
class Image : public Texture
|
||||
class Image : public Texture, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
enum Format
|
||||
enum FlagType
|
||||
{
|
||||
FORMAT_NORMAL,
|
||||
FORMAT_SRGB,
|
||||
FORMAT_MAX_ENUM
|
||||
FLAG_TYPE_MIPMAPS,
|
||||
FLAG_TYPE_SRGB,
|
||||
FLAG_TYPE_MAX_ENUM
|
||||
};
|
||||
|
||||
struct Flags
|
||||
{
|
||||
bool mipmaps = false;
|
||||
bool sRGB = false;;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -64,22 +71,20 @@ public:
|
||||
*
|
||||
* @param data The data from which to load the image.
|
||||
**/
|
||||
Image(love::image::ImageData *data, Format format = FORMAT_NORMAL);
|
||||
Image(love::image::ImageData *data, const Flags &flags);
|
||||
|
||||
/**
|
||||
* Creates a new Image with compressed image data.
|
||||
*
|
||||
* @param cdata The compressed data from which to load the image.
|
||||
**/
|
||||
Image(love::image::CompressedData *cdata, Format format = FORMAT_NORMAL);
|
||||
Image(love::image::CompressedImageData *cdata, const Flags &flags);
|
||||
|
||||
/**
|
||||
* Destructor. Deletes the hardware texture and other resources.
|
||||
**/
|
||||
virtual ~Image();
|
||||
|
||||
love::image::ImageData *getImageData() const;
|
||||
love::image::CompressedData *getCompressedData() const;
|
||||
// Implements Volatile.
|
||||
bool loadVolatile();
|
||||
void unloadVolatile();
|
||||
|
||||
/**
|
||||
* @copydoc Drawable::draw()
|
||||
@@ -91,15 +96,10 @@ public:
|
||||
**/
|
||||
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
virtual void predraw();
|
||||
virtual void postdraw();
|
||||
virtual const void *getHandle() const;
|
||||
|
||||
virtual GLuint getGLTexture() const;
|
||||
love::image::ImageData *getImageData() const;
|
||||
love::image::CompressedImageData *getCompressedData() const;
|
||||
|
||||
virtual void setFilter(const Texture::Filter &f);
|
||||
virtual bool setWrap(const Texture::Wrap &w);
|
||||
@@ -108,52 +108,44 @@ public:
|
||||
float getMipmapSharpness() const;
|
||||
|
||||
/**
|
||||
* Whether this Image is using a compressed texture (via CompressedData).
|
||||
* Whether this Image is using a compressed texture (via CompressedImageData).
|
||||
**/
|
||||
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().
|
||||
* Re-uploads the ImageData or CompressedImageData associated with this Image to
|
||||
* the GPU.
|
||||
**/
|
||||
bool refresh();
|
||||
bool refresh(int xoffset, int yoffset, int w, int h);
|
||||
|
||||
Format getFormat() const;
|
||||
const Flags &getFlags() const;
|
||||
|
||||
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);
|
||||
|
||||
static bool hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB);
|
||||
static bool hasSRGBSupport();
|
||||
|
||||
static bool getConstant(const char *in, Format &out);
|
||||
static bool getConstant(Format in, const char *&out);
|
||||
static bool getConstant(const char *in, FlagType &out);
|
||||
static bool getConstant(FlagType in, const char *&out);
|
||||
|
||||
static int imageCount;
|
||||
|
||||
private:
|
||||
|
||||
void uploadDefaultTexture();
|
||||
void drawv(const Matrix4 &t, const Vertex *v);
|
||||
|
||||
void drawv(const Matrix &t, const Vertex *v);
|
||||
void preload();
|
||||
|
||||
void generateMipmaps();
|
||||
void loadDefaultTexture();
|
||||
void loadFromCompressedData();
|
||||
void loadFromImageData();
|
||||
|
||||
GLenum getCompressedFormat(image::CompressedImageData::Format cformat) const;
|
||||
|
||||
// The ImageData from which the texture is created. May be null if
|
||||
// Compressed image data was used to create the texture.
|
||||
@@ -161,10 +153,7 @@ private:
|
||||
|
||||
// Or the Compressed Image Data from which the texture is created. May be
|
||||
// null if raw ImageData was used to create the texture.
|
||||
StrongRef<love::image::CompressedData> cdata;
|
||||
|
||||
// Real dimensions of the texture, if it was auto-padded to POT size.
|
||||
int paddedWidth, paddedHeight;
|
||||
StrongRef<love::image::CompressedImageData> cdata;
|
||||
|
||||
// OpenGL texture identifier.
|
||||
GLuint texture;
|
||||
@@ -172,14 +161,11 @@ private:
|
||||
// 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;
|
||||
|
||||
// The format to interpret the image's data as.
|
||||
Format format;
|
||||
// The flags used to initialize this Image.
|
||||
Flags flags;
|
||||
|
||||
// True if the image wasn't able to be properly created and it had to fall
|
||||
// back to a default texture.
|
||||
@@ -187,24 +173,13 @@ private:
|
||||
|
||||
size_t textureMemorySize;
|
||||
|
||||
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 cformat) const;
|
||||
|
||||
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
|
||||
static StringMap<Format, FORMAT_MAX_ENUM> formats;
|
||||
static StringMap<FlagType, FLAG_TYPE_MAX_ENUM>::Entry flagNameEntries[];
|
||||
static StringMap<FlagType, FLAG_TYPE_MAX_ENUM> flagNames;
|
||||
|
||||
}; // Image
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
#include "Mesh.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "common/Exception.h"
|
||||
#include "Shader.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -33,130 +35,373 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
|
||||
: vbo(nullptr)
|
||||
, vertex_count(0)
|
||||
, ibo(nullptr)
|
||||
, element_count(0)
|
||||
, element_data_type(getGLDataTypeFromMax(verts.size()))
|
||||
, draw_mode(mode)
|
||||
, range_min(-1)
|
||||
, range_max(-1)
|
||||
, texture(nullptr)
|
||||
, colors_enabled(false)
|
||||
static const char *getBuiltinAttribName(VertexAttribID attribid)
|
||||
{
|
||||
setVertices(verts);
|
||||
const char *name = "";
|
||||
Shader::getConstant(attribid, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
Mesh::Mesh(int vertexcount, Mesh::DrawMode mode)
|
||||
: vbo(nullptr)
|
||||
, vertex_count(0)
|
||||
, ibo(nullptr)
|
||||
, element_count(0)
|
||||
, element_data_type(getGLDataTypeFromMax(vertexcount))
|
||||
, draw_mode(mode)
|
||||
, range_min(-1)
|
||||
, range_max(-1)
|
||||
, texture(nullptr)
|
||||
, colors_enabled(false)
|
||||
static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct");
|
||||
static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct");
|
||||
static_assert(offsetof(Vertex, r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
|
||||
|
||||
static std::vector<Mesh::AttribFormat> getDefaultVertexFormat()
|
||||
{
|
||||
if (vertexcount < 1)
|
||||
throw love::Exception("Invalid number of vertices.");
|
||||
// Corresponds to the love::Vertex struct.
|
||||
std::vector<Mesh::AttribFormat> vertexformat = {
|
||||
{getBuiltinAttribName(ATTRIB_POS), Mesh::DATA_FLOAT, 2},
|
||||
{getBuiltinAttribName(ATTRIB_TEXCOORD), Mesh::DATA_FLOAT, 2},
|
||||
{getBuiltinAttribName(ATTRIB_COLOR), Mesh::DATA_BYTE, 4},
|
||||
};
|
||||
|
||||
std::vector<Vertex> verts(vertexcount);
|
||||
return vertexformat;
|
||||
}
|
||||
|
||||
// Default-initialized vertices should have a white opaque color.
|
||||
for (size_t i = 0; i < verts.size(); i++)
|
||||
{
|
||||
verts[i].r = 255;
|
||||
verts[i].g = 255;
|
||||
verts[i].b = 255;
|
||||
verts[i].a = 255;
|
||||
}
|
||||
Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, Usage usage)
|
||||
: vertexFormat(vertexformat)
|
||||
, vbo(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, vboUsedOffset(0)
|
||||
, vboUsedSize(0)
|
||||
, ibo(nullptr)
|
||||
, elementCount(0)
|
||||
, elementDataType(0)
|
||||
, drawMode(drawmode)
|
||||
, rangeMin(-1)
|
||||
, rangeMax(-1)
|
||||
{
|
||||
setupAttachedAttributes();
|
||||
calculateAttributeSizes();
|
||||
|
||||
setVertices(verts);
|
||||
vertexCount = datasize / vertexStride;
|
||||
elementDataType = getGLDataTypeFromMax(vertexCount);
|
||||
|
||||
if (vertexCount == 0)
|
||||
throw love::Exception("Data size is too small for specified vertex attribute formats.");
|
||||
|
||||
vbo = new GLBuffer(datasize, data, GL_ARRAY_BUFFER, getGLBufferUsage(usage));
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, Usage usage)
|
||||
: vertexFormat(vertexformat)
|
||||
, vbo(nullptr)
|
||||
, vertexCount((size_t) vertexcount)
|
||||
, vertexStride(0)
|
||||
, vboUsedOffset(0)
|
||||
, vboUsedSize(0)
|
||||
, ibo(nullptr)
|
||||
, elementCount(0)
|
||||
, elementDataType(getGLDataTypeFromMax(vertexcount))
|
||||
, drawMode(drawmode)
|
||||
, rangeMin(-1)
|
||||
, rangeMax(-1)
|
||||
{
|
||||
if (vertexcount <= 0)
|
||||
throw love::Exception("Invalid number of vertices (%d).", vertexcount);
|
||||
|
||||
setupAttachedAttributes();
|
||||
calculateAttributeSizes();
|
||||
|
||||
size_t buffersize = vertexCount * vertexStride;
|
||||
|
||||
vbo = new GLBuffer(buffersize, nullptr, GL_ARRAY_BUFFER, getGLBufferUsage(usage));
|
||||
|
||||
// Initialize the buffer's contents to 0.
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
memset(vbo->map(), 0, buffersize);
|
||||
vbo->unmap();
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::Mesh(const std::vector<Vertex> &vertices, DrawMode drawmode, Usage usage)
|
||||
: Mesh(getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage)
|
||||
{
|
||||
}
|
||||
|
||||
Mesh::Mesh(int vertexcount, DrawMode drawmode, Usage usage)
|
||||
: Mesh(getDefaultVertexFormat(), vertexcount, drawmode, usage)
|
||||
{
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
delete vbo;
|
||||
delete ibo;
|
||||
delete vertexScratchBuffer;
|
||||
|
||||
for (const auto &attrib : attachedAttributes)
|
||||
{
|
||||
if (attrib.second.mesh != this)
|
||||
attrib.second.mesh->release();
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setVertices(const std::vector<Vertex> &verts)
|
||||
void Mesh::setupAttachedAttributes()
|
||||
{
|
||||
if (verts.size() == 0)
|
||||
throw love::Exception("At least one vertex is required.");
|
||||
|
||||
size_t size = sizeof(Vertex) * verts.size();
|
||||
|
||||
if (vbo && size > vbo->getSize())
|
||||
for (size_t i = 0; i < vertexFormat.size(); i++)
|
||||
{
|
||||
delete vbo;
|
||||
vbo = nullptr;
|
||||
const std::string &name = vertexFormat[i].name;
|
||||
|
||||
if (attachedAttributes.find(name) != attachedAttributes.end())
|
||||
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
||||
|
||||
attachedAttributes[name] = {this, i, true};
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::calculateAttributeSizes()
|
||||
{
|
||||
size_t stride = 0;
|
||||
|
||||
for (const AttribFormat &format : vertexFormat)
|
||||
{
|
||||
// Hardware really doesn't like attributes that aren't 32 bit-aligned.
|
||||
if (format.type == DATA_BYTE && format.components != 4)
|
||||
throw love::Exception("byte vertex attributes must have 4 components.");
|
||||
|
||||
if (format.components <= 0 || format.components > 4)
|
||||
throw love::Exception("Vertex attributes must have between 1 and 4 components.");
|
||||
|
||||
// Total size in bytes of each attribute in a single vertex.
|
||||
attributeSizes.push_back(getAttribFormatSize(format));
|
||||
stride += attributeSizes.back();
|
||||
}
|
||||
|
||||
if (!vbo)
|
||||
vertexStride = stride;
|
||||
}
|
||||
|
||||
size_t Mesh::getAttributeOffset(size_t attribindex) const
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
for (size_t i = 0; i < attribindex; i++)
|
||||
offset += attributeSizes[i];
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
uint8 *bufferdata = (uint8 *) vbo->map();
|
||||
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vboUsedOffset = std::min(vboUsedOffset, offset);
|
||||
vboUsedSize = std::max(vboUsedSize, (offset + size) - vboUsedOffset);
|
||||
}
|
||||
|
||||
size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
// We're relying on vbo->map() returning read/write data... ew.
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
const uint8 *bufferdata = (const uint8 *) vbo->map();
|
||||
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
if (vboUsedSize == 0)
|
||||
{
|
||||
// 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);
|
||||
vboUsedOffset = std::min(vboUsedOffset, offset);
|
||||
vboUsedSize = std::max(vboUsedSize, (offset + size) - vboUsedOffset);
|
||||
}
|
||||
|
||||
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);
|
||||
return size;
|
||||
}
|
||||
|
||||
const Vertex *Mesh::getVertices() const
|
||||
void *Mesh::getVertexScratchBuffer()
|
||||
{
|
||||
if (vbo)
|
||||
return vertexScratchBuffer;
|
||||
}
|
||||
|
||||
void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
|
||||
size_t size = std::min(datasize, attributeSizes[attribindex]);
|
||||
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
uint8 *bufferdata = (uint8 *) vbo->map();
|
||||
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vboUsedOffset = std::min(vboUsedOffset, offset);
|
||||
vboUsedSize = std::max(vboUsedSize, (offset + size) - vboUsedOffset);
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
|
||||
size_t size = std::min(datasize, attributeSizes[attribindex]);
|
||||
|
||||
// We're relying on vbo->map() returning read/write data... ew.
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
const uint8 *bufferdata = (const uint8 *) vbo->map();
|
||||
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
if (vboUsedSize == 0)
|
||||
{
|
||||
VertexBuffer::Bind vbo_bind(*vbo);
|
||||
return (Vertex *) vbo->map();
|
||||
vboUsedOffset = std::min(vboUsedOffset, offset);
|
||||
vboUsedSize = std::max(vboUsedSize, (offset + size) - vboUsedOffset);
|
||||
}
|
||||
|
||||
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];
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexCount() const
|
||||
{
|
||||
return vertex_count;
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexStride() const
|
||||
{
|
||||
return vertexStride;
|
||||
}
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
|
||||
{
|
||||
return vertexFormat;
|
||||
}
|
||||
|
||||
Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
|
||||
{
|
||||
if (attribindex < 0 || attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
DataType type = vertexFormat[attribindex].type;
|
||||
components = vertexFormat[attribindex].components;
|
||||
return type;
|
||||
}
|
||||
|
||||
void Mesh::setAttributeEnabled(const std::string &name, bool enable)
|
||||
{
|
||||
auto it = attachedAttributes.find(name);
|
||||
|
||||
if (it == attachedAttributes.end())
|
||||
throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
|
||||
|
||||
it->second.enabled = enable;
|
||||
}
|
||||
|
||||
bool Mesh::isAttributeEnabled(const std::string &name) const
|
||||
{
|
||||
const auto it = attachedAttributes.find(name);
|
||||
|
||||
if (it == attachedAttributes.end())
|
||||
throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str());
|
||||
|
||||
return it->second.enabled;
|
||||
}
|
||||
|
||||
void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
|
||||
{
|
||||
if (mesh != this)
|
||||
{
|
||||
for (const auto &it : mesh->attachedAttributes)
|
||||
{
|
||||
// If the supplied Mesh has attached attributes of its own, then we
|
||||
// prevent it from being attached to avoid reference cycles.
|
||||
if (it.second.mesh != mesh)
|
||||
throw love::Exception("Cannot attach a Mesh which has attached Meshes of its own.");
|
||||
}
|
||||
}
|
||||
|
||||
AttachedAttribute oldattrib = {};
|
||||
AttachedAttribute newattrib = {};
|
||||
|
||||
auto it = attachedAttributes.find(name);
|
||||
if (it != attachedAttributes.end())
|
||||
oldattrib = it->second;
|
||||
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.index = std::numeric_limits<size_t>::max();
|
||||
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
||||
|
||||
// Find the index of the attribute in the mesh.
|
||||
for (size_t i = 0; i < mesh->vertexFormat.size(); i++)
|
||||
{
|
||||
if (mesh->vertexFormat[i].name == name)
|
||||
{
|
||||
newattrib.index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newattrib.index == std::numeric_limits<size_t>::max())
|
||||
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
|
||||
|
||||
if (newattrib.mesh != this)
|
||||
newattrib.mesh->retain();
|
||||
|
||||
attachedAttributes[name] = newattrib;
|
||||
|
||||
if (oldattrib.mesh && oldattrib.mesh != this)
|
||||
oldattrib.mesh->release();
|
||||
}
|
||||
|
||||
void *Mesh::mapVertexData()
|
||||
{
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
return vbo->map();
|
||||
}
|
||||
|
||||
void Mesh::unmapVertexData()
|
||||
{
|
||||
// Assume the whole buffer was modified.
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
vbo->unmap();
|
||||
|
||||
vboUsedOffset = vboUsedSize = 0;
|
||||
}
|
||||
|
||||
void Mesh::flush()
|
||||
{
|
||||
{
|
||||
GLBuffer::Bind vbobind(*vbo);
|
||||
vbo->unmap(vboUsedOffset, vboUsedSize);
|
||||
vboUsedOffset = vboUsedSize = 0;
|
||||
}
|
||||
|
||||
if (ibo != nullptr)
|
||||
{
|
||||
GLBuffer::Bind ibobind(*ibo);
|
||||
ibo->unmap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies index data from a vector to a mapped index buffer.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, VertexBuffer::Mapper &buffermap, size_t maxval)
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, GLBuffer::Mapper &buffermap, size_t maxval)
|
||||
{
|
||||
T *elems = (T *) buffermap.get();
|
||||
|
||||
@@ -171,7 +416,9 @@ static void copyToIndexBuffer(const std::vector<uint32> &indices, VertexBuffer::
|
||||
|
||||
void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
{
|
||||
GLenum datatype = getGLDataTypeFromMax(vertex_count);
|
||||
size_t maxval = getVertexCount();
|
||||
|
||||
GLenum datatype = getGLDataTypeFromMax(maxval);
|
||||
|
||||
// Calculate the size in bytes of the index buffer data.
|
||||
size_t size = map.size() * getGLDataTypeSize(datatype);
|
||||
@@ -183,35 +430,29 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
ibo = new GLBuffer(size, nullptr, GL_ELEMENT_ARRAY_BUFFER, vbo->getUsage());
|
||||
|
||||
element_count = map.size();
|
||||
elementCount = map.size();
|
||||
|
||||
if (!ibo || element_count == 0)
|
||||
if (!ibo || elementCount == 0)
|
||||
return;
|
||||
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
VertexBuffer::Mapper ibo_map(*ibo);
|
||||
GLBuffer::Bind ibobind(*ibo);
|
||||
GLBuffer::Mapper ibomap(*ibo);
|
||||
|
||||
// Fill the buffer with the index values from the vector.
|
||||
switch (datatype)
|
||||
{
|
||||
case GL_UNSIGNED_BYTE:
|
||||
copyToIndexBuffer<uint8>(map, ibo_map, vertex_count);
|
||||
break;
|
||||
case GL_UNSIGNED_SHORT:
|
||||
copyToIndexBuffer<uint16>(map, ibo_map, vertex_count);
|
||||
copyToIndexBuffer<uint16>(map, ibomap, maxval);
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
default:
|
||||
copyToIndexBuffer<uint32>(map, ibo_map, vertex_count);
|
||||
copyToIndexBuffer<uint32>(map, ibomap, maxval);
|
||||
break;
|
||||
}
|
||||
|
||||
element_data_type = datatype;
|
||||
elementDataType = datatype;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,36 +468,33 @@ static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32>
|
||||
|
||||
void Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
{
|
||||
if (!ibo || element_count == 0)
|
||||
if (!ibo || elementCount == 0)
|
||||
return;
|
||||
|
||||
map.clear();
|
||||
map.reserve(element_count);
|
||||
map.reserve(elementCount);
|
||||
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
GLBuffer::Bind ibobind(*ibo);
|
||||
|
||||
// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
|
||||
// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
|
||||
void *buffer = ibo->map();
|
||||
|
||||
// Fill the vector from the buffer.
|
||||
switch (element_data_type)
|
||||
switch (elementDataType)
|
||||
{
|
||||
case GL_UNSIGNED_BYTE:
|
||||
copyFromIndexBuffer<uint8>(buffer, element_count, map);
|
||||
break;
|
||||
case GL_UNSIGNED_SHORT:
|
||||
copyFromIndexBuffer<uint16>(buffer, element_count, map);
|
||||
copyFromIndexBuffer<uint16>(buffer, elementCount, map);
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
default:
|
||||
copyFromIndexBuffer<uint32>(buffer, element_count, map);
|
||||
copyFromIndexBuffer<uint32>(buffer, elementCount, map);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexMapCount() const
|
||||
{
|
||||
return element_count;
|
||||
return elementCount;
|
||||
}
|
||||
|
||||
void Mesh::setTexture(Texture *tex)
|
||||
@@ -274,14 +512,14 @@ Texture *Mesh::getTexture() const
|
||||
return texture.get();
|
||||
}
|
||||
|
||||
void Mesh::setDrawMode(Mesh::DrawMode mode)
|
||||
void Mesh::setDrawMode(DrawMode mode)
|
||||
{
|
||||
draw_mode = mode;
|
||||
drawMode = mode;
|
||||
}
|
||||
|
||||
Mesh::DrawMode Mesh::getDrawMode() const
|
||||
{
|
||||
return draw_mode;
|
||||
return drawMode;
|
||||
}
|
||||
|
||||
void Mesh::setDrawRange(int min, int max)
|
||||
@@ -289,139 +527,165 @@ void Mesh::setDrawRange(int min, int max)
|
||||
if (min < 0 || max < 0 || min > max)
|
||||
throw love::Exception("Invalid draw range.");
|
||||
|
||||
range_min = min;
|
||||
range_max = max;
|
||||
rangeMin = min;
|
||||
rangeMax = max;
|
||||
}
|
||||
|
||||
void Mesh::setDrawRange()
|
||||
{
|
||||
range_min = range_max = -1;
|
||||
rangeMin = rangeMax = -1;
|
||||
}
|
||||
|
||||
void Mesh::getDrawRange(int &min, int &max) const
|
||||
{
|
||||
min = range_min;
|
||||
max = range_max;
|
||||
}
|
||||
|
||||
void Mesh::setVertexColors(bool enable)
|
||||
{
|
||||
colors_enabled = enable;
|
||||
}
|
||||
|
||||
bool Mesh::hasVertexColors() const
|
||||
{
|
||||
return colors_enabled;
|
||||
min = rangeMin;
|
||||
max = rangeMax;
|
||||
}
|
||||
|
||||
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
const size_t pos_offset = offsetof(Vertex, x);
|
||||
const size_t tex_offset = offsetof(Vertex, s);
|
||||
const size_t color_offset = offsetof(Vertex, r);
|
||||
OpenGL::TempDebugGroup debuggroup("Mesh draw");
|
||||
|
||||
if (vertex_count == 0)
|
||||
return;
|
||||
uint32 enabledattribs = 0;
|
||||
|
||||
for (const auto &attrib : attachedAttributes)
|
||||
{
|
||||
if (!attrib.second.enabled)
|
||||
continue;
|
||||
|
||||
Mesh *mesh = attrib.second.mesh;
|
||||
const AttribFormat &format = mesh->vertexFormat[attrib.second.index];
|
||||
|
||||
GLint attriblocation = -1;
|
||||
|
||||
// If the attribute is one of the LOVE-defined ones, use the constant
|
||||
// attribute index for it, otherwise query the index from the shader.
|
||||
VertexAttribID builtinattrib;
|
||||
if (Shader::getConstant(format.name.c_str(), builtinattrib))
|
||||
attriblocation = (GLint) builtinattrib;
|
||||
else if (Shader::current)
|
||||
attriblocation = Shader::current->getAttribLocation(format.name);
|
||||
|
||||
// The active shader might not use this vertex attribute name.
|
||||
if (attriblocation < 0)
|
||||
continue;
|
||||
|
||||
// Needed for unmap and glVertexAttribPointer.
|
||||
GLBuffer::Bind vbobind(*mesh->vbo);
|
||||
|
||||
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
||||
mesh->vbo->unmap(mesh->vboUsedOffset, mesh->vboUsedSize);
|
||||
mesh->vboUsedOffset = mesh->vboUsedSize = 0;
|
||||
|
||||
size_t offset = mesh->getAttributeOffset(attrib.second.index);
|
||||
const void *gloffset = mesh->vbo->getPointer(offset);
|
||||
GLenum datatype = getGLDataType(format.type);
|
||||
GLboolean normalized = (datatype == GL_UNSIGNED_BYTE);
|
||||
|
||||
glVertexAttribPointer(attriblocation, format.components, datatype, normalized, mesh->vertexStride, gloffset);
|
||||
|
||||
enabledattribs |= 1 << uint32(attriblocation);
|
||||
}
|
||||
|
||||
if (!(enabledattribs & ATTRIBFLAG_POS))
|
||||
{
|
||||
// Not supported on all platforms or GL versions at least, I believe.
|
||||
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
|
||||
}
|
||||
|
||||
gl.useVertexAttribArrays(enabledattribs);
|
||||
|
||||
if (texture.get())
|
||||
texture->predraw();
|
||||
gl.bindTexture(*(GLuint *) texture->getHandle());
|
||||
else
|
||||
gl.bindTexture(gl.getDefaultTexture());
|
||||
|
||||
Matrix m;
|
||||
m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
OpenGL::TempTransform transform(gl);
|
||||
transform.get() *= 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));
|
||||
}
|
||||
|
||||
GLenum mode = getGLDrawMode(draw_mode);
|
||||
|
||||
gl.prepareDraw();
|
||||
|
||||
if (ibo && element_count > 0)
|
||||
if (ibo && elementCount > 0)
|
||||
{
|
||||
// Use the custom vertex map (index buffer) to draw the vertices.
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
GLBuffer::Bind ibo_bind(*ibo);
|
||||
|
||||
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
|
||||
ibo->unmap();
|
||||
|
||||
int max = element_count - 1;
|
||||
if (range_max >= 0)
|
||||
max = std::min(range_max, max);
|
||||
int max = (int) elementCount - 1;
|
||||
if (rangeMax >= 0)
|
||||
max = std::min(rangeMax, max);
|
||||
|
||||
int min = 0;
|
||||
if (range_min >= 0)
|
||||
min = std::min(range_min, max);
|
||||
if (rangeMin >= 0)
|
||||
min = std::min(rangeMin, max);
|
||||
|
||||
GLenum type = element_data_type;
|
||||
GLenum type = elementDataType;
|
||||
const void *indices = ibo->getPointer(min * getGLDataTypeSize(type));
|
||||
|
||||
gl.drawElements(mode, max - min + 1, type, indices);
|
||||
gl.drawElements(getGLDrawMode(drawMode), max - min + 1, type, indices);
|
||||
}
|
||||
else
|
||||
{
|
||||
int max = vertex_count - 1;
|
||||
if (range_max >= 0)
|
||||
max = std::min(range_max, max);
|
||||
int max = (int) vertexCount - 1;
|
||||
if (rangeMax >= 0)
|
||||
max = std::min(rangeMax, max);
|
||||
|
||||
int min = 0;
|
||||
if (range_min >= 0)
|
||||
min = std::min(range_min, max);
|
||||
if (rangeMin >= 0)
|
||||
min = std::min(rangeMin, max);
|
||||
|
||||
// Normal non-indexed drawing (no custom vertex map.)
|
||||
gl.drawArrays(mode, min, max - min + 1);
|
||||
gl.drawArrays(getGLDrawMode(drawMode), min, max - min + 1);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
if (texture.get())
|
||||
texture->postdraw();
|
||||
}
|
||||
|
||||
GLenum Mesh::getGLDrawMode(DrawMode mode) const
|
||||
size_t Mesh::getAttribFormatSize(const AttribFormat &format)
|
||||
{
|
||||
switch (format.type)
|
||||
{
|
||||
case DATA_BYTE:
|
||||
return format.components * sizeof(uint8);
|
||||
case DATA_FLOAT:
|
||||
return format.components * sizeof(float);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum Mesh::getGLDrawMode(DrawMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case DRAW_MODE_FAN:
|
||||
case DRAWMODE_FAN:
|
||||
return GL_TRIANGLE_FAN;
|
||||
case DRAW_MODE_STRIP:
|
||||
case DRAWMODE_STRIP:
|
||||
return GL_TRIANGLE_STRIP;
|
||||
case DRAW_MODE_TRIANGLES:
|
||||
case DRAWMODE_TRIANGLES:
|
||||
default:
|
||||
return GL_TRIANGLES;
|
||||
case DRAW_MODE_POINTS:
|
||||
case DRAWMODE_POINTS:
|
||||
return GL_POINTS;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) const
|
||||
GLenum Mesh::getGLDataType(DataType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DATA_BYTE:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case DATA_FLOAT:
|
||||
return GL_FLOAT;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue)
|
||||
{
|
||||
if (maxvalue > LOVE_UINT16_MAX)
|
||||
return GL_UNSIGNED_INT;
|
||||
@@ -429,7 +693,7 @@ GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) const
|
||||
return GL_UNSIGNED_SHORT;
|
||||
}
|
||||
|
||||
size_t Mesh::getGLDataTypeSize(GLenum datatype) const
|
||||
size_t Mesh::getGLDataTypeSize(GLenum datatype)
|
||||
{
|
||||
switch (datatype)
|
||||
{
|
||||
@@ -444,6 +708,31 @@ size_t Mesh::getGLDataTypeSize(GLenum datatype) const
|
||||
}
|
||||
}
|
||||
|
||||
GLenum Mesh::getGLBufferUsage(Usage usage)
|
||||
{
|
||||
switch (usage)
|
||||
{
|
||||
case USAGE_STREAM:
|
||||
return GL_STREAM_DRAW;
|
||||
case USAGE_DYNAMIC:
|
||||
return GL_DYNAMIC_DRAW;
|
||||
case USAGE_STATIC:
|
||||
return GL_STATIC_DRAW;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, Usage &out)
|
||||
{
|
||||
return usages.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(Usage in, const char *&out)
|
||||
{
|
||||
return usages.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
|
||||
{
|
||||
return drawModes.find(in, out);
|
||||
@@ -454,15 +743,42 @@ 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[] =
|
||||
bool Mesh::getConstant(const char *in, DataType &out)
|
||||
{
|
||||
{"fan", Mesh::DRAW_MODE_FAN},
|
||||
{"strip", Mesh::DRAW_MODE_STRIP},
|
||||
{"triangles", Mesh::DRAW_MODE_TRIANGLES},
|
||||
{"points", Mesh::DRAW_MODE_POINTS},
|
||||
return dataTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(DataType in, const char *&out)
|
||||
{
|
||||
return dataTypes.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Mesh::Usage, Mesh::USAGE_MAX_ENUM>::Entry Mesh::usageEntries[] =
|
||||
{
|
||||
{"stream", USAGE_STREAM},
|
||||
{"dynamic", USAGE_DYNAMIC},
|
||||
{"static", USAGE_STATIC},
|
||||
};
|
||||
|
||||
StringMap<Mesh::DrawMode, Mesh::DRAW_MODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
|
||||
StringMap<Mesh::Usage, Mesh::USAGE_MAX_ENUM> Mesh::usages(Mesh::usageEntries, sizeof(Mesh::usageEntries));
|
||||
|
||||
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
|
||||
{
|
||||
{"fan", DRAWMODE_FAN},
|
||||
{"strip", DRAWMODE_STRIP},
|
||||
{"triangles", DRAWMODE_TRIANGLES},
|
||||
{"points", DRAWMODE_POINTS},
|
||||
};
|
||||
|
||||
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
|
||||
|
||||
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM>::Entry Mesh::dataTypeEntries[] =
|
||||
{
|
||||
{"byte", DATA_BYTE},
|
||||
{"float", DATA_FLOAT},
|
||||
};
|
||||
|
||||
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM> Mesh::dataTypes(Mesh::dataTypeEntries, sizeof(Mesh::dataTypeEntries));
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
|
||||
@@ -27,11 +27,12 @@
|
||||
#include "common/math.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "Texture.h"
|
||||
#include "VertexBuffer.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "GLBuffer.h"
|
||||
|
||||
// C++
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -42,64 +43,110 @@ namespace opengl
|
||||
|
||||
/**
|
||||
* Holds and draws arbitrary vertex geometry.
|
||||
* Each vertex in the Mesh has a position, texture coordinate, and color.
|
||||
* Each vertex in the Mesh has a collection of vertex attributes specified on
|
||||
* creation.
|
||||
**/
|
||||
class Mesh : public Drawable
|
||||
{
|
||||
public:
|
||||
|
||||
// The expected usage pattern of the Mesh's vertex data.
|
||||
enum Usage
|
||||
{
|
||||
USAGE_STREAM,
|
||||
USAGE_DYNAMIC,
|
||||
USAGE_STATIC,
|
||||
USAGE_MAX_ENUM
|
||||
};
|
||||
|
||||
// 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
|
||||
DRAWMODE_FAN,
|
||||
DRAWMODE_STRIP,
|
||||
DRAWMODE_TRIANGLES,
|
||||
DRAWMODE_POINTS,
|
||||
DRAWMODE_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);
|
||||
// The type of data a vertex attribute can store.
|
||||
enum DataType
|
||||
{
|
||||
DATA_BYTE,
|
||||
DATA_FLOAT,
|
||||
DATA_MAX_ENUM
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Creates a Mesh with a certain number of default-initialized (hidden)
|
||||
* vertices.
|
||||
* @param vertexcount The number of vertices to use in the Mesh.
|
||||
* @param mode The draw mode to use when drawing the Mesh.
|
||||
**/
|
||||
Mesh(int vertexcount, DrawMode mode = DRAW_MODE_FAN);
|
||||
struct AttribFormat
|
||||
{
|
||||
std::string name;
|
||||
DataType type;
|
||||
int components; // max 4
|
||||
};
|
||||
|
||||
Mesh(const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, Usage usage);
|
||||
Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, Usage usage);
|
||||
|
||||
Mesh(const std::vector<Vertex> &vertices, DrawMode drawmode, Usage usage);
|
||||
Mesh(int vertexcount, DrawMode drawmode, Usage usage);
|
||||
|
||||
virtual ~Mesh();
|
||||
|
||||
/**
|
||||
* Replaces all the vertices in the Mesh with a new set of vertices.
|
||||
* Sets the values of all attributes at a specific vertex index in the Mesh.
|
||||
* The size of the data must be less than or equal to the total size of all
|
||||
* vertex attributes.
|
||||
**/
|
||||
void setVertices(const std::vector<Vertex> &verts);
|
||||
void setVertex(size_t vertindex, const void *data, size_t datasize);
|
||||
size_t getVertex(size_t vertindex, void *data, size_t datasize);
|
||||
void *getVertexScratchBuffer();
|
||||
|
||||
/**
|
||||
* Gets all of the vertices in the Mesh as an array.
|
||||
* Sets the values for a single attribute at a specific vertex index in the
|
||||
* Mesh. The size of the data must be less than or equal to the size of the
|
||||
* attribute.
|
||||
**/
|
||||
const Vertex *getVertices() const;
|
||||
void setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize);
|
||||
size_t getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Gets the total number of vertices that can be used when drawing the Mesh.
|
||||
**/
|
||||
size_t getVertexCount() const;
|
||||
|
||||
/**
|
||||
* Gets the size in bytes of the start of one vertex to the start of the
|
||||
* next, in the buffer.
|
||||
**/
|
||||
size_t getVertexStride() const;
|
||||
|
||||
/**
|
||||
* Gets the format of each vertex attribute stored in the Mesh.
|
||||
**/
|
||||
const std::vector<AttribFormat> &getVertexFormat() const;
|
||||
DataType getAttributeInfo(int attribindex, int &components) const;
|
||||
|
||||
/**
|
||||
* Sets whether a specific vertex attribute is used when drawing the Mesh.
|
||||
**/
|
||||
void setAttributeEnabled(const std::string &name, bool enable);
|
||||
bool isAttributeEnabled(const std::string &name) const;
|
||||
|
||||
/**
|
||||
* Attaches a vertex attribute from another Mesh to this one. The attribute
|
||||
* will be used when drawing this Mesh.
|
||||
**/
|
||||
void attachAttribute(const std::string &name, Mesh *mesh);
|
||||
|
||||
void *mapVertexData();
|
||||
void unmapVertexData();
|
||||
|
||||
/**
|
||||
* Flushes all modified data to the GPU.
|
||||
**/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -145,46 +192,79 @@ public:
|
||||
void setDrawRange();
|
||||
void getDrawRange(int &min, int &max) 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);
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override;
|
||||
|
||||
static GLenum getGLBufferUsage(Usage usage);
|
||||
|
||||
static bool getConstant(const char *in, Usage &out);
|
||||
static bool getConstant(Usage in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, DrawMode &out);
|
||||
static bool getConstant(DrawMode in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, DataType &out);
|
||||
static bool getConstant(DataType in, const char *&out);
|
||||
|
||||
private:
|
||||
|
||||
GLenum getGLDrawMode(DrawMode mode) const;
|
||||
GLenum getGLDataTypeFromMax(size_t maxvalue) const;
|
||||
size_t getGLDataTypeSize(GLenum datatype) const;
|
||||
struct AttachedAttribute
|
||||
{
|
||||
Mesh *mesh;
|
||||
size_t index;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
// Vertex buffer.
|
||||
VertexBuffer *vbo;
|
||||
size_t vertex_count;
|
||||
void setupAttachedAttributes();
|
||||
void calculateAttributeSizes();
|
||||
size_t getAttributeOffset(size_t attribindex) const;
|
||||
|
||||
static size_t getAttribFormatSize(const AttribFormat &format);
|
||||
|
||||
static GLenum getGLDrawMode(DrawMode mode);
|
||||
static GLenum getGLDataType(DataType type);
|
||||
static GLenum getGLDataTypeFromMax(size_t maxvalue);
|
||||
static size_t getGLDataTypeSize(GLenum datatype);
|
||||
|
||||
std::vector<AttribFormat> vertexFormat;
|
||||
std::vector<size_t> attributeSizes;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
|
||||
|
||||
// Vertex buffer, for the vertex data.
|
||||
GLBuffer *vbo;
|
||||
size_t vertexCount;
|
||||
size_t vertexStride;
|
||||
|
||||
// Block of memory whose size is at least as large as a single vertex. Helps
|
||||
// avoid memory allocations when using Mesh::setVertex etc.
|
||||
char *vertexScratchBuffer;
|
||||
|
||||
// Tracks the range of the vertex buffer that has been used, to make unmap()
|
||||
// calls as efficient as possible.
|
||||
size_t vboUsedOffset;
|
||||
size_t vboUsedSize;
|
||||
|
||||
// Element (vertex index) buffer, for the vertex map.
|
||||
VertexBuffer *ibo;
|
||||
size_t element_count;
|
||||
GLenum element_data_type;
|
||||
GLBuffer *ibo;
|
||||
size_t elementCount;
|
||||
GLenum elementDataType;
|
||||
|
||||
DrawMode draw_mode;
|
||||
DrawMode drawMode;
|
||||
|
||||
int range_min;
|
||||
int range_max;
|
||||
int rangeMin;
|
||||
int rangeMax;
|
||||
|
||||
StrongRef<Texture> texture;
|
||||
|
||||
// Whether the per-vertex colors are used when drawing.
|
||||
bool colors_enabled;
|
||||
static StringMap<Usage, USAGE_MAX_ENUM>::Entry usageEntries[];
|
||||
static StringMap<Usage, USAGE_MAX_ENUM> usages;
|
||||
|
||||
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM>::Entry drawModeEntries[];
|
||||
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM> drawModes;
|
||||
static StringMap<DrawMode, DRAWMODE_MAX_ENUM>::Entry drawModeEntries[];
|
||||
static StringMap<DrawMode, DRAWMODE_MAX_ENUM> drawModes;
|
||||
|
||||
static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[];
|
||||
static StringMap<DataType, DATA_MAX_ENUM> dataTypes;
|
||||
|
||||
}; // Mesh
|
||||
|
||||
|
||||
@@ -33,6 +33,13 @@
|
||||
// C
|
||||
#include <cstring>
|
||||
|
||||
// For SDL_GL_GetProcAddress.
|
||||
#include <SDL_video.h>
|
||||
|
||||
#ifdef LOVE_IOS
|
||||
#include <SDL_syswm.h>
|
||||
#endif
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
@@ -45,7 +52,9 @@ OpenGL::OpenGL()
|
||||
, contextInitialized(false)
|
||||
, maxAnisotropy(1.0f)
|
||||
, maxTextureSize(0)
|
||||
, maxRenderTargets(0)
|
||||
, maxRenderTargets(1)
|
||||
, maxRenderbufferSamples(0)
|
||||
, maxTextureUnits(1)
|
||||
, vendor(VENDOR_UNKNOWN)
|
||||
, state()
|
||||
{
|
||||
@@ -58,30 +67,33 @@ bool OpenGL::initContext()
|
||||
if (contextInitialized)
|
||||
return true;
|
||||
|
||||
if (!gladLoadGL())
|
||||
if (!gladLoadGLLoader(SDL_GL_GetProcAddress))
|
||||
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;
|
||||
contextInitialized = true;
|
||||
|
||||
// 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;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGL::setupContext()
|
||||
{
|
||||
if (!contextInitialized)
|
||||
return;
|
||||
|
||||
initMaxValues();
|
||||
|
||||
GLfloat glcolor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
glVertexAttrib4fv(ATTRIB_COLOR, glcolor);
|
||||
glVertexAttrib4fv(ATTRIB_CONSTANTCOLOR, glcolor);
|
||||
|
||||
GLint maxvertexattribs = 1;
|
||||
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxvertexattribs);
|
||||
state.enabledAttribArrays = (uint32) ((1ull << uint32(maxvertexattribs)) - 1);
|
||||
useVertexAttribArrays(0);
|
||||
|
||||
// Get the current viewport.
|
||||
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
|
||||
@@ -96,45 +108,32 @@ bool OpenGL::initContext()
|
||||
else
|
||||
state.pointSize = 1.0f;
|
||||
|
||||
// Initialize multiple texture unit support for shaders, if available.
|
||||
state.textureUnits.clear();
|
||||
if (Shader::isSupported())
|
||||
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|
||||
|| GLAD_EXT_sRGB_write_control)
|
||||
{
|
||||
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 = (int) 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);
|
||||
state.framebufferSRGBEnabled = (glIsEnabled(GL_FRAMEBUFFER_SRGB) == GL_TRUE);
|
||||
}
|
||||
else
|
||||
state.framebufferSRGBEnabled = false;
|
||||
|
||||
// Initialize multiple texture unit support for shaders.
|
||||
state.boundTextures.clear();
|
||||
state.boundTextures.resize(maxTextureUnits, 0);
|
||||
|
||||
GLenum curgltextureunit;
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
|
||||
|
||||
state.curTextureUnit = (int) curgltextureunit - GL_TEXTURE0;
|
||||
|
||||
// Retrieve currently bound textures for each texture unit.
|
||||
for (int i = 0; i < (int) state.boundTextures.size(); i++)
|
||||
{
|
||||
// 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]);
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.boundTextures[i]);
|
||||
}
|
||||
|
||||
// This will be non-zero on some platforms.
|
||||
if (Canvas::isSupported())
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint *) &state.defaultFBO);
|
||||
glActiveTexture(curgltextureunit);
|
||||
|
||||
BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
|
||||
setBlendState(blend);
|
||||
|
||||
initMaxValues();
|
||||
createDefaultTexture();
|
||||
|
||||
// Invalidate the cached matrices by setting some elements to NaN.
|
||||
@@ -142,11 +141,10 @@ bool OpenGL::initContext()
|
||||
state.lastProjectionMatrix.setTranslation(nan, nan);
|
||||
state.lastTransformMatrix.setTranslation(nan, nan);
|
||||
|
||||
if (GLAD_VERSION_1_1)
|
||||
if (GLAD_VERSION_1_0)
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
contextInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGL::deInitContext()
|
||||
@@ -172,14 +170,14 @@ void OpenGL::initVendor()
|
||||
// http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR
|
||||
// http://stackoverflow.com/questions/2093594/opengl-extensions-available-on-different-android-devices
|
||||
if (strstr(vstr, "ATI Technologies"))
|
||||
vendor = VENDOR_ATI_AMD;
|
||||
vendor = VENDOR_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"))
|
||||
else if (strstr(vstr, "Apple Computer") || strstr(vstr, "Apple Inc."))
|
||||
vendor = VENDOR_APPLE;
|
||||
else if (strstr(vstr, "Microsoft"))
|
||||
vendor = VENDOR_MICROSOFT;
|
||||
@@ -199,22 +197,42 @@ void OpenGL::initVendor()
|
||||
|
||||
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)
|
||||
// Alias extension-suffixed framebuffer functions to core versions since
|
||||
// there are so many different-named extensions that do the same things...
|
||||
if (!(GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
|
||||
{
|
||||
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;
|
||||
if (GLAD_VERSION_1_0 && GLAD_EXT_framebuffer_object)
|
||||
{
|
||||
fp_glBindRenderbuffer = fp_glBindRenderbufferEXT;
|
||||
fp_glDeleteRenderbuffers = fp_glDeleteRenderbuffersEXT;
|
||||
fp_glGenRenderbuffers = fp_glGenRenderbuffersEXT;
|
||||
fp_glRenderbufferStorage = fp_glRenderbufferStorageEXT;
|
||||
fp_glGetRenderbufferParameteriv = fp_glGetRenderbufferParameterivEXT;
|
||||
fp_glBindFramebuffer = fp_glBindFramebufferEXT;
|
||||
fp_glDeleteFramebuffers = fp_glDeleteFramebuffersEXT;
|
||||
fp_glGenFramebuffers = fp_glGenFramebuffersEXT;
|
||||
fp_glCheckFramebufferStatus = fp_glCheckFramebufferStatusEXT;
|
||||
fp_glFramebufferTexture2D = fp_glFramebufferTexture2DEXT;
|
||||
fp_glFramebufferRenderbuffer = fp_glFramebufferRenderbufferEXT;
|
||||
fp_glGetFramebufferAttachmentParameteriv = fp_glGetFramebufferAttachmentParameterivEXT;
|
||||
fp_glGenerateMipmap = fp_glGenerateMipmapEXT;
|
||||
}
|
||||
|
||||
if (GLAD_EXT_framebuffer_blit)
|
||||
fp_glBlitFramebuffer = fp_glBlitFramebufferEXT;
|
||||
else if (GLAD_ANGLE_framebuffer_blit)
|
||||
fp_glBlitFramebuffer = fp_glBlitFramebufferANGLE;
|
||||
else if (GLAD_NV_framebuffer_blit)
|
||||
fp_glBlitFramebuffer = fp_glBlitFramebufferNV;
|
||||
|
||||
if (GLAD_EXT_framebuffer_multisample)
|
||||
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleEXT;
|
||||
else if (GLAD_APPLE_framebuffer_multisample)
|
||||
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleAPPLE;
|
||||
else if (GLAD_ANGLE_framebuffer_multisample)
|
||||
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleANGLE;
|
||||
else if (GLAD_NV_framebuffer_multisample)
|
||||
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleNV;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,18 +246,27 @@ void OpenGL::initMaxValues()
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
|
||||
if (Canvas::isSupported() && (GLAD_VERSION_2_0 || GLAD_ARB_draw_buffers))
|
||||
int maxattachments = 1;
|
||||
int maxdrawbuffers = 1;
|
||||
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_2_0)
|
||||
{
|
||||
int maxattachments = 0;
|
||||
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
|
||||
|
||||
int maxdrawbuffers = 0;
|
||||
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
|
||||
}
|
||||
|
||||
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
|
||||
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
|
||||
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
|
||||
|| GLAD_EXT_framebuffer_multisample || GLAD_APPLE_framebuffer_multisample
|
||||
|| GLAD_ANGLE_framebuffer_multisample)
|
||||
{
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &maxRenderbufferSamples);
|
||||
}
|
||||
else
|
||||
maxRenderTargets = 0;
|
||||
maxRenderbufferSamples = 0;
|
||||
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
|
||||
}
|
||||
|
||||
void OpenGL::initMatrices()
|
||||
@@ -247,18 +274,18 @@ void OpenGL::initMatrices()
|
||||
matrices.transform.clear();
|
||||
matrices.projection.clear();
|
||||
|
||||
matrices.transform.push_back(Matrix());
|
||||
matrices.projection.push_back(Matrix());
|
||||
matrices.transform.push_back(Matrix4());
|
||||
matrices.projection.push_back(Matrix4());
|
||||
}
|
||||
|
||||
void OpenGL::createDefaultTexture()
|
||||
{
|
||||
// Set the 'default' texture as a repeating white pixel. Otherwise,
|
||||
// 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];
|
||||
GLuint curtexture = state.boundTextures[state.curTextureUnit];
|
||||
|
||||
glGenTextures(1, &state.defaultTexture);
|
||||
bindTexture(state.defaultTexture);
|
||||
@@ -269,8 +296,8 @@ void OpenGL::createDefaultTexture()
|
||||
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);
|
||||
GLubyte pix[] = {255, 255, 255, 255};
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pix);
|
||||
|
||||
bindTexture(curtexture);
|
||||
}
|
||||
@@ -285,68 +312,44 @@ void OpenGL::popTransform()
|
||||
matrices.transform.pop_back();
|
||||
}
|
||||
|
||||
Matrix &OpenGL::getTransform()
|
||||
Matrix4 &OpenGL::getTransform()
|
||||
{
|
||||
return matrices.transform.back();
|
||||
}
|
||||
|
||||
void OpenGL::prepareDraw()
|
||||
{
|
||||
const Matrix &transform = matrices.transform.back();
|
||||
const Matrix &proj = matrices.projection.back();
|
||||
TempDebugGroup debuggroup("Prepare OpenGL draw");
|
||||
|
||||
Shader *shader = Shader::current;
|
||||
// Make sure the active shader's love-provided uniforms are up to date.
|
||||
if (Shader::current != nullptr)
|
||||
Shader::current->checkSetBuiltinUniforms();
|
||||
|
||||
// Make sure the active shader has the correct values for its love-provided
|
||||
// uniforms.
|
||||
if (shader)
|
||||
// We use glLoadMatrix rather than uniforms for our matrices when possible,
|
||||
// because uniform uploads can be significantly slower than glLoadMatrix.
|
||||
if (GLAD_VERSION_1_0)
|
||||
{
|
||||
shader->checkSetScreenParams();
|
||||
const Matrix4 &curproj = matrices.projection.back();
|
||||
const Matrix4 &curxform = matrices.transform.back();
|
||||
|
||||
// We need to make sure antialiased Canvases are properly resolved
|
||||
// before sampling from their textures in a shader.
|
||||
// This is kind of a big hack. :(
|
||||
for (auto &r : shader->getBoundRetainables())
|
||||
{
|
||||
// Even bigger hack! D:
|
||||
Canvas *canvas = dynamic_cast<Canvas *>(r.second);
|
||||
if (canvas != nullptr)
|
||||
canvas->resolveMSAA();
|
||||
}
|
||||
}
|
||||
|
||||
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_PROJECTION_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)
|
||||
{
|
||||
const float *lastproj = state.lastProjectionMatrix.getElements();
|
||||
const Matrix4 &lastproj = state.lastProjectionMatrix;
|
||||
const Matrix4 &lastxform = state.lastTransformMatrix;
|
||||
|
||||
// We only need to re-upload the projection matrix if it's changed.
|
||||
if (memcmp(proj.getElements(), lastproj, sizeof(float) * 16) != 0)
|
||||
if (memcmp(curproj.getElements(), lastproj.getElements(), sizeof(float) * 16) != 0)
|
||||
{
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadMatrixf(proj.getElements());
|
||||
glLoadMatrixf(curproj.getElements());
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
state.lastProjectionMatrix = proj;
|
||||
state.lastProjectionMatrix = matrices.projection.back();
|
||||
}
|
||||
|
||||
const float *lastxform = state.lastTransformMatrix.getElements();
|
||||
|
||||
// Same with the transform matrix.
|
||||
if (memcmp(transform.getElements(), lastxform, sizeof(float) * 16) != 0)
|
||||
if (memcmp(curxform.getElements(), lastxform.getElements(), sizeof(float) * 16) != 0)
|
||||
{
|
||||
glLoadMatrixf(transform.getElements());
|
||||
state.lastTransformMatrix = transform;
|
||||
glLoadMatrixf(curxform.getElements());
|
||||
state.lastTransformMatrix = matrices.transform.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,107 +366,36 @@ void OpenGL::drawElements(GLenum mode, GLsizei count, GLenum type, const void *i
|
||||
++stats.drawCalls;
|
||||
}
|
||||
|
||||
void OpenGL::drawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex)
|
||||
void OpenGL::useVertexAttribArrays(uint32 arraybits)
|
||||
{
|
||||
glDrawElementsBaseVertex(mode, count, type, indices, basevertex);
|
||||
++stats.drawCalls;
|
||||
}
|
||||
uint32 diff = arraybits ^ state.enabledAttribArrays;
|
||||
|
||||
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);
|
||||
if (diff == 0)
|
||||
return;
|
||||
|
||||
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)
|
||||
// Max 32 attributes. As of when this was written, no GL driver exposes more
|
||||
// than 32. Lets hope that doesn't change...
|
||||
for (uint32 i = 0; i < 32; i++)
|
||||
{
|
||||
// The enum value maps to a generic vertex attribute index.
|
||||
return GLint(attrib);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (attrib)
|
||||
uint32 bit = 1 << i;
|
||||
|
||||
if (diff & bit)
|
||||
{
|
||||
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);
|
||||
if (arraybits & bit)
|
||||
glEnableVertexAttribArray(i);
|
||||
else
|
||||
glDisableVertexAttribArray(i);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
state.enabledAttribArrays = arraybits;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// glDisableVertexAttribArray will make the constant value for a vertex
|
||||
// attribute undefined. We rely on the per-vertex color attribute being
|
||||
// white when no per-vertex color is used, so we set it here.
|
||||
// FIXME: Is there a better place to do this?
|
||||
if ((diff & ATTRIBFLAG_COLOR) && !(arraybits & ATTRIBFLAG_COLOR))
|
||||
glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void OpenGL::setViewport(const OpenGL::Viewport &v)
|
||||
@@ -501,40 +433,6 @@ OpenGL::Viewport OpenGL::getScissor() const
|
||||
return state.scissor;
|
||||
}
|
||||
|
||||
void OpenGL::setBlendState(const BlendState &blend)
|
||||
{
|
||||
if (GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_4)
|
||||
glBlendEquation(blend.func);
|
||||
else if (GLAD_EXT_blend_minmax && GLAD_EXT_blend_subtract)
|
||||
glBlendEquationEXT(blend.func);
|
||||
else
|
||||
{
|
||||
if (blend.func == GL_FUNC_REVERSE_SUBTRACT)
|
||||
throw love::Exception("This graphics card does not support the subtractive blend mode!");
|
||||
// GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work.
|
||||
}
|
||||
|
||||
if (blend.srcRGB == blend.srcA && blend.dstRGB == blend.dstA)
|
||||
glBlendFunc(blend.srcRGB, blend.dstRGB);
|
||||
else
|
||||
{
|
||||
if (GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_4)
|
||||
glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
|
||||
else if (GLAD_EXT_blend_func_separate)
|
||||
glBlendFuncSeparateEXT(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
|
||||
else
|
||||
throw love::Exception("This graphics card does not support separated rgb and alpha blend functions!");
|
||||
}
|
||||
|
||||
state.blend = blend;
|
||||
}
|
||||
|
||||
OpenGL::BlendState OpenGL::getBlendState() const
|
||||
{
|
||||
return state.blend;
|
||||
}
|
||||
|
||||
|
||||
void OpenGL::setPointSize(float size)
|
||||
{
|
||||
if (GLAD_VERSION_1_0)
|
||||
@@ -548,9 +446,40 @@ float OpenGL::getPointSize() const
|
||||
return state.pointSize;
|
||||
}
|
||||
|
||||
void OpenGL::setFramebufferSRGB(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
else
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
state.framebufferSRGBEnabled = enable;
|
||||
}
|
||||
|
||||
bool OpenGL::hasFramebufferSRGB() const
|
||||
{
|
||||
return state.framebufferSRGBEnabled;
|
||||
}
|
||||
|
||||
void OpenGL::bindFramebuffer(GLenum target, GLuint framebuffer)
|
||||
{
|
||||
glBindFramebuffer(target, framebuffer);
|
||||
|
||||
if (target == GL_FRAMEBUFFER)
|
||||
++stats.framebufferBinds;
|
||||
}
|
||||
|
||||
GLuint OpenGL::getDefaultFBO() const
|
||||
{
|
||||
return state.defaultFBO;
|
||||
#ifdef LOVE_IOS
|
||||
// Hack: iOS uses a custom FBO.
|
||||
SDL_SysWMinfo info = {};
|
||||
SDL_VERSION(&info.version);
|
||||
SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
|
||||
return info.info.uikit.framebuffer;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
GLuint OpenGL::getDefaultTexture() const
|
||||
@@ -560,40 +489,35 @@ GLuint OpenGL::getDefaultTexture() const
|
||||
|
||||
void OpenGL::setTextureUnit(int textureunit)
|
||||
{
|
||||
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
|
||||
if (textureunit < 0 || (size_t) textureunit >= state.boundTextures.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 is not supported.");
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0 + textureunit);
|
||||
|
||||
state.curTextureUnit = textureunit;
|
||||
}
|
||||
|
||||
void OpenGL::bindTexture(GLuint texture)
|
||||
{
|
||||
if (texture != state.textureUnits[state.curTextureUnit])
|
||||
if (texture != state.boundTextures[state.curTextureUnit])
|
||||
{
|
||||
state.textureUnits[state.curTextureUnit] = texture;
|
||||
state.boundTextures[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())
|
||||
if (textureunit < 0 || (size_t) textureunit >= state.boundTextures.size())
|
||||
throw love::Exception("Invalid texture unit index.");
|
||||
|
||||
if (texture != state.textureUnits[textureunit])
|
||||
if (texture != state.boundTextures[textureunit])
|
||||
{
|
||||
int oldtextureunit = state.curTextureUnit;
|
||||
setTextureUnit(textureunit);
|
||||
|
||||
state.textureUnits[textureunit] = texture;
|
||||
state.boundTextures[textureunit] = texture;
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
if (restoreprev)
|
||||
@@ -605,7 +529,7 @@ void OpenGL::deleteTexture(GLuint texture)
|
||||
{
|
||||
// glDeleteTextures binds texture 0 to all texture units the deleted texture
|
||||
// was bound to before deletion.
|
||||
for (GLuint &texid : state.textureUnits)
|
||||
for (GLuint &texid : state.boundTextures)
|
||||
{
|
||||
if (texid == texture)
|
||||
texid = 0;
|
||||
@@ -614,7 +538,7 @@ void OpenGL::deleteTexture(GLuint texture)
|
||||
glDeleteTextures(1, &texture);
|
||||
}
|
||||
|
||||
float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
||||
void OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
||||
{
|
||||
GLint gmin, gmag;
|
||||
|
||||
@@ -639,7 +563,6 @@ float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
||||
gmin = GL_LINEAR;
|
||||
}
|
||||
|
||||
|
||||
switch (f.mag)
|
||||
{
|
||||
case Texture::FILTER_NEAREST:
|
||||
@@ -659,8 +582,8 @@ float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
||||
f.anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, f.anisotropy);
|
||||
}
|
||||
|
||||
return f.anisotropy;
|
||||
else
|
||||
f.anisotropy = 1.0f;
|
||||
}
|
||||
|
||||
void OpenGL::setTextureWrap(const graphics::Texture::Wrap &w)
|
||||
@@ -693,6 +616,16 @@ int OpenGL::getMaxRenderTargets() const
|
||||
return maxRenderTargets;
|
||||
}
|
||||
|
||||
int OpenGL::getMaxRenderbufferSamples() const
|
||||
{
|
||||
return maxRenderbufferSamples;
|
||||
}
|
||||
|
||||
int OpenGL::getMaxTextureUnits() const
|
||||
{
|
||||
return maxTextureUnits;
|
||||
}
|
||||
|
||||
void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize)
|
||||
{
|
||||
int64 memsize = (int64) stats.textureMemory + ((int64 )newsize - (int64) oldsize);
|
||||
@@ -715,9 +648,8 @@ const char *OpenGL::debugSeverityString(GLenum severity)
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
return "low";
|
||||
default:
|
||||
break;
|
||||
return "unknown";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *OpenGL::debugSourceString(GLenum source)
|
||||
@@ -737,9 +669,8 @@ const char *OpenGL::debugSourceString(GLenum source)
|
||||
case GL_DEBUG_SOURCE_OTHER:
|
||||
return "other";
|
||||
default:
|
||||
break;
|
||||
return "unknown";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *OpenGL::debugTypeString(GLenum type)
|
||||
@@ -759,9 +690,8 @@ const char *OpenGL::debugTypeString(GLenum type)
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
return "other";
|
||||
default:
|
||||
break;
|
||||
return "unknown";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_OPENGL_H
|
||||
#define LOVE_GRAPHICS_OPENGL_OPENGL_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/int.h"
|
||||
#include "graphics/Color.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "common/Matrix.h"
|
||||
@@ -50,6 +50,25 @@ namespace opengl
|
||||
// no clashes with other GL libraries when linking, etc.
|
||||
using namespace glad;
|
||||
|
||||
// Vertex attribute indices used in shaders by LOVE. The values map to OpenGL
|
||||
// generic vertex attribute indices.
|
||||
enum VertexAttribID
|
||||
{
|
||||
ATTRIB_POS = 0,
|
||||
ATTRIB_TEXCOORD,
|
||||
ATTRIB_COLOR,
|
||||
ATTRIB_CONSTANTCOLOR,
|
||||
ATTRIB_MAX_ENUM
|
||||
};
|
||||
|
||||
enum VertexAttribFlags
|
||||
{
|
||||
ATTRIBFLAG_POS = 1 << ATTRIB_POS,
|
||||
ATTRIBFLAG_TEXCOORD = 1 << ATTRIB_TEXCOORD,
|
||||
ATTRIBFLAG_COLOR = 1 << ATTRIB_COLOR,
|
||||
ATTRIBFLAG_CONSTANTCOLOR = 1 << ATTRIB_CONSTANTCOLOR
|
||||
};
|
||||
|
||||
/**
|
||||
* Thin layer between OpenGL and the rest of the program.
|
||||
* Internally shadows some OpenGL context state for improved efficiency and
|
||||
@@ -64,11 +83,11 @@ public:
|
||||
// OpenGL GPU vendors.
|
||||
enum Vendor
|
||||
{
|
||||
VENDOR_ATI_AMD,
|
||||
VENDOR_AMD,
|
||||
VENDOR_NVIDIA,
|
||||
VENDOR_INTEL,
|
||||
VENDOR_MESA_SOFT, // Software renderer.
|
||||
VENDOR_APPLE, // Software renderer (or Apple A7 chips and newer.)
|
||||
VENDOR_APPLE, // Software renderer on desktops.
|
||||
VENDOR_MICROSOFT, // Software renderer.
|
||||
VENDOR_IMGTEC,
|
||||
VENDOR_ARM,
|
||||
@@ -78,49 +97,22 @@ public:
|
||||
VENDOR_UNKNOWN
|
||||
};
|
||||
|
||||
// Vertex attributes used in shaders by LOVE. The values map to OpenGL
|
||||
// generic vertex attribute indices, when applicable.
|
||||
// LOVE uses the old hard-coded attribute APIs on desktop GL (for now.)
|
||||
enum VertexAttrib
|
||||
{
|
||||
ATTRIB_POS = 0,
|
||||
ATTRIB_TEXCOORD = 1,
|
||||
ATTRIB_COLOR = 2,
|
||||
ATTRIB_PSEUDO_INSTANCE_ID = 3, // Shader pseudo-instance ID.
|
||||
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)
|
||||
{}
|
||||
|
||||
bool operator == (const Viewport &rhs) const
|
||||
{
|
||||
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
|
||||
}
|
||||
};
|
||||
|
||||
struct BlendState
|
||||
{
|
||||
GLenum srcRGB, srcA;
|
||||
GLenum dstRGB, dstA;
|
||||
GLenum func;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
std::vector<Matrix> transform;
|
||||
std::vector<Matrix> projection;
|
||||
std::vector<Matrix4> transform;
|
||||
std::vector<Matrix4> projection;
|
||||
} matrices;
|
||||
|
||||
class TempTransform
|
||||
@@ -138,7 +130,7 @@ public:
|
||||
gl.popTransform();
|
||||
}
|
||||
|
||||
Matrix &get()
|
||||
Matrix4 &get()
|
||||
{
|
||||
return gl.getTransform();
|
||||
}
|
||||
@@ -147,21 +139,49 @@ public:
|
||||
OpenGL ≷
|
||||
};
|
||||
|
||||
class TempDebugGroup
|
||||
{
|
||||
public:
|
||||
|
||||
#if defined(DEBUG) && DEBUG == 1
|
||||
TempDebugGroup(const char *name)
|
||||
{
|
||||
if (GLAD_EXT_debug_marker)
|
||||
glPushGroupMarkerEXT(0, (const GLchar *) name);
|
||||
}
|
||||
#else
|
||||
TempDebugGroup(const char *) {}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG) && DEBUG == 1
|
||||
~TempDebugGroup()
|
||||
{
|
||||
if (GLAD_EXT_debug_marker)
|
||||
glPopGroupMarkerEXT();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct Stats
|
||||
{
|
||||
size_t textureMemory;
|
||||
int drawCalls;
|
||||
int framebufferBinds;
|
||||
} stats;
|
||||
|
||||
OpenGL();
|
||||
virtual ~OpenGL() {}
|
||||
|
||||
/**
|
||||
* Initializes some required context state based on current and default
|
||||
* OpenGL state. Call this directly after creating an OpenGL context!
|
||||
* Initializes the active OpenGL context.
|
||||
**/
|
||||
bool initContext();
|
||||
|
||||
/**
|
||||
* Sets up some required context state based on current and default OpenGL
|
||||
* state. Call this directly after initializing an OpenGL context!
|
||||
**/
|
||||
void setupContext();
|
||||
|
||||
/**
|
||||
* Marks current context state as invalid and deletes OpenGL objects owned
|
||||
* by this class instance. Call this directly before potentially deleting
|
||||
@@ -171,58 +191,29 @@ public:
|
||||
|
||||
void pushTransform();
|
||||
void popTransform();
|
||||
Matrix &getTransform();
|
||||
Matrix4 &getTransform();
|
||||
|
||||
/**
|
||||
* Set up necessary state (matrices etc.) for drawing. This *must* be called
|
||||
* directly before GL draws.
|
||||
* Set up necessary state (LOVE-provided shader uniforms, etc.) for drawing.
|
||||
* This *MUST* be called directly before OpenGL drawing functions.
|
||||
**/
|
||||
void prepareDraw();
|
||||
|
||||
/**
|
||||
* glDraw* functions which increment the draw-call counter by themselves.
|
||||
* glDrawArrays and glDrawElements which increment the draw-call counter by
|
||||
* themselves.
|
||||
**/
|
||||
void drawArrays(GLenum mode, GLint first, GLsizei count);
|
||||
void drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices);
|
||||
void drawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex);
|
||||
|
||||
/**
|
||||
* Sets the current constant color.
|
||||
* Sets the enabled vertex attribute arrays based on the specified attribute
|
||||
* bits. Each bit in the uint32 represents an enabled attribute array index.
|
||||
* For example, useVertexAttribArrays(1 << 0) will enable attribute index 0.
|
||||
* See the VertexAttribFlags enum for the standard vertex attributes.
|
||||
* This function *must* be used instead of glEnable/DisableVertexAttribArray.
|
||||
**/
|
||||
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);
|
||||
void useVertexAttribArrays(uint32 arraybits);
|
||||
|
||||
/**
|
||||
* Sets the OpenGL rendering viewport to the specified rectangle.
|
||||
@@ -246,17 +237,6 @@ public:
|
||||
**/
|
||||
Viewport getScissor() const;
|
||||
|
||||
/**
|
||||
* Sets blending functionality.
|
||||
* Note: This does not globally enable or disable blending.
|
||||
**/
|
||||
void setBlendState(const BlendState &blend);
|
||||
|
||||
/**
|
||||
* Gets the currently set blending functionality.
|
||||
**/
|
||||
BlendState getBlendState() const;
|
||||
|
||||
/**
|
||||
* Sets the global point size.
|
||||
**/
|
||||
@@ -267,6 +247,21 @@ public:
|
||||
**/
|
||||
float getPointSize() const;
|
||||
|
||||
/**
|
||||
* Calls glEnable/glDisable(GL_FRAMEBUFFER_SRGB).
|
||||
**/
|
||||
void setFramebufferSRGB(bool enable);
|
||||
|
||||
/**
|
||||
* Equivalent to glIsEnabled(GL_FRAMEBUFFER_SRGB).
|
||||
**/
|
||||
bool hasFramebufferSRGB() const;
|
||||
|
||||
/**
|
||||
* Binds a Framebuffer Object to the specified target.
|
||||
**/
|
||||
void bindFramebuffer(GLenum target, GLuint framebuffer);
|
||||
|
||||
/**
|
||||
* This will usually be 0 (system drawable), but some platforms require a
|
||||
* non-zero FBO for rendering.
|
||||
@@ -274,7 +269,7 @@ public:
|
||||
GLuint getDefaultFBO() const;
|
||||
|
||||
/**
|
||||
* Gets the ID for love's default texture (used for "untextured" draws.)
|
||||
* Gets the ID for love's default texture (used for "untextured" primitives.)
|
||||
**/
|
||||
GLuint getDefaultTexture() const;
|
||||
|
||||
@@ -307,9 +302,10 @@ public:
|
||||
|
||||
/**
|
||||
* Sets the texture filter mode for the currently bound texture.
|
||||
* Returns the actual amount of anisotropic filtering set.
|
||||
* The anisotropy parameter of the argument is set to the actual amount of
|
||||
* anisotropy that was used.
|
||||
**/
|
||||
float setTextureFilter(graphics::Texture::Filter &f);
|
||||
void setTextureFilter(graphics::Texture::Filter &f);
|
||||
|
||||
/**
|
||||
* Sets the texture wrap mode for the currently bound texture.
|
||||
@@ -326,6 +322,16 @@ public:
|
||||
**/
|
||||
int getMaxRenderTargets() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum supported number of MSAA samples for renderbuffers.
|
||||
**/
|
||||
int getMaxRenderbufferSamples() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum number of accessible texture units.
|
||||
**/
|
||||
int getMaxTextureUnits() const;
|
||||
|
||||
void updateTextureMemorySize(size_t oldsize, size_t newsize);
|
||||
|
||||
/**
|
||||
@@ -346,43 +352,38 @@ private:
|
||||
void initMatrices();
|
||||
void createDefaultTexture();
|
||||
|
||||
GLint getGLAttrib(VertexAttrib attrib);
|
||||
|
||||
bool contextInitialized;
|
||||
|
||||
float maxAnisotropy;
|
||||
int maxTextureSize;
|
||||
int maxRenderTargets;
|
||||
int maxRenderbufferSamples;
|
||||
int maxTextureUnits;
|
||||
|
||||
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;
|
||||
std::vector<GLuint> boundTextures;
|
||||
|
||||
// Currently active texture unit.
|
||||
int curTextureUnit;
|
||||
|
||||
uint32 enabledAttribArrays;
|
||||
|
||||
Viewport viewport;
|
||||
Viewport scissor;
|
||||
|
||||
float pointSize;
|
||||
|
||||
bool framebufferSRGBEnabled;
|
||||
|
||||
GLuint defaultTexture;
|
||||
|
||||
GLuint defaultFBO;
|
||||
|
||||
BlendState blend;
|
||||
|
||||
Matrix lastProjectionMatrix;
|
||||
Matrix lastTransformMatrix;
|
||||
Matrix4 lastProjectionMatrix;
|
||||
Matrix4 lastTransformMatrix;
|
||||
|
||||
} state;
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
|
||||
, pHead(nullptr)
|
||||
, pTail(nullptr)
|
||||
, particleVerts(nullptr)
|
||||
, ibo(nullptr)
|
||||
, quadIndices(1)
|
||||
, texture(texture)
|
||||
, active(true)
|
||||
, insertMode(INSERT_MODE_TOP)
|
||||
@@ -95,8 +95,8 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
|
||||
, spinStart(0)
|
||||
, spinEnd(0)
|
||||
, spinVariation(0)
|
||||
, offsetX(float(texture->getWidth())*0.5f)
|
||||
, offsetY(float(texture->getHeight())*0.5f)
|
||||
, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
|
||||
, defaultOffset(true)
|
||||
, relativeRotation(false)
|
||||
{
|
||||
if (size == 0 || size > MAX_PARTICLES)
|
||||
@@ -113,7 +113,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
|
||||
, pHead(nullptr)
|
||||
, pTail(nullptr)
|
||||
, particleVerts(nullptr)
|
||||
, ibo(nullptr)
|
||||
, quadIndices(p.quadIndices)
|
||||
, texture(p.texture)
|
||||
, active(p.active)
|
||||
, insertMode(p.insertMode)
|
||||
@@ -148,8 +148,8 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
|
||||
, spinStart(p.spinStart)
|
||||
, spinEnd(p.spinEnd)
|
||||
, spinVariation(p.spinVariation)
|
||||
, offsetX(p.offsetX)
|
||||
, offsetY(p.offsetY)
|
||||
, offset(p.offset)
|
||||
, defaultOffset(p.defaultOffset)
|
||||
, colors(p.colors)
|
||||
, quads(p.quads)
|
||||
, relativeRotation(p.relativeRotation)
|
||||
@@ -167,20 +167,25 @@ ParticleSystem *ParticleSystem::clone()
|
||||
return new ParticleSystem(*this);
|
||||
}
|
||||
|
||||
void ParticleSystem::resetOffset()
|
||||
{
|
||||
if (quads.empty())
|
||||
offset = love::Vector(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f);
|
||||
else
|
||||
{
|
||||
Quad::Viewport v = quads[0]->getViewport();
|
||||
offset = love::Vector(v.x*0.5f, v.y*0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -193,12 +198,9 @@ void ParticleSystem::deleteBuffers()
|
||||
// Clean up for great gracefulness!
|
||||
delete[] pMem;
|
||||
delete[] particleVerts;
|
||||
delete ibo;
|
||||
|
||||
pMem = nullptr;
|
||||
particleVerts = nullptr;
|
||||
ibo = nullptr;
|
||||
|
||||
maxParticles = 0;
|
||||
activeParticles = 0;
|
||||
}
|
||||
@@ -207,6 +209,7 @@ void ParticleSystem::setBufferSize(uint32 size)
|
||||
{
|
||||
if (size == 0 || size > MAX_PARTICLES)
|
||||
throw love::Exception("Invalid buffer size");
|
||||
quadIndices = QuadIndices(size);
|
||||
deleteBuffers();
|
||||
createBuffers(size);
|
||||
reset();
|
||||
@@ -431,6 +434,9 @@ ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p)
|
||||
void ParticleSystem::setTexture(Texture *tex)
|
||||
{
|
||||
texture.set(tex);
|
||||
|
||||
if (defaultOffset)
|
||||
resetOffset();
|
||||
}
|
||||
|
||||
Texture *ParticleSystem::getTexture() const
|
||||
@@ -691,13 +697,13 @@ float ParticleSystem::getSpinVariation() const
|
||||
|
||||
void ParticleSystem::setOffset(float x, float y)
|
||||
{
|
||||
offsetX = x;
|
||||
offsetY = y;
|
||||
offset = love::Vector(x, y);
|
||||
defaultOffset = false;
|
||||
}
|
||||
|
||||
love::Vector ParticleSystem::getOffset() const
|
||||
{
|
||||
return love::Vector(offsetX, offsetY);
|
||||
return offset;
|
||||
}
|
||||
|
||||
void ParticleSystem::setColor(const Color &color)
|
||||
@@ -738,6 +744,9 @@ void ParticleSystem::setQuads(const std::vector<Quad *> &newQuads)
|
||||
quadlist.push_back(q);
|
||||
|
||||
quads = quadlist;
|
||||
|
||||
if (defaultOffset)
|
||||
resetOffset();
|
||||
}
|
||||
|
||||
void ParticleSystem::setQuads()
|
||||
@@ -843,10 +852,9 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
|
||||
if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || particleVerts == nullptr)
|
||||
return;
|
||||
|
||||
Color curcolor = gl.getColor();
|
||||
OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
|
||||
|
||||
Matrix t;
|
||||
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
OpenGL::TempTransform transform(gl);
|
||||
transform.get() *= t;
|
||||
@@ -864,7 +872,7 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
|
||||
textureVerts = quads[p->quadIndex]->getVertices();
|
||||
|
||||
// particle vertices are image vertices transformed by particle information
|
||||
t.setTransformation(p->position[0], p->position[1], p->angle, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
|
||||
t.setTransformation(p->position[0], p->position[1], p->angle, p->size, p->size, offset.x, offset.y, 0.0f, 0.0f);
|
||||
t.transform(pVerts, textureVerts, 4);
|
||||
|
||||
// set the texture coordinate and color data for particle vertices
|
||||
@@ -884,29 +892,20 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
texture->predraw();
|
||||
gl.bindTexture(*(GLuint *) texture->getHandle());
|
||||
gl.prepareDraw();
|
||||
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
|
||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_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);
|
||||
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &particleVerts[0].r);
|
||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].x);
|
||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].s);
|
||||
|
||||
{
|
||||
VertexBuffer::Bind ibo_bind(*ibo->getVertexBuffer());
|
||||
gl.drawElements(GL_TRIANGLES, ibo->getIndexCount(pCount), ibo->getType(), ibo->getPointer(0));
|
||||
GLsizei count = (GLsizei) quadIndices.getIndexCount(pCount);
|
||||
GLBuffer::Bind ibo_bind(*quadIndices.getBuffer());
|
||||
gl.drawElements(GL_TRIANGLES, count, quadIndices.getType(), quadIndices.getPointer(0));
|
||||
}
|
||||
|
||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
|
||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
|
||||
|
||||
texture->postdraw();
|
||||
|
||||
gl.setColor(curcolor);
|
||||
}
|
||||
|
||||
void ParticleSystem::update(float dt)
|
||||
@@ -998,7 +997,7 @@ void ParticleSystem::update(float dt)
|
||||
{
|
||||
s = t * (float) k; // [0:numquads-1] (clamped below)
|
||||
i = (s > 0.0f) ? (size_t) s : 0;
|
||||
p->quadIndex = (i < k) ? i : k - 1;
|
||||
p->quadIndex = (int) ((i < k) ? i : k - 1);
|
||||
}
|
||||
|
||||
// Next particle.
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
#include "common/Vector.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "graphics/Color.h"
|
||||
#include "VertexBuffer.h"
|
||||
#include "graphics/Quad.h"
|
||||
#include "Texture.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "GLBuffer.h"
|
||||
|
||||
// STL
|
||||
#include <vector>
|
||||
@@ -577,7 +577,7 @@ protected:
|
||||
Vertex *particleVerts;
|
||||
|
||||
// Vertex index buffer.
|
||||
VertexIndex *ibo;
|
||||
QuadIndices quadIndices;
|
||||
|
||||
// The texture to be drawn.
|
||||
StrongRef<Texture> texture;
|
||||
@@ -653,8 +653,10 @@ protected:
|
||||
float spinVariation;
|
||||
|
||||
// Offsets
|
||||
float offsetX;
|
||||
float offsetY;
|
||||
love::Vector offset;
|
||||
|
||||
// Is the ParticleSystem using a default offset?
|
||||
bool defaultOffset;
|
||||
|
||||
// Color.
|
||||
std::vector<Colorf> colors;
|
||||
@@ -664,6 +666,8 @@ protected:
|
||||
|
||||
bool relativeRotation;
|
||||
|
||||
void resetOffset();
|
||||
|
||||
void createBuffers(size_t size);
|
||||
void deleteBuffers();
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
// OpenGL
|
||||
#include "OpenGL.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
|
||||
// treat adjacent segments with angles between their directions <5 degree as straight
|
||||
@@ -36,16 +37,6 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Polyline::Polyline(GLenum mode, bool quadindices)
|
||||
: vertices(nullptr)
|
||||
, overdraw(nullptr)
|
||||
, vertex_count(0)
|
||||
, overdraw_vertex_count(0)
|
||||
, draw_mode(mode)
|
||||
, use_quad_indices(quadindices)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -58,7 +49,7 @@ void Polyline::render(const float *coords, size_t count, size_t size_hint, float
|
||||
|
||||
// prepare vertex arrays
|
||||
if (draw_overdraw)
|
||||
halfwidth -= pixel_size * .3;
|
||||
halfwidth -= pixel_size * 0.3f;
|
||||
|
||||
// compute sleeve
|
||||
bool is_looping = (coords[0] == coords[count - 2]) && (coords[1] == coords[count - 1]);
|
||||
@@ -84,12 +75,40 @@ void Polyline::render(const float *coords, size_t count, size_t size_hint, float
|
||||
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
|
||||
|
||||
vertex_count = normals.size();
|
||||
vertices = new Vector[vertex_count];
|
||||
|
||||
size_t extra_vertices = 0;
|
||||
|
||||
if (draw_overdraw)
|
||||
{
|
||||
calc_overdraw_vertex_count(is_looping);
|
||||
|
||||
// When drawing overdraw lines using triangle strips, we want to add an
|
||||
// extra degenerate triangle in between the core line and the overdraw
|
||||
// line in order to break up the strip into two. This will let us draw
|
||||
// everything in one draw call.
|
||||
if (draw_mode == GL_TRIANGLE_STRIP)
|
||||
extra_vertices = 2;
|
||||
}
|
||||
|
||||
// Use a single linear array for both the regular and overdraw vertices.
|
||||
vertices = new Vector[vertex_count + extra_vertices + overdraw_vertex_count];
|
||||
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
vertices[i] = anchors[i] + normals[i];
|
||||
|
||||
if (draw_overdraw)
|
||||
{
|
||||
overdraw = vertices + vertex_count + extra_vertices;
|
||||
overdraw_vertex_start = vertex_count + extra_vertices;
|
||||
render_overdraw(normals, pixel_size, is_looping);
|
||||
}
|
||||
|
||||
// Add the degenerate triangle strip.
|
||||
if (extra_vertices)
|
||||
{
|
||||
vertices[vertex_count + 0] = vertices[vertex_count - 1];
|
||||
vertices[vertex_count + 1] = vertices[overdraw_vertex_start];
|
||||
}
|
||||
}
|
||||
|
||||
void NoneJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
|
||||
@@ -247,10 +266,13 @@ void BevelJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vec
|
||||
ns = nt;
|
||||
}
|
||||
|
||||
void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping)
|
||||
void Polyline::calc_overdraw_vertex_count(bool is_looping)
|
||||
{
|
||||
overdraw_vertex_count = 2 * vertex_count + (is_looping ? 0 : 2);
|
||||
overdraw = new Vector[overdraw_vertex_count];
|
||||
}
|
||||
|
||||
void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping)
|
||||
{
|
||||
// upper segment
|
||||
for (size_t i = 0; i + 1 < vertex_count; i += 2)
|
||||
{
|
||||
@@ -293,10 +315,13 @@ void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_s
|
||||
}
|
||||
}
|
||||
|
||||
void NoneJoinPolyline::render_overdraw(const std::vector<Vector> &/*normals*/, float pixel_size, bool /*is_looping*/)
|
||||
void NoneJoinPolyline::calc_overdraw_vertex_count(bool /*is_looping*/)
|
||||
{
|
||||
overdraw_vertex_count = 4 * (vertex_count-2); // less than ideal
|
||||
overdraw = new Vector[overdraw_vertex_count];
|
||||
}
|
||||
|
||||
void NoneJoinPolyline::render_overdraw(const std::vector<Vector> &/*normals*/, float pixel_size, bool /*is_looping*/)
|
||||
{
|
||||
for (size_t i = 2; i + 3 < vertex_count; i += 4)
|
||||
{
|
||||
Vector s = vertices[i] - vertices[i+3];
|
||||
@@ -331,32 +356,43 @@ Polyline::~Polyline()
|
||||
{
|
||||
if (vertices)
|
||||
delete[] vertices;
|
||||
if (overdraw)
|
||||
delete[] overdraw;
|
||||
}
|
||||
|
||||
void Polyline::draw()
|
||||
{
|
||||
GLushort *indices = nullptr;
|
||||
OpenGL::TempDebugGroup debuggroup("Line draw");
|
||||
|
||||
GLushort *indices = nullptr;
|
||||
Color *colors = nullptr;
|
||||
|
||||
size_t total_vertex_count = vertex_count;
|
||||
if (overdraw)
|
||||
total_vertex_count = overdraw_vertex_start + overdraw_vertex_count;
|
||||
|
||||
// TODO: We should probably be using a reusable index buffer.
|
||||
if (use_quad_indices)
|
||||
{
|
||||
size_t numindices = (vertex_count / 4) * 6;
|
||||
if (overdraw)
|
||||
numindices = std::max(numindices, (overdraw_vertex_count / 4) * 6);
|
||||
size_t numindices = (total_vertex_count / 4) * 6;
|
||||
|
||||
indices = new GLushort[numindices];
|
||||
try
|
||||
{
|
||||
indices = new GLushort[numindices];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
// Fill the index array to make the draw call render 2 triangles from
|
||||
// a 4-vertex quad.
|
||||
// Fill the index array to make 2 triangles from each quad.
|
||||
// NOTE: The triangle vertex ordering here is important!
|
||||
for (size_t i = 0; i < numindices / 6; i++)
|
||||
{
|
||||
// First triangle: vertices 0-1-2.
|
||||
// First triangle.
|
||||
indices[i * 6 + 0] = GLushort(i * 4 + 0);
|
||||
indices[i * 6 + 1] = GLushort(i * 4 + 1);
|
||||
indices[i * 6 + 2] = GLushort(i * 4 + 2);
|
||||
|
||||
// Second triangle: vertices 0-2-3.
|
||||
// Second triangle.
|
||||
indices[i * 6 + 3] = GLushort(i * 4 + 0);
|
||||
indices[i * 6 + 4] = GLushort(i * 4 + 2);
|
||||
indices[i * 6 + 5] = GLushort(i * 4 + 3);
|
||||
@@ -365,61 +401,57 @@ void Polyline::draw()
|
||||
|
||||
gl.prepareDraw();
|
||||
|
||||
// draw the core line
|
||||
gl.bindTexture(gl.getDefaultTexture());
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, vertices);
|
||||
|
||||
if (use_quad_indices)
|
||||
gl.drawElements(draw_mode, (vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
|
||||
else
|
||||
gl.drawArrays(draw_mode, 0, vertex_count);
|
||||
uint32 enabledattribs = ATTRIBFLAG_POS;
|
||||
|
||||
if (overdraw)
|
||||
{
|
||||
// prepare colors:
|
||||
Color c = gl.getColor();
|
||||
Color *colors = new Color[overdraw_vertex_count];
|
||||
fill_color_array(colors, c);
|
||||
// Prepare per-vertex colors. Set the core to white, and the overdraw
|
||||
// line's colors to white on one side and transparent on the other.
|
||||
colors = new Color[total_vertex_count];
|
||||
memset(colors, 255, overdraw_vertex_start * sizeof(Color));
|
||||
fill_color_array(colors + overdraw_vertex_start);
|
||||
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
|
||||
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
|
||||
|
||||
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, overdraw);
|
||||
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 0, colors);
|
||||
enabledattribs |= ATTRIBFLAG_COLOR;
|
||||
}
|
||||
|
||||
if (use_quad_indices)
|
||||
gl.drawElements(draw_mode, (overdraw_vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
|
||||
else
|
||||
gl.drawArrays(draw_mode, 0, overdraw_vertex_count);
|
||||
gl.useVertexAttribArrays(enabledattribs);
|
||||
|
||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
|
||||
gl.setColor(c);
|
||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, vertices);
|
||||
|
||||
// Draw the core line and the overdraw in a single draw call. We can do this
|
||||
// because the vertex array contains both the core line and the overdraw
|
||||
// vertices.
|
||||
if (use_quad_indices)
|
||||
gl.drawElements(draw_mode, (int) (total_vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
|
||||
else
|
||||
gl.drawArrays(draw_mode, 0, (int) total_vertex_count);
|
||||
|
||||
if (overdraw)
|
||||
delete[] colors;
|
||||
}
|
||||
|
||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||
|
||||
delete[] indices;
|
||||
if (indices)
|
||||
delete[] indices;
|
||||
}
|
||||
|
||||
void Polyline::fill_color_array(Color *colors, const Color &c)
|
||||
void Polyline::fill_color_array(Color *colors)
|
||||
{
|
||||
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);
|
||||
colors[i] = {255, 255, 255, GLubyte(255 * ((i+1) % 2))};
|
||||
}
|
||||
}
|
||||
|
||||
void NoneJoinPolyline::fill_color_array(Color *colors, const Color &c)
|
||||
void NoneJoinPolyline::fill_color_array(Color *colors)
|
||||
{
|
||||
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);
|
||||
colors[i] = {255, 255, 255, GLubyte(255 * ((i+1) % 4 < 2))};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_POLYLINE_H
|
||||
#define LOVE_GRAPHICS_OPENGL_POLYLINE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/Vector.h"
|
||||
@@ -30,6 +28,10 @@
|
||||
// OpenGL
|
||||
#include "OpenGL.h"
|
||||
|
||||
// C++
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
@@ -44,8 +46,15 @@ namespace opengl
|
||||
class Polyline
|
||||
{
|
||||
public:
|
||||
|
||||
Polyline(GLenum mode = GL_TRIANGLE_STRIP, bool quadindices = false);
|
||||
Polyline(GLenum mode = GL_TRIANGLE_STRIP, bool quadindices = false)
|
||||
: vertices(nullptr)
|
||||
, overdraw(nullptr)
|
||||
, vertex_count(0)
|
||||
, overdraw_vertex_count(0)
|
||||
, draw_mode(mode)
|
||||
, use_quad_indices(quadindices)
|
||||
, overdraw_vertex_start(0)
|
||||
{}
|
||||
virtual ~Polyline();
|
||||
|
||||
/**
|
||||
@@ -63,8 +72,9 @@ public:
|
||||
void draw();
|
||||
|
||||
protected:
|
||||
virtual void calc_overdraw_vertex_count(bool is_looping);
|
||||
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 fill_color_array(Color *colors);
|
||||
|
||||
/** Calculate line boundary points.
|
||||
*
|
||||
@@ -87,6 +97,7 @@ protected:
|
||||
size_t overdraw_vertex_count;
|
||||
GLenum draw_mode;
|
||||
bool use_quad_indices;
|
||||
size_t overdraw_vertex_start;
|
||||
|
||||
}; // Polyline
|
||||
|
||||
@@ -99,22 +110,30 @@ class NoneJoinPolyline : public Polyline
|
||||
{
|
||||
public:
|
||||
NoneJoinPolyline()
|
||||
// Draw quads using GL_TRIANGLES and a vertex index array.
|
||||
: Polyline(GL_TRIANGLES, true)
|
||||
{}
|
||||
|
||||
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)
|
||||
for (size_t i = 0; i < vertex_count - 4; ++i)
|
||||
this->vertices[i] = this->vertices[i+2];
|
||||
vertex_count -= 2;
|
||||
|
||||
// The last quad is now garbage, so zero it out to make sure it doesn't
|
||||
// get rasterized. These vertices are in between the core line vertices
|
||||
// and the overdraw vertices in the combined vertex array, so they still
|
||||
// get "rendered" since we draw everything with one draw call.
|
||||
memset(&this->vertices[vertex_count - 4], 0, sizeof(love::Vector) * 4);
|
||||
|
||||
vertex_count -= 4;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void calc_overdraw_vertex_count(bool is_looping);
|
||||
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 fill_color_array(Color *colors);
|
||||
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);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -66,7 +67,6 @@ Shader *Shader::defaultShader = nullptr;
|
||||
|
||||
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
GLint Shader::maxTexUnits = 0;
|
||||
std::vector<int> Shader::textureCounters;
|
||||
|
||||
Shader::Shader(const ShaderSource &source)
|
||||
@@ -76,20 +76,14 @@ Shader::Shader(const ShaderSource &source)
|
||||
, builtinAttributes()
|
||||
, lastCanvas((Canvas *) -1)
|
||||
, lastViewport()
|
||||
, lastPointSize(0.0f)
|
||||
{
|
||||
if (source.vertex.empty() && source.pixel.empty())
|
||||
throw love::Exception("Cannot create shader: no source code!");
|
||||
|
||||
if (maxTexUnits <= 0)
|
||||
{
|
||||
GLint maxtexunits;
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtexunits);
|
||||
maxTexUnits = std::max(maxtexunits - 1, 0);
|
||||
}
|
||||
|
||||
// initialize global texture id counters if needed
|
||||
if (textureCounters.size() < (size_t) maxTexUnits)
|
||||
textureCounters.resize(maxTexUnits, 0);
|
||||
if ((int) textureCounters.size() < gl.getMaxTextureUnits() - 1)
|
||||
textureCounters.resize(gl.getMaxTextureUnits() - 1, 0);
|
||||
|
||||
// load shader source and create program object
|
||||
loadVolatile();
|
||||
@@ -129,9 +123,6 @@ GLuint Shader::compileCode(ShaderStage stage, const std::string &code)
|
||||
break;
|
||||
}
|
||||
|
||||
// clear existing errors
|
||||
while (glGetError() != GL_NO_ERROR);
|
||||
|
||||
GLuint shaderid = glCreateShader(glstage);
|
||||
|
||||
if (shaderid == 0)
|
||||
@@ -221,30 +212,37 @@ void Shader::mapActiveUniforms()
|
||||
|
||||
bool Shader::loadVolatile()
|
||||
{
|
||||
// Creating the shader will invalidate the uniforms that rely on these.
|
||||
lastCanvas = (Canvas *) -1;
|
||||
lastViewport = OpenGL::Viewport();
|
||||
OpenGL::TempDebugGroup debuggroup("Shader load");
|
||||
|
||||
// Recreating the shader program will invalidate uniforms that rely on these.
|
||||
lastCanvas = (Canvas *) -1;
|
||||
lastViewport = OpenGL::Viewport();
|
||||
|
||||
lastPointSize = -1.0f;
|
||||
|
||||
// Invalidate the cached matrices by setting some elements to NaN.
|
||||
float nan = std::numeric_limits<float>::quiet_NaN();
|
||||
lastProjectionMatrix.setTranslation(nan, nan);
|
||||
lastTransformMatrix.setTranslation(nan, nan);
|
||||
|
||||
// zero out active texture list
|
||||
activeTexUnits.clear();
|
||||
activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
|
||||
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
|
||||
|
||||
std::vector<GLuint> shaderids;
|
||||
|
||||
const ShaderSource *defaults = &defaultCode[Graphics::RENDERER_OPENGL];
|
||||
if (GLAD_ES_VERSION_2_0)
|
||||
defaults = &defaultCode[Graphics::RENDERER_OPENGLES];
|
||||
|
||||
// The shader program must have both vertex and pixel shader stages.
|
||||
const std::string &vertexcode = shaderSource.vertex.empty() ? defaults->vertex : shaderSource.vertex;
|
||||
const std::string &pixelcode = shaderSource.pixel.empty() ? defaults->pixel : shaderSource.pixel;
|
||||
|
||||
try
|
||||
{
|
||||
// All shader programs in ES2 must have a vertex and pixel shader.
|
||||
const ShaderSource &defaultESCode = defaultCode[Graphics::RENDERER_OPENGLES];
|
||||
|
||||
if (!shaderSource.vertex.empty())
|
||||
shaderids.push_back(compileCode(STAGE_VERTEX, shaderSource.vertex));
|
||||
else if (GLAD_ES_VERSION_2_0)
|
||||
shaderids.push_back(compileCode(STAGE_VERTEX, defaultESCode.vertex));
|
||||
|
||||
if (!shaderSource.pixel.empty())
|
||||
shaderids.push_back(compileCode(STAGE_PIXEL, shaderSource.pixel));
|
||||
else if (GLAD_ES_VERSION_2_0)
|
||||
shaderids.push_back(compileCode(STAGE_PIXEL, defaultESCode.pixel));
|
||||
shaderids.push_back(compileCode(STAGE_VERTEX, vertexcode));
|
||||
shaderids.push_back(compileCode(STAGE_PIXEL, pixelcode));
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
@@ -253,9 +251,6 @@ bool Shader::loadVolatile()
|
||||
throw;
|
||||
}
|
||||
|
||||
if (shaderids.empty())
|
||||
throw love::Exception("Cannot create shader: no valid source code!");
|
||||
|
||||
program = glCreateProgram();
|
||||
|
||||
if (program == 0)
|
||||
@@ -269,22 +264,10 @@ bool Shader::loadVolatile()
|
||||
glAttachShader(program, id);
|
||||
|
||||
// Bind generic vertex attribute indices to names in the shader.
|
||||
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
|
||||
for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
|
||||
{
|
||||
// Desktop OpenGL should ignore some of these.
|
||||
if (!GLAD_ES_VERSION_2_0 && i <= int(OpenGL::ATTRIB_COLOR))
|
||||
continue;
|
||||
|
||||
OpenGL::VertexAttrib attrib = (OpenGL::VertexAttrib) i;
|
||||
|
||||
// FIXME: We skip this both because pseudo-instancing is temporarily
|
||||
// disabled (see graphics.lua), and because binding a non-existant
|
||||
// attribute name to a location causes a shader linker warning.
|
||||
if (attrib == OpenGL::ATTRIB_PSEUDO_INSTANCE_ID)
|
||||
continue;
|
||||
|
||||
const char *name = nullptr;
|
||||
if (attribNames.find(attrib, name))
|
||||
if (attribNames.find((VertexAttribID) i, name))
|
||||
glBindAttribLocation(program, i, (const GLchar *) name);
|
||||
}
|
||||
|
||||
@@ -305,13 +288,13 @@ bool Shader::loadVolatile()
|
||||
throw love::Exception("Cannot link shader program object:\n%s", warnings.c_str());
|
||||
}
|
||||
|
||||
// Retrieve all active uniform variables in this shader from OpenGL.
|
||||
// Get all active uniform variables in this shader from OpenGL.
|
||||
mapActiveUniforms();
|
||||
|
||||
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
|
||||
for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
if (attribNames.find(OpenGL::VertexAttrib(i), name))
|
||||
if (attribNames.find(VertexAttribID(i), name))
|
||||
builtinAttributes[i] = glGetAttribLocation(program, name);
|
||||
else
|
||||
builtinAttributes[i] = -1;
|
||||
@@ -322,7 +305,7 @@ bool Shader::loadVolatile()
|
||||
// make sure glUseProgram gets called.
|
||||
current = nullptr;
|
||||
attach();
|
||||
checkSetScreenParams();
|
||||
checkSetBuiltinUniforms();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -348,7 +331,9 @@ void Shader::unloadVolatile()
|
||||
|
||||
// active texture list is probably invalid, clear it
|
||||
activeTexUnits.clear();
|
||||
activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
|
||||
activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0);
|
||||
|
||||
attributes.clear();
|
||||
|
||||
// same with uniform location list
|
||||
uniforms.clear();
|
||||
@@ -399,12 +384,10 @@ std::string Shader::getWarnings() const
|
||||
|
||||
void Shader::attach(bool temporary)
|
||||
{
|
||||
Shader *oldshader = current;
|
||||
if (oldshader != this)
|
||||
if (current != this)
|
||||
{
|
||||
glUseProgram(program);
|
||||
current = this;
|
||||
|
||||
// retain/release happens in Graphics::setShader.
|
||||
}
|
||||
|
||||
@@ -425,20 +408,18 @@ void Shader::attach(bool temporary)
|
||||
|
||||
void Shader::detach()
|
||||
{
|
||||
// We always need a shader set in ES2.
|
||||
if (GLAD_ES_VERSION_2_0)
|
||||
if (defaultShader)
|
||||
{
|
||||
if (defaultShader && current != defaultShader)
|
||||
if (current != defaultShader)
|
||||
defaultShader->attach();
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (current != nullptr)
|
||||
{
|
||||
glUseProgram(0);
|
||||
current = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (current != nullptr)
|
||||
glUseProgram(0);
|
||||
|
||||
current = nullptr;
|
||||
}
|
||||
|
||||
const Shader::Uniform &Shader::getUniform(const std::string &name) const
|
||||
@@ -452,72 +433,6 @@ const Shader::Uniform &Shader::getUniform(const std::string &name) const
|
||||
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)
|
||||
@@ -618,7 +533,7 @@ void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int
|
||||
|
||||
void Shader::sendTexture(const std::string &name, Texture *texture)
|
||||
{
|
||||
GLuint gltex = texture->getGLTexture();
|
||||
GLuint gltex = *(GLuint *) texture->getHandle();
|
||||
|
||||
TemporaryAttacher attacher(this);
|
||||
|
||||
@@ -628,13 +543,10 @@ void Shader::sendTexture(const std::string &name, Texture *texture)
|
||||
checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
|
||||
|
||||
// bind texture to assigned texture unit and send uniform to shader program
|
||||
gl.bindTextureToUnit(gltex, texunit, false);
|
||||
gl.bindTextureToUnit(gltex, texunit, true);
|
||||
|
||||
glUniform1i(u.location, texunit);
|
||||
|
||||
// reset texture unit
|
||||
gl.setTextureUnit(0);
|
||||
|
||||
// increment global shader texture id counter for this texture unit, if we haven't already
|
||||
if (activeTexUnits[texunit-1] == 0)
|
||||
++textureCounters[texunit-1];
|
||||
@@ -647,11 +559,12 @@ void Shader::sendTexture(const std::string &name, Texture *texture)
|
||||
|
||||
void Shader::retainObject(const std::string &name, Object *object)
|
||||
{
|
||||
object->retain();
|
||||
|
||||
auto it = boundRetainables.find(name);
|
||||
if (it != boundRetainables.end())
|
||||
it->second->release();
|
||||
|
||||
object->retain();
|
||||
boundRetainables[name] = object;
|
||||
}
|
||||
|
||||
@@ -670,7 +583,7 @@ int Shader::getTextureUnit(const std::string &name)
|
||||
if (freeunit_it != textureCounters.end())
|
||||
{
|
||||
// we don't want to use unit 0
|
||||
texunit = std::distance(textureCounters.begin(), freeunit_it) + 1;
|
||||
texunit = (int) std::distance(textureCounters.begin(), freeunit_it) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -681,7 +594,7 @@ int Shader::getTextureUnit(const std::string &name)
|
||||
throw love::Exception("No more texture units available for shader.");
|
||||
|
||||
// we don't want to use unit 0
|
||||
texunit = std::distance(activeTexUnits.begin(), nextunit_it) + 1;
|
||||
texunit = (int) std::distance(activeTexUnits.begin(), nextunit_it) + 1;
|
||||
}
|
||||
|
||||
texUnitPool[name] = texunit;
|
||||
@@ -705,73 +618,23 @@ Shader::UniformType Shader::getExternVariable(const std::string &name, int &comp
|
||||
return it->second.baseType;
|
||||
}
|
||||
|
||||
bool Shader::hasVertexAttrib(OpenGL::VertexAttrib attrib) const
|
||||
GLint Shader::getAttribLocation(const std::string &name)
|
||||
{
|
||||
auto it = attributes.find(name);
|
||||
if (it != attributes.end())
|
||||
return it->second;
|
||||
|
||||
GLint location = glGetAttribLocation(program, name.c_str());
|
||||
|
||||
attributes[name] = location;
|
||||
return location;
|
||||
}
|
||||
|
||||
bool Shader::hasVertexAttrib(VertexAttribID attrib) const
|
||||
{
|
||||
return builtinAttributes[int(attrib)] != -1;
|
||||
}
|
||||
|
||||
bool Shader::hasBuiltinUniform(BuiltinUniform builtin) const
|
||||
{
|
||||
return builtinUniforms[int(builtin)] != -1;
|
||||
}
|
||||
|
||||
bool Shader::sendBuiltinFloat(BuiltinUniform builtin, int size, const GLfloat *vec, int count)
|
||||
{
|
||||
if (!hasBuiltinUniform(builtin))
|
||||
return false;
|
||||
|
||||
GLint location = builtinUniforms[int(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;
|
||||
}
|
||||
|
||||
bool Shader::sendBuiltinMatrix(BuiltinUniform 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;
|
||||
}
|
||||
|
||||
void Shader::checkSetScreenParams()
|
||||
{
|
||||
OpenGL::Viewport view = gl.getViewport();
|
||||
@@ -780,33 +643,115 @@ void Shader::checkSetScreenParams()
|
||||
return;
|
||||
|
||||
// In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w.
|
||||
// This lets us flip pixcoord.y when needed, to be consistent (Canvases
|
||||
// have flipped y-values for pixel coordinates.)
|
||||
// This lets us flip pixcoord.y when needed, to be consistent (drawing with
|
||||
// no Canvas active makes the y-values for pixel coordinates flipped.)
|
||||
GLfloat params[] = {
|
||||
(GLfloat) view.w, (GLfloat) view.h,
|
||||
0.0f, 0.0f,
|
||||
};
|
||||
|
||||
if (Canvas::current != nullptr)
|
||||
{
|
||||
// gl_FragCoord.y is flipped in Canvases, so we un-flip:
|
||||
// pixcoord.y = gl_FragCoord.y * -1.0 + height.
|
||||
params[2] = -1.0f;
|
||||
params[3] = (GLfloat) view.h;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
|
||||
params[2] = 1.0f;
|
||||
params[3] = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// gl_FragCoord.y is flipped when drawing to the screen, so we un-flip:
|
||||
// pixcoord.y = gl_FragCoord.y * -1.0 + height.
|
||||
params[2] = -1.0f;
|
||||
params[3] = (GLfloat) view.h;
|
||||
}
|
||||
|
||||
sendBuiltinFloat(BUILTIN_SCREEN_SIZE, 4, params, 1);
|
||||
GLint location = builtinUniforms[BUILTIN_SCREEN_SIZE];
|
||||
|
||||
if (location >= 0)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
glUniform4fv(location, 1, params);
|
||||
}
|
||||
|
||||
lastCanvas = Canvas::current;
|
||||
lastViewport = view;
|
||||
}
|
||||
|
||||
void Shader::checkSetPointSize(float size)
|
||||
{
|
||||
if (size == lastPointSize)
|
||||
return;
|
||||
|
||||
GLint location = builtinUniforms[BUILTIN_POINT_SIZE];
|
||||
|
||||
if (location >= 0)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
glUniform1f(location, size);
|
||||
}
|
||||
|
||||
lastPointSize = size;
|
||||
}
|
||||
|
||||
void Shader::checkSetBuiltinUniforms()
|
||||
{
|
||||
checkSetScreenParams();
|
||||
|
||||
// We use a more efficient method for sending transformation matrices to
|
||||
// the GPU on desktop GL.
|
||||
if (GLAD_ES_VERSION_2_0)
|
||||
{
|
||||
checkSetPointSize(gl.getPointSize());
|
||||
|
||||
const Matrix4 &curxform = gl.matrices.transform.back();
|
||||
const Matrix4 &curproj = gl.matrices.projection.back();
|
||||
|
||||
TemporaryAttacher attacher(this);
|
||||
|
||||
bool tpmatrixneedsupdate = false;
|
||||
|
||||
// Only upload the matrices if they've changed.
|
||||
if (memcmp(curxform.getElements(), lastTransformMatrix.getElements(), sizeof(float) * 16) != 0)
|
||||
{
|
||||
GLint location = builtinUniforms[BUILTIN_TRANSFORM_MATRIX];
|
||||
if (location >= 0)
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, curxform.getElements());
|
||||
|
||||
// Also upload the re-calculated normal matrix, if possible. The
|
||||
// normal matrix is the transpose of the inverse of the rotation
|
||||
// portion (top-left 3x3) of the transform matrix.
|
||||
location = builtinUniforms[BUILTIN_NORMAL_MATRIX];
|
||||
if (location >= 0)
|
||||
{
|
||||
Matrix3 normalmatrix = Matrix3(curxform).transposedInverse();
|
||||
glUniformMatrix3fv(location, 1, GL_FALSE, normalmatrix.getElements());
|
||||
}
|
||||
|
||||
tpmatrixneedsupdate = true;
|
||||
lastTransformMatrix = curxform;
|
||||
}
|
||||
|
||||
if (memcmp(curproj.getElements(), lastProjectionMatrix.getElements(), sizeof(float) * 16) != 0)
|
||||
{
|
||||
GLint location = builtinUniforms[BUILTIN_PROJECTION_MATRIX];
|
||||
if (location >= 0)
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, curproj.getElements());
|
||||
|
||||
tpmatrixneedsupdate = true;
|
||||
lastProjectionMatrix = curproj;
|
||||
}
|
||||
|
||||
if (tpmatrixneedsupdate)
|
||||
{
|
||||
GLint location = builtinUniforms[BUILTIN_TRANSFORM_PROJECTION_MATRIX];
|
||||
if (location >= 0)
|
||||
{
|
||||
Matrix4 tp_matrix(curproj * curxform);
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, tp_matrix.getElements());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<std::string, Object *> &Shader::getBoundRetainables() const
|
||||
{
|
||||
return boundRetainables;
|
||||
@@ -814,11 +759,7 @@ const std::map<std::string, Object *> &Shader::getBoundRetainables() const
|
||||
|
||||
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);
|
||||
const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
|
||||
if (tmp == nullptr)
|
||||
return "0.0";
|
||||
@@ -835,7 +776,69 @@ std::string Shader::getGLSLVersion()
|
||||
|
||||
bool Shader::isSupported()
|
||||
{
|
||||
return GLAD_ES_VERSION_2_0 || (GLAD_VERSION_2_0 && getGLSLVersion() >= "1.2");
|
||||
return GLAD_ES_VERSION_2_0 || (getGLSLVersion() >= "1.2");
|
||||
}
|
||||
|
||||
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:
|
||||
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:
|
||||
return UNIFORM_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::getConstant(const char *in, UniformType &out)
|
||||
@@ -848,6 +851,16 @@ bool Shader::getConstant(UniformType in, const char *&out)
|
||||
return uniformTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Shader::getConstant(const char *in, VertexAttribID &out)
|
||||
{
|
||||
return attribNames.find(in, out);
|
||||
}
|
||||
|
||||
bool Shader::getConstant(VertexAttribID in, const char *&out)
|
||||
{
|
||||
return attribNames.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Shader::ShaderStage, Shader::STAGE_MAX_ENUM>::Entry Shader::stageNameEntries[] =
|
||||
{
|
||||
{"vertex", Shader::STAGE_VERTEX},
|
||||
@@ -867,21 +880,22 @@ StringMap<Shader::UniformType, Shader::UNIFORM_MAX_ENUM>::Entry Shader::uniformT
|
||||
|
||||
StringMap<Shader::UniformType, Shader::UNIFORM_MAX_ENUM> Shader::uniformTypes(Shader::uniformTypeEntries, sizeof(Shader::uniformTypeEntries));
|
||||
|
||||
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
|
||||
StringMap<VertexAttribID, ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
|
||||
{
|
||||
{"VertexPosition", OpenGL::ATTRIB_POS},
|
||||
{"VertexTexCoord", OpenGL::ATTRIB_TEXCOORD},
|
||||
{"VertexColor", OpenGL::ATTRIB_COLOR},
|
||||
{"love_PseudoInstanceID", OpenGL::ATTRIB_PSEUDO_INSTANCE_ID},
|
||||
{"VertexPosition", ATTRIB_POS},
|
||||
{"VertexTexCoord", ATTRIB_TEXCOORD},
|
||||
{"VertexColor", ATTRIB_COLOR},
|
||||
{"ConstantColor", ATTRIB_CONSTANTCOLOR},
|
||||
};
|
||||
|
||||
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
|
||||
StringMap<VertexAttribID, ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
|
||||
|
||||
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
|
||||
{
|
||||
{"TransformMatrix", Shader::BUILTIN_TRANSFORM_MATRIX},
|
||||
{"ProjectionMatrix", Shader::BUILTIN_PROJECTION_MATRIX},
|
||||
{"TransformProjectionMatrix", Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX},
|
||||
{"NormalMatrix", Shader::BUILTIN_NORMAL_MATRIX},
|
||||
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
|
||||
{"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE},
|
||||
};
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
#include "common/Object.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "OpenGL.h"
|
||||
#include "Texture.h"
|
||||
|
||||
// STL
|
||||
#include <string>
|
||||
@@ -47,12 +48,6 @@ 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 ShaderStage
|
||||
{
|
||||
STAGE_VERTEX,
|
||||
@@ -66,6 +61,7 @@ public:
|
||||
BUILTIN_TRANSFORM_MATRIX = 0,
|
||||
BUILTIN_PROJECTION_MATRIX,
|
||||
BUILTIN_TRANSFORM_PROJECTION_MATRIX,
|
||||
BUILTIN_NORMAL_MATRIX,
|
||||
BUILTIN_POINT_SIZE,
|
||||
BUILTIN_SCREEN_SIZE,
|
||||
BUILTIN_MAX_ENUM
|
||||
@@ -88,6 +84,15 @@ public:
|
||||
std::string pixel;
|
||||
};
|
||||
|
||||
// Pointer to currently active Shader.
|
||||
static Shader *current;
|
||||
|
||||
// Pointer to the default Shader.
|
||||
static Shader *defaultShader;
|
||||
|
||||
// Default shader code (a shader is always required internally.)
|
||||
static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
/**
|
||||
* Creates a new Shader using a list of source codes.
|
||||
* Source must contain either vertex or pixel shader code, or both.
|
||||
@@ -170,26 +175,28 @@ public:
|
||||
**/
|
||||
UniformType getExternVariable(const std::string &name, int &components, int &count);
|
||||
|
||||
GLint getAttribLocation(const std::string &name);
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
**/
|
||||
bool hasVertexAttrib(OpenGL::VertexAttrib attrib) const;
|
||||
bool hasBuiltinUniform(BuiltinUniform builtin) const;
|
||||
bool sendBuiltinMatrix(BuiltinUniform builtin, int size, const GLfloat *m, int count);
|
||||
bool sendBuiltinFloat(BuiltinUniform builtin, int size, const GLfloat *m, int count);
|
||||
bool hasVertexAttrib(VertexAttribID attrib) const;
|
||||
|
||||
void checkSetScreenParams();
|
||||
void checkSetPointSize(float size);
|
||||
void checkSetBuiltinUniforms();
|
||||
|
||||
const std::map<std::string, Object *> &getBoundRetainables() const;
|
||||
|
||||
static std::string getGLSLVersion();
|
||||
static bool isSupported();
|
||||
|
||||
// Default code used when renderers require code for a shader stage.
|
||||
static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
static bool getConstant(const char *in, UniformType &out);
|
||||
static bool getConstant(UniformType in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, VertexAttribID &out);
|
||||
static bool getConstant(VertexAttribID in, const char *&out);
|
||||
|
||||
private:
|
||||
|
||||
// Represents a single uniform/extern shader variable.
|
||||
@@ -233,7 +240,9 @@ private:
|
||||
GLint builtinUniforms[BUILTIN_MAX_ENUM];
|
||||
|
||||
// Location values for any generic vertex attribute variables.
|
||||
GLint builtinAttributes[OpenGL::ATTRIB_MAX_ENUM];
|
||||
GLint builtinAttributes[ATTRIB_MAX_ENUM];
|
||||
|
||||
std::map<std::string, GLint> attributes;
|
||||
|
||||
// Uniform location buffer map
|
||||
std::map<std::string, Uniform> uniforms;
|
||||
@@ -249,8 +258,10 @@ private:
|
||||
Canvas *lastCanvas;
|
||||
OpenGL::Viewport lastViewport;
|
||||
|
||||
// Max GPU texture units available for sent images
|
||||
static GLint maxTexUnits;
|
||||
float lastPointSize;
|
||||
|
||||
Matrix4 lastTransformMatrix;
|
||||
Matrix4 lastProjectionMatrix;
|
||||
|
||||
// Counts total number of textures bound to each texture unit in all shaders
|
||||
static std::vector<int> textureCounters;
|
||||
@@ -262,13 +273,13 @@ private:
|
||||
static StringMap<UniformType, UNIFORM_MAX_ENUM> uniformTypes;
|
||||
|
||||
// Names for the generic vertex attributes used by love.
|
||||
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
|
||||
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> attribNames;
|
||||
static StringMap<VertexAttribID, ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
|
||||
static StringMap<VertexAttribID, ATTRIB_MAX_ENUM> attribNames;
|
||||
|
||||
// Names for the built-in uniform variables.
|
||||
static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
|
||||
static StringMap<BuiltinUniform, BUILTIN_MAX_ENUM> builtinNames;
|
||||
|
||||
}; // Shader
|
||||
};
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
#include "OpenGL.h"
|
||||
|
||||
// LOVE
|
||||
#include "VertexBuffer.h"
|
||||
#include "Texture.h"
|
||||
#include "GLBuffer.h"
|
||||
#include "graphics/Texture.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
@@ -41,39 +41,26 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
|
||||
SpriteBatch::SpriteBatch(Texture *texture, int size, Mesh::Usage usage)
|
||||
: texture(texture)
|
||||
, size(size)
|
||||
, next(0)
|
||||
, color(0)
|
||||
, array_buf(nullptr)
|
||||
, element_buf(size)
|
||||
, quad_indices(size)
|
||||
, buffer_used_offset(0)
|
||||
, buffer_used_size(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;
|
||||
}
|
||||
GLenum gl_usage = Mesh::getGLBufferUsage(usage);
|
||||
|
||||
const size_t vertex_size = sizeof(Vertex) * 4 * size;
|
||||
|
||||
try
|
||||
{
|
||||
array_buf = VertexBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
|
||||
array_buf = new GLBuffer(vertex_size, nullptr, GL_ARRAY_BUFFER, gl_usage);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
@@ -99,7 +86,7 @@ int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, fl
|
||||
if ((index == -1 && next >= size) || index < -1 || index >= size)
|
||||
return -1;
|
||||
|
||||
Matrix t(x, y, a, sx, sy, ox, oy, kx, ky);
|
||||
Matrix4 t(x, y, a, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
addv(texture->getVertices(), t, (index == -1) ? next : index);
|
||||
|
||||
@@ -116,7 +103,7 @@ int SpriteBatch::addq(Quad *quad, float x, float y, float a, float sx, float sy,
|
||||
if ((index == -1 && next >= size) || index < -1 || index >= next)
|
||||
return -1;
|
||||
|
||||
Matrix t(x, y, a, sx, sy, ox, oy, kx, ky);
|
||||
Matrix4 t(x, y, a, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
addv(quad->getVertices(), t, (index == -1) ? next : index);
|
||||
|
||||
@@ -135,7 +122,7 @@ void SpriteBatch::clear()
|
||||
|
||||
void SpriteBatch::flush()
|
||||
{
|
||||
VertexBuffer::Bind bind(*array_buf);
|
||||
GLBuffer::Bind bind(*array_buf);
|
||||
array_buf->unmap(buffer_used_offset, buffer_used_size);
|
||||
|
||||
buffer_used_offset = buffer_used_size = 0;
|
||||
@@ -162,7 +149,7 @@ void SpriteBatch::setColor(const Color &color)
|
||||
void SpriteBatch::setColor()
|
||||
{
|
||||
delete color;
|
||||
color = 0;
|
||||
color = nullptr;
|
||||
}
|
||||
|
||||
const Color *SpriteBatch::getColor() const
|
||||
@@ -183,26 +170,26 @@ void SpriteBatch::setBufferSize(int newsize)
|
||||
if (newsize == size)
|
||||
return;
|
||||
|
||||
// Map (lock) the old VertexBuffer to get a pointer to its data.
|
||||
// Map the old GLBuffer to get a pointer to its data.
|
||||
void *old_data = nullptr;
|
||||
{
|
||||
VertexBuffer::Bind bind(*array_buf);
|
||||
GLBuffer::Bind bind(*array_buf);
|
||||
old_data = array_buf->map();
|
||||
}
|
||||
|
||||
size_t vertex_size = sizeof(Vertex) * 4 * newsize;
|
||||
VertexBuffer *new_array_buf = nullptr;
|
||||
GLBuffer *new_array_buf = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
|
||||
new_array_buf = new GLBuffer(vertex_size, nullptr, array_buf->getTarget(), array_buf->getUsage());
|
||||
|
||||
// Copy as much of the old data into the new VertexBuffer as can fit.
|
||||
VertexBuffer::Bind bind(*new_array_buf);
|
||||
// Copy as much of the old data into the new GLBuffer as can fit.
|
||||
GLBuffer::Bind bind(*new_array_buf);
|
||||
void *new_data = new_array_buf->map();
|
||||
memcpy(new_data, old_data, sizeof(Vertex) * 4 * std::min(newsize, size));
|
||||
|
||||
element_buf = VertexIndex(newsize);
|
||||
quad_indices = QuadIndices(newsize);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
@@ -210,7 +197,7 @@ void SpriteBatch::setBufferSize(int newsize)
|
||||
throw;
|
||||
}
|
||||
|
||||
// We don't need to unmap the old VertexBuffer since we're deleting it.
|
||||
// We don't need to unmap the old GLBuffer since we're deleting it.
|
||||
delete array_buf;
|
||||
|
||||
array_buf = new_array_buf;
|
||||
@@ -226,58 +213,48 @@ int SpriteBatch::getBufferSize() const
|
||||
|
||||
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
const size_t pos_offset = offsetof(Vertex, x);
|
||||
const size_t pos_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(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
OpenGL::TempDebugGroup debuggroup("SpriteBatch draw");
|
||||
|
||||
Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
OpenGL::TempTransform transform(gl);
|
||||
transform.get() *= t;
|
||||
|
||||
texture->predraw();
|
||||
gl.bindTexture(*(GLuint *) texture->getHandle());
|
||||
|
||||
VertexBuffer::Bind array_bind(*array_buf);
|
||||
VertexBuffer::Bind element_bind(*element_buf.getVertexBuffer());
|
||||
GLBuffer::Bind array_bind(*array_buf);
|
||||
GLBuffer::Bind element_bind(*quad_indices.getBuffer());
|
||||
|
||||
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
||||
array_buf->unmap(buffer_used_offset, buffer_used_size);
|
||||
buffer_used_offset = buffer_used_size = 0;
|
||||
|
||||
Color curcolor = gl.getColor();
|
||||
uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
|
||||
|
||||
// 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));
|
||||
enabledattribs |= ATTRIBFLAG_COLOR;
|
||||
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
|
||||
}
|
||||
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(pos_offset));
|
||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
|
||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
|
||||
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
|
||||
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
|
||||
gl.useVertexAttribArrays(enabledattribs);
|
||||
|
||||
gl.prepareDraw();
|
||||
gl.drawElements(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);
|
||||
}
|
||||
|
||||
texture->postdraw();
|
||||
gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(next), quad_indices.getType(), quad_indices.getPointer(0));
|
||||
}
|
||||
|
||||
void SpriteBatch::addv(const Vertex *v, const Matrix &m, int index)
|
||||
void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index)
|
||||
{
|
||||
// Needed for colors.
|
||||
Vertex sprite[4] = {v[0], v[1], v[2], v[3]};
|
||||
@@ -288,7 +265,7 @@ void SpriteBatch::addv(const Vertex *v, const Matrix &m, int index)
|
||||
if (color)
|
||||
setColorv(sprite, *color);
|
||||
|
||||
VertexBuffer::Bind bind(*array_buf);
|
||||
GLBuffer::Bind bind(*array_buf);
|
||||
|
||||
// Always keep the VBO mapped when adding data for now (it'll be unmapped
|
||||
// on draw.)
|
||||
@@ -311,25 +288,6 @@ void SpriteBatch::setColorv(Vertex *v, const Color &color)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -27,36 +27,29 @@
|
||||
// LOVE
|
||||
#include "common/math.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"
|
||||
#include "VertexBuffer.h"
|
||||
#include "GLBuffer.h"
|
||||
#include "Mesh.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
class Texture;
|
||||
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class SpriteBatch : public Drawable
|
||||
{
|
||||
public:
|
||||
|
||||
enum UsageHint
|
||||
{
|
||||
USAGE_DYNAMIC,
|
||||
USAGE_STATIC,
|
||||
USAGE_STREAM,
|
||||
USAGE_MAX_ENUM
|
||||
};
|
||||
|
||||
SpriteBatch(Texture *texture, int size, int usage);
|
||||
SpriteBatch(Texture *texture, int size, Mesh::Usage 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);
|
||||
@@ -108,12 +101,9 @@ public:
|
||||
// Implements Drawable.
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
static bool getConstant(const char *in, UsageHint &out);
|
||||
static bool getConstant(UsageHint in, const char *&out);
|
||||
|
||||
private:
|
||||
|
||||
void addv(const Vertex *v, const Matrix &m, int index);
|
||||
void addv(const Vertex *v, const Matrix4 &m, int index);
|
||||
|
||||
/**
|
||||
* Set the color for vertices.
|
||||
@@ -136,16 +126,13 @@ private:
|
||||
// added sprite.
|
||||
Color *color;
|
||||
|
||||
VertexBuffer *array_buf;
|
||||
VertexIndex element_buf;
|
||||
GLBuffer *array_buf;
|
||||
QuadIndices quad_indices;
|
||||
|
||||
// The portion of the vertex buffer that's been modified while mapped.
|
||||
size_t buffer_used_offset;
|
||||
size_t buffer_used_size;
|
||||
|
||||
static StringMap<UsageHint, USAGE_MAX_ENUM>::Entry usageHintEntries[];
|
||||
static StringMap<UsageHint, USAGE_MAX_ENUM> usageHints;
|
||||
|
||||
}; // SpriteBatch
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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 "Text.h"
|
||||
#include "common/Matrix.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Text::Text(Font *font, const std::string &text)
|
||||
: font(font)
|
||||
, vbo(nullptr)
|
||||
, text_info()
|
||||
, vert_offset(0)
|
||||
, texture_cache_id((uint32) -1)
|
||||
{
|
||||
set(text);
|
||||
}
|
||||
|
||||
Text::~Text()
|
||||
{
|
||||
delete vbo;
|
||||
}
|
||||
|
||||
void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
|
||||
{
|
||||
size_t offset = vertoffset * sizeof(Font::GlyphVertex);
|
||||
size_t datasize = vertices.size() * sizeof(Font::GlyphVertex);
|
||||
uint8 *vbodata = nullptr;
|
||||
|
||||
// If we haven't created a VBO or the vertices are too big, make a new one.
|
||||
if (datasize > 0 && (!vbo || (offset + datasize) > vbo->getSize()))
|
||||
{
|
||||
// Make it bigger than necessary to reduce potential future allocations.
|
||||
size_t newsize = size_t((offset + datasize) * 1.5);
|
||||
if (vbo != nullptr)
|
||||
newsize = std::max(size_t(vbo->getSize() * 1.5), newsize);
|
||||
|
||||
GLBuffer *new_vbo = new GLBuffer(newsize, nullptr, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW);
|
||||
|
||||
if (vbo != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
vbodata = (uint8 *) vbo->map();
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
delete new_vbo;
|
||||
throw;
|
||||
}
|
||||
|
||||
GLBuffer::Bind bind(*new_vbo);
|
||||
new_vbo->fill(0, vbo->getSize(), vbodata);
|
||||
}
|
||||
|
||||
delete vbo;
|
||||
vbo = new_vbo;
|
||||
}
|
||||
|
||||
if (vbo != nullptr)
|
||||
{
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
vbodata = (uint8 *) vbo->map();
|
||||
memcpy(vbodata + offset, &vertices[0], datasize);
|
||||
// We unmap when we draw, to avoid unnecessary full map()/unmap() calls.
|
||||
}
|
||||
}
|
||||
|
||||
void Text::regenerateVertices()
|
||||
{
|
||||
// If the font's texture cache was invalidated then we need to recreate the
|
||||
// text's vertices, since glyph texcoords might have changed.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
{
|
||||
std::vector<TextData> textdata = text_data;
|
||||
|
||||
clear();
|
||||
|
||||
for (const TextData &t : textdata)
|
||||
addTextData(t);
|
||||
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::addTextData(const TextData &t)
|
||||
{
|
||||
std::vector<Font::GlyphVertex> vertices;
|
||||
std::vector<Font::DrawCommand> new_commands;
|
||||
|
||||
// We only have formatted text if the align mode is valid.
|
||||
if (t.align == Font::ALIGN_MAX_ENUM)
|
||||
new_commands = font->generateVertices(t.text, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info);
|
||||
else
|
||||
new_commands = font->generateVerticesFormatted(t.text, t.wrap, t.align, vertices, &text_info);
|
||||
|
||||
if (t.use_matrix)
|
||||
t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size());
|
||||
|
||||
size_t voffset = vert_offset;
|
||||
|
||||
if (!t.append_vertices)
|
||||
{
|
||||
voffset = 0;
|
||||
draw_commands.clear();
|
||||
}
|
||||
|
||||
uploadVertices(vertices, voffset);
|
||||
|
||||
if (!new_commands.empty())
|
||||
{
|
||||
// The start vertex should be adjusted to account for the vertex offset.
|
||||
for (Font::DrawCommand &cmd : new_commands)
|
||||
cmd.startvertex += (int) voffset;
|
||||
|
||||
auto firstcmd = new_commands.begin();
|
||||
|
||||
// If the first draw command in the new list has the same texture as the
|
||||
// last one in the existing list we're building and its vertices are
|
||||
// in-order, we can combine them (saving a draw call.)
|
||||
if (!draw_commands.empty())
|
||||
{
|
||||
auto prevcmd = draw_commands.back();
|
||||
if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
|
||||
{
|
||||
draw_commands.back().vertexcount += firstcmd->vertexcount;
|
||||
++firstcmd;
|
||||
}
|
||||
}
|
||||
|
||||
// Append the new draw commands to the list we're building.
|
||||
draw_commands.insert(draw_commands.end(), firstcmd, new_commands.end());
|
||||
}
|
||||
|
||||
vert_offset = voffset + vertices.size();
|
||||
text_data.push_back(t);
|
||||
|
||||
// Font::generateVertices can invalidate the font's texture cache.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
void Text::set(const std::string &text)
|
||||
{
|
||||
if (text.empty())
|
||||
return set();
|
||||
|
||||
addTextData({text, -1.0f, Font::ALIGN_MAX_ENUM, false, false, Matrix4()});
|
||||
}
|
||||
|
||||
void Text::set(const std::string &text, float wrap, Font::AlignMode align)
|
||||
{
|
||||
if (text.empty())
|
||||
return set();
|
||||
|
||||
addTextData({text, wrap, align, false, false, Matrix4()});
|
||||
}
|
||||
|
||||
void Text::set()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void Text::add(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
if (text.empty())
|
||||
return;
|
||||
|
||||
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
addTextData({text, -1.0f, Font::ALIGN_MAX_ENUM, true, true, m});
|
||||
}
|
||||
|
||||
void Text::addf(const std::string &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
if (text.empty())
|
||||
return;
|
||||
|
||||
Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
addTextData({text, wrap, align, true, true, m});
|
||||
}
|
||||
|
||||
void Text::clear()
|
||||
{
|
||||
text_data.clear();
|
||||
draw_commands.clear();
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
text_info = {};
|
||||
vert_offset = 0;
|
||||
}
|
||||
|
||||
void Text::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
if (vbo == nullptr || draw_commands.empty())
|
||||
return;
|
||||
|
||||
OpenGL::TempDebugGroup debuggroup("Text object draw");
|
||||
|
||||
// Re-generate the text if the Font's texture cache was invalidated.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
regenerateVertices();
|
||||
|
||||
const size_t pos_offset = offsetof(Font::GlyphVertex, x);
|
||||
const size_t tex_offset = offsetof(Font::GlyphVertex, s);
|
||||
const size_t stride = sizeof(Font::GlyphVertex);
|
||||
|
||||
Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
OpenGL::TempTransform transform(gl);
|
||||
transform.get() *= t;
|
||||
|
||||
{
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
vbo->unmap(); // Make sure all pending data is flushed to the GPU.
|
||||
|
||||
// Font::drawVertices expects AttribPointer calls to be done already.
|
||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset));
|
||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(tex_offset));
|
||||
}
|
||||
|
||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
|
||||
|
||||
font->drawVertices(draw_commands);
|
||||
}
|
||||
|
||||
void Text::setFont(Font *f)
|
||||
{
|
||||
font.set(f);
|
||||
|
||||
// Invalidate the texture cache ID since the font is different. We also have
|
||||
// to re-upload all the vertices based on the new font's textures.
|
||||
texture_cache_id = (uint32) -1;
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
Font *Text::getFont() const
|
||||
{
|
||||
return font.get();
|
||||
}
|
||||
|
||||
int Text::getWidth() const
|
||||
{
|
||||
return text_info.width;
|
||||
}
|
||||
|
||||
int Text::getHeight() const
|
||||
{
|
||||
return text_info.height;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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_TEXT_H
|
||||
#define LOVE_GRAPHICS_OPENGL_TEXT_H
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "Font.h"
|
||||
#include "GLBuffer.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class Text : public Drawable
|
||||
{
|
||||
public:
|
||||
|
||||
Text(Font *font, const std::string &text = "");
|
||||
virtual ~Text();
|
||||
|
||||
void set(const std::string &text);
|
||||
void set(const std::string &text, float wrap, Font::AlignMode align);
|
||||
void set();
|
||||
|
||||
void add(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
void addf(const std::string &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
void clear();
|
||||
|
||||
// Implements Drawable.
|
||||
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
void setFont(Font *f);
|
||||
Font *getFont() const;
|
||||
|
||||
/**
|
||||
* Gets the width of the currently set text.
|
||||
**/
|
||||
int getWidth() const;
|
||||
|
||||
/**
|
||||
* Gets the height of the currently set text.
|
||||
**/
|
||||
int getHeight() const;
|
||||
|
||||
private:
|
||||
|
||||
struct TextData
|
||||
{
|
||||
std::string text;
|
||||
float wrap;
|
||||
Font::AlignMode align;
|
||||
bool use_matrix;
|
||||
bool append_vertices;
|
||||
Matrix4 matrix;
|
||||
};
|
||||
|
||||
void uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset);
|
||||
void regenerateVertices();
|
||||
void addTextData(const TextData &s);
|
||||
|
||||
StrongRef<Font> font;
|
||||
GLBuffer *vbo;
|
||||
|
||||
std::vector<Font::DrawCommand> draw_commands;
|
||||
|
||||
std::vector<TextData> text_data;
|
||||
Font::TextInfo text_info;
|
||||
|
||||
size_t vert_offset;
|
||||
|
||||
// Used so we know when the font's texture cache is invalidated.
|
||||
uint32 texture_cache_id;
|
||||
|
||||
}; // Text
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_TEXT_H
|
||||
@@ -1,486 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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 <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(size_t /*usedOffset*/, size_t /*usedSize*/)
|
||||
{
|
||||
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)
|
||||
// FIXME:
|
||||
// ES2 can't do glGetBufferSubData.
|
||||
: VertexBuffer(size, target, usage, GLAD_ES_VERSION_2_0 ? BACKING_FULL : backing)
|
||||
, vbo(0)
|
||||
, memory_map(nullptr)
|
||||
, 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 = (char *) 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 = (char * ) malloc(getSize());
|
||||
if (!memory_map)
|
||||
throw love::Exception("Out of memory (oh the humanity!)");
|
||||
}
|
||||
|
||||
if (is_dirty)
|
||||
{
|
||||
glGetBufferSubData(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
|
||||
is_dirty = false;
|
||||
}
|
||||
|
||||
is_mapped = true;
|
||||
|
||||
return memory_map;
|
||||
}
|
||||
|
||||
void VBO::unmapStatic(size_t offset, size_t size)
|
||||
{
|
||||
// Upload the mapped data to the buffer.
|
||||
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
|
||||
}
|
||||
|
||||
void VBO::unmapStream()
|
||||
{
|
||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), nullptr, getUsage());
|
||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
||||
}
|
||||
|
||||
void VBO::unmap(size_t usedOffset, size_t usedSize)
|
||||
{
|
||||
if (!is_mapped)
|
||||
return;
|
||||
|
||||
usedOffset = std::min(usedOffset, getSize());
|
||||
usedSize = std::min(usedSize, getSize() - usedOffset);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
switch (getUsage())
|
||||
{
|
||||
case GL_STATIC_DRAW:
|
||||
unmapStatic(usedOffset, usedSize);
|
||||
break;
|
||||
case GL_STREAM_DRAW:
|
||||
unmapStream();
|
||||
break;
|
||||
case GL_DYNAMIC_DRAW:
|
||||
default:
|
||||
// It's probably more efficient to treat it like a streaming buffer if
|
||||
// more than a third of its contents have been modified during the map().
|
||||
if (usedSize >= getSize() / 3)
|
||||
unmapStream();
|
||||
else
|
||||
unmapStatic(usedOffset, usedSize);
|
||||
break;
|
||||
}
|
||||
|
||||
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(memory_map + offset, data, size);
|
||||
|
||||
if (!is_mapped)
|
||||
{
|
||||
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) 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 : nullptr;
|
||||
|
||||
while (GL_NO_ERROR != glGetError())
|
||||
/* clear error messages */;
|
||||
|
||||
// Note that if 'src' is '0', no data will be copied.
|
||||
glBufferData(getTarget(), (GLsizeiptr) 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);
|
||||
map(); // saves buffer content to memory_map.
|
||||
}
|
||||
|
||||
is_mapped = false;
|
||||
|
||||
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(const VertexIndex &other)
|
||||
: size(other.size)
|
||||
{
|
||||
addSize(size);
|
||||
}
|
||||
|
||||
VertexIndex &VertexIndex::operator = (const VertexIndex &other)
|
||||
{
|
||||
addSize(other.size);
|
||||
removeSize(size);
|
||||
size = other.size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,533 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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 "common/config.h"
|
||||
#include "graphics/Volatile.h"
|
||||
|
||||
// OpenGL
|
||||
#include "OpenGL.h"
|
||||
|
||||
// C
|
||||
#include <stddef.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.
|
||||
*
|
||||
* @param usedOffset The offset into the mapped buffer indicating the
|
||||
* sub-range of data modified. Optional.
|
||||
* @param usedSize The size of the sub-range of modified data. Optional.
|
||||
*/
|
||||
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1) = 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(size_t usedOffset = 0, size_t usedSize = -1);
|
||||
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(size_t usedOffset = 0, size_t usedSize = -1);
|
||||
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);
|
||||
|
||||
void unmapStatic(size_t offset, size_t size);
|
||||
void unmapStream();
|
||||
|
||||
// The VBO identifier. Assigned by OpenGL.
|
||||
GLuint vbo;
|
||||
|
||||
// A pointer to mapped memory. Will be inialized on the first
|
||||
// call to map().
|
||||
char *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);
|
||||
|
||||
VertexIndex(const VertexIndex &other);
|
||||
VertexIndex &operator = (const VertexIndex &other);
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -30,7 +30,7 @@ namespace opengl
|
||||
|
||||
Canvas *luax_checkcanvas(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Canvas>(L, idx, "Canvas", GRAPHICS_CANVAS_T);
|
||||
return luax_checktype<Canvas>(L, idx, GRAPHICS_CANVAS_ID);
|
||||
}
|
||||
|
||||
int w_Canvas_renderTo(lua_State *L)
|
||||
@@ -62,64 +62,23 @@ int w_Canvas_renderTo(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Canvas_getImageData(lua_State *L)
|
||||
int w_Canvas_newImageData(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);
|
||||
love::image::Image *image = luax_getmodule<love::image::Image>(L, MODULE_IMAGE_ID);
|
||||
int x = (int) luaL_optnumber(L, 2, 0);
|
||||
int y = (int) luaL_optnumber(L, 3, 0);
|
||||
int w = (int) luaL_optnumber(L, 4, canvas->getWidth());
|
||||
int h = (int) luaL_optnumber(L, 5, canvas->getHeight());
|
||||
|
||||
love::image::ImageData *img = nullptr;
|
||||
luax_catchexcept(L, [&](){ img = canvas->newImageData(image, x, y, w, h); });
|
||||
|
||||
luax_pushtype(L, IMAGE_IMAGE_DATA_ID, img);
|
||||
img->release();
|
||||
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];
|
||||
|
||||
luax_catchexcept(L, [&](){ 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_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_getFormat(lua_State *L)
|
||||
{
|
||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||
@@ -151,24 +110,15 @@ static const luaL_Reg functions[] =
|
||||
{ "getWrap", w_Texture_getWrap },
|
||||
|
||||
{ "renderTo", w_Canvas_renderTo },
|
||||
{ "getImageData", w_Canvas_getImageData },
|
||||
{ "getPixel", w_Canvas_getPixel },
|
||||
{ "clear", w_Canvas_clear },
|
||||
{ "newImageData", w_Canvas_newImageData },
|
||||
{ "getFormat", w_Canvas_getFormat },
|
||||
{ "getMSAA", w_Canvas_getMSAA },
|
||||
|
||||
// Deprecated since 0.9.1.
|
||||
{ "getType", w_Canvas_getFormat },
|
||||
|
||||
// Deprecated since 0.9.2.
|
||||
{ "getFSAA", w_Canvas_getMSAA },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_canvas(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, "Canvas", functions);
|
||||
return luax_register_type(L, GRAPHICS_CANVAS_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
// LOVE
|
||||
#include "common/runtime.h"
|
||||
#include "Canvas.h"
|
||||
#include "wrap_Texture.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -36,9 +36,7 @@ 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_clear(lua_State *L);
|
||||
int w_Canvas_newImageData(lua_State *L);
|
||||
int w_Canvas_getFormat(lua_State *L);
|
||||
int w_Canvas_getMSAA(lua_State *L);
|
||||
extern "C" int luaopen_canvas(lua_State *L);
|
||||
|
||||
@@ -19,8 +19,12 @@
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "wrap_Font.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
@@ -30,7 +34,7 @@ namespace opengl
|
||||
|
||||
Font *luax_checkfont(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Font>(L, idx, "Font", GRAPHICS_FONT_T);
|
||||
return luax_checktype<Font>(L, idx, GRAPHICS_FONT_ID);
|
||||
}
|
||||
|
||||
int w_Font_getHeight(lua_State *L)
|
||||
@@ -54,15 +58,24 @@ 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;
|
||||
int max_width = 0;
|
||||
std::vector<std::string> lines;
|
||||
std::vector<int> widths;
|
||||
|
||||
luax_catchexcept(L, [&]() {
|
||||
std::vector<std::string> lines = t->getWrap(str, wrap, &max_width);
|
||||
numlines = lines.size();
|
||||
});
|
||||
luax_catchexcept(L, [&]() { t->getWrap(str, wrap, lines, &widths); });
|
||||
|
||||
for (int width : widths)
|
||||
max_width = std::max(max_width, width);
|
||||
|
||||
lua_pushinteger(L, max_width);
|
||||
lua_pushinteger(L, numlines);
|
||||
lua_createtable(L, (int) lines.size(), 0);
|
||||
|
||||
for (int i = 0; i < (int) lines.size(); i++)
|
||||
{
|
||||
lua_pushstring(L, lines[i].c_str());
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -140,8 +153,7 @@ 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;
|
||||
int count = std::max(lua_gettop(L) - 1, 1);
|
||||
|
||||
luax_catchexcept(L, [&]() {
|
||||
for (int i = 2; i < count + 2; i++)
|
||||
@@ -160,6 +172,18 @@ int w_Font_hasGlyphs(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Font_setFallbacks(lua_State *L)
|
||||
{
|
||||
Font *t = luax_checkfont(L, 1);
|
||||
std::vector<Font *> fallbacks;
|
||||
|
||||
for (int i = 2; i <= lua_gettop(L); i++)
|
||||
fallbacks.push_back(luax_checkfont(L, i));
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setFallbacks(fallbacks); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "getHeight", w_Font_getHeight },
|
||||
@@ -173,12 +197,13 @@ static const luaL_Reg functions[] =
|
||||
{ "getDescent", w_Font_getDescent },
|
||||
{ "getBaseline", w_Font_getBaseline },
|
||||
{ "hasGlyphs", w_Font_hasGlyphs },
|
||||
{ "setFallbacks", w_Font_setFallbacks },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_font(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, "Font", functions);
|
||||
return luax_register_type(L, GRAPHICS_FONT_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -44,6 +44,7 @@ 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);
|
||||
int w_Font_setFallbacks(lua_State *L);
|
||||
extern "C" int luaopen_font(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,14 +22,16 @@
|
||||
#define LOVE_GRAPHICS_OPENGL_WRAP_GRAPHICS_H
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "wrap_Font.h"
|
||||
#include "wrap_Image.h"
|
||||
#include "wrap_Quad.h"
|
||||
#include "graphics/wrap_Quad.h"
|
||||
#include "wrap_SpriteBatch.h"
|
||||
#include "wrap_ParticleSystem.h"
|
||||
#include "wrap_Canvas.h"
|
||||
#include "wrap_Shader.h"
|
||||
#include "wrap_Mesh.h"
|
||||
#include "wrap_Text.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
@@ -41,6 +43,7 @@ namespace opengl
|
||||
|
||||
int w_reset(lua_State *L);
|
||||
int w_clear(lua_State *L);
|
||||
int w_discard(lua_State *L);
|
||||
int w_present(lua_State *L);
|
||||
int w_isCreated(lua_State *L);
|
||||
int w_isActive(lua_State *L);
|
||||
@@ -49,18 +52,19 @@ 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_getMaxTextureSize(lua_State *L);
|
||||
int w_stencil(lua_State *L);
|
||||
int w_setStencilTest(lua_State *L);
|
||||
int w_getStencilTest(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_newCanvas(lua_State *L);
|
||||
int w_newShader(lua_State *L);
|
||||
int w_newMesh(lua_State *L);
|
||||
int w_newText(lua_State *L);
|
||||
int w_setColor(lua_State *L);
|
||||
int w_getColor(lua_State *L);
|
||||
int w_setBackgroundColor(lua_State *L);
|
||||
@@ -83,10 +87,7 @@ 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_setWireframe(lua_State *L);
|
||||
int w_isWireframe(lua_State *L);
|
||||
int w_newScreenshot(lua_State *L);
|
||||
@@ -95,12 +96,12 @@ 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_getSupported(lua_State *L);
|
||||
int w_getCanvasFormats(lua_State *L);
|
||||
int w_getCompressedImageFormats(lua_State *L);
|
||||
int w_getRendererInfo(lua_State *L);
|
||||
int w_getSystemLimits(lua_State *L);
|
||||
int w_getStats(lua_State *L);
|
||||
int w_getSystemLimit(lua_State *L);
|
||||
int w_draw(lua_State *L);
|
||||
int w_print(lua_State *L);
|
||||
int w_printf(lua_State *L);
|
||||
@@ -108,6 +109,7 @@ 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_ellipse(lua_State *L);
|
||||
int w_arc(lua_State *L);
|
||||
int w_polygon(lua_State *L);
|
||||
int w_push(lua_State *L);
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
R"luastring"--(
|
||||
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2015 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.
|
||||
--]]
|
||||
|
||||
local table_concat = table.concat
|
||||
|
||||
-- SHADERS
|
||||
|
||||
local GLSL = {}
|
||||
|
||||
GLSL.VERSION = "#version 120"
|
||||
GLSL.VERSION_ES = "#version 100"
|
||||
|
||||
GLSL.SYNTAX = [[
|
||||
#ifndef GL_ES
|
||||
#define lowp
|
||||
#define mediump
|
||||
#define highp
|
||||
#endif
|
||||
#define number float
|
||||
#define Image sampler2D
|
||||
#define extern uniform
|
||||
#define Texel texture2D
|
||||
#pragma optionNV(strict on)]]
|
||||
|
||||
-- Uniforms shared by the vertex and pixel shader stages.
|
||||
GLSL.UNIFORMS = [[
|
||||
#ifdef GL_ES
|
||||
// According to the GLSL ES 1.0 spec, uniform precision must match between stages,
|
||||
// but we can't guarantee that highp is always supported in fragment shaders...
|
||||
// We *really* don't want to use mediump for these in vertex shaders though.
|
||||
#if defined(VERTEX) || defined(GL_FRAGMENT_PRECISION_HIGH)
|
||||
#define LOVE_UNIFORM_PRECISION highp
|
||||
#else
|
||||
#define LOVE_UNIFORM_PRECISION mediump
|
||||
#endif
|
||||
uniform LOVE_UNIFORM_PRECISION mat4 TransformMatrix;
|
||||
uniform LOVE_UNIFORM_PRECISION mat4 ProjectionMatrix;
|
||||
uniform LOVE_UNIFORM_PRECISION mat4 TransformProjectionMatrix;
|
||||
uniform LOVE_UNIFORM_PRECISION mat3 NormalMatrix;
|
||||
#else
|
||||
#define TransformMatrix gl_ModelViewMatrix
|
||||
#define ProjectionMatrix gl_ProjectionMatrix
|
||||
#define TransformProjectionMatrix gl_ModelViewProjectionMatrix
|
||||
#define NormalMatrix gl_NormalMatrix
|
||||
#endif
|
||||
uniform mediump vec4 love_ScreenSize;]]
|
||||
|
||||
GLSL.VERTEX = {
|
||||
HEADER = [[
|
||||
#define VERTEX
|
||||
|
||||
attribute vec4 VertexPosition;
|
||||
attribute vec4 VertexTexCoord;
|
||||
attribute vec4 VertexColor;
|
||||
attribute vec4 ConstantColor;
|
||||
|
||||
varying vec4 VaryingTexCoord;
|
||||
varying vec4 VaryingColor;
|
||||
|
||||
#ifdef GL_ES
|
||||
uniform mediump float love_PointSize;
|
||||
#endif]],
|
||||
|
||||
FOOTER = [[
|
||||
void main() {
|
||||
VaryingTexCoord = VertexTexCoord;
|
||||
VaryingColor = VertexColor * ConstantColor;
|
||||
#ifdef GL_ES
|
||||
gl_PointSize = love_PointSize;
|
||||
#endif
|
||||
gl_Position = position(TransformProjectionMatrix, VertexPosition);
|
||||
}]],
|
||||
}
|
||||
|
||||
GLSL.PIXEL = {
|
||||
HEADER = [[
|
||||
#define PIXEL
|
||||
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying mediump vec4 VaryingTexCoord;
|
||||
varying lowp vec4 VaryingColor;
|
||||
|
||||
#define love_Canvases gl_FragData
|
||||
|
||||
uniform sampler2D _tex0_;]],
|
||||
|
||||
FOOTER = [[
|
||||
void main() {
|
||||
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
||||
float dummy = Texel(_tex0_, vec2(.5)).r;
|
||||
|
||||
// See Shader::checkSetScreenParams in Shader.cpp.
|
||||
vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w);
|
||||
|
||||
gl_FragColor = effect(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord);
|
||||
}]],
|
||||
|
||||
FOOTER_MULTI_CANVAS = [[
|
||||
void main() {
|
||||
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
||||
float dummy = Texel(_tex0_, vec2(.5)).r;
|
||||
|
||||
// See Shader::checkSetScreenParams in Shader.cpp.
|
||||
vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w);
|
||||
|
||||
effects(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord);
|
||||
}]],
|
||||
}
|
||||
|
||||
local function createVertexCode(vertexcode, lang)
|
||||
local vertexcodes = {
|
||||
lang == "glsles" and GLSL.VERSION_ES or GLSL.VERSION,
|
||||
GLSL.SYNTAX, GLSL.VERTEX.HEADER, GLSL.UNIFORMS,
|
||||
lang == "glsles" and "#line 1" or "#line 0",
|
||||
vertexcode,
|
||||
GLSL.VERTEX.FOOTER,
|
||||
}
|
||||
return table_concat(vertexcodes, "\n")
|
||||
end
|
||||
|
||||
local function createPixelCode(pixelcode, is_multicanvas, lang)
|
||||
local pixelcodes = {
|
||||
lang == "glsles" and GLSL.VERSION_ES or GLSL.VERSION,
|
||||
GLSL.SYNTAX, GLSL.PIXEL.HEADER, GLSL.UNIFORMS,
|
||||
lang == "glsles" and "#line 1" or "#line 0",
|
||||
pixelcode,
|
||||
is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER,
|
||||
}
|
||||
return table_concat(pixelcodes, "\n")
|
||||
end
|
||||
|
||||
local function isVertexCode(code)
|
||||
return code:match("vec4%s+position%s*%(") ~= nil
|
||||
end
|
||||
|
||||
local function isPixelCode(code)
|
||||
if code:match("vec4%s+effect%s*%(") then
|
||||
return true
|
||||
elseif code:match("void%s+effects%s*%(") then
|
||||
-- function for rendering to multiple canvases simultaneously
|
||||
return true, true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function love.graphics._shaderCodeToGLSL(arg1, arg2)
|
||||
local vertexcode, pixelcode
|
||||
local is_multicanvas = false -- whether pixel code has "effects" function instead of "effect"
|
||||
|
||||
local lang = "glsl"
|
||||
if (love.graphics.getRendererInfo()) == "OpenGL ES" then
|
||||
lang = "glsles"
|
||||
end
|
||||
|
||||
if arg1 then
|
||||
if isVertexCode(arg1) then
|
||||
vertexcode = arg1 -- first arg contains vertex shader code
|
||||
end
|
||||
|
||||
local ispixel, isMultiCanvas = isPixelCode(arg1)
|
||||
if ispixel then
|
||||
pixelcode = arg1 -- first arg contains pixel shader code
|
||||
is_multicanvas = isMultiCanvas
|
||||
end
|
||||
end
|
||||
|
||||
if arg2 then
|
||||
if isVertexCode(arg2) then
|
||||
vertexcode = arg2 -- second arg contains vertex shader code
|
||||
end
|
||||
|
||||
local ispixel, isMultiCanvas = isPixelCode(arg2)
|
||||
if ispixel then
|
||||
pixelcode = arg2 -- second arg contains pixel shader code
|
||||
is_multicanvas = isMultiCanvas
|
||||
end
|
||||
end
|
||||
|
||||
if vertexcode then
|
||||
vertexcode = createVertexCode(vertexcode, lang)
|
||||
end
|
||||
if pixelcode then
|
||||
pixelcode = createPixelCode(pixelcode, is_multicanvas, lang)
|
||||
end
|
||||
|
||||
return vertexcode, pixelcode
|
||||
end
|
||||
|
||||
function love.graphics._transformGLSLErrorMessages(message)
|
||||
local shadertype = message:match("Cannot compile (%a+) shader code")
|
||||
if not shadertype then return message end
|
||||
local lines = {"Cannot compile "..shadertype.." shader code:"}
|
||||
for l in message:gmatch("[^\n]+") do
|
||||
-- nvidia compiler message:
|
||||
-- 0(<linenumber>) : error/warning [NUMBER]: <error message>
|
||||
local linenumber, what, message = l:match("^0%((%d+)%)%s*:%s*(%w+)[^:]+:%s*(.+)$")
|
||||
if not linenumber then
|
||||
-- ati compiler message:
|
||||
-- ERROR 0:<linenumber>: error/warning(#[NUMBER]) [ERRORNAME]: <errormessage>
|
||||
linenumber, what, message = l:match("^%w+: 0:(%d+):%s*(%w+)%([^%)]+%)%s*(.+)$")
|
||||
if not linenumber then
|
||||
-- OSX compiler message (?):
|
||||
-- ERROR: 0:<linenumber>: <errormessage>
|
||||
what, linenumber, message = l:match("^(%w+): %d+:(%d+): (.+)$")
|
||||
end
|
||||
end
|
||||
if linenumber and what and message then
|
||||
lines[#lines+1] = ("Line %d: %s: %s"):format(linenumber, what, message)
|
||||
end
|
||||
end
|
||||
-- did not match any known error messages
|
||||
if #lines == 1 then return message end
|
||||
return table_concat(lines, "\n")
|
||||
end
|
||||
|
||||
local defaultcode = {
|
||||
vertex = [[
|
||||
vec4 position(mat4 transform_proj, vec4 vertpos) {
|
||||
return transform_proj * vertpos;
|
||||
}]],
|
||||
pixel = [[
|
||||
vec4 effect(lowp vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||
return Texel(tex, texcoord) * vcolor;
|
||||
}]]
|
||||
}
|
||||
|
||||
local defaults = {
|
||||
opengl = {
|
||||
createVertexCode(defaultcode.vertex, "glsl"),
|
||||
createPixelCode(defaultcode.pixel, false, "glsl"),
|
||||
},
|
||||
opengles = {
|
||||
createVertexCode(defaultcode.vertex, "glsles"),
|
||||
createPixelCode(defaultcode.pixel, false, "glsles"),
|
||||
},
|
||||
}
|
||||
|
||||
love.graphics._setDefaultShaderCode(defaults)
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
@@ -30,7 +30,7 @@ namespace opengl
|
||||
|
||||
Image *luax_checkimage(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Image>(L, idx, "Image", GRAPHICS_IMAGE_T);
|
||||
return luax_checktype<Image>(L, idx, GRAPHICS_IMAGE_ID);
|
||||
}
|
||||
|
||||
int w_Image_setMipmapFilter(lua_State *L)
|
||||
@@ -48,9 +48,7 @@ int w_Image_setMipmapFilter(lua_State *L)
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setFilter(f); });
|
||||
|
||||
float sharpness = (float) luaL_optnumber(L, 3, 0);
|
||||
t->setMipmapSharpness(sharpness);
|
||||
t->setMipmapSharpness((float) luaL_optnumber(L, 3, 0.0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -81,7 +79,13 @@ int w_Image_isCompressed(lua_State *L)
|
||||
int w_Image_refresh(lua_State *L)
|
||||
{
|
||||
Image *i = luax_checkimage(L, 1);
|
||||
luax_catchexcept(L, [&](){ i->refresh(); });
|
||||
|
||||
int xoffset = (int) luaL_optnumber(L, 2, 0);
|
||||
int yoffset = (int) luaL_optnumber(L, 3, 0);
|
||||
int w = (int) luaL_optnumber(L, 4, i->getWidth());
|
||||
int h = (int) luaL_optnumber(L, 5, i->getHeight());
|
||||
|
||||
luax_catchexcept(L, [&](){ i->refresh(xoffset, yoffset, w, h); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -90,21 +94,32 @@ int w_Image_getData(lua_State *L)
|
||||
Image *i = luax_checkimage(L, 1);
|
||||
|
||||
if (i->isCompressed())
|
||||
{
|
||||
love::image::CompressedData *t = i->getCompressedData();
|
||||
if (t)
|
||||
luax_pushtype(L, "CompressedData", IMAGE_COMPRESSED_DATA_T, t);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
}
|
||||
luax_pushtype(L, IMAGE_COMPRESSED_IMAGE_DATA_ID, i->getCompressedData());
|
||||
else
|
||||
{
|
||||
love::image::ImageData *t = i->getImageData();
|
||||
if (t)
|
||||
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, t);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
}
|
||||
luax_pushtype(L, IMAGE_IMAGE_DATA_ID, i->getImageData());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *imageFlagName(Image::FlagType flagtype)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
Image::getConstant(flagtype, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
int w_Image_getFlags(lua_State *L)
|
||||
{
|
||||
Image *i = luax_checkimage(L, 1);
|
||||
Image::Flags flags = i->getFlags();
|
||||
|
||||
lua_createtable(L, 0, 2);
|
||||
|
||||
lua_pushboolean(L, flags.mipmaps);
|
||||
lua_setfield(L, -2, imageFlagName(Image::FLAG_TYPE_MIPMAPS));
|
||||
|
||||
lua_pushboolean(L, flags.sRGB);
|
||||
lua_setfield(L, -2, imageFlagName(Image::FLAG_TYPE_SRGB));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -125,12 +140,13 @@ static const luaL_Reg functions[] =
|
||||
{ "isCompressed", w_Image_isCompressed },
|
||||
{ "refresh", w_Image_refresh },
|
||||
{ "getData", w_Image_getData },
|
||||
{ "getFlags", w_Image_getFlags },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_image(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, "Image", functions);
|
||||
return luax_register_type(L, GRAPHICS_IMAGE_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
// LOVE
|
||||
#include "common/runtime.h"
|
||||
#include "Image.h"
|
||||
#include "wrap_Texture.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -39,6 +39,7 @@ int w_Image_getMipmapFilter(lua_State *L);
|
||||
int w_Image_isCompressed(lua_State *L);
|
||||
int w_Image_refresh(lua_State *L);
|
||||
int w_Image_getData(lua_State *L);
|
||||
int w_Image_getFlags(lua_State *L);
|
||||
extern "C" int luaopen_image(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "wrap_Mesh.h"
|
||||
#include "Image.h"
|
||||
#include "Canvas.h"
|
||||
#include "wrap_Texture.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
|
||||
// C++
|
||||
#include <typeinfo>
|
||||
@@ -36,155 +36,209 @@ namespace opengl
|
||||
|
||||
Mesh *luax_checkmesh(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Mesh>(L, idx, "Mesh", GRAPHICS_MESH_T);
|
||||
return luax_checktype<Mesh>(L, idx, GRAPHICS_MESH_ID);
|
||||
}
|
||||
|
||||
static inline size_t writeByteData(lua_State *L, int startidx, int components, char *data)
|
||||
{
|
||||
uint8 *componentdata = (uint8 *) data;
|
||||
for (int i = 0; i < components; i++)
|
||||
componentdata[i] = (uint8) luaL_optnumber(L, startidx + i, 255);
|
||||
|
||||
return sizeof(uint8) * components;
|
||||
}
|
||||
|
||||
static inline size_t writeFloatData(lua_State *L, int startidx, int components, char *data)
|
||||
{
|
||||
float *componentdata = (float *) data;
|
||||
for (int i = 0; i < components; i++)
|
||||
componentdata[i] = (float) luaL_optnumber(L, startidx + i, 0);
|
||||
|
||||
return sizeof(float) * components;
|
||||
}
|
||||
|
||||
static inline char *writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, int components, char *data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Mesh::DATA_BYTE:
|
||||
return data + writeByteData(L, startidx, components, data);
|
||||
case Mesh::DATA_FLOAT:
|
||||
return data + writeFloatData(L, startidx, components, data);
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline size_t readData(lua_State *L, int components, const char *data)
|
||||
{
|
||||
const T *componentdata = (const T *) data;
|
||||
for (int i = 0; i < components; i++)
|
||||
lua_pushnumber(L, (lua_Number) componentdata[i]);
|
||||
|
||||
return sizeof(T) * components;
|
||||
}
|
||||
|
||||
static inline const char *readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Mesh::DATA_BYTE:
|
||||
return data + readData<uint8>(L, components, data);
|
||||
case Mesh::DATA_FLOAT:
|
||||
return data + readData<float>(L, components, data);
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
int w_Mesh_setVertices(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
|
||||
size_t nvertices = luax_objlen(L, 2);
|
||||
if (nvertices != t->getVertexCount())
|
||||
return luaL_error(L, "Invalid number of vertices (expected %d, got %d)", (int) t->getVertexCount(), (int) nvertices);
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||
|
||||
int ncomponents = 0;
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
ncomponents += format.components;
|
||||
|
||||
char *data = (char *) t->mapVertexData();
|
||||
|
||||
for (size_t i = 0; i < nvertices; i++)
|
||||
{
|
||||
// get vertices[vertindex]
|
||||
lua_rawgeti(L, 2, i + 1);
|
||||
luaL_checktype(L, -1, LUA_TTABLE);
|
||||
|
||||
// get vertices[vertindex][j]
|
||||
for (int j = 1; j <= ncomponents; j++)
|
||||
lua_rawgeti(L, -j, j);
|
||||
|
||||
int idx = -ncomponents;
|
||||
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
{
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
data = writeAttributeData(L, idx, format.type, format.components, data);
|
||||
|
||||
idx += format.components;
|
||||
}
|
||||
|
||||
lua_pop(L, ncomponents + 1);
|
||||
}
|
||||
|
||||
t->unmapVertexData();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_setVertex(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
size_t i = size_t(luaL_checkinteger(L, 2) - 1);
|
||||
size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
|
||||
|
||||
Vertex v;
|
||||
bool istable = lua_istable(L, 3);
|
||||
|
||||
if (lua_istable(L, 3))
|
||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||
|
||||
char *data = (char *) t->getVertexScratchBuffer();
|
||||
char *writtendata = data;
|
||||
|
||||
int idx = istable ? 1 : 3;
|
||||
|
||||
if (istable)
|
||||
{
|
||||
for (int i = 1; i <= 8; i++)
|
||||
lua_rawgeti(L, 3, i);
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
{
|
||||
for (int i = idx; i < idx + format.components; i++)
|
||||
lua_rawgeti(L, 3, i);
|
||||
|
||||
v.x = luaL_checknumber(L, -8);
|
||||
v.y = luaL_checknumber(L, -7);
|
||||
v.s = luaL_optnumber(L, -6, 0.0);
|
||||
v.t = luaL_optnumber(L, -5, 0.0);
|
||||
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);
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
writtendata = writeAttributeData(L, -format.components, format.type, format.components, writtendata);
|
||||
|
||||
lua_pop(L, 8);
|
||||
idx += format.components;
|
||||
lua_pop(L, format.components);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
v.x = luaL_checknumber(L, 3);
|
||||
v.y = luaL_checknumber(L, 4);
|
||||
v.s = luaL_optnumber(L, 5, 0.0);
|
||||
v.t = luaL_optnumber(L, 6, 0.0);
|
||||
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);
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
{
|
||||
// Fetch the values from Lua and store them in data buffer.
|
||||
writtendata = writeAttributeData(L, idx, format.type, format.components, writtendata);
|
||||
idx += format.components;
|
||||
}
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setVertex(i, v); });
|
||||
luax_catchexcept(L, [&](){ t->setVertex(index, data, t->getVertexStride()); });
|
||||
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);
|
||||
size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
|
||||
|
||||
Vertex v;
|
||||
luax_catchexcept(L, [&](){ v = t->getVertex(i); });
|
||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||
|
||||
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);
|
||||
char *data = (char *) t->getVertexScratchBuffer();
|
||||
const char *readdata = data;
|
||||
|
||||
return 8;
|
||||
}
|
||||
luax_catchexcept(L, [&](){ t->getVertex(index, data, t->getVertexStride()); });
|
||||
|
||||
int w_Mesh_setVertices(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
int n = 0;
|
||||
|
||||
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++)
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
{
|
||||
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_optnumber(L, -6, 0.0);
|
||||
v.t = (float) luaL_optnumber(L, -5, 0.0);
|
||||
|
||||
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);
|
||||
readdata = readAttributeData(L, format.type, format.components, readdata);
|
||||
n += format.components;
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setVertices(vertices); });
|
||||
return n;
|
||||
}
|
||||
|
||||
int w_Mesh_setVertexAttribute(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
|
||||
int attribindex = (int) luaL_checkinteger(L, 3) - 1;
|
||||
|
||||
Mesh::DataType type;
|
||||
int components;
|
||||
luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
|
||||
|
||||
// Maximum possible size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
|
||||
// Fetch the values from Lua and store them in the data buffer.
|
||||
writeAttributeData(L, 4, type, components, data);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_getVertices(lua_State *L)
|
||||
int w_Mesh_getVertexAttribute(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
|
||||
int attribindex = (int) luaL_checkinteger(L, 3) - 1;
|
||||
|
||||
const Vertex *vertices = t->getVertices();
|
||||
Mesh::DataType type;
|
||||
int components;
|
||||
luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
|
||||
|
||||
size_t count = t->getVertexCount();
|
||||
lua_createtable(L, count, 0);
|
||||
// Maximum possible size for a single vertex attribute.
|
||||
char data[sizeof(float) * 4];
|
||||
|
||||
if (count == 0 || vertices == nullptr)
|
||||
return 1;
|
||||
luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
|
||||
|
||||
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;
|
||||
readAttributeData(L, type, components, data);
|
||||
return components;
|
||||
}
|
||||
|
||||
int w_Mesh_getVertexCount(lua_State *L)
|
||||
@@ -194,25 +248,95 @@ int w_Mesh_getVertexCount(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_getVertexFormat(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||
lua_createtable(L, (int) vertexformat.size(), 0);
|
||||
|
||||
const char *tname = nullptr;
|
||||
|
||||
for (size_t i = 0; i < vertexformat.size(); i++)
|
||||
{
|
||||
if (!Mesh::getConstant(vertexformat[i].type, tname))
|
||||
return luaL_error(L, "Unknown vertex attribute data type.");
|
||||
|
||||
lua_createtable(L, 3, 0);
|
||||
|
||||
lua_pushstring(L, vertexformat[i].name.c_str());
|
||||
lua_rawseti(L, -2, 1);
|
||||
|
||||
lua_pushstring(L, tname);
|
||||
lua_rawseti(L, -2, 2);
|
||||
|
||||
lua_pushinteger(L, vertexformat[i].components);
|
||||
lua_rawseti(L, -2, 3);
|
||||
|
||||
// format[i] = {name, type, components}
|
||||
lua_rawseti(L, -2, (int) i + 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setAttributeEnabled(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
bool enable = luax_toboolean(L, 3);
|
||||
luax_catchexcept(L, [&](){ t->setAttributeEnabled(name, enable); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_isAttributeEnabled(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
bool enabled = false;
|
||||
luax_catchexcept(L, [&](){ enabled = t->isAttributeEnabled(name); });
|
||||
lua_pushboolean(L, enabled);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_attachAttribute(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const char *name = luaL_checkstring(L, 2);
|
||||
Mesh *mesh = luax_checkmesh(L, 3);
|
||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_flush(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
t->flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
int nargs = is_table ? (int) luax_objlen(L, 2) : lua_gettop(L) - 1;
|
||||
|
||||
std::vector<uint32> vertexmap;
|
||||
vertexmap.reserve(nargs);
|
||||
|
||||
for (int i = 0; i < nargs; i++)
|
||||
if (is_table)
|
||||
{
|
||||
if (is_table)
|
||||
for (int i = 0; i < nargs; i++)
|
||||
{
|
||||
lua_rawgeti(L, 2, i + 1);
|
||||
vertexmap.push_back(uint32(luaL_checkinteger(L, -1) - 1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < nargs; i++)
|
||||
vertexmap.push_back(uint32(luaL_checkinteger(L, i + 2) - 1));
|
||||
}
|
||||
|
||||
@@ -227,11 +351,11 @@ int w_Mesh_getVertexMap(lua_State *L)
|
||||
std::vector<uint32> vertex_map;
|
||||
luax_catchexcept(L, [&](){ t->getVertexMap(vertex_map); });
|
||||
|
||||
size_t element_count = vertex_map.size();
|
||||
int element_count = (int) vertex_map.size();
|
||||
|
||||
lua_createtable(L, element_count, 0);
|
||||
|
||||
for (size_t i = 0; i < element_count; i++)
|
||||
for (int i = 0; i < element_count; i++)
|
||||
{
|
||||
lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
@@ -265,9 +389,9 @@ int w_Mesh_getTexture(lua_State *L)
|
||||
|
||||
// FIXME: big hack right here.
|
||||
if (typeid(*tex) == typeid(Image))
|
||||
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, tex);
|
||||
luax_pushtype(L, GRAPHICS_IMAGE_ID, tex);
|
||||
else if (typeid(*tex) == typeid(Canvas))
|
||||
luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
|
||||
luax_pushtype(L, GRAPHICS_CANVAS_ID, tex);
|
||||
else
|
||||
return luaL_error(L, "Unable to determine texture type.");
|
||||
|
||||
@@ -308,8 +432,8 @@ int w_Mesh_setDrawRange(lua_State *L)
|
||||
t->setDrawRange();
|
||||
else
|
||||
{
|
||||
int rangemin = luaL_checkint(L, 2) - 1;
|
||||
int rangemax = luaL_checkint(L, 3) - 1;
|
||||
int rangemin = (int) luaL_checknumber(L, 2) - 1;
|
||||
int rangemax = (int) luaL_checknumber(L, 3) - 1;
|
||||
luax_catchexcept(L, [&](){ t->setDrawRange(rangemin, rangemax); });
|
||||
}
|
||||
|
||||
@@ -332,27 +456,19 @@ int w_Mesh_getDrawRange(lua_State *L)
|
||||
return 2;
|
||||
}
|
||||
|
||||
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[] =
|
||||
{
|
||||
{ "setVertices", w_Mesh_setVertices },
|
||||
{ "setVertex", w_Mesh_setVertex },
|
||||
{ "getVertex", w_Mesh_getVertex },
|
||||
{ "setVertices", w_Mesh_setVertices },
|
||||
{ "getVertices", w_Mesh_getVertices },
|
||||
{ "setVertexAttribute", w_Mesh_setVertexAttribute },
|
||||
{ "getVertexAttribute", w_Mesh_getVertexAttribute },
|
||||
{ "getVertexCount", w_Mesh_getVertexCount },
|
||||
{ "getVertexFormat", w_Mesh_getVertexFormat },
|
||||
{ "setAttributeEnabled", w_Mesh_setAttributeEnabled },
|
||||
{ "isAttributeEnabled", w_Mesh_isAttributeEnabled },
|
||||
{ "attachAttribute", w_Mesh_attachAttribute },
|
||||
{ "flush", w_Mesh_flush },
|
||||
{ "setVertexMap", w_Mesh_setVertexMap },
|
||||
{ "getVertexMap", w_Mesh_getVertexMap },
|
||||
{ "setTexture", w_Mesh_setTexture },
|
||||
@@ -361,19 +477,12 @@ static const luaL_Reg functions[] =
|
||||
{ "getDrawMode", w_Mesh_getDrawMode },
|
||||
{ "setDrawRange", w_Mesh_setDrawRange },
|
||||
{ "getDrawRange", w_Mesh_getDrawRange },
|
||||
{ "setVertexColors", w_Mesh_setVertexColors },
|
||||
{ "hasVertexColors", w_Mesh_hasVertexColors },
|
||||
|
||||
// Deprecated since 0.9.1.
|
||||
{ "setImage", w_Mesh_setTexture },
|
||||
{ "getImage", w_Mesh_getTexture },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_mesh(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, "Mesh", functions);
|
||||
return luax_register_type(L, GRAPHICS_MESH_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -33,12 +33,17 @@ namespace opengl
|
||||
{
|
||||
|
||||
Mesh *luax_checkmesh(lua_State *L, int idx);
|
||||
|
||||
int w_Mesh_setVertices(lua_State *L);
|
||||
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_setVertexAttribute(lua_State *L);
|
||||
int w_Mesh_getVertexAttribute(lua_State *L);
|
||||
int w_Mesh_getVertexCount(lua_State *L);
|
||||
int w_Mesh_getVertexFormat(lua_State *L);
|
||||
int w_Mesh_setAttributeEnabled(lua_State *L);
|
||||
int w_Mesh_isAttributeEnabled(lua_State *L);
|
||||
int w_Mesh_attachAttribute(lua_State *L);
|
||||
int w_Mesh_flush(lua_State *L);
|
||||
int w_Mesh_setVertexMap(lua_State *L);
|
||||
int w_Mesh_getVertexMap(lua_State *L);
|
||||
int w_Mesh_setTexture(lua_State *L);
|
||||
@@ -47,9 +52,6 @@ int w_Mesh_setDrawMode(lua_State *L);
|
||||
int w_Mesh_getDrawMode(lua_State *L);
|
||||
int w_Mesh_setDrawRange(lua_State *L);
|
||||
int w_Mesh_getDrawRange(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
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "Image.h"
|
||||
#include "Canvas.h"
|
||||
#include "wrap_Texture.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
|
||||
// C
|
||||
#include <cstring>
|
||||
@@ -41,7 +41,7 @@ namespace opengl
|
||||
|
||||
ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<ParticleSystem>(L, idx, "ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_T);
|
||||
return luax_checktype<ParticleSystem>(L, idx, GRAPHICS_PARTICLE_SYSTEM_ID);
|
||||
}
|
||||
|
||||
int w_ParticleSystem_clone(lua_State *L)
|
||||
@@ -51,7 +51,7 @@ int w_ParticleSystem_clone(lua_State *L)
|
||||
ParticleSystem *clone = nullptr;
|
||||
luax_catchexcept(L, [&](){ clone = t->clone(); });
|
||||
|
||||
luax_pushtype(L, "ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_T, clone);
|
||||
luax_pushtype(L, GRAPHICS_PARTICLE_SYSTEM_ID, clone);
|
||||
clone->release();
|
||||
return 1;
|
||||
}
|
||||
@@ -71,9 +71,9 @@ int w_ParticleSystem_getTexture(lua_State *L)
|
||||
|
||||
// FIXME: big hack right here.
|
||||
if (typeid(*tex) == typeid(Image))
|
||||
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, tex);
|
||||
luax_pushtype(L, GRAPHICS_IMAGE_ID, tex);
|
||||
else if (typeid(*tex) == typeid(Canvas))
|
||||
luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
|
||||
luax_pushtype(L, GRAPHICS_CANVAS_ID, tex);
|
||||
else
|
||||
return luaL_error(L, "Unable to determine texture type.");
|
||||
|
||||
@@ -381,7 +381,7 @@ int w_ParticleSystem_setSizes(lua_State *L)
|
||||
{
|
||||
std::vector<float> sizes(nSizes);
|
||||
for (size_t i = 0; i < nSizes; ++i)
|
||||
sizes[i] = luax_checkfloat(L, 1 + i + 1);
|
||||
sizes[i] = luax_checkfloat(L, (int) (1 + i + 1));
|
||||
|
||||
t->setSizes(sizes);
|
||||
}
|
||||
@@ -396,7 +396,7 @@ int w_ParticleSystem_getSizes(lua_State *L)
|
||||
for (size_t i = 0; i < sizes.size(); i++)
|
||||
lua_pushnumber(L, sizes[i]);
|
||||
|
||||
return sizes.size();
|
||||
return (int) sizes.size();
|
||||
}
|
||||
|
||||
int w_ParticleSystem_setSizeVariation(lua_State *L)
|
||||
@@ -494,18 +494,18 @@ int w_ParticleSystem_setColors(lua_State *L)
|
||||
|
||||
if (lua_istable(L, 2)) // setColors({r,g,b,a}, {r,g,b,a}, ...)
|
||||
{
|
||||
size_t nColors = lua_gettop(L) - 1;
|
||||
int nColors = (int) 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++)
|
||||
for (int i = 0; i < nColors; i++)
|
||||
{
|
||||
luaL_checktype(L, i + 2, LUA_TTABLE);
|
||||
|
||||
if (lua_objlen(L, i + 2) < 3)
|
||||
if (luax_objlen(L, i + 2) < 3)
|
||||
return luaL_argerror(L, i + 2, "expected 4 color components");
|
||||
|
||||
for (int j = 0; j < 4; j++)
|
||||
@@ -528,7 +528,7 @@ int w_ParticleSystem_setColors(lua_State *L)
|
||||
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)
|
||||
int 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);
|
||||
@@ -547,7 +547,7 @@ int w_ParticleSystem_setColors(lua_State *L)
|
||||
else
|
||||
{
|
||||
std::vector<Color> colors(nColors);
|
||||
for (size_t i = 0; i < nColors; ++i)
|
||||
for (int 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);
|
||||
@@ -582,7 +582,7 @@ int w_ParticleSystem_getColors(lua_State *L)
|
||||
lua_rawseti(L, -2, 4);
|
||||
}
|
||||
|
||||
return colors.size();
|
||||
return (int) colors.size();
|
||||
}
|
||||
|
||||
int w_ParticleSystem_setQuads(lua_State *L)
|
||||
@@ -592,11 +592,11 @@ int w_ParticleSystem_setQuads(lua_State *L)
|
||||
|
||||
if (lua_istable(L, 2))
|
||||
{
|
||||
for (size_t i = 1; i <= lua_objlen(L, 2); i++)
|
||||
for (int i = 1; i <= (int) luax_objlen(L, 2); i++)
|
||||
{
|
||||
lua_rawgeti(L, 2, i);
|
||||
|
||||
Quad *q = luax_checktype<Quad>(L, -1, "Quad", GRAPHICS_QUAD_T);
|
||||
Quad *q = luax_checktype<Quad>(L, -1, GRAPHICS_QUAD_ID);
|
||||
quads.push_back(q);
|
||||
|
||||
lua_pop(L, 1);
|
||||
@@ -606,7 +606,7 @@ int w_ParticleSystem_setQuads(lua_State *L)
|
||||
{
|
||||
for (int i = 2; i <= lua_gettop(L); i++)
|
||||
{
|
||||
Quad *q = luax_checktype<Quad>(L, i, "Quad", GRAPHICS_QUAD_T);
|
||||
Quad *q = luax_checktype<Quad>(L, i, GRAPHICS_QUAD_ID);
|
||||
quads.push_back(q);
|
||||
}
|
||||
}
|
||||
@@ -622,9 +622,9 @@ int w_ParticleSystem_getQuads(lua_State *L)
|
||||
|
||||
lua_createtable(L, (int) quads.size(), 0);
|
||||
|
||||
for (size_t i = 0; i < quads.size(); i++)
|
||||
for (int i = 0; i < (int) quads.size(); i++)
|
||||
{
|
||||
luax_pushtype(L, "Quad", GRAPHICS_QUAD_T, quads[i]);
|
||||
luax_pushtype(L, GRAPHICS_QUAD_ID, quads[i]);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
|
||||
@@ -683,7 +683,7 @@ int w_ParticleSystem_reset(lua_State *L)
|
||||
int w_ParticleSystem_emit(lua_State *L)
|
||||
{
|
||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||
int num = luaL_checkint(L, 2);
|
||||
int num = (int) luaL_checknumber(L, 2);
|
||||
t->emit(num);
|
||||
return 0;
|
||||
}
|
||||
@@ -779,17 +779,12 @@ static const luaL_Reg functions[] =
|
||||
{ "isPaused", w_ParticleSystem_isPaused },
|
||||
{ "isStopped", w_ParticleSystem_isStopped },
|
||||
{ "update", w_ParticleSystem_update },
|
||||
|
||||
// Deprecated since 0.9.1.
|
||||
{ "setImage", w_ParticleSystem_setTexture },
|
||||
{ "getImage", w_ParticleSystem_getTexture },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_particlesystem(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, "ParticleSystem", functions);
|
||||
return luax_register_type(L, GRAPHICS_PARTICLE_SYSTEM_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
**/
|
||||
|
||||
#include "wrap_Shader.h"
|
||||
#include "wrap_Texture.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace opengl
|
||||
|
||||
Shader *luax_checkshader(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Shader>(L, idx, "Shader", GRAPHICS_SHADER_T);
|
||||
return luax_checktype<Shader>(L, idx, GRAPHICS_SHADER_ID);
|
||||
}
|
||||
|
||||
int w_Shader_getWarnings(lua_State *L)
|
||||
@@ -68,7 +68,7 @@ static T *_getScalars(lua_State *L, int count, size_t &dimension)
|
||||
template <typename T>
|
||||
static T *_getVectors(lua_State *L, int count, size_t &dimension)
|
||||
{
|
||||
dimension = lua_objlen(L, 3);
|
||||
dimension = luax_objlen(L, 3);
|
||||
T *values = new T[count * dimension];
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
@@ -79,15 +79,15 @@ static T *_getVectors(lua_State *L, int count, size_t &dimension)
|
||||
luax_typerror(L, 3 + i, "table");
|
||||
return 0;
|
||||
}
|
||||
if (lua_objlen(L, 3 + i) != dimension)
|
||||
if (luax_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));
|
||||
3+i, dimension, luax_objlen(L, 3+i));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t k = 1; k <= dimension; ++k)
|
||||
for (int k = 1; k <= (int) dimension; ++k)
|
||||
{
|
||||
lua_rawgeti(L, 3 + i, k);
|
||||
if (lua_isnumber(L, -1))
|
||||
@@ -132,7 +132,7 @@ int w_Shader_sendInt(lua_State *L)
|
||||
bool should_error = false;
|
||||
try
|
||||
{
|
||||
shader->sendInt(name, dimension, values, count);
|
||||
shader->sendInt(name, (int) dimension, values, count);
|
||||
}
|
||||
catch (love::Exception &e)
|
||||
{
|
||||
@@ -173,7 +173,7 @@ int w_Shader_sendFloat(lua_State *L)
|
||||
bool should_error = false;
|
||||
try
|
||||
{
|
||||
shader->sendFloat(name, dimension, values, count);
|
||||
shader->sendFloat(name, (int) dimension, values, count);
|
||||
}
|
||||
catch (love::Exception &e)
|
||||
{
|
||||
@@ -199,7 +199,7 @@ int w_Shader_sendMatrix(lua_State *L)
|
||||
return luax_typerror(L, 3, "matrix table");
|
||||
|
||||
lua_getfield(L, 3, "dimension");
|
||||
int dimension = lua_tointeger(L, -1);
|
||||
int dimension = (int) lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (dimension < 2 || dimension > 4)
|
||||
@@ -217,7 +217,7 @@ int w_Shader_sendMatrix(lua_State *L)
|
||||
// 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);
|
||||
int other_dimension = (int) 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);
|
||||
@@ -234,7 +234,7 @@ int w_Shader_sendMatrix(lua_State *L)
|
||||
|
||||
luax_catchexcept(L,
|
||||
[&]() { shader->sendMatrix(name, dimension, values, count); },
|
||||
[&]() { delete[] values; }
|
||||
[&](bool) { delete[] values; }
|
||||
);
|
||||
|
||||
return 0;
|
||||
@@ -258,19 +258,19 @@ static void w_convertMatrices(lua_State *L, int idx)
|
||||
for (int matrix = idx; matrix < idx + matrixcount; matrix++)
|
||||
{
|
||||
luaL_checktype(L, matrix, LUA_TTABLE);
|
||||
int dimension = lua_objlen(L, matrix);
|
||||
int dimension = (int) luax_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++)
|
||||
for (int i = 1; i <= (int) luax_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++)
|
||||
for (int j = 1; j <= (int) luax_objlen(L, -1); j++)
|
||||
{
|
||||
// Push args[matrix[i][j] onto the stack.
|
||||
lua_rawgeti(L, -1, j);
|
||||
@@ -308,7 +308,7 @@ int w_Shader_send(lua_State *L)
|
||||
// Texture (Image or Canvas).
|
||||
p = (Proxy *) lua_touserdata(L, 3);
|
||||
|
||||
if (p->flags[GRAPHICS_TEXTURE_ID])
|
||||
if (typeFlags[p->type][GRAPHICS_TEXTURE_ID])
|
||||
return w_Shader_sendTexture(L);
|
||||
|
||||
break;
|
||||
@@ -376,17 +376,12 @@ static const luaL_Reg functions[] =
|
||||
{ "sendTexture", w_Shader_sendTexture },
|
||||
{ "send", w_Shader_send },
|
||||
{ "getExternVariable", w_Shader_getExternVariable },
|
||||
|
||||
// Deprecated since 0.9.1.
|
||||
{ "sendImage", w_Shader_sendTexture },
|
||||
{ "sendCanvas", w_Shader_sendTexture },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_shader(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, "Shader", functions);
|
||||
return luax_register_type(L, GRAPHICS_SHADER_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "wrap_SpriteBatch.h"
|
||||
#include "Image.h"
|
||||
#include "Canvas.h"
|
||||
#include "wrap_Texture.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
|
||||
// C++
|
||||
#include <typeinfo>
|
||||
@@ -36,7 +36,7 @@ namespace opengl
|
||||
|
||||
SpriteBatch *luax_checkspritebatch(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<SpriteBatch>(L, idx, "SpriteBatch", GRAPHICS_SPRITE_BATCH_T);
|
||||
return luax_checktype<SpriteBatch>(L, idx, GRAPHICS_SPRITE_BATCH_ID);
|
||||
}
|
||||
|
||||
int w_SpriteBatch_add(lua_State *L)
|
||||
@@ -45,9 +45,9 @@ int w_SpriteBatch_add(lua_State *L)
|
||||
Quad *quad = nullptr;
|
||||
int startidx = 2;
|
||||
|
||||
if (luax_istype(L, 2, GRAPHICS_QUAD_T))
|
||||
if (luax_istype(L, 2, GRAPHICS_QUAD_ID))
|
||||
{
|
||||
quad = luax_totype<Quad>(L, 2, "Quad", GRAPHICS_QUAD_T);
|
||||
quad = luax_totype<Quad>(L, 2, GRAPHICS_QUAD_ID);
|
||||
startidx = 3;
|
||||
}
|
||||
else if (lua_isnil(L, 2) && !lua_isnoneornil(L, 3))
|
||||
@@ -63,29 +63,29 @@ int w_SpriteBatch_add(lua_State *L)
|
||||
float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
|
||||
float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
|
||||
|
||||
int id = 0;
|
||||
int index = 0;
|
||||
luax_catchexcept(L, [&]() {
|
||||
if (quad)
|
||||
id = t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky);
|
||||
index = 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);
|
||||
index = t->add(x, y, a, sx, sy, ox, oy, kx, ky);
|
||||
});
|
||||
|
||||
lua_pushinteger(L, id);
|
||||
lua_pushinteger(L, index + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_SpriteBatch_set(lua_State *L)
|
||||
{
|
||||
SpriteBatch *t = luax_checkspritebatch(L, 1);
|
||||
int id = luaL_checkinteger(L, 2);
|
||||
int index = (int) luaL_checknumber(L, 2) - 1;
|
||||
|
||||
Quad *quad = nullptr;
|
||||
int startidx = 3;
|
||||
|
||||
if (luax_istype(L, 3, GRAPHICS_QUAD_T))
|
||||
if (luax_istype(L, 3, GRAPHICS_QUAD_ID))
|
||||
{
|
||||
quad = luax_totype<Quad>(L, 3, "Quad", GRAPHICS_QUAD_T);
|
||||
quad = luax_totype<Quad>(L, 3, GRAPHICS_QUAD_ID);
|
||||
startidx = 4;
|
||||
}
|
||||
else if (lua_isnil(L, 3) && !lua_isnoneornil(L, 4))
|
||||
@@ -103,9 +103,9 @@ int w_SpriteBatch_set(lua_State *L)
|
||||
|
||||
luax_catchexcept(L, [&]() {
|
||||
if (quad)
|
||||
t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky, id);
|
||||
t->addq(quad, x, y, a, sx, sy, ox, oy, kx, ky, index);
|
||||
else
|
||||
t->add(x, y, a, sx, sy, ox, oy, kx, ky, id);
|
||||
t->add(x, y, a, sx, sy, ox, oy, kx, ky, index);
|
||||
});
|
||||
|
||||
return 0;
|
||||
@@ -118,12 +118,6 @@ int w_SpriteBatch_clear(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_SpriteBatch_bind(lua_State* /*L*/)
|
||||
{
|
||||
// No-op (deprecated.)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_SpriteBatch_flush(lua_State *L)
|
||||
{
|
||||
SpriteBatch *t = luax_checkspritebatch(L, 1);
|
||||
@@ -146,9 +140,9 @@ int w_SpriteBatch_getTexture(lua_State *L)
|
||||
|
||||
// FIXME: big hack right here.
|
||||
if (typeid(*tex) == typeid(Image))
|
||||
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, tex);
|
||||
luax_pushtype(L, GRAPHICS_IMAGE_ID, tex);
|
||||
else if (typeid(*tex) == typeid(Canvas))
|
||||
luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
|
||||
luax_pushtype(L, GRAPHICS_CANVAS_ID, tex);
|
||||
else
|
||||
return luaL_error(L, "Unable to determine texture type.");
|
||||
|
||||
@@ -217,7 +211,7 @@ int w_SpriteBatch_getCount(lua_State *L)
|
||||
int w_SpriteBatch_setBufferSize(lua_State *L)
|
||||
{
|
||||
SpriteBatch *t = luax_checkspritebatch(L, 1);
|
||||
int size = luaL_checkint(L, 2);
|
||||
int size = (int) luaL_checknumber(L, 2);
|
||||
luax_catchexcept(L, [&]() {t->setBufferSize(size); });
|
||||
return 0;
|
||||
}
|
||||
@@ -242,21 +236,12 @@ static const luaL_Reg functions[] =
|
||||
{ "getCount", w_SpriteBatch_getCount },
|
||||
{ "setBufferSize", w_SpriteBatch_setBufferSize },
|
||||
{ "getBufferSize", w_SpriteBatch_getBufferSize },
|
||||
|
||||
// Deprecated since 0.9.1.
|
||||
{ "setImage", w_SpriteBatch_setTexture },
|
||||
{ "getImage", w_SpriteBatch_getTexture },
|
||||
|
||||
// Deprecated since 0.9.2.
|
||||
{ "bind", w_SpriteBatch_bind },
|
||||
{ "unbind", w_SpriteBatch_flush },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_spritebatch(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, "SpriteBatch", functions);
|
||||
return luax_register_type(L, GRAPHICS_SPRITE_BATCH_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -33,11 +33,8 @@ 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_flush(lua_State *L);
|
||||
int w_SpriteBatch_setTexture(lua_State *L);
|
||||
int w_SpriteBatch_getTexture(lua_State *L);
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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_Text.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Text *luax_checktext(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Text>(L, idx, GRAPHICS_TEXT_ID);
|
||||
}
|
||||
|
||||
int w_Text_set(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
|
||||
if (lua_isnoneornil(L, 2))
|
||||
{
|
||||
// No argument: clear all current text.
|
||||
luax_catchexcept(L, [&](){ t->set(); });
|
||||
}
|
||||
else if (lua_isnoneornil(L, 3))
|
||||
{
|
||||
// Single argument: unformatted text.
|
||||
std::string newtext = luax_checkstring(L, 2);
|
||||
luax_catchexcept(L, [&](){ t->set(newtext); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multiple arguments: formatted text.
|
||||
float wraplimit = (float) luaL_checknumber(L, 3);
|
||||
|
||||
Font::AlignMode align;
|
||||
const char *alignstr = luaL_checkstring(L, 4);
|
||||
if (!Font::getConstant(alignstr, align))
|
||||
return luaL_error(L, "Invalid align mode: %s", alignstr);
|
||||
|
||||
std::string newtext = luax_checkstring(L, 2);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); });
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_setf(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
|
||||
float wraplimit = (float) luaL_checknumber(L, 3);
|
||||
|
||||
Font::AlignMode align;
|
||||
const char *alignstr = luaL_checkstring(L, 4);
|
||||
if (!Font::getConstant(alignstr, align))
|
||||
return luaL_error(L, "Invalid align mode: %s", alignstr);
|
||||
|
||||
std::string newtext = luax_checkstring(L, 2);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_add(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
std::string text = luax_checkstring(L, 2);
|
||||
|
||||
float x = (float) luaL_optnumber(L, 3, 0.0);
|
||||
float y = (float) luaL_optnumber(L, 4, 0.0);
|
||||
float a = (float) luaL_optnumber(L, 5, 0.0);
|
||||
float sx = (float) luaL_optnumber(L, 6, 1.0);
|
||||
float sy = (float) luaL_optnumber(L, 7, sx);
|
||||
float ox = (float) luaL_optnumber(L, 8, 0.0);
|
||||
float oy = (float) luaL_optnumber(L, 9, 0.0);
|
||||
float kx = (float) luaL_optnumber(L, 10, 0.0);
|
||||
float ky = (float) luaL_optnumber(L, 11, 0.0);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->add(text, x, y, a, sx, sy, ox, oy, kx, ky); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_addf(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
std::string text = luax_checkstring(L, 2);
|
||||
float wrap = (float) luaL_checknumber(L, 3);
|
||||
|
||||
Font::AlignMode align = Font::ALIGN_MAX_ENUM;
|
||||
const char *alignstr = luaL_checkstring(L, 4);
|
||||
|
||||
if (!Font::getConstant(alignstr, align))
|
||||
return luaL_error(L, "Invalid align mode: %s", alignstr);
|
||||
|
||||
float x = (float) luaL_optnumber(L, 5, 0.0);
|
||||
float y = (float) luaL_optnumber(L, 6, 0.0);
|
||||
float a = (float) luaL_optnumber(L, 7, 0.0);
|
||||
float sx = (float) luaL_optnumber(L, 8, 1.0);
|
||||
float sy = (float) luaL_optnumber(L, 9, sx);
|
||||
float ox = (float) luaL_optnumber(L, 10, 0.0);
|
||||
float oy = (float) luaL_optnumber(L, 11, 0.0);
|
||||
float kx = (float) luaL_optnumber(L, 12, 0.0);
|
||||
float ky = (float) luaL_optnumber(L, 13, 0.0);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->addf(text, wrap, align, x, y, a, sx, sy, ox, oy, kx, ky); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_clear(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
luax_catchexcept(L, [&](){ t->clear(); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_setFont(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
Font *f = luax_checktype<Font>(L, 2, GRAPHICS_FONT_ID);
|
||||
luax_catchexcept(L, [&](){ t->setFont(f); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_getFont(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
Font *f = t->getFont();
|
||||
luax_pushtype(L, GRAPHICS_FONT_ID, f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Text_getWidth(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
lua_pushnumber(L, t->getWidth());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Text_getHeight(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
lua_pushnumber(L, t->getHeight());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "set", w_Text_set },
|
||||
{ "setf", w_Text_setf },
|
||||
{ "add", w_Text_add },
|
||||
{ "addf", w_Text_addf },
|
||||
{ "clear", w_Text_clear },
|
||||
{ "setFont", w_Text_setFont },
|
||||
{ "getFont", w_Text_getFont },
|
||||
{ "getWidth", w_Text_getWidth },
|
||||
{ "getHeight", w_Text_getHeight },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_text(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, GRAPHICS_TEXT_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
+17
-39
@@ -18,13 +18,11 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_TEXTURE_H
|
||||
#define LOVE_GRAPHICS_OPENGL_TEXTURE_H
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_TEXT_H
|
||||
#define LOVE_GRAPHICS_OPENGL_WRAP_TEXT_H
|
||||
|
||||
// LOVE
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "OpenGL.h"
|
||||
#include "Text.h"
|
||||
#include "common/runtime.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -33,41 +31,21 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class Texture : public love::graphics::Texture, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~Texture() {}
|
||||
|
||||
/**
|
||||
* Gets the OpenGL id for this texture.
|
||||
**/
|
||||
virtual GLuint getGLTexture() const = 0;
|
||||
|
||||
/**
|
||||
* Any setup the texture might need to do before drawing, e.g. binding
|
||||
* the OpenGL texture for use.
|
||||
**/
|
||||
virtual void predraw() {}
|
||||
|
||||
/**
|
||||
* Any cleanup the texture might need to do directly after drawing.
|
||||
**/
|
||||
virtual void postdraw() {}
|
||||
|
||||
/**
|
||||
* Unextended OpenGL ES 2 doesn't support non-clamp wrap modes or mipmapping
|
||||
* with non-power-of-two textures.
|
||||
**/
|
||||
static bool hasLimitedNpot()
|
||||
{
|
||||
return GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot);
|
||||
}
|
||||
|
||||
}; // Texture
|
||||
Text *luax_checktext(lua_State *L, int idx);
|
||||
int w_Text_set(lua_State *L);
|
||||
int w_Text_setf(lua_State *L);
|
||||
int w_Text_add(lua_State *L);
|
||||
int w_Text_addf(lua_State *L);
|
||||
int w_Text_clear(lua_State *L);
|
||||
int w_Text_setFont(lua_State *L);
|
||||
int w_Text_getFont(lua_State *L);
|
||||
int w_Text_getWidth(lua_State *L);
|
||||
int w_Text_getHeight(lua_State *L);
|
||||
extern "C" int luaopen_text(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_TEXTURE_H
|
||||
#endif // LOVE_GRAPHICS_OPENGL_WRAP_TEXT_H
|
||||
|
||||
+2
-5
@@ -25,12 +25,10 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Quad *luax_checkquad(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Quad>(L, idx, "Quad", GRAPHICS_QUAD_T);
|
||||
return luax_checktype<Quad>(L, idx, GRAPHICS_QUAD_ID);
|
||||
}
|
||||
|
||||
int w_Quad_setViewport(lua_State *L)
|
||||
@@ -75,9 +73,8 @@ static const luaL_Reg functions[] =
|
||||
|
||||
extern "C" int luaopen_quad(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, "Quad", functions);
|
||||
return luax_register_type(L, GRAPHICS_QUAD_ID, functions);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
+4
-7
@@ -18,27 +18,24 @@
|
||||
* 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
|
||||
#ifndef LOVE_GRAPHICS_WRAP_QUAD_H
|
||||
#define LOVE_GRAPHICS_WRAP_QUAD_H
|
||||
|
||||
// LOVE
|
||||
#include "common/runtime.h"
|
||||
#include "graphics/Quad.h"
|
||||
#include "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
|
||||
#endif // LOVE_GRAPHICS_WRAP_QUAD_H
|
||||
+1
-5
@@ -24,12 +24,10 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Texture *luax_checktexture(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Texture>(L, idx, "Texture", GRAPHICS_TEXTURE_T);
|
||||
return luax_checktype<Texture>(L, idx, GRAPHICS_TEXTURE_ID);
|
||||
}
|
||||
|
||||
int w_Texture_getWidth(lua_State *L)
|
||||
@@ -127,7 +125,5 @@ int w_Texture_getWrap(lua_State *L)
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
+3
-6
@@ -18,8 +18,8 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_TEXTURE_H
|
||||
#define LOVE_GRAPHICS_OPENGL_WRAP_TEXTURE_H
|
||||
#ifndef LOVE_GRAPHICS_WRAP_TEXTURE_H
|
||||
#define LOVE_GRAPHICS_WRAP_TEXTURE_H
|
||||
|
||||
#include "Texture.h"
|
||||
#include "common/runtime.h"
|
||||
@@ -28,8 +28,6 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Texture *luax_checktexture(lua_State *L, int idx);
|
||||
int w_Texture_getWidth(lua_State *L);
|
||||
@@ -40,8 +38,7 @@ int w_Texture_getFilter(lua_State *L);
|
||||
int w_Texture_setWrap(lua_State *L);
|
||||
int w_Texture_getWrap(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_WRAP_TEXTURE_H
|
||||
#endif // LOVE_GRAPHICS_WRAP_TEXTURE_H
|
||||
Reference in New Issue
Block a user