diff --git a/CMakeLists.txt b/CMakeLists.txt index 88bfd4606..2437a0683 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -474,6 +474,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT src/modules/graphics/ParticleSystem.h src/modules/graphics/Quad.cpp src/modules/graphics/Quad.h + src/modules/graphics/StreamBuffer.cpp + src/modules/graphics/StreamBuffer.h src/modules/graphics/Texture.cpp src/modules/graphics/Texture.h src/modules/graphics/Volatile.cpp diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index e992d8f40..8643eeec6 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -876,6 +876,10 @@ FA27B3C61B4985D8008A9DCE /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA27B3C31B4985D8008A9DCE /* Video.cpp */; }; FA27B3C71B4985D8008A9DCE /* Video.h in Headers */ = {isa = PBXBuildFile; fileRef = FA27B3C41B4985D8008A9DCE /* Video.h */; }; FA27B3C91B498623008A9DCE /* Theora.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA27B3C81B498623008A9DCE /* Theora.framework */; }; + FA29C0051E12355B00268CD8 /* StreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */; }; + FA29C0061E12355B00268CD8 /* StreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */; }; + FA2AF6741DAD64970032B62C /* vertex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA2AF6731DAD64970032B62C /* vertex.cpp */; }; + FA2AF6751DAD64970032B62C /* vertex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA2AF6731DAD64970032B62C /* vertex.cpp */; }; FA317EBA18F28B6D00B0BCD7 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FA317EB918F28B6D00B0BCD7 /* libz.dylib */; }; FA41A3C81C0A1F950084430C /* ASTCHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */; }; FA41A3C91C0A1F950084430C /* ASTCHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */; }; @@ -1311,13 +1315,13 @@ FA0B7B861A95902C000E1D17 /* wrap_Rasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Rasterizer.h; sourceTree = ""; }; FA0B7B881A95902C000E1D17 /* Color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Color.h; sourceTree = ""; }; FA0B7B891A95902C000E1D17 /* Drawable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Drawable.h; sourceTree = ""; }; - FA0B7B8A1A95902C000E1D17 /* Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Graphics.cpp; sourceTree = ""; }; + FA0B7B8A1A95902C000E1D17 /* Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Graphics.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; FA0B7B8B1A95902C000E1D17 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = ""; }; FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Canvas.cpp; sourceTree = ""; }; FA0B7B8E1A95902C000E1D17 /* Canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Canvas.h; sourceTree = ""; }; FA0B7B8F1A95902C000E1D17 /* Font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Font.cpp; sourceTree = ""; }; FA0B7B901A95902C000E1D17 /* Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Font.h; sourceTree = ""; }; - FA0B7B911A95902C000E1D17 /* Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Graphics.cpp; sourceTree = ""; }; + FA0B7B911A95902C000E1D17 /* Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Graphics.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; FA0B7B921A95902C000E1D17 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = ""; }; FA0B7B931A95902C000E1D17 /* Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Image.cpp; sourceTree = ""; }; FA0B7B941A95902C000E1D17 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = ""; }; @@ -1357,7 +1361,7 @@ FA0B7BB91A95902C000E1D17 /* wrap_Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Text.h; sourceTree = ""; }; FA0B7BBC1A95902C000E1D17 /* Quad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Quad.cpp; sourceTree = ""; }; FA0B7BBD1A95902C000E1D17 /* Quad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Quad.h; sourceTree = ""; }; - FA0B7BBE1A95902C000E1D17 /* Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Texture.cpp; sourceTree = ""; }; + FA0B7BBE1A95902C000E1D17 /* Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = Texture.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; FA0B7BBF1A95902C000E1D17 /* Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Texture.h; sourceTree = ""; }; FA0B7BC01A95902C000E1D17 /* Volatile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Volatile.cpp; sourceTree = ""; }; FA0B7BC11A95902C000E1D17 /* Volatile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Volatile.h; sourceTree = ""; }; @@ -1626,6 +1630,10 @@ FA27B3C81B498623008A9DCE /* Theora.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Theora.framework; path = /Library/Frameworks/Theora.framework; sourceTree = ""; }; FA283EDC1B27CFAA00C70067 /* nogame.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = nogame.lua; sourceTree = ""; }; FA283EDD1B27CFAA00C70067 /* nogame.lua.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nogame.lua.h; sourceTree = ""; }; + FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StreamBuffer.cpp; sourceTree = ""; }; + FA2AF6711DAC76FF0032B62C /* vertex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = vertex.h; sourceTree = ""; }; + FA2AF6721DAD62710032B62C /* StreamBuffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StreamBuffer.h; sourceTree = ""; }; + FA2AF6731DAD64970032B62C /* vertex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vertex.cpp; sourceTree = ""; }; FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_RandomGenerator.lua; sourceTree = ""; }; FA317EB918F28B6D00B0BCD7 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ASTCHandler.cpp; sourceTree = ""; }; @@ -2431,8 +2439,12 @@ FAE272511C05A15B00A67640 /* ParticleSystem.h */, FA0B7BBC1A95902C000E1D17 /* Quad.cpp */, FA0B7BBD1A95902C000E1D17 /* Quad.h */, + FA29C0041E12355B00268CD8 /* StreamBuffer.cpp */, + FA2AF6721DAD62710032B62C /* StreamBuffer.h */, FA0B7BBE1A95902C000E1D17 /* Texture.cpp */, FA0B7BBF1A95902C000E1D17 /* Texture.h */, + FA2AF6731DAD64970032B62C /* vertex.cpp */, + FA2AF6711DAC76FF0032B62C /* vertex.h */, FA0B7BC01A95902C000E1D17 /* Volatile.cpp */, FA0B7BC11A95902C000E1D17 /* Volatile.h */, FA620A2E1AA2F8DB005DB4C2 /* wrap_Quad.cpp */, @@ -3675,6 +3687,7 @@ FA0B7DAC1A95902C000E1D17 /* STBHandler.cpp in Sources */, FA0B79301A958E3B000E1D17 /* Module.cpp in Sources */, FA0B7DDA1A95902C000E1D17 /* RandomGenerator.cpp in Sources */, + FA2AF6751DAD64970032B62C /* vertex.cpp in Sources */, FA0B7D801A95902C000E1D17 /* Volatile.cpp in Sources */, FA0B792D1A958E3B000E1D17 /* Memoizer.cpp in Sources */, FA0B7EBC1A95902C000E1D17 /* LuaThread.cpp in Sources */, @@ -3778,6 +3791,7 @@ FAB17BF61ABFC4B100F9BA27 /* lz4hc.c in Sources */, FA0B7EA71A95902C000E1D17 /* wrap_Decoder.cpp in Sources */, FA0B7E1C1A95902C000E1D17 /* MouseJoint.cpp in Sources */, + FA29C0061E12355B00268CD8 /* StreamBuffer.cpp in Sources */, FA0B7CF51A95902C000E1D17 /* File.cpp in Sources */, FA0B7E341A95902C000E1D17 /* WeldJoint.cpp in Sources */, FA4F2C091DE936E200CA37D7 /* luasocket.c in Sources */, @@ -3989,6 +4003,7 @@ FA0B7AB81A958EA3000E1D17 /* enet.cpp in Sources */, FA0B7DD91A95902C000E1D17 /* RandomGenerator.cpp in Sources */, FA0B7A9B1A958EA3000E1D17 /* b2MouseJoint.cpp in Sources */, + FA2AF6741DAD64970032B62C /* vertex.cpp in Sources */, FA0B7D7F1A95902C000E1D17 /* Volatile.cpp in Sources */, FA0B7A3B1A958EA3000E1D17 /* b2TimeOfImpact.cpp in Sources */, 217DFBED1D9F6D490055D849 /* luasocket.c in Sources */, @@ -4092,6 +4107,7 @@ FA0B7D5B1A95902C000E1D17 /* wrap_Font.cpp in Sources */, FA0B7D301A95902C000E1D17 /* Graphics.cpp in Sources */, FA0B7E9D1A95902C000E1D17 /* WaveDecoder.cpp in Sources */, + FA29C0051E12355B00268CD8 /* StreamBuffer.cpp in Sources */, FA0B7EB21A95902C000E1D17 /* System.cpp in Sources */, FA0B7D1B1A95902C000E1D17 /* GlyphData.cpp in Sources */, FA0B7AD11A958EA3000E1D17 /* protocol.c in Sources */, diff --git a/src/common/Matrix.cpp b/src/common/Matrix.cpp index 3cb9b3013..2e6167ad0 100644 --- a/src/common/Matrix.cpp +++ b/src/common/Matrix.cpp @@ -97,7 +97,7 @@ Matrix4::~Matrix4() Matrix4 Matrix4::operator * (const Matrix4 &m) const { Matrix4 t; - multiply(*this, m, t); + multiply(*this, m, t.e); return t; } diff --git a/src/common/Vector.h b/src/common/Vector.h index 8aba271bb..4d63eac05 100644 --- a/src/common/Vector.h +++ b/src/common/Vector.h @@ -86,6 +86,8 @@ public: **/ Vector getNormal(float scale) const; + float cross(const Vector &v) const; + /** * Adds a Vector to this Vector. * @param v The Vector we want to add to this Vector. @@ -200,6 +202,11 @@ inline Vector Vector::getNormal(float scale) const return Vector(-y * scale, x * scale); } +inline float Vector::cross(const Vector &v) const +{ + return x * v.getY() - y * v.getX(); +} + inline float Vector::normalize(float length) { @@ -283,7 +290,7 @@ inline float Vector::operator * (const Vector &v) const inline float Vector::operator ^ (const Vector &v) const { - return x * v.getY() - y * v.getX(); + return cross(v); } inline bool Vector::operator == (const Vector &v) const diff --git a/src/common/math.h b/src/common/math.h index d0534426d..6d81bdc10 100644 --- a/src/common/math.h +++ b/src/common/math.h @@ -72,13 +72,6 @@ struct Rect } }; -struct Vertex -{ - float x, y; - float s, t; - unsigned char r, g, b, a; -}; - inline int nextP2(int x) { x += (x == 0); diff --git a/src/modules/graphics/Color.h b/src/modules/graphics/Color.h index 2c6522134..3671bf21b 100644 --- a/src/modules/graphics/Color.h +++ b/src/modules/graphics/Color.h @@ -48,6 +48,7 @@ struct ColorT bool operator!=(const ColorT &other) const; ColorT operator+=(const ColorT &other); + ColorT operator*=(const ColorT &other); ColorT operator*=(T s); ColorT operator/=(T s); }; @@ -74,6 +75,16 @@ ColorT ColorT::operator+=(const ColorT &other) return *this; } +template +ColorT ColorT::operator*=(const ColorT &other) +{ + r *= other.r; + g *= other.g; + b *= other.b; + a *= other.a; + return *this; +} + template ColorT ColorT::operator*=(T s) { @@ -101,6 +112,17 @@ ColorT operator+(const ColorT &a, const ColorT &b) return tmp += b; } +template +ColorT operator*(const ColorT &a, const ColorT &b) +{ + ColorT res; + res.r = a.r * b.r; + res.g = a.g * b.g; + res.b = a.b * b.b; + res.a = a.a * b.a; + return res; +} + template ColorT operator*(const ColorT &a, T s) { @@ -118,6 +140,19 @@ ColorT operator/(const ColorT &a, T s) typedef ColorT Color; typedef ColorT Colorf; +inline Color toColor(Colorf cf) +{ + return Color((unsigned char) (cf.r * 255.0f), + (unsigned char) (cf.g * 255.0f), + (unsigned char) (cf.b * 255.0f), + (unsigned char) (cf.a * 255.0f)); +} + +inline Colorf toColorf(Color c) +{ + return Colorf(c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f); +} + } // graphics } // love diff --git a/src/modules/graphics/Drawable.h b/src/modules/graphics/Drawable.h index 8c3288b5e..8d0878a73 100644 --- a/src/modules/graphics/Drawable.h +++ b/src/modules/graphics/Drawable.h @@ -30,6 +30,8 @@ namespace love namespace graphics { +class Graphics; + /** * A Drawable is anything that can be drawn on screen with a * position, scale and orientation. @@ -48,7 +50,7 @@ public: /** * Draws the object with the specified transformation matrix. **/ - virtual void draw(const Matrix4 &m) = 0; + virtual void draw(Graphics *gfx, const Matrix4 &m) = 0; }; } // graphics diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index c2271be8a..a83a72e4d 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -60,8 +60,154 @@ void unGammaCorrectColor(Colorf &c) love::Type Graphics::type("graphics", &Module::type); +Graphics::Graphics() + : streamBufferState() + , projectionMatrix() +{ + transformStack.reserve(16); + transformStack.push_back(Matrix4()); +} + Graphics::~Graphics() { + delete streamBufferState.vb[0]; + delete streamBufferState.vb[1]; + delete streamBufferState.indexBuffer; +} + +Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest &req) +{ + using namespace vertex; + + StreamBufferState &state = streamBufferState; + + bool shouldflush = false; + bool shouldresize = false; + + if (req.primitiveMode != state.primitiveMode + || req.formats[0] != state.formats[0] || req.formats[1] != state.formats[1] + || ((req.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0)) + || req.texture != state.texture) + { + shouldflush = true; + } + + int totalvertices = state.vertexCount + req.vertexCount; + + if (totalvertices > LOVE_UINT16_MAX && req.indexMode != TriangleIndexMode::NONE) + shouldflush = true; + + int reqIndexCount = getIndexCount(req.indexMode, req.vertexCount); + size_t reqIndexSize = reqIndexCount * sizeof(uint16); + + size_t newdatasizes[2] = {0, 0}; + size_t buffersizes[3] = {0, 0, 0}; + + for (int i = 0; i < 2; i++) + { + if (req.formats[i] != CommonFormat::NONE) + { + size_t stride = getFormatStride(req.formats[i]); + size_t datasize = stride * totalvertices; + + size_t cursize = state.vb[i]->getSize(); + + if (datasize > cursize) + { + shouldflush = true; + + if (stride * req.vertexCount > cursize) + { + buffersizes[i] = std::max(datasize, cursize * 2); + shouldresize = true; + } + } + + newdatasizes[i] = stride * req.vertexCount; + } + } + + { + size_t datasize = (state.indexCount + reqIndexCount) * sizeof(uint16); + size_t cursize = state.indexBuffer->getSize(); + + if (datasize > cursize) + { + shouldflush = true; + + if (reqIndexSize > cursize) + { + buffersizes[2] = std::max(datasize, cursize * 2); + shouldresize = true; + } + } + } + + if (shouldflush) + { + flushStreamDraws(); + + state.primitiveMode = req.primitiveMode; + state.formats[0] = req.formats[0]; + state.formats[1] = req.formats[1]; + state.texture = req.texture; + } + + if (shouldresize) + { + for (int i = 0; i < 2; i++) + { + if (state.vb[i]->getSize() < buffersizes[i]) + state.vb[i]->setSize(buffersizes[i]); + } + + if (state.indexBuffer->getSize() < buffersizes[2]) + state.indexBuffer->setSize(buffersizes[2]); + } + + if (req.indexMode != TriangleIndexMode::NONE) + { + uint16 *indices = (uint16 *) state.indexBuffer->getOffsetData(); + fillIndices(req.indexMode, state.vertexCount, req.vertexCount, indices); + state.indexBuffer->incrementOffset(reqIndexSize); + } + + StreamVertexData d; + d.stream[0] = state.vb[0]->getOffsetData(); + d.stream[1] = state.vb[1]->getOffsetData(); + + state.vertexCount += req.vertexCount; + state.indexCount += reqIndexCount; + + state.vb[0]->incrementOffset(newdatasizes[0]); + state.vb[1]->incrementOffset(newdatasizes[1]); + + return d; +} + +const Matrix4 &Graphics::getTransform() const +{ + return transformStack.back(); +} + +const Matrix4 &Graphics::getProjection() const +{ + return projectionMatrix; +} + +void Graphics::pushTransform() +{ + transformStack.push_back(transformStack.back()); +} + +void Graphics::pushIdentityTransform() +{ + transformStack.push_back(Matrix4()); +} + +void Graphics::popTransform() +{ + transformStack.pop_back(); } bool Graphics::getConstant(const char *in, DrawMode &out) @@ -232,12 +378,12 @@ StringMap Graphics::lineJoins( StringMap::Entry Graphics::stencilActionEntries[] = { - { "replace", STENCIL_REPLACE }, - { "increment", STENCIL_INCREMENT }, - { "decrement", STENCIL_DECREMENT }, + { "replace", STENCIL_REPLACE }, + { "increment", STENCIL_INCREMENT }, + { "decrement", STENCIL_DECREMENT }, { "incrementwrap", STENCIL_INCREMENT_WRAP }, { "decrementwrap", STENCIL_DECREMENT_WRAP }, - { "invert", STENCIL_INVERT }, + { "invert", STENCIL_INVERT }, }; StringMap Graphics::stencilActions(Graphics::stencilActionEntries, sizeof(Graphics::stencilActionEntries)); @@ -258,8 +404,8 @@ StringMap Graphics::compareMo StringMap::Entry Graphics::featureEntries[] = { { "multicanvasformats", FEATURE_MULTI_CANVAS_FORMATS }, - { "clampzero", FEATURE_CLAMP_ZERO }, - { "lighten", FEATURE_LIGHTEN }, + { "clampzero", FEATURE_CLAMP_ZERO }, + { "lighten", FEATURE_LIGHTEN }, { "fullnpot", FEATURE_FULL_NPOT }, { "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP }, }; @@ -279,7 +425,7 @@ StringMap Graphics::systemLimit StringMap::Entry Graphics::stackTypeEntries[] = { - { "all", STACK_ALL }, + { "all", STACK_ALL }, { "transform", STACK_TRANSFORM }, }; diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 5944088c4..2d1dfa8b8 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -24,10 +24,14 @@ // LOVE #include "common/Module.h" #include "common/StringMap.h" +#include "StreamBuffer.h" +#include "vertex.h" #include "Color.h" +#include "Texture.h" // C++ #include +#include namespace love { @@ -222,6 +226,47 @@ public: bool enabled; }; + struct StreamDrawRequest + { + vertex::PrimitiveMode primitiveMode = vertex::PrimitiveMode::TRIANGLES; + vertex::CommonFormat formats[2] = {vertex::CommonFormat::NONE, vertex::CommonFormat::NONE}; + vertex::TriangleIndexMode indexMode = vertex::TriangleIndexMode::NONE; + int vertexCount = 0; + Texture *texture = nullptr; + }; + + struct StreamVertexData + { + void *stream[2]; + }; + + class TempTransform + { + public: + + TempTransform(Graphics *gfx) + : gfx(gfx) + { + gfx->pushTransform(); + } + + TempTransform(Graphics *gfx, const Matrix4 &t) + : gfx(gfx) + { + gfx->pushTransform(); + gfx->transformStack.back() *= t; + } + + ~TempTransform() + { + gfx->popTransform(); + } + + private: + Graphics *gfx; + }; + + Graphics(); virtual ~Graphics(); // Implements Module. @@ -261,6 +306,25 @@ public: virtual bool isCanvasActive() const = 0; + virtual void flushStreamDraws() = 0; + StreamVertexData requestStreamDraw(const StreamDrawRequest &request); + + virtual Colorf getColor() const = 0; + + const Matrix4 &getTransform() const; + const Matrix4 &getProjection() const; + + template + T *getScratchBuffer(size_t count) + { + size_t bytes = sizeof(T) * count; + + if (scratchBuffer.size() < bytes) + scratchBuffer.resize(bytes); + + return (T *) scratchBuffer.data(); + } + static bool getConstant(const char *in, DrawMode &out); static bool getConstant(DrawMode in, const char *&out); @@ -294,8 +358,32 @@ public: static bool getConstant(const char *in, StackType &out); static bool getConstant(StackType in, const char *&out); +protected: + + struct StreamBufferState + { + StreamBuffer *vb[2]; + StreamBuffer *indexBuffer; + vertex::PrimitiveMode primitiveMode; + vertex::CommonFormat formats[2]; + StrongRef texture; + int vertexCount; + int indexCount; + }; + + void pushTransform(); + void pushIdentityTransform(); + void popTransform(); + + StreamBufferState streamBufferState; + + std::vector transformStack; + Matrix4 projectionMatrix; + private: + std::vector scratchBuffer; + static StringMap::Entry drawModeEntries[]; static StringMap drawModes; diff --git a/src/modules/graphics/Quad.h b/src/modules/graphics/Quad.h index 82874dd82..d9075a6ac 100644 --- a/src/modules/graphics/Quad.h +++ b/src/modules/graphics/Quad.h @@ -24,6 +24,7 @@ // LOVE #include "common/Object.h" #include "common/math.h" +#include "vertex.h" namespace love { diff --git a/src/modules/graphics/StreamBuffer.cpp b/src/modules/graphics/StreamBuffer.cpp new file mode 100644 index 000000000..d4da9737d --- /dev/null +++ b/src/modules/graphics/StreamBuffer.cpp @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2006-2016 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 "StreamBuffer.h" +#include "common/Exception.h" + +namespace love +{ +namespace graphics +{ + +StreamBuffer::StreamBuffer(Mode mode, size_t size) + : data(nullptr) + , offset(0) + , totalSize(size) + , mode(mode) +{ + setSize(size); +} + +StreamBuffer::~StreamBuffer() +{ + delete[] data; +} + +void *StreamBuffer::getData() const +{ + return data; +} + +void *StreamBuffer::getOffsetData() const +{ + return data + offset; +} + +void StreamBuffer::incrementOffset(size_t amount) +{ + offset += amount; +} + +void StreamBuffer::resetOffset() +{ + offset = 0; +} + +void StreamBuffer::setSize(size_t size) +{ + delete[] data; + + try + { + data = new uint8[size]; + } + catch (std::exception &) + { + throw love::Exception("Out of memory."); + } + + this->totalSize = size; +} + +} // graphics +} // love diff --git a/src/modules/graphics/StreamBuffer.h b/src/modules/graphics/StreamBuffer.h new file mode 100644 index 000000000..f2cf42876 --- /dev/null +++ b/src/modules/graphics/StreamBuffer.h @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2006-2016 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. + **/ + +#pragma once + +// LOVE +#include "common/int.h" + +// C +#include + +namespace love +{ +namespace graphics +{ + +// TODO: This class will need to be changed significantly in the future to +// accomodate non-client-side vertex/index data. +class StreamBuffer +{ +public: + + enum Mode + { + MODE_VERTEX, + MODE_INDEX, + }; + + StreamBuffer(Mode mode, size_t size); + ~StreamBuffer(); + + void *getData() const; + void *getOffsetData() const; + + void incrementOffset(size_t amount); + void resetOffset(); + + void setSize(size_t size); + + size_t getSize() const + { + return totalSize; + } + + Mode getMode() const + { + return mode; + } + +private: + + uint8 *data; + size_t offset; + size_t totalSize; + Mode mode; + +}; // StreamBuffer + +} // graphics +} // love diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index 0ca902758..8cbdbf5f6 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -19,6 +19,7 @@ **/ #include "Texture.h" +#include "Graphics.h" namespace love { @@ -47,6 +48,38 @@ PixelFormat Texture::getPixelFormat() const return format; } +void Texture::draw(Graphics *gfx, const Matrix4 &m) +{ + drawv(gfx, m, vertices); +} + +void Texture::drawq(Graphics *gfx, Quad *quad, const Matrix4 &m) +{ + drawv(gfx, m, quad->getVertices()); +} + +void Texture::drawv(Graphics *gfx, const Matrix4 &localTransform, const Vertex *v) +{ + Matrix4 t = gfx->getTransform() * localTransform; + + Vertex verts[4] = {v[0], v[1], v[2], v[3]}; + t.transform(verts, v, 4); + + Color c = toColor(gfx->getColor()); + + for (int i = 0; i < 4; i++) + verts[i].color = c; + + Graphics::StreamDrawRequest req; + req.formats[0] = vertex::CommonFormat::XYf_STf_RGBAub; + req.indexMode = vertex::TriangleIndexMode::QUADS; + req.vertexCount = 4; + req.texture = this; + + Graphics::StreamVertexData data = gfx->requestStreamDraw(req); + memcpy(data.stream[0], verts, sizeof(Vertex) * 4); +} + int Texture::getWidth() const { return width; diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index 4ecf28c22..ac6ff72f1 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -27,6 +27,10 @@ #include "common/pixelformat.h" #include "Drawable.h" #include "Quad.h" +#include "vertex.h" + +// C +#include namespace love { @@ -77,10 +81,13 @@ public: Texture(); virtual ~Texture(); + // Drawable. + void draw(Graphics *gfx, const Matrix4 &m) override; + /** * Draws the texture using the specified transformation with a Quad applied. **/ - virtual void drawq(Quad *quad, const Matrix4 &m) = 0; + void drawq(Graphics *gfx, Quad *quad, const Matrix4 &m); PixelFormat getPixelFormat() const; @@ -95,7 +102,7 @@ public: virtual const Vertex *getVertices() const; - virtual const void *getHandle() const = 0; + virtual ptrdiff_t getHandle() const = 0; // The default filter. static void setDefaultFilter(const Filter &f); @@ -111,6 +118,8 @@ public: protected: + virtual void drawv(Graphics *gfx, const Matrix4 &localTransform, const Vertex *v); + PixelFormat format; int width; diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 593637d4f..af0909da8 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -250,37 +250,14 @@ void Canvas::unloadVolatile() texture_memory = 0; } -void Canvas::drawv(const Matrix4 &t, const Vertex *v) +void Canvas::drawv(love::graphics::Graphics *gfx, const Matrix4 &localTransform, const Vertex *v) { - auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr && gfx->isCanvasActive(this)) + Graphics *glgfx = (Graphics *) gfx; + + if (glgfx != nullptr && glgfx->isCanvasActive(this)) throw love::Exception("Cannot render a Canvas to itself!"); - OpenGL::TempDebugGroup debuggroup("Canvas draw"); - - OpenGL::TempTransform transform(gl); - transform.get() *= t; - - gl.bindTextureToUnit(texture, 0, false); - - gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD); - - gl.bindBuffer(BUFFER_VERTEX, 0); - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s); - - gl.prepareDraw(); - gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); -} - -void Canvas::draw(const Matrix4 &m) -{ - drawv(m, vertices); -} - -void Canvas::drawq(Quad *quad, const Matrix4 &m) -{ - drawv(m, quad->getVertices()); + Texture::drawv(gfx, localTransform, v); } void Canvas::setFilter(const Texture::Filter &f) @@ -322,9 +299,9 @@ bool Canvas::setWrap(const Texture::Wrap &w) return success; } -const void *Canvas::getHandle() const +ptrdiff_t Canvas::getHandle() const { - return &texture; + return texture; } love::image::ImageData *Canvas::newImageData(love::image::Image *module, int x, int y, int w, int h) diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index a041e06f0..4dcb808cd 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -52,14 +52,10 @@ public: bool loadVolatile() override; void unloadVolatile() override; - // Implements Drawable. - void draw(const Matrix4 &m) override; - // Implements Texture. - void drawq(Quad *quad, const Matrix4 &m) override; void setFilter(const Texture::Filter &f) override; bool setWrap(const Texture::Wrap &w) override; - const void *getHandle() const override; + ptrdiff_t getHandle() const override; love::image::ImageData *newImageData(love::image::Image *module, int x, int y, int w, int h); @@ -97,7 +93,7 @@ public: private: - void drawv(const Matrix4 &t, const Vertex *v); + void drawv(Graphics *gfx, const Matrix4 &t, const Vertex *v) override; GLuint fbo; diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index 65e78563f..67e33e843 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -431,7 +431,9 @@ std::vector Font::generateVertices(const ColoredCodepoints &c uint32 prevglyph = 0; - Color curcolor(255, 255, 255, 255); + Color initialcolor(255, 255, 255, 255); + + Color curcolor = initialcolor; int curcolori = -1; int ncolors = (int) codepoints.colors.size(); @@ -441,11 +443,7 @@ std::vector Font::generateVertices(const ColoredCodepoints &c if (curcolori + 1 < ncolors && codepoints.colors[curcolori + 1].index == i) { - const Colorf &c = codepoints.colors[++curcolori].color; - curcolor.r = (unsigned char) (c.r * 255.0f); - curcolor.g = (unsigned char) (c.g * 255.0f); - curcolor.b = (unsigned char) (c.b * 255.0f); - curcolor.a = (unsigned char) (c.a * 255.0f); + curcolor = toColor(codepoints.colors[++curcolori].color); } if (g == '\n') @@ -478,7 +476,7 @@ std::vector Font::generateVertices(const ColoredCodepoints &c vertices.resize(vertstartsize); prevglyph = 0; curcolori = -1; - curcolor = Color(255, 255, 255, 255); + curcolor = initialcolor; continue; } @@ -689,10 +687,13 @@ void Font::printv(const Matrix4 &t, const std::vector &drawcommands if (vertices.empty() || drawcommands.empty()) return; + auto gfx = Module::getInstance(Module::M_GRAPHICS); + + gfx->flushStreamDraws(); + OpenGL::TempDebugGroup debuggroup("Font print"); - OpenGL::TempTransform transform(gl); - transform.get() *= t; + Graphics::TempTransform transform(gfx, t); gl.bindBuffer(BUFFER_VERTEX, 0); glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x); diff --git a/src/modules/graphics/opengl/GLBuffer.cpp b/src/modules/graphics/opengl/GLBuffer.cpp index 679a4794b..20ea8559f 100644 --- a/src/modules/graphics/opengl/GLBuffer.cpp +++ b/src/modules/graphics/opengl/GLBuffer.cpp @@ -21,6 +21,7 @@ #include "GLBuffer.h" #include "common/Exception.h" +#include "graphics/vertex.h" #include #include @@ -362,22 +363,9 @@ const void *QuadIndices::getIndices(size_t offset) const template void QuadIndices::fill() { - T *inds = (T *) indices; + using namespace love::graphics::vertex; - // 0----2 - // | / | - // | / | - // 1----3 - for (size_t i = 0; i < maxSize; ++i) - { - inds[i*6+0] = T(i * 4 + 0); - inds[i*6+1] = T(i * 4 + 1); - inds[i*6+2] = T(i * 4 + 2); - - inds[i*6+3] = T(i * 4 + 2); - inds[i*6+4] = T(i * 4 + 1); - inds[i*6+5] = T(i * 4 + 3); - } + fillIndices(TriangleIndexMode::QUADS, 0, maxSize * 4, (T *) indices); indexBuffer->fill(0, indexBuffer->getSize(), indices); } diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index c2cc8fa95..3e11c687a 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -232,7 +232,7 @@ void Graphics::setViewportSize(int width, int height) gl.setViewport({0, 0, width, height}, false); // Set up the projection matrix - gl.matrices.projection = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); + projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); } } @@ -289,6 +289,15 @@ bool Graphics::setMode(int width, int height) setDebug(enabledebug); + if (streamBufferState.vb[0] == nullptr) + { + // Initial sizes that should be good enough for most cases. It will + // resize to fit if needed, later. + streamBufferState.vb[0] = new StreamBuffer(StreamBuffer::MODE_VERTEX, 1024 * 1024 * 1); + streamBufferState.vb[1] = new StreamBuffer(StreamBuffer::MODE_VERTEX, 256 * 1024 * 1); + streamBufferState.indexBuffer = new StreamBuffer(StreamBuffer::MODE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); + } + // Reload all volatile objects. if (!Volatile::loadAll()) ::printf("Could not reload all volatile objects.\n"); @@ -339,6 +348,8 @@ void Graphics::unSetMode() if (!isCreated()) return; + flushStreamDraws(); + // Unload all volatile objects. These must be reloaded after the display // mode change. Volatile::unloadAll(); @@ -359,6 +370,8 @@ void Graphics::unSetMode() void Graphics::setActive(bool enable) { + flushStreamDraws(); + // Make sure all pending OpenGL commands have fully executed before // returning, when going from active to inactive. This is required on iOS. if (isCreated() && this->active && !enable) @@ -375,6 +388,113 @@ bool Graphics::isActive() const return active && isCreated() && window != nullptr && window->isOpen(); } +void Graphics::flushStreamDraws() +{ + using namespace vertex; + + const auto &sbstate = streamBufferState; + + if (sbstate.vertexCount == 0 && sbstate.indexCount == 0) + return; + + OpenGL::TempDebugGroup debuggroup("Stream vertices flush and draw"); + + uint32 attribs = 0; + + for (int i = 0; i < 2; i++) + { + if (sbstate.formats[i] == CommonFormat::NONE) + continue; + + StreamBuffer *buffer = sbstate.vb[i]; + + buffer->resetOffset(); + ptrdiff_t offset = (ptrdiff_t) buffer->getData(); + GLsizei stride = (GLsizei) getFormatStride(sbstate.formats[i]); + + gl.bindBuffer(BUFFER_VERTEX, 0); + + switch (sbstate.formats[i]) + { + case CommonFormat::NONE: + break; + case CommonFormat::XYf: + attribs |= ATTRIBFLAG_POS; + glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset)); + break; + case CommonFormat::RGBAub: + attribs |= ATTRIBFLAG_COLOR; + glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset)); + break; + case CommonFormat::XYf_STf: + attribs |= ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD; + glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf, x))); + glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf, s))); + break; + case CommonFormat::XYf_STf_RGBAub: + attribs |= ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR; + glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, x))); + glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, s))); + glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, color.r))); + break; + case CommonFormat::XYf_STus_RGBAub: + attribs |= ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR; + glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, x))); + glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, s))); + glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, color.r))); + break; + } + } + + if (attribs == 0) + return; + + GLenum glmode = GL_ZERO; + + switch (sbstate.primitiveMode) + { + case PrimitiveMode::TRIANGLES: + glmode = GL_TRIANGLES; + break; + case PrimitiveMode::POINTS: + glmode = GL_POINTS; + break; + } + + if (attribs & ATTRIBFLAG_COLOR) + glVertexAttrib4f(ATTRIB_CONSTANTCOLOR, 1.0f, 1.0f, 1.0f, 1.0f); + + pushIdentityTransform(); + + gl.prepareDraw(); + + gl.bindTextureToUnit(sbstate.texture, 0, false); + + gl.useVertexAttribArrays(attribs); + + if (sbstate.indexCount > 0) + { + sbstate.indexBuffer->resetOffset(); + ptrdiff_t offset = (ptrdiff_t) sbstate.indexBuffer->getData(); + + gl.bindBuffer(BUFFER_INDEX, 0); + gl.drawElements(glmode, sbstate.indexCount, GL_UNSIGNED_SHORT, BUFFER_OFFSET(offset)); + } + else + gl.drawArrays(glmode, 0, sbstate.vertexCount); + + popTransform(); + + if (attribs & ATTRIB_CONSTANTCOLOR) + { + Colorf nc = states.back().gammaCorrectedColor; + glVertexAttrib4f(ATTRIB_CONSTANTCOLOR, nc.r, nc.g, nc.b, nc.a); + } + + streamBufferState.vertexCount = 0; + streamBufferState.indexCount = 0; +} + static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/) { // Human-readable strings for the debug info. @@ -513,7 +633,7 @@ void Graphics::setCanvas(const std::vector &canvases) int h = firstcanvas->getHeight(); gl.setViewport({0, 0, w, h}, true); - gl.matrices.projection = Matrix4::ortho(0.0, (float) w, 0.0, (float) h); + projectionMatrix = Matrix4::ortho(0.0, (float) w, 0.0, (float) h); // Make sure the correct sRGB setting is used when drawing to the canvases. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) @@ -564,7 +684,7 @@ void Graphics::setCanvas() // The projection matrix is flipped compared to rendering to a canvas, due // to OpenGL considering (0,0) bottom-left instead of top-left. - gl.matrices.projection = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); + projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) { @@ -590,6 +710,8 @@ std::vector Graphics::getCanvas() const void Graphics::endPass() { + flushStreamDraws(); + // Discard the stencil buffer. discard({}, true); @@ -617,6 +739,8 @@ void Graphics::endPass() void Graphics::clear(Colorf c) { + flushStreamDraws(); + gammaCorrectColor(c); glClearColor(c.r, c.g, c.b, c.a); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -646,6 +770,8 @@ void Graphics::clear(const std::vector &colors) return; } + flushStreamDraws(); + bool drawbuffersmodified = false; for (int i = 0; i < ncolors; i++) @@ -712,6 +838,7 @@ bool Graphics::isCanvasActive(Canvas *canvas) const void Graphics::discard(const std::vector &colorbuffers, bool depthstencil) { + flushStreamDraws(); discard(OpenGL::FRAMEBUFFER_ALL, colorbuffers, depthstencil); } @@ -799,7 +926,7 @@ void Graphics::bindCachedFBO(const std::vector &canvases) } else { - GLuint tex = *(GLuint *) canvases[i]->getHandle(); + GLuint tex = (GLuint) canvases[i]->getHandle(); glFramebufferTexture2D(GL_FRAMEBUFFER, drawbuffers[i], GL_TEXTURE_2D, tex, 0); } } @@ -1047,6 +1174,8 @@ bool Graphics::isCreated() const void Graphics::setScissor(const Rect &rect) { + flushStreamDraws(); + DisplayState &state = states.back(); glEnable(GL_SCISSOR_TEST); @@ -1081,6 +1210,7 @@ void Graphics::intersectScissor(const Rect &rect) void Graphics::setScissor() { + flushStreamDraws(); states.back().scissor = false; glDisable(GL_SCISSOR_TEST); } @@ -1135,6 +1265,8 @@ void Graphics::stopDrawToStencilBuffer() if (!writingToStencil) return; + flushStreamDraws(); + writingToStencil = false; const DisplayState &state = states.back(); @@ -1324,11 +1456,17 @@ bool Graphics::isGammaCorrect() const void Graphics::setColor(Colorf c) { + c.r = std::min(std::max(c.r, 0.0f), 1.0f); + c.g = std::min(std::max(c.g, 0.0f), 1.0f); + c.b = std::min(std::max(c.b, 0.0f), 1.0f); + c.a = std::min(std::max(c.a, 0.0f), 1.0f); + Colorf nc = c; gammaCorrectColor(nc); glVertexAttrib4f(ATTRIB_CONSTANTCOLOR, nc.r, nc.g, nc.b, nc.a); states.back().color = c; + states.back().gammaCorrectedColor = nc; } Colorf Graphics::getColor() const @@ -1350,7 +1488,6 @@ void Graphics::setFont(Font *font) { // We don't need to set a default font here if null is passed in, since we // only care about the default font in getFont and print. - DisplayState &state = states.back(); state.font.set(font); } @@ -1366,6 +1503,8 @@ void Graphics::setShader(Shader *shader) if (shader == nullptr) return setShader(); + flushStreamDraws(); + DisplayState &state = states.back(); shader->attach(); @@ -1375,6 +1514,8 @@ void Graphics::setShader(Shader *shader) void Graphics::setShader() { + flushStreamDraws(); + DisplayState &state = states.back(); // This will activate the default shader. @@ -1390,6 +1531,8 @@ Shader *Graphics::getShader() const void Graphics::setColorMask(ColorMask mask) { + flushStreamDraws(); + glColorMask(mask.r, mask.g, mask.b, mask.a); states.back().colorMask = mask; } @@ -1401,6 +1544,8 @@ Graphics::ColorMask Graphics::getColorMask() const void Graphics::setBlendMode(BlendMode mode, BlendAlpha alphamode) { + flushStreamDraws(); + GLenum func = GL_FUNC_ADD; GLenum srcRGB = GL_ONE; GLenum srcA = GL_ONE; @@ -1538,6 +1683,9 @@ Graphics::LineJoin Graphics::getLineJoin() const void Graphics::setPointSize(float size) { + if (streamBufferState.primitiveMode == vertex::PrimitiveMode::POINTS) + flushStreamDraws(); + gl.setPointSize(size); states.back().pointSize = size; } @@ -1553,6 +1701,8 @@ void Graphics::setWireframe(bool enable) if (GLAD_ES_VERSION_2_0) return; + flushStreamDraws(); + glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL); states.back().wireframe = enable; } @@ -1564,12 +1714,12 @@ bool Graphics::isWireframe() const void Graphics::draw(Drawable *drawable, const Matrix4 &m) { - drawable->draw(m); + drawable->draw(this, m); } -void Graphics::drawq(Texture *texture, Quad *quad, const Matrix4 &m) +void Graphics::draw(Texture *texture, Quad *quad, const Matrix4 &m) { - texture->drawq(quad, m); + texture->drawq(this, quad, m); } void Graphics::print(const std::vector &str, const Matrix4 &m) @@ -1596,25 +1746,51 @@ void Graphics::printf(const std::vector &str, float wrap, F * Primitives **/ -void Graphics::points(const float *coords, const uint8 *colors, size_t numpoints) +void Graphics::points(const float *coords, const Colorf *colors, size_t numpoints) { - OpenGL::TempDebugGroup debuggroup("Graphics points draw"); + StreamDrawRequest req; + req.primitiveMode = vertex::PrimitiveMode::POINTS; + req.formats[0] = vertex::CommonFormat::XYf; + if (colors) + req.formats[1] = vertex::CommonFormat::RGBAub; + req.vertexCount = (int) numpoints; - gl.prepareDraw(); - gl.bindTextureToUnit(gl.getDefaultTexture(), 0, false); - gl.bindBuffer(BUFFER_VERTEX, 0); + StreamVertexData data = requestStreamDraw(req); - uint32 attribflags = ATTRIBFLAG_POS; - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords); + const Matrix4 &t = getTransform(); + t.transform((Vector *) data.stream[0], (const Vector *) coords, req.vertexCount); + + Color *colordata = (Color *) data.stream[1]; if (colors) { - attribflags |= ATTRIBFLAG_COLOR; - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors); - } + Colorf nc = getColor(); + gammaCorrectColor(nc); - gl.useVertexAttribArrays(attribflags); - gl.drawArrays(GL_POINTS, 0, (GLsizei) numpoints); + if (isGammaCorrect()) + { + for (int i = 0; i < req.vertexCount; i++) + { + Colorf ci = colors[i]; + gammaCorrectColor(ci); + ci *= nc; + unGammaCorrectColor(ci); + colordata[i] = toColor(ci); + } + } + else + { + for (int i = 0; i < req.vertexCount; i++) + colordata[i] = toColor(nc * colors[i]); + } + } + else + { + Color c = toColor(getColor()); + + for (int i = 0; i < req.vertexCount; i++) + colordata[i] = c; + } } void Graphics::polyline(const float *coords, size_t count) @@ -1626,19 +1802,19 @@ void Graphics::polyline(const float *coords, size_t count) { NoneJoinPolyline line; line.render(coords, count, state.lineWidth * .5f, pixelsize, state.lineStyle == LINE_SMOOTH); - line.draw(); + line.draw(this); } else if (state.lineJoin == LINE_JOIN_BEVEL) { BevelJoinPolyline line; line.render(coords, count, state.lineWidth * .5f, pixelsize, state.lineStyle == LINE_SMOOTH); - line.draw(); + line.draw(this); } else // LINE_JOIN_MITER { MiterJoinPolyline line; line.render(coords, count, state.lineWidth * .5f, pixelsize, state.lineStyle == LINE_SMOOTH); - line.draw(); + line.draw(this); } } @@ -1669,7 +1845,7 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, floa float angle_shift = half_pi / ((float) points + 1.0f); int num_coords = (points + 2) * 8; - float *coords = new float[num_coords + 2]; + float *coords = getScratchBuffer(num_coords + 2); float phi = .0f; for (int i = 0; i <= points + 2; ++i, phi += angle_shift) @@ -1706,8 +1882,6 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, floa coords[num_coords + 1] = coords[1]; polygon(mode, coords, num_coords + 2); - - delete[] coords; } int Graphics::calculateEllipsePoints(float rx, float ry) const @@ -1733,12 +1907,12 @@ void Graphics::circle(DrawMode mode, float x, float y, float radius) void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int points) { - float two_pi = static_cast(LOVE_M_PI * 2); + float two_pi = (float) (LOVE_M_PI * 2); if (points <= 0) points = 1; float angle_shift = (two_pi / points); float phi = .0f; - float *coords = new float[2 * (points + 1)]; + float *coords = getScratchBuffer(2 * (points + 1)); for (int i = 0; i < points; ++i, phi += angle_shift) { coords[2*i+0] = x + a * cosf(phi); @@ -1749,8 +1923,6 @@ void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int po coords[2*points+1] = coords[1]; polygon(mode, coords, (points + 1) * 2); - - delete[] coords; } void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b) @@ -1804,7 +1976,7 @@ void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float r if (arcmode == ARC_PIE) { num_coords = (points + 3) * 2; - coords = new float[num_coords]; + coords = getScratchBuffer(num_coords); coords[0] = coords[num_coords - 2] = x; coords[1] = coords[num_coords - 1] = y; @@ -1814,14 +1986,14 @@ void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float r else if (arcmode == ARC_OPEN) { num_coords = (points + 1) * 2; - coords = new float[num_coords]; + coords = getScratchBuffer(num_coords); createPoints(coords); } else // ARC_CLOSED { num_coords = (points + 2) * 2; - coords = new float[num_coords]; + coords = getScratchBuffer(num_coords); createPoints(coords); @@ -1830,10 +2002,7 @@ void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float r coords[num_coords - 1] = coords[1]; } - // NOTE: We rely on polygon() using GL_TRIANGLE_FAN, when fill mode is used. polygon(drawmode, coords, num_coords); - - delete[] coords; } void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, float angle2) @@ -1861,14 +2030,21 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count) } else { - OpenGL::TempDebugGroup debuggroup("Filled polygon draw"); + StreamDrawRequest req; + req.formats[0] = vertex::CommonFormat::XYf; + req.formats[1] = vertex::CommonFormat::RGBAub; + req.indexMode = vertex::TriangleIndexMode::FAN; + req.vertexCount = (int)count/2 - 1; - gl.prepareDraw(); - gl.bindTextureToUnit(gl.getDefaultTexture(), 0, false); - gl.bindBuffer(BUFFER_VERTEX, 0); - gl.useVertexAttribArrays(ATTRIBFLAG_POS); - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords); - gl.drawArrays(GL_TRIANGLE_FAN, 0, (int)count/2-1); // opengl will close the polygon for us + StreamVertexData data = requestStreamDraw(req); + + const Matrix4 &t = getTransform(); + t.transform((Vector *) data.stream[0], (const Vector *) coords, req.vertexCount); + + Color c = toColor(getColor()); + Color *colordata = (Color *) data.stream[1]; + for (int i = 0; i < req.vertexCount; i++) + colordata[i] = c; } } @@ -1904,9 +2080,14 @@ Graphics::RendererInfo Graphics::getRendererInfo() const Graphics::Stats Graphics::getStats() const { + int drawcalls = gl.stats.drawCalls; + + if (streamBufferState.vertexCount > 0) + drawcalls++; + Stats stats; - stats.drawCalls = gl.stats.drawCalls; + stats.drawCalls = drawcalls; stats.canvasSwitches = canvasSwitchCount; stats.shaderSwitches = gl.stats.shaderSwitches; stats.canvases = Canvas::canvasCount; @@ -1960,7 +2141,7 @@ void Graphics::push(StackType type) if (stackTypes.size() == MAX_USER_STACK_DEPTH) throw Exception("Maximum stack depth reached (more pushes than pops?)"); - gl.pushTransform(); + pushTransform(); pixelScaleStack.push_back(pixelScaleStack.back()); @@ -1975,7 +2156,7 @@ void Graphics::pop() if (stackTypes.size() < 1) throw Exception("Minimum stack depth reached (more pops than pushes?)"); - gl.popTransform(); + popTransform(); pixelScaleStack.pop_back(); if (stackTypes.back() == STACK_ALL) @@ -1993,34 +2174,34 @@ void Graphics::pop() void Graphics::rotate(float r) { - gl.getTransform().rotate(r); + transformStack.back().rotate(r); } void Graphics::scale(float x, float y) { - gl.getTransform().scale(x, y); + transformStack.back().scale(x, y); pixelScaleStack.back() *= (fabs(x) + fabs(y)) / 2.0; } void Graphics::translate(float x, float y) { - gl.getTransform().translate(x, y); + transformStack.back().translate(x, y); } void Graphics::shear(float kx, float ky) { - gl.getTransform().shear(kx, ky); + transformStack.back().shear(kx, ky); } void Graphics::origin() { - gl.getTransform().setIdentity(); + transformStack.back().setIdentity(); pixelScaleStack.back() = 1; } void Graphics::applyTransform(love::math::Transform *transform) { - Matrix4 &m = gl.getTransform(); + Matrix4 &m = transformStack.back(); m *= transform->getMatrix(); float sx, sy; @@ -2031,7 +2212,7 @@ void Graphics::applyTransform(love::math::Transform *transform) void Graphics::replaceTransform(love::math::Transform *transform) { const Matrix4 &m = transform->getMatrix(); - gl.getTransform() = m; + transformStack.back() = m; float sx, sy; m.getApproximateScale(sx, sy); @@ -2041,7 +2222,7 @@ void Graphics::replaceTransform(love::math::Transform *transform) Vector Graphics::transformPoint(Vector point) { Vector p; - gl.getTransform().transform(&p, &point, 1); + transformStack.back().transform(&p, &point, 1); return p; } @@ -2050,7 +2231,7 @@ Vector Graphics::inverseTransformPoint(Vector point) Vector p; // TODO: We should probably cache the inverse transform so we don't have to // re-calculate it every time this is called. - gl.getTransform().inverse().transform(&p, &point, 1); + transformStack.back().inverse().transform(&p, &point, 1); return p; } diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 8f1886fc6..e27463af2 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -62,7 +62,7 @@ namespace graphics namespace opengl { -class Graphics : public love::graphics::Graphics +class Graphics final : public love::graphics::Graphics { public: @@ -78,16 +78,16 @@ public: virtual ~Graphics(); // Implements Module. - const char *getName() const; + const char *getName() const override; - virtual void setViewportSize(int width, int height); - virtual bool setMode(int width, int height); - virtual void unSetMode(); + void setViewportSize(int width, int height) override; + bool setMode(int width, int height) override; + void unSetMode() override; - virtual void setActive(bool active); - virtual bool isActive() const; + void setActive(bool active) override; + bool isActive() const override; - void setDebug(bool enable); + void flushStreamDraws() override; /** * Resets the current color, background color, @@ -101,7 +101,7 @@ public: void discard(const std::vector &colorbuffers, bool depthstencil); - virtual bool isCanvasActive() const; + bool isCanvasActive() const override; bool isCanvasActive(Canvas *canvas) const; /** @@ -203,15 +203,14 @@ public: bool isGammaCorrect() const; /** - * Sets the foreground color. - * @param c The new foreground color. + * Sets the current constant color. **/ void setColor(Colorf c); /** * Gets current color. **/ - Colorf getColor() const; + Colorf getColor() const override; /** * Sets the background Color. @@ -324,7 +323,7 @@ public: bool isWireframe() const; void draw(Drawable *drawable, const Matrix4 &m); - void drawq(Texture *texture, Quad *quad, const Matrix4 &m); + void draw(Texture *texture, Quad *quad, const Matrix4 &m); /** * Draws text at the specified coordinates @@ -341,7 +340,7 @@ public: * @param x Point along x-axis. * @param y Point along y-axis. **/ - void points(const float *coords, const uint8 *colors, size_t numpoints); + void points(const float *coords, const Colorf *colors, size_t numpoints); /** * Draws a series of lines connecting the given vertices. @@ -462,6 +461,7 @@ private: struct DisplayState { Colorf color = Colorf(1.0, 1.0, 1.0, 1.0); + Colorf gammaCorrectedColor = Colorf(1.0f, 1.0f, 1.0f, 1.0f); Colorf backgroundColor = Colorf(0.0, 0.0, 0.0, 1.0); BlendMode blendMode = BLEND_ALPHA; @@ -512,6 +512,8 @@ private: void discard(OpenGL::FramebufferTarget target, const std::vector &colorbuffers, bool depthstencil); GLuint attachCachedStencilBuffer(int w, int h, int samples); + void setDebug(bool enable); + void checkSetDefaultFont(); int calculateEllipsePoints(float rx, float ry) const; diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index b9e13a4c1..da5d2fcbb 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -169,7 +169,7 @@ Image::~Image() void Image::preload() { for (int i = 0; i < 4; i++) - vertices[i].r = vertices[i].g = vertices[i].b = vertices[i].a = 255; + vertices[i].color = Color(255, 255, 255, 255); // Vertices are ordered for use with triangle strips: // 0----2 @@ -448,38 +448,9 @@ bool Image::refresh(int xoffset, int yoffset, int w, int h) return true; } -void Image::drawv(const Matrix4 &t, const Vertex *v) +ptrdiff_t Image::getHandle() const { - OpenGL::TempDebugGroup debuggroup("Image draw"); - - OpenGL::TempTransform transform(gl); - transform.get() *= t; - - gl.bindTextureToUnit(texture, 0, false); - - gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD); - - gl.bindBuffer(BUFFER_VERTEX, 0); - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s); - - gl.prepareDraw(); - gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); -} - -void Image::draw(const Matrix4 &m) -{ - drawv(m, vertices); -} - -void Image::drawq(Quad *quad, const Matrix4 &m) -{ - drawv(m, quad->getVertices()); -} - -const void *Image::getHandle() const -{ - return &texture; + return texture; } const std::vector> &Image::getImageData() const diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index e5850d640..775be446b 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -90,17 +90,7 @@ public: bool loadVolatile() override; void unloadVolatile() override; - /** - * @copydoc Drawable::draw() - **/ - void draw(const Matrix4 &m) override; - - /** - * @copydoc Texture::drawq() - **/ - void drawq(Quad *quad, const Matrix4 &m) override; - - const void *getHandle() const override; + ptrdiff_t getHandle() const override; const std::vector> &getImageData() const; const std::vector> &getCompressedData() const; @@ -139,8 +129,6 @@ public: private: - void drawv(const Matrix4 &t, const Vertex *v); - void preload(); void generateMipmaps(); diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index 7815f80f3..1d0250777 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -23,6 +23,7 @@ #include "common/Matrix.h" #include "common/Exception.h" #include "Shader.h" +#include "graphics/Graphics.h" // C++ #include @@ -44,7 +45,7 @@ static const char *getBuiltinAttribName(VertexAttribID attribid) 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_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct"); static std::vector getDefaultVertexFormat() { @@ -574,11 +575,13 @@ int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inpu return attriblocation; } -void Mesh::draw(const Matrix4 &m) +void Mesh::draw(Graphics *gfx, const Matrix4 &m) { if (vertexCount <= 0) return; + gfx->flushStreamDraws(); + OpenGL::TempDebugGroup debuggroup("Mesh draw"); uint32 enabledattribs = 0; @@ -601,13 +604,9 @@ void Mesh::draw(const Matrix4 &m) gl.useVertexAttribArrays(enabledattribs); - if (texture.get()) - gl.bindTextureToUnit(*(GLuint *) texture->getHandle(), 0, false); - else - gl.bindTextureToUnit(gl.getDefaultTexture(), 0, false); + gl.bindTextureToUnit(texture, 0, false); - OpenGL::TempTransform transform(gl); - transform.get() *= m; + Graphics::TempTransform transform(gfx, m); gl.prepareDraw(); diff --git a/src/modules/graphics/opengl/Mesh.h b/src/modules/graphics/opengl/Mesh.h index 51ad66c14..3c99f352a 100644 --- a/src/modules/graphics/opengl/Mesh.h +++ b/src/modules/graphics/opengl/Mesh.h @@ -200,7 +200,7 @@ public: int bindAttributeToShaderInput(int attributeindex, const std::string &inputname); // Implements Drawable. - void draw(const Matrix4 &m) override; + void draw(Graphics *gfx, const Matrix4 &m) override; static GLenum getGLBufferUsage(Usage usage); diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 278924fd2..e6fd9b763 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -25,6 +25,8 @@ #include "Shader.h" #include "common/Exception.h" +#include "graphics/Graphics.h" + // C++ #include #include @@ -74,7 +76,6 @@ OpenGL::OpenGL() , vendor(VENDOR_UNKNOWN) , state() { - matrices.transform.reserve(10); } bool OpenGL::initContext() @@ -87,7 +88,6 @@ bool OpenGL::initContext() initOpenGLFunctions(); initVendor(); - initMatrices(); bugs = {}; @@ -314,14 +314,6 @@ void OpenGL::initMaxValues() maxPointSize = limits[1]; } -void OpenGL::initMatrices() -{ - matrices.transform.clear(); - - matrices.transform.push_back(Matrix4()); - matrices.projection = Matrix4(); -} - void OpenGL::createDefaultTexture() { // Set the 'default' texture (id 0) as a repeating white pixel. Otherwise, @@ -346,21 +338,6 @@ void OpenGL::createDefaultTexture() bindTextureToUnit(curtexture, 0, false); } -void OpenGL::pushTransform() -{ - matrices.transform.push_back(matrices.transform.back()); -} - -void OpenGL::popTransform() -{ - matrices.transform.pop_back(); -} - -Matrix4 &OpenGL::getTransform() -{ - return matrices.transform.back(); -} - void OpenGL::prepareDraw() { TempDebugGroup debuggroup("Prepare OpenGL draw"); @@ -373,8 +350,9 @@ void OpenGL::prepareDraw() // because uniform uploads can be significantly slower than glLoadMatrix. if (GLAD_VERSION_1_0) { - const Matrix4 &curproj = matrices.projection; - const Matrix4 &curxform = matrices.transform.back(); + auto gfx = Module::getInstance(Module::M_GRAPHICS); + const Matrix4 &curxform = gfx->getTransform(); + const Matrix4 &curproj = gfx->getProjection(); const Matrix4 &lastproj = state.lastProjectionMatrix; const Matrix4 &lastxform = state.lastTransformMatrix; @@ -386,14 +364,14 @@ void OpenGL::prepareDraw() glLoadMatrixf(curproj.getElements()); glMatrixMode(GL_MODELVIEW); - state.lastProjectionMatrix = matrices.projection; + state.lastProjectionMatrix = curproj; } // Same with the transform matrix. if (memcmp(curxform.getElements(), lastxform.getElements(), sizeof(float) * 16) != 0) { glLoadMatrixf(curxform.getElements()); - state.lastTransformMatrix = matrices.transform.back(); + state.lastTransformMatrix = curxform; } } } @@ -454,7 +432,7 @@ void OpenGL::useVertexAttribArrays(uint32 arraybits) // than 32. Lets hope that doesn't change... for (uint32 i = 0; i < 32; i++) { - uint32 bit = 1 << i; + uint32 bit = 1u << i; if (diff & bit) { @@ -637,6 +615,12 @@ void OpenGL::bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev } } +void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev) +{ + GLuint handle = texture != nullptr ? (GLuint) texture->getHandle() : getDefaultTexture(); + bindTextureToUnit(handle, textureunit, restoreprev); +} + void OpenGL::deleteTexture(GLuint texture) { // glDeleteTextures binds texture 0 to all texture units the deleted texture diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 024e086cf..4faebd20f 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -27,6 +27,7 @@ #include "common/math.h" #include "graphics/Color.h" #include "graphics/Texture.h" +#include "graphics/vertex.h" #include "common/Matrix.h" // GLAD @@ -70,13 +71,6 @@ enum VertexAttribFlags ATTRIBFLAG_CONSTANTCOLOR = 1 << ATTRIB_CONSTANTCOLOR }; -enum BufferType -{ - BUFFER_VERTEX = 0, - BUFFER_INDEX, - BUFFER_MAX_ENUM -}; - /** * Thin layer between OpenGL and the rest of the program. * Internally shadows some OpenGL context state for improved efficiency and @@ -119,36 +113,6 @@ public: GLenum type = 0; }; - struct - { - std::vector transform; - Matrix4 projection; - } matrices; - - class TempTransform - { - public: - - TempTransform(OpenGL &gl) - : gl(gl) - { - gl.pushTransform(); - } - - ~TempTransform() - { - gl.popTransform(); - } - - Matrix4 &get() - { - return gl.getTransform(); - } - - private: - OpenGL ≷ - }; - class TempDebugGroup { public: @@ -242,10 +206,6 @@ public: **/ void deInitContext(); - void pushTransform(); - void popTransform(); - Matrix4 &getTransform(); - /** * Set up necessary state (LOVE-provided shader uniforms, etc.) for drawing. * This *MUST* be called directly before OpenGL drawing functions. @@ -359,6 +319,7 @@ public: * @param restoreprev Restore previously bound texture unit when done. **/ void bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev); + void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev); /** * Helper for deleting an OpenGL texture. @@ -440,7 +401,6 @@ private: void initVendor(); void initOpenGLFunctions(); void initMaxValues(); - void initMatrices(); void createDefaultTexture(); bool contextInitialized; diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index 43ac27dda..df90aae9e 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -21,6 +21,7 @@ //LOVE #include "common/config.h" #include "ParticleSystem.h" +#include "graphics/Graphics.h" #include "OpenGL.h" @@ -61,7 +62,7 @@ void ParticleSystem::createVertices(size_t numparticles) { try { - love::Vertex *pverts = new love::Vertex[numparticles * 4]; + Vertex *pverts = new Vertex[numparticles * 4]; delete[] particleVerts; particleVerts = pverts; } @@ -84,17 +85,18 @@ void ParticleSystem::setBufferSize(uint32 size) createVertices(size); } -void ParticleSystem::draw(const Matrix4 &m) +void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m) { uint32 pCount = getCount(); if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || particleVerts == nullptr) return; + gfx->flushStreamDraws(); + OpenGL::TempDebugGroup debuggroup("ParticleSystem draw"); - OpenGL::TempTransform transform(gl); - transform.get() *= m; + Graphics::TempTransform transform(gfx, m); const Vertex *textureVerts = texture->getVertices(); Vertex *pVerts = particleVerts; @@ -114,31 +116,29 @@ void ParticleSystem::draw(const Matrix4 &m) t.setTransformation(p->position.x, p->position.y, p->angle, p->size, p->size, offset.x, offset.y, 0.0f, 0.0f); t.transform(pVerts, textureVerts, 4); + // Particle colors are stored as floats (0-1) but vertex colors are + // unsigned bytes (0-255). + Color c = toColor(p->color); + // set the texture coordinate and color data for particle vertices for (int v = 0; v < 4; v++) { pVerts[v].s = textureVerts[v].s; pVerts[v].t = textureVerts[v].t; - - // Particle colors are stored as floats (0-1) but vertex colors are - // unsigned bytes (0-255). - pVerts[v].r = (unsigned char) (p->color.r*255); - pVerts[v].g = (unsigned char) (p->color.g*255); - pVerts[v].b = (unsigned char) (p->color.b*255); - pVerts[v].a = (unsigned char) (p->color.a*255); + pVerts[v].color = c; } pVerts += 4; p = p->next; } - gl.bindTextureToUnit(*(GLuint *) texture->getHandle(), 0, false); + gl.bindTextureToUnit(texture, 0, false); gl.prepareDraw(); gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR); gl.bindBuffer(BUFFER_VERTEX, 0); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &particleVerts[0].r); + glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &particleVerts[0].color.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); diff --git a/src/modules/graphics/opengl/ParticleSystem.h b/src/modules/graphics/opengl/ParticleSystem.h index 8fbdcb0c7..6737ad7a9 100644 --- a/src/modules/graphics/opengl/ParticleSystem.h +++ b/src/modules/graphics/opengl/ParticleSystem.h @@ -43,7 +43,7 @@ public: ParticleSystem *clone() override; void setBufferSize(uint32 size) override; - void draw(const Matrix4 &m) override; + void draw(Graphics *gfx, const Matrix4 &m) override; private: diff --git a/src/modules/graphics/opengl/Polyline.cpp b/src/modules/graphics/opengl/Polyline.cpp index dad500b64..1887e9c10 100644 --- a/src/modules/graphics/opengl/Polyline.cpp +++ b/src/modules/graphics/opengl/Polyline.cpp @@ -20,9 +20,7 @@ // LOVE #include "Polyline.h" - -// OpenGL -#include "OpenGL.h" +#include "graphics/Graphics.h" // C++ #include @@ -86,7 +84,7 @@ void Polyline::render(const float *coords, size_t count, size_t size_hint, float // 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) + if (triangle_mode == vertex::TriangleIndexMode::STRIP) extra_vertices = 2; } @@ -115,6 +113,12 @@ void NoneJoinPolyline::renderEdge(std::vector &anchors, std::vector &anchors, std::vector &/*normals*/, f { for (size_t i = 2; i + 3 < vertex_count; i += 4) { - Vector s = vertices[i] - vertices[i+3]; - Vector t = vertices[i] - vertices[i+1]; + // v0-v2 + // | / | <- main quad line + // v1-v3 + + Vector s = vertices[i+0] - vertices[i+2]; + Vector t = vertices[i+0] - vertices[i+1]; s.normalize(pixel_size); t.normalize(pixel_size); const size_t k = 4 * (i - 2); - overdraw[k ] = vertices[i]; - overdraw[k+1] = vertices[i] + s + t; - overdraw[k+2] = vertices[i+1] + s - t; - overdraw[k+3] = vertices[i+1]; + + overdraw[k+0] = vertices[i+0]; + overdraw[k+1] = vertices[i+1]; + overdraw[k+2] = vertices[i+0] + s + t; + overdraw[k+3] = vertices[i+1] + s - t; overdraw[k+4] = vertices[i+1]; - overdraw[k+5] = vertices[i+1] + s - t; - overdraw[k+6] = vertices[i+2] - s - t; - overdraw[k+7] = vertices[i+2]; + overdraw[k+5] = vertices[i+3]; + overdraw[k+6] = vertices[i+1] + s - t; + overdraw[k+7] = vertices[i+3] - s - t; - overdraw[k+8] = vertices[i+2]; - overdraw[k+9] = vertices[i+2] - s - t; - overdraw[k+10] = vertices[i+3] - s + t; - overdraw[k+11] = vertices[i+3]; + overdraw[k+ 8] = vertices[i+3]; + overdraw[k+ 9] = vertices[i+2]; + overdraw[k+10] = vertices[i+3] - s - t; + overdraw[k+11] = vertices[i+2] - s + t; - overdraw[k+12] = vertices[i+3]; - overdraw[k+13] = vertices[i+3] - s + t; - overdraw[k+14] = vertices[i] + s + t; - overdraw[k+15] = vertices[i]; + overdraw[k+12] = vertices[i+2]; + overdraw[k+13] = vertices[i+0]; + overdraw[k+14] = vertices[i+2] - s + t; + overdraw[k+15] = vertices[i+0] + s + t; } } @@ -358,104 +367,50 @@ Polyline::~Polyline() delete[] vertices; } -void Polyline::draw() +void Polyline::draw(love::graphics::Graphics *gfx) { - OpenGL::TempDebugGroup debuggroup("Line draw"); - - GLushort *indices = nullptr; - Color *colors = nullptr; - - size_t total_vertex_count = vertex_count; + int total_vertex_count = (int) vertex_count; if (overdraw) - total_vertex_count = overdraw_vertex_start + overdraw_vertex_count; + total_vertex_count = (int) (overdraw_vertex_start + overdraw_vertex_count); - // TODO: We should probably be using a reusable index buffer. - if (use_quad_indices) - { - size_t numindices = (total_vertex_count / 4) * 6; + Graphics::StreamDrawRequest req; + req.formats[0] = vertex::CommonFormat::XYf; + req.formats[1] = vertex::CommonFormat::RGBAub; + req.indexMode = triangle_mode; + req.vertexCount = total_vertex_count; - try - { - indices = new GLushort[numindices]; - } - catch (std::bad_alloc &) - { - throw love::Exception("Out of memory."); - } + Graphics::StreamVertexData data = gfx->requestStreamDraw(req); - // 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. - indices[i * 6 + 0] = GLushort(i * 4 + 0); - indices[i * 6 + 1] = GLushort(i * 4 + 1); - indices[i * 6 + 2] = GLushort(i * 4 + 2); + const Matrix4 &t = gfx->getTransform(); + t.transform((Vector *) data.stream[0], vertices, total_vertex_count); - // 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); - } - } + Color curcolor = toColor(gfx->getColor()); + Color *colordata = (Color *) data.stream[1]; - gl.prepareDraw(); - - gl.bindTextureToUnit(gl.getDefaultTexture(), 0, false); - gl.bindBuffer(BUFFER_VERTEX, 0); - - uint32 enabledattribs = ATTRIBFLAG_POS; + for (int i = 0; i < (int) vertex_count; i++) + colordata[i] = curcolor; if (overdraw) - { - // 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); - - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors); - - enabledattribs |= ATTRIBFLAG_COLOR; - } - - gl.useVertexAttribArrays(enabledattribs); - - 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.bindBuffer(BUFFER_INDEX, 0); - 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; - - if (indices) - delete[] indices; + fill_color_array(curcolor, colordata + overdraw_vertex_start); } -void Polyline::fill_color_array(Color *colors) +void Polyline::fill_color_array(Color constant_color, Color *colors) { for (size_t i = 0; i < overdraw_vertex_count; ++i) { - // avoids branching. equiv to if (i%2 == 1) colors[i].a = 0; - colors[i] = {255, 255, 255, GLubyte(255 * ((i+1) % 2))}; + Color c = constant_color; + c.a *= (i+1) % 2; // avoids branching. equiv to if (i%2 == 1) c.a = 0; + colors[i] = c; } } -void NoneJoinPolyline::fill_color_array(Color *colors) +void NoneJoinPolyline::fill_color_array(Color constant_color, Color *colors) { for (size_t i = 0; i < overdraw_vertex_count; ++i) { - // if (i % 4 == 1 || i % 4 == 2) colors[i].a = 0 - colors[i] = {255, 255, 255, GLubyte(255 * ((i+1) % 4 < 2))}; + Color c = constant_color; + c.a *= (i & 3) < 2; // if (i % 4 == 2 || i % 4 == 3) c.a = 0 + colors[i] = c; } } diff --git a/src/modules/graphics/opengl/Polyline.h b/src/modules/graphics/opengl/Polyline.h index dfe5d22ef..787418304 100644 --- a/src/modules/graphics/opengl/Polyline.h +++ b/src/modules/graphics/opengl/Polyline.h @@ -24,9 +24,7 @@ // LOVE #include "common/config.h" #include "common/Vector.h" - -// OpenGL -#include "OpenGL.h" +#include "graphics/vertex.h" // C++ #include @@ -36,6 +34,9 @@ namespace love { namespace graphics { + +class Graphics; + namespace opengl { @@ -46,15 +47,16 @@ namespace opengl class Polyline { public: - Polyline(GLenum mode = GL_TRIANGLE_STRIP, bool quadindices = false) + + Polyline(vertex::TriangleIndexMode mode = vertex::TriangleIndexMode::STRIP) : vertices(nullptr) , overdraw(nullptr) , vertex_count(0) , overdraw_vertex_count(0) - , draw_mode(mode) - , use_quad_indices(quadindices) + , triangle_mode(mode) , overdraw_vertex_start(0) {} + virtual ~Polyline(); /** @@ -69,12 +71,13 @@ public: /** Draws the line on the screen */ - void draw(); + void draw(love::graphics::Graphics *gfx); protected: + virtual void calc_overdraw_vertex_count(bool is_looping); virtual void render_overdraw(const std::vector &normals, float pixel_size, bool is_looping); - virtual void fill_color_array(Color *colors); + virtual void fill_color_array(Color constant_color, Color *colors); /** Calculate line boundary points. * @@ -95,8 +98,7 @@ protected: Vector *overdraw; size_t vertex_count; size_t overdraw_vertex_count; - GLenum draw_mode; - bool use_quad_indices; + vertex::TriangleIndexMode triangle_mode; size_t overdraw_vertex_start; }; // Polyline @@ -109,8 +111,9 @@ protected: class NoneJoinPolyline : public Polyline { public: + NoneJoinPolyline() - : Polyline(GL_TRIANGLES, true) + : Polyline(vertex::TriangleIndexMode::QUADS) {} void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw) @@ -131,13 +134,15 @@ public: } protected: + virtual void calc_overdraw_vertex_count(bool is_looping); virtual void render_overdraw(const std::vector &normals, float pixel_size, bool is_looping); - virtual void fill_color_array(Color *colors); + virtual void fill_color_array(Color constant_color, Color *colors); virtual void renderEdge(std::vector &anchors, std::vector &normals, Vector &s, float &len_s, Vector &ns, const Vector &q, const Vector &r, float hw); -}; + +}; // NoneJoinPolyline /** @@ -147,16 +152,19 @@ protected: class MiterJoinPolyline : public Polyline { public: + void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw) { Polyline::render(vertices, count, count, halfwidth, pixel_size, draw_overdraw); } protected: + virtual void renderEdge(std::vector &anchors, std::vector &normals, Vector &s, float &len_s, Vector &ns, const Vector &q, const Vector &r, float hw); -}; + +}; // MiterJoinPolyline /** @@ -166,16 +174,19 @@ protected: class BevelJoinPolyline : public Polyline { public: + void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw) { Polyline::render(vertices, count, 2 * count - 4, halfwidth, pixel_size, draw_overdraw); } protected: + virtual void renderEdge(std::vector &anchors, std::vector &normals, Vector &s, float &len_s, Vector &ns, const Vector &q, const Vector &r, float hw); -}; + +}; // BevelJoinPolyline } // opengl } // graphics diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index a0815f03d..ba723a14e 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -587,6 +587,9 @@ const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const void Shader::updateUniform(const UniformInfo *info, int count, bool internalUpdate) { + if (!internalUpdate) + flushStreamDraws(); + TemporaryAttacher attacher(this, !internalUpdate); int location = info->location; @@ -679,6 +682,9 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count if (info->baseType != UNIFORM_SAMPLER) return; + if (!internalUpdate) + flushStreamDraws(); + count = std::min(count, info->count); bool updateuniform = false; @@ -713,7 +719,7 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count if (textures[i] != nullptr) { - GLuint gltex = *(GLuint *) textures[i]->getHandle(); + GLuint gltex = (GLuint) textures[i]->getHandle(); gl.bindTextureToUnit(gltex, texunit, false); @@ -722,13 +728,23 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count } else { - gl.bindTextureToUnit(0, texunit, false); + gl.bindTextureToUnit((GLuint) 0, texunit, false); textureUnits[texunit].texture = 0; textureUnits[texunit].active = false; } } } +void Shader::flushStreamDraws() const +{ + if (current == this) + { + auto gfx = Module::getInstance(Module::M_GRAPHICS); + if (gfx != nullptr) + gfx->flushStreamDraws(); + } +} + bool Shader::hasUniform(const std::string &name) const { return uniforms.find(name) != uniforms.end(); @@ -871,9 +887,10 @@ void Shader::checkSetBuiltinUniforms() { checkSetPointSize(gl.getPointSize()); - const Matrix4 &curxform = gl.matrices.transform.back(); - const Matrix4 &curproj = gl.matrices.projection; + auto gfx = Module::getInstance(Module::M_GRAPHICS); + const Matrix4 &curproj = gfx->getProjection(); + const Matrix4 &curxform = gfx->getTransform(); TemporaryAttacher attacher(this, true); bool tpmatrixneedsupdate = false; diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index dd64d80ca..3ebce2c06 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -215,6 +215,8 @@ private: int getFreeTextureUnits(int count); + void flushStreamDraws() const; + // Get any warnings or errors generated only by the shader program object. std::string getProgramWarnings() const; diff --git a/src/modules/graphics/opengl/SpriteBatch.cpp b/src/modules/graphics/opengl/SpriteBatch.cpp index abcc1e576..73b887831 100644 --- a/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/src/modules/graphics/opengl/SpriteBatch.cpp @@ -27,6 +27,7 @@ // LOVE #include "GLBuffer.h" #include "graphics/Texture.h" +#include "graphics/Graphics.h" // C++ #include @@ -240,21 +241,22 @@ bool SpriteBatch::getDrawRange(int &start, int &count) const return true; } -void SpriteBatch::draw(const Matrix4 &m) +void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) { const size_t pos_offset = offsetof(Vertex, x); const size_t texel_offset = offsetof(Vertex, s); - const size_t color_offset = offsetof(Vertex, r); + const size_t color_offset = offsetof(Vertex, color.r); if (next == 0) return; + gfx->flushStreamDraws(); + OpenGL::TempDebugGroup debuggroup("SpriteBatch draw"); - OpenGL::TempTransform transform(gl); - transform.get() *= m; + Graphics::TempTransform transform(gfx, m); - gl.bindTextureToUnit(*(GLuint *) texture->getHandle(), 0, false); + gl.bindTextureToUnit(texture, 0, false); uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD; @@ -315,8 +317,11 @@ void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index) m.transform(sprite, sprite, 4); - if (color) - setColorv(sprite, *color); + if (color != nullptr) + { + for (int i = 0; i < 4; i++) + sprite[i].color = *color; + } // Always keep the VBO mapped when adding data for now (it'll be unmapped // on draw.) @@ -325,17 +330,6 @@ void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index) array_buf->fill(index * sprite_size, sprite_size, sprite); } -void SpriteBatch::setColorv(Vertex *v, const Color &color) -{ - for (size_t i = 0; i < 4; ++i) - { - v[i].r = color.r; - v[i].g = color.g; - v[i].b = color.b; - v[i].a = color.a; - } -} - } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/SpriteBatch.h b/src/modules/graphics/opengl/SpriteBatch.h index 8824f5b39..98b53c9e6 100644 --- a/src/modules/graphics/opengl/SpriteBatch.h +++ b/src/modules/graphics/opengl/SpriteBatch.h @@ -108,7 +108,7 @@ public: bool getDrawRange(int &start, int &count) const; // Implements Drawable. - void draw(const Matrix4 &m) override; + void draw(Graphics *gfx, const Matrix4 &m) override; private: @@ -126,15 +126,6 @@ private: void addv(const Vertex *v, const Matrix4 &m, int index); - /** - * Set the color for vertices. - * - * @param v The vertices to set the color for. Must be an array of - * of size 4. - * @param color The color to assign to each vertex. - */ - void setColorv(Vertex *v, const Color &color); - StrongRef texture; // Max number of sprites in the batch. diff --git a/src/modules/graphics/opengl/Text.cpp b/src/modules/graphics/opengl/Text.cpp index 828699014..a00fd1190 100644 --- a/src/modules/graphics/opengl/Text.cpp +++ b/src/modules/graphics/opengl/Text.cpp @@ -20,6 +20,7 @@ #include "Text.h" #include "common/Matrix.h" +#include "graphics/Graphics.h" #include @@ -207,11 +208,13 @@ void Text::clear() vert_offset = 0; } -void Text::draw(const Matrix4 &m) +void Text::draw(Graphics *gfx, const Matrix4 &m) { if (vbo == nullptr || draw_commands.empty()) return; + gfx->flushStreamDraws(); + OpenGL::TempDebugGroup debuggroup("Text object draw"); // Re-generate the text if the Font's texture cache was invalidated. @@ -223,18 +226,15 @@ void Text::draw(const Matrix4 &m) const size_t color_offset = offsetof(Font::GlyphVertex, color.r); const size_t stride = sizeof(Font::GlyphVertex); - OpenGL::TempTransform transform(gl); - transform.get() *= m; + Graphics::TempTransform transform(gfx, m); - { - vbo->bind(); - vbo->unmap(); // Make sure all pending data is flushed to the GPU. + vbo->bind(); + 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_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset)); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset)); - } + // 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_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset)); + glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset)); gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR); diff --git a/src/modules/graphics/opengl/Text.h b/src/modules/graphics/opengl/Text.h index e91590b8d..9e2387eb1 100644 --- a/src/modules/graphics/opengl/Text.h +++ b/src/modules/graphics/opengl/Text.h @@ -52,7 +52,7 @@ public: void clear(); // Implements Drawable. - void draw(const Matrix4 &m) override; + void draw(Graphics *gfx, const Matrix4 &m) override; void setFont(Font *f); Font *getFont() const; diff --git a/src/modules/graphics/opengl/Video.cpp b/src/modules/graphics/opengl/Video.cpp index 8cddaac27..5eac5d29f 100644 --- a/src/modules/graphics/opengl/Video.cpp +++ b/src/modules/graphics/opengl/Video.cpp @@ -22,6 +22,7 @@ // LOVE #include "Shader.h" +#include "graphics/Graphics.h" namespace love { @@ -41,7 +42,7 @@ Video::Video(love::video::VideoStream *stream) stream->fillBackBuffer(); for (int i = 0; i < 4; i++) - vertices[i].r = vertices[i].g = vertices[i].b = vertices[i].a = 255; + vertices[i].color = Color(255, 255, 255, 255); // Vertices are ordered for use with triangle strips: // 0----2 @@ -116,10 +117,12 @@ love::video::VideoStream *Video::getStream() return stream; } -void Video::draw(const Matrix4 &m) +void Video::draw(Graphics *gfx, const Matrix4 &m) { update(); + gfx->flushStreamDraws(); + Shader *shader = Shader::current; bool defaultShader = (shader == Shader::defaultShader); if (defaultShader) @@ -131,8 +134,7 @@ void Video::draw(const Matrix4 &m) shader->setVideoTextures(textures[0], textures[1], textures[2]); - OpenGL::TempTransform transform(gl); - transform.get() *= m; + Graphics::TempTransform transform(gfx, m); gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD); diff --git a/src/modules/graphics/opengl/Video.h b/src/modules/graphics/opengl/Video.h index 1813c2742..325afbc21 100644 --- a/src/modules/graphics/opengl/Video.h +++ b/src/modules/graphics/opengl/Video.h @@ -50,7 +50,7 @@ public: void unloadVolatile() override; // Drawable - void draw(const Matrix4 &m) override; + void draw(Graphics *gfx, const Matrix4 &m) override; love::video::VideoStream *getStream(); diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 3c0f022ea..0413d3aea 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -826,10 +826,10 @@ static Mesh *newStandardMesh(lua_State *L) v.s = (float) luaL_optnumber(L, -6, 0.0); v.t = (float) luaL_optnumber(L, -5, 0.0); - v.r = (unsigned char) (luaL_optnumber(L, -4, 1.0) * 255.0); - v.g = (unsigned char) (luaL_optnumber(L, -3, 1.0) * 255.0); - v.b = (unsigned char) (luaL_optnumber(L, -2, 1.0) * 255.0); - v.a = (unsigned char) (luaL_optnumber(L, -1, 1.0) * 255.0); + v.color.r = (unsigned char) (luaL_optnumber(L, -4, 1.0) * 255.0); + v.color.g = (unsigned char) (luaL_optnumber(L, -3, 1.0) * 255.0); + v.color.b = (unsigned char) (luaL_optnumber(L, -2, 1.0) * 255.0); + v.color.a = (unsigned char) (luaL_optnumber(L, -1, 1.0) * 255.0); lua_pop(L, 9); vertices.push_back(v); @@ -1544,7 +1544,7 @@ int w_draw(lua_State *L) math::Transform *tf = luax_totype(L, startidx); luax_catchexcept(L, [&]() { if (texture && quad) - instance()->drawq(texture, quad, tf->getMatrix()); + instance()->draw(texture, quad, tf->getMatrix()); else instance()->draw(drawable, tf->getMatrix()); }); @@ -1565,7 +1565,7 @@ int w_draw(lua_State *L) luax_catchexcept(L, [&]() { if (texture && quad) - instance()->drawq(texture, quad, m); + instance()->draw(texture, quad, m); else if (drawable) instance()->draw(drawable, m); }); @@ -1673,12 +1673,18 @@ int w_points(lua_State *L) numpoints = args; float *coords = nullptr; - uint8 *colors = nullptr; - - coords = new float[numpoints * 2]; + Colorf *colors = nullptr; if (is_table_of_tables) - colors = new uint8[numpoints * 4]; + { + size_t datasize = (sizeof(float) * 2 + sizeof(Colorf)) * numpoints; + uint8 *data = instance()->getScratchBuffer(datasize); + + coords = (float *) data; + colors = (Colorf *) (data + sizeof(float) * numpoints * 2); + } + else + coords = instance()->getScratchBuffer(numpoints * 2); if (is_table) { @@ -1694,10 +1700,10 @@ int w_points(lua_State *L) coords[i * 2 + 0] = luax_tofloat(L, -6); coords[i * 2 + 1] = luax_tofloat(L, -5); - colors[i * 4 + 0] = (uint8) (luaL_optnumber(L, -4, 1.0) * 255.0); - colors[i * 4 + 1] = (uint8) (luaL_optnumber(L, -3, 1.0) * 255.0); - colors[i * 4 + 2] = (uint8) (luaL_optnumber(L, -2, 1.0) * 255.0); - colors[i * 4 + 3] = (uint8) (luaL_optnumber(L, -1, 1.0) * 255.0); + colors[i].r = luaL_optnumber(L, -4, 1.0); + colors[i].g = luaL_optnumber(L, -3, 1.0); + colors[i].b = luaL_optnumber(L, -2, 1.0); + colors[i].a = luaL_optnumber(L, -1, 1.0); lua_pop(L, 7); } @@ -1719,15 +1725,7 @@ int w_points(lua_State *L) coords[i] = luax_tofloat(L, i + 1); } - luax_catchexcept(L, - [&](){ instance()->points(coords, colors, numpoints); }, - [&](bool) { - delete[] coords; - if (colors) - delete[] colors; - } - ); - + luax_catchexcept(L, [&](){ instance()->points(coords, colors, numpoints); }); return 0; } @@ -1746,7 +1744,7 @@ int w_line(lua_State *L) else if (args < 4) return luaL_error(L, "Need at least two vertices to draw a line"); - float *coords = new float[args]; + float *coords = instance()->getScratchBuffer(args); if (is_table) { for (int i = 0; i < args; ++i) @@ -1894,7 +1892,6 @@ int w_polygon(lua_State *L) return luaL_error(L, "Invalid draw mode: %s", str); bool is_table = false; - float *coords; if (args == 1 && lua_istable(L, 2)) { args = (int) luax_objlen(L, 2); @@ -1907,7 +1904,7 @@ int w_polygon(lua_State *L) return luaL_error(L, "Need at least three vertices to draw a polygon"); // fetch coords - coords = new float[args + 2]; + float *coords = instance()->getScratchBuffer(args + 2); if (is_table) { for (int i = 0; i < args; ++i) @@ -1927,11 +1924,13 @@ int w_polygon(lua_State *L) coords[args] = coords[0]; coords[args+1] = coords[1]; - luax_catchexcept(L, - [&](){ instance()->polygon(mode, coords, args+2); }, - [&](bool) { delete[] coords; } - ); + luax_catchexcept(L, [&](){ instance()->polygon(mode, coords, args+2); }); + return 0; +} +int w_flush(lua_State *) +{ + instance()->flushStreamDraws(); return 0; } @@ -2123,9 +2122,10 @@ static const luaL_Reg functions[] = { "circle", w_circle }, { "ellipse", w_ellipse }, { "arc", w_arc }, - { "polygon", w_polygon }, + { "flush", w_flush }, + { "push", w_push }, { "pop", w_pop }, { "rotate", w_rotate }, diff --git a/src/modules/graphics/vertex.cpp b/src/modules/graphics/vertex.cpp new file mode 100644 index 000000000..8bd6b7003 --- /dev/null +++ b/src/modules/graphics/vertex.cpp @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2006-2016 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 "vertex.h" + +namespace love +{ +namespace graphics +{ +namespace vertex +{ + +static_assert(sizeof(Color) == 4, "sizeof(Color) incorrect!"); +static_assert(sizeof(Vertex) == sizeof(float)*2 + sizeof(float)*2 + sizeof(Color), "sizeof(Vertex) incorrect!"); +static_assert(sizeof(XYf_STf) == sizeof(float)*2 + sizeof(float)*2, "sizeof(XYf_STf) incorrect!"); +static_assert(sizeof(XYf_STf_RGBAub) == sizeof(float)*2 + sizeof(float)*2 + sizeof(Color), "sizeof(XYf_STf_RGBAub) incorrect!"); +static_assert(sizeof(XYf_STus_RGBAub) == sizeof(float)*2 + sizeof(uint16)*2 + sizeof(Color), "sizeof(XYf_STus_RGBAub) incorrect!"); + +size_t getFormatStride(CommonFormat format) +{ + switch (format) + { + case CommonFormat::NONE: + return 0; + case CommonFormat::XYf: + return sizeof(float) * 2; + case CommonFormat::RGBAub: + return sizeof(uint8) * 4; + case CommonFormat::XYf_STf: + return sizeof(XYf_STf); + case CommonFormat::XYf_STf_RGBAub: + return sizeof(XYf_STf_RGBAub); + case CommonFormat::XYf_STus_RGBAub: + return sizeof(XYf_STus_RGBAub); + } +} + +int getIndexCount(TriangleIndexMode mode, int vertexCount) +{ + switch (mode) + { + case TriangleIndexMode::NONE: + return 0; + case TriangleIndexMode::STRIP: + case TriangleIndexMode::FAN: + return 3 * (vertexCount - 2); + case TriangleIndexMode::QUADS: + return vertexCount * 6 / 4; + } +} + +template +static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T *indices) +{ + switch (mode) + { + case TriangleIndexMode::NONE: + break; + case TriangleIndexMode::STRIP: + { + int i = 0; + for (T index = 0; index < vertexCount - 2; index++) + { + indices[i++] = vertexStart + index; + indices[i++] = vertexStart + index + 1 + (index & 1); + indices[i++] = vertexStart + index + 2 - (index & 1); + } + } + break; + case TriangleIndexMode::FAN: + { + int i = 0; + for (T index = 2; index < vertexCount; index++) + { + indices[i++] = vertexStart; + indices[i++] = vertexStart + index - 1; + indices[i++] = vertexStart + index; + } + } + break; + case TriangleIndexMode::QUADS: + { + // 0---2 + // | / | + // 1---3 + int count = vertexCount / 4; + for (int i = 0; i < count; i++) + { + int ii = i * 6; + T vi = T(vertexStart + i * 4); + + indices[ii + 0] = vi + 0; + indices[ii + 1] = vi + 1; + indices[ii + 2] = vi + 2; + + indices[ii + 3] = vi + 2; + indices[ii + 4] = vi + 1; + indices[ii + 5] = vi + 3; + } + } + break; + } +} + +void fillIndices(TriangleIndexMode mode, uint16 vertexStart, uint16 vertexCount, uint16 *indices) +{ + fillIndicesT(mode, vertexStart, vertexCount, indices); +} + +void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, uint32 *indices) +{ + fillIndicesT(mode, vertexStart, vertexCount, indices); +} + +} // vertex +} // graphics +} // love diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h new file mode 100644 index 000000000..46396cb90 --- /dev/null +++ b/src/modules/graphics/vertex.h @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2006-2016 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. + **/ + +#pragma once + +// LOVE +#include "common/int.h" +#include "Color.h" + +// C +#include + +namespace love +{ +namespace graphics +{ + +enum BufferType +{ + BUFFER_VERTEX = 0, + BUFFER_INDEX, + BUFFER_MAX_ENUM +}; + +struct Vertex +{ + float x, y; + float s, t; + Color color; +}; + +namespace vertex +{ + +enum class PrimitiveMode +{ + TRIANGLES, + POINTS, +}; + +enum class TriangleIndexMode +{ + NONE, + STRIP, + FAN, + QUADS, +}; + +enum class CommonFormat +{ + NONE, + XYf, + RGBAub, + XYf_STf, + XYf_STf_RGBAub, + XYf_STus_RGBAub, +}; + +struct XYf_STf +{ + float x, y; + float s, t; +}; + +struct XYf_STf_RGBAub +{ + float x, y; + float s, t; + Color color; +}; + +struct XYf_STus_RGBAub +{ + float x, y; + uint16 s, t; + Color color; +}; + +size_t getFormatStride(CommonFormat format); + +int getIndexCount(TriangleIndexMode mode, int vertexCount); + +void fillIndices(TriangleIndexMode mode, uint16 vertexStart, uint16 vertexCount, uint16 *indices); +void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, uint32 *indices); + +} // vertex +} // graphics +} // love diff --git a/src/scripts/nogame.lua b/src/scripts/nogame.lua index 0c92a9474..bf8ad10d3 100644 --- a/src/scripts/nogame.lua +++ b/src/scripts/nogame.lua @@ -738,7 +738,7 @@ function love.nogame() local SIZE_Y = math.floor(wh / 32 + 2) local SIZE = SIZE_X * SIZE_Y - self.batch = love.graphics.newSpriteBatch(mosaic_image, SIZE, "stream") + self.image = mosaic_image self.pieces = {} self.color_t = 1 self.generation = 1 @@ -915,8 +915,6 @@ function love.nogame() end function Mosaic:draw() - self.batch:clear() - love.graphics.setColor(1, 1, 1, 0.25) for idx,piece in ipairs(self.pieces) do local ct = 1 - self.color_t local c0 = piece.color.prev @@ -925,11 +923,10 @@ function love.nogame() local g = easeOut(ct, c0[2], c1[2] - c0[2], 1) local b = easeOut(ct, c0[3], c1[3] - c0[3], 1) - self.batch:setColor(r, g, b) - self.batch:add(piece.quad, piece.x, piece.y, piece.r, 1, 1, 16, 16) + love.graphics.setColor(r, g, b) + love.graphics.draw(self.image, piece.quad, piece.x, piece.y, piece.r, 1, 1, 16, 16) end love.graphics.setColor(1, 1, 1, 1) - love.graphics.draw(self.batch, 0, 0) end function love.load() diff --git a/src/scripts/nogame.lua.h b/src/scripts/nogame.lua.h index e551cf36f..621384d1c 100644 --- a/src/scripts/nogame.lua.h +++ b/src/scripts/nogame.lua.h @@ -3148,11 +3148,8 @@ const unsigned char nogame_lua[] = 0x2b, 0x20, 0x32, 0x29, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x53, 0x49, 0x5a, 0x45, 0x20, 0x3d, 0x20, 0x53, 0x49, 0x5a, 0x45, 0x5f, 0x58, 0x20, 0x2a, 0x20, 0x53, 0x49, 0x5a, 0x45, 0x5f, 0x59, 0x0a, - 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, - 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x53, 0x70, 0x72, 0x69, - 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6d, 0x6f, 0x73, 0x61, 0x69, 0x63, 0x5f, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x2c, 0x20, 0x53, 0x49, 0x5a, 0x45, 0x2c, 0x20, 0x22, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x22, - 0x29, 0x0a, + 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x6d, 0x6f, 0x73, + 0x61, 0x69, 0x63, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x0a, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x69, 0x65, 0x63, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x74, 0x20, 0x3d, 0x20, 0x31, 0x0a, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, @@ -3527,11 +3524,6 @@ const unsigned char nogame_lua[] = 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4d, 0x6f, 0x73, 0x61, 0x69, 0x63, 0x3a, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a, - 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x63, 0x6c, 0x65, 0x61, 0x72, - 0x28, 0x29, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, - 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2e, - 0x32, 0x35, 0x29, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x78, 0x2c, 0x70, 0x69, 0x65, 0x63, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x70, 0x69, 0x65, 0x63, 0x65, 0x73, 0x29, 0x20, 0x64, 0x6f, 0x0a, @@ -3550,18 +3542,16 @@ const unsigned char nogame_lua[] = 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x65, 0x61, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x28, 0x63, 0x74, 0x2c, 0x20, 0x63, 0x30, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x63, 0x31, 0x5b, 0x33, 0x5d, 0x20, 0x2d, 0x20, 0x63, 0x30, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x0a, - 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x73, 0x65, 0x74, 0x43, - 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x72, 0x2c, 0x20, 0x67, 0x2c, 0x20, 0x62, 0x29, 0x0a, - 0x09, 0x09, 0x09, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x61, 0x64, 0x64, 0x28, - 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x61, 0x64, 0x2c, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, - 0x78, 0x2c, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x79, 0x2c, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, - 0x72, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x36, 0x2c, 0x20, 0x31, 0x36, 0x29, 0x0a, + 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, + 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x72, 0x2c, 0x20, 0x67, 0x2c, 0x20, 0x62, 0x29, 0x0a, + 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x64, + 0x72, 0x61, 0x77, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x70, 0x69, + 0x65, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x61, 0x64, 0x2c, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x78, 0x2c, + 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x79, 0x2c, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x72, 0x2c, + 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x36, 0x2c, 0x20, 0x31, 0x36, 0x29, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x29, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x64, 0x72, - 0x61, 0x77, 0x28, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x2c, 0x20, 0x30, 0x2c, 0x20, - 0x30, 0x29, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x29, 0x0a,