Using latest android branch of LÖVE (6d14516b05c9) that now syncs with default (woooo!)

This commit is contained in:
fysx
2015-07-03 19:09:18 +02:00
parent 1924bbdeeb
commit 357a7237a4
304 changed files with 28694 additions and 23064 deletions
+33 -82
View File
@@ -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));
+10 -52
View File
@@ -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;
+13 -8
View File
@@ -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)
+1 -3
View File
@@ -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;
+18 -14
View File
@@ -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));
+10 -8
View File
@@ -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
+27 -24
View File
@@ -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
+84 -52
View File
@@ -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
+73 -43
View File
@@ -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
+44 -69
View File
@@ -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
+515 -199
View File
@@ -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
+139 -59
View File
@@ -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
+203 -273
View File
@@ -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";
}
+106 -105
View File
@@ -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 &gl;
};
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);
+238 -224
View File
@@ -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},
};
+32 -21
View File
@@ -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
+103
View File
@@ -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
+255 -146
View File
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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