diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index cb343b9fe..9e7c9f934 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -3150,7 +3150,7 @@ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0610; + LastUpgradeCheck = 0630; TargetAttributes = { FA0B78DC1A958B90000E1D17 = { CreatedOnToolsVersion = 6.1.1; @@ -3761,7 +3761,6 @@ 10D5479E63C26BB35EB5482E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -3810,7 +3809,6 @@ 64274E785071353E1A1D0D4B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -3863,7 +3861,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", LOVE_SUPPORT_IMAGEIO, @@ -3905,7 +3902,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -3949,7 +3945,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -3992,7 +3987,6 @@ FA5326C4189719C700F7BBF4 /* Distribution */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -4043,7 +4037,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ENABLE_MODULES = NO; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 9.0; @@ -4076,7 +4069,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ENABLE_MODULES = NO; COMBINE_HIDPI_IMAGES = YES; DYLIB_COMPATIBILITY_VERSION = 9.0; DYLIB_CURRENT_VERSION = 9.0; @@ -4108,7 +4100,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ENABLE_MODULES = NO; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 9.0; diff --git a/platform/xcode/love.xcodeproj/project.pbxproj b/platform/xcode/love.xcodeproj/project.pbxproj index 153b345f9..363fae34d 100644 --- a/platform/xcode/love.xcodeproj/project.pbxproj +++ b/platform/xcode/love.xcodeproj/project.pbxproj @@ -8,17 +8,17 @@ /* Begin PBXBuildFile section */ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; - A9255DD11043183600BA1496 /* FreeType.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E4810420B4A007D418B /* FreeType.framework */; }; - A9255DD31043183600BA1496 /* Lua.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E5310420B57007D418B /* Lua.framework */; }; - A9255E031043195A00BA1496 /* Vorbis.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9255E021043195A00BA1496 /* Vorbis.framework */; }; - A9255F58104324E100BA1496 /* Ogg.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9255F51104324D700BA1496 /* Ogg.framework */; }; + A9255DD11043183600BA1496 /* FreeType.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E4810420B4A007D418B /* FreeType.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + A9255DD31043183600BA1496 /* Lua.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E5310420B57007D418B /* Lua.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + A9255E031043195A00BA1496 /* Vorbis.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9255E021043195A00BA1496 /* Vorbis.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + A9255F58104324E100BA1496 /* Ogg.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9255F51104324D700BA1496 /* Ogg.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; A93E6E5510420B57007D418B /* Lua.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E6E5310420B57007D418B /* Lua.framework */; }; A93E6EED10420BA8007D418B /* love.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A93E6A3410420AC0007D418B /* love.cpp */; }; A9D307F2106635D3004FEDF8 /* physfs.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9D307E9106635C3004FEDF8 /* physfs.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; A9F169AC109E825000FC83D1 /* mpg123.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9F169A6109E824900FC83D1 /* mpg123.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; A9F169AD109E825000FC83D1 /* libmodplug.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9F16926109E7BAD00FC83D1 /* libmodplug.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; FA08F69616C766E000F007B5 /* love.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; }; - FA08F69716C766E700F007B5 /* love.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; }; + FA08F69716C766E700F007B5 /* love.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; FA0B78D21A95814A000E1D17 /* love.icns in Resources */ = {isa = PBXBuildFile; fileRef = FA0B78D01A95814A000E1D17 /* love.icns */; }; FA0B78D31A95814A000E1D17 /* lovedocument.icns in Resources */ = {isa = PBXBuildFile; fileRef = FA0B78D11A95814A000E1D17 /* lovedocument.icns */; }; FA0B7F301A95AC7D000E1D17 /* love.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A93E6A3410420AC0007D418B /* love.cpp */; }; @@ -35,7 +35,7 @@ FA5D249C1A96CF4300C6FC8F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FA5D249A1A96CF4300C6FC8F /* Images.xcassets */; }; FA5D24C21A96D78000C6FC8F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA5D24C11A96D78000C6FC8F /* Foundation.framework */; }; FA5D24D11A96E73300C6FC8F /* liblove.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA0B7EEF1A95924A000E1D17 /* liblove.a */; }; - FA77A36518F1172600D23AE9 /* jpeg-turbo.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA77A36418F1172600D23AE9 /* jpeg-turbo.framework */; }; + FA77A36518F1172600D23AE9 /* jpeg-turbo.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA77A36418F1172600D23AE9 /* jpeg-turbo.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; FA7C636A1A9C49570000FD29 /* Launch Screen.xib in Resources */ = {isa = PBXBuildFile; fileRef = FA7C63691A9C49570000FD29 /* Launch Screen.xib */; }; FA9B4A0A16E1579F00074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0916E1579F00074F42 /* SDL2.framework */; }; FA9B4A0B16E157B500074F42 /* SDL2.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0916E1579F00074F42 /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -317,7 +317,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0610; + LastUpgradeCheck = 0630; TargetAttributes = { FA0B7F051A95AAF3000E1D17 = { CreatedOnToolsVersion = 6.1.1; @@ -421,7 +421,6 @@ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -445,7 +444,6 @@ C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( @@ -510,6 +508,7 @@ 100000000, ); PRODUCT_NAME = love; + SDKROOT = macosx; WARNING_CFLAGS = ( "-Wall", "-W", @@ -567,6 +566,7 @@ ); PRODUCT_NAME = love; SCAN_ALL_SOURCE_FILES_FOR_INCLUDES = YES; + SDKROOT = macosx; WARNING_CFLAGS = ( "-Wall", "-W", @@ -748,6 +748,7 @@ ); PRODUCT_NAME = love; SCAN_ALL_SOURCE_FILES_FOR_INCLUDES = YES; + SDKROOT = macosx; WARNING_CFLAGS = ( "-Wall", "-W", @@ -758,7 +759,6 @@ FA5326C718971A0900F7BBF4 /* Distribution */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( diff --git a/src/common/runtime.h b/src/common/runtime.h index ff227152f..ce852162f 100644 --- a/src/common/runtime.h +++ b/src/common/runtime.h @@ -524,7 +524,7 @@ int luax_catchexcept(lua_State *L, const T& func, const F& finallyfunc) lua_pushstring(L, e.what()); } - finallyfunc(); + finallyfunc(should_error); if (should_error) return luaL_error(L, "%s", lua_tostring(L, -1)); diff --git a/src/modules/filesystem/wrap_Filesystem.cpp b/src/modules/filesystem/wrap_Filesystem.cpp index 8752d8364..71d6be9f2 100644 --- a/src/modules/filesystem/wrap_Filesystem.cpp +++ b/src/modules/filesystem/wrap_Filesystem.cpp @@ -186,7 +186,7 @@ FileData *luax_getfiledata(lua_State *L, int idx) { luax_catchexcept(L, [&]() { data = file->read(); }, - [&]() { file->release(); } + [&](bool) { file->release(); } ); } diff --git a/src/modules/font/wrap_Font.cpp b/src/modules/font/wrap_Font.cpp index 103827870..01b4dfa20 100644 --- a/src/modules/font/wrap_Font.cpp +++ b/src/modules/font/wrap_Font.cpp @@ -50,7 +50,7 @@ int w_newRasterizer(lua_State *L) luax_catchexcept(L, [&]() { t = instance()->newRasterizer(d); }, - [&]() { d->release(); } + [&](bool) { d->release(); } ); luax_pushtype(L, FONT_RASTERIZER_ID, t); @@ -97,7 +97,7 @@ int w_newTrueTypeRasterizer(lua_State *L) luax_catchexcept(L, [&]() { t = instance()->newTrueTypeRasterizer(d, size, hinting); }, - [&]() { d->release(); } + [&](bool) { d->release(); } ); } @@ -146,7 +146,7 @@ int w_newBMFontRasterizer(lua_State *L) luax_catchexcept(L, [&]() { t = instance()->newBMFontRasterizer(d, images); }, - [&]() { d->release(); for (auto id : images) id->release(); } + [&](bool) { d->release(); for (auto id : images) id->release(); } ); luax_pushtype(L, FONT_RASTERIZER_ID, t); diff --git a/src/modules/graphics/opengl/GLBuffer.cpp b/src/modules/graphics/opengl/GLBuffer.cpp index 0be278437..587e32e05 100644 --- a/src/modules/graphics/opengl/GLBuffer.cpp +++ b/src/modules/graphics/opengl/GLBuffer.cpp @@ -34,14 +34,7 @@ namespace graphics namespace opengl { -// GLBuffer - -GLBuffer *GLBuffer::Create(size_t size, GLenum target, GLenum usage) -{ - return new GLBuffer(size, target, usage); -} - -GLBuffer::GLBuffer(size_t size, GLenum target, GLenum usage) +GLBuffer::GLBuffer(size_t size, const void *data, GLenum target, GLenum usage) : is_bound(false) , is_mapped(false) , size(size) @@ -59,7 +52,10 @@ GLBuffer::GLBuffer(size_t size, GLenum target, GLenum usage) throw love::Exception("Out of memory."); } - bool ok = load(false); + if (data != nullptr) + memcpy(memory_map, data, size); + + bool ok = load(data != nullptr); if (!ok) { @@ -330,7 +326,7 @@ void VertexIndex::resize(size_t size) // VertexIndex will propagate the exception and keep the old GLBuffer. try { - new_element_array = GLBuffer::Create(array_size, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW); + new_element_array = new GLBuffer(array_size, nullptr, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW); } catch (std::bad_alloc &) { diff --git a/src/modules/graphics/opengl/GLBuffer.h b/src/modules/graphics/opengl/GLBuffer.h index 818de6e32..4a4f68271 100644 --- a/src/modules/graphics/opengl/GLBuffer.h +++ b/src/modules/graphics/opengl/GLBuffer.h @@ -47,17 +47,6 @@ class GLBuffer : public Volatile { public: - /** - * Create a new GLBuffer. - * - * @param size The size of the GLBuffer (in bytes). - * @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER. - * @param usage GL_DYNAMIC_DRAW, etc. - * @param backing Determines what guarantees are placed on the data. - * @return A new GLBuffer. - */ - static GLBuffer *Create(size_t size, GLenum target, GLenum usage); - /** * Constructor. * @@ -66,7 +55,7 @@ public: * @param usage Usage hint, e.g. GL_DYNAMIC_DRAW. * @param backing Determines what guarantees are placed on the data. */ - GLBuffer(size_t size, GLenum target, GLenum usage); + GLBuffer(size_t size, const void *data, GLenum target, GLenum usage); /** * Destructor. @@ -123,7 +112,7 @@ public: * * @return A pointer to memory which represents the buffer. */ - virtual void *map(); + void *map(); /** * Unmap a previously mapped GLBuffer. The buffer must be unmapped @@ -135,18 +124,18 @@ public: * sub-range of data modified. Optional. * @param usedSize The size of the sub-range of modified data. Optional. */ - virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1); + void unmap(size_t usedOffset = 0, size_t usedSize = -1); /** * Bind the GLBuffer to its specified target. * (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc). */ - virtual void bind(); + void bind(); /** * Unbind a prevously bound GLBuffer. */ - virtual void unbind(); + void unbind(); /** * Fill a portion of the buffer with data. @@ -157,7 +146,7 @@ public: * @param size The size of the incoming data. * @param data Pointer to memory to copy data from. */ - virtual void fill(size_t offset, size_t size, const void *data); + void fill(size_t offset, size_t size, const void *data); /** * Get a pointer which represents the specified byte offset. @@ -165,11 +154,11 @@ public: * @param offset The byte offset. (0 is first byte). * @return A pointer which represents the offset. */ - virtual const void *getPointer(size_t offset) const; + const void *getPointer(size_t offset) const; // Implements Volatile. - virtual bool loadVolatile(); - virtual void unloadVolatile(); + bool loadVolatile() override; + void unloadVolatile() override; /** * This helper class can bind a GLBuffer temporarily, and diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 9a325cb5c..2b50246da 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -671,7 +671,7 @@ Font *Graphics::newFont(love::font::Rasterizer *r, const Texture::Filter &filter return new Font(r, filter); } -SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, int usage) +SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, Mesh::Usage usage) { return new SpriteBatch(texture, size, usage); } @@ -747,14 +747,24 @@ Shader *Graphics::newShader(const Shader::ShaderSource &source) return new Shader(source); } -Mesh *Graphics::newMesh(const std::vector &vertices, Mesh::DrawMode mode) +Mesh *Graphics::newMesh(const std::vector &vertices, Mesh::DrawMode drawmode, Mesh::Usage usage) { - return new Mesh(vertices, mode); + return new Mesh(vertices, drawmode, usage); } -Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode mode) +Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage) { - return new Mesh(vertexcount, mode); + return new Mesh(vertexcount, drawmode, usage); +} + +Mesh *Graphics::newMesh(const std::vector &vertexformat, int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage) +{ + return new Mesh(vertexformat, vertexcount, drawmode, usage); +} + +Mesh *Graphics::newMesh(const std::vector &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, Mesh::Usage usage) +{ + return new Mesh(vertexformat, data, datasize, drawmode, usage); } Text *Graphics::newText(Font *font, const std::string &text) @@ -762,7 +772,7 @@ Text *Graphics::newText(Font *font, const std::string &text) return new Text(font, text); } -void Graphics::setColor(const Color &c) +void Graphics::setColor(Color c) { gl.setColor(c); states.back().color = c; @@ -773,7 +783,7 @@ Color Graphics::getColor() const return states.back().color; } -void Graphics::setBackgroundColor(const Color &c) +void Graphics::setBackgroundColor(Color c) { states.back().backgroundColor = c; } diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 2a8808e62..3e2211f65 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -167,7 +167,7 @@ public: **/ Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::getDefaultFilter()); - SpriteBatch *newSpriteBatch(Texture *texture, int size, int usage); + SpriteBatch *newSpriteBatch(Texture *texture, int size, Mesh::Usage usage); ParticleSystem *newParticleSystem(Texture *texture, int size); @@ -175,8 +175,11 @@ public: Shader *newShader(const Shader::ShaderSource &source); - Mesh *newMesh(const std::vector &vertices, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN); - Mesh *newMesh(int vertexcount, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN); + Mesh *newMesh(const std::vector &vertices, Mesh::DrawMode drawmode, Mesh::Usage usage); + Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage); + + Mesh *newMesh(const std::vector &vertexformat, int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage); + Mesh *newMesh(const std::vector &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, Mesh::Usage usage); Text *newText(Font *font, const std::string &text = ""); @@ -184,7 +187,7 @@ public: * Sets the foreground color. * @param c The new foreground color. **/ - void setColor(const Color &c); + void setColor(Color c); /** * Gets current color. @@ -194,7 +197,7 @@ public: /** * Sets the background Color. **/ - void setBackgroundColor(const Color &c); + void setBackgroundColor(Color c); /** * Gets the current background color. diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index a18a78540..1294ef890 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -22,9 +22,11 @@ #include "Mesh.h" #include "common/Matrix.h" #include "common/Exception.h" +#include "Shader.h" // C++ #include +#include namespace love { @@ -33,120 +35,351 @@ namespace graphics namespace opengl { -Mesh::Mesh(const std::vector &verts, Mesh::DrawMode mode) - : vbo(nullptr) - , vertex_count(0) - , ibo(nullptr) - , element_count(0) - , element_data_type(getGLDataTypeFromMax(verts.size())) - , draw_mode(mode) - , range_min(-1) - , range_max(-1) - , texture(nullptr) - , colors_enabled(false) +static const char *getBuiltinAttribName(VertexAttribID attribid) { - setVertices(verts); + const char *name = ""; + Shader::getConstant(attribid, name); + return name; } -Mesh::Mesh(int vertexcount, Mesh::DrawMode mode) - : vbo(nullptr) - , vertex_count(0) - , ibo(nullptr) - , element_count(0) - , element_data_type(getGLDataTypeFromMax(vertexcount)) - , draw_mode(mode) - , range_min(-1) - , range_max(-1) - , texture(nullptr) - , colors_enabled(false) +static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct"); +static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct"); +static_assert(offsetof(Vertex, r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct"); + +static std::vector getDefaultVertexFormat() { - if (vertexcount < 1) - throw love::Exception("Invalid number of vertices."); + // Corresponds to the love::Vertex struct. + std::vector vertexformat = { + {getBuiltinAttribName(ATTRIB_POS), Mesh::DATA_FLOAT, 2}, + {getBuiltinAttribName(ATTRIB_TEXCOORD), Mesh::DATA_FLOAT, 2}, + {getBuiltinAttribName(ATTRIB_COLOR), Mesh::DATA_BYTE, 4}, + }; - std::vector verts(vertexcount); + return vertexformat; +} - // Default-initialized vertices should have a white opaque color. - for (size_t i = 0; i < verts.size(); i++) - { - verts[i].r = 255; - verts[i].g = 255; - verts[i].b = 255; - verts[i].a = 255; - } +Mesh::Mesh(const std::vector &vertexformat, const void *data, size_t datasize, DrawMode drawmode, Usage usage) + : vertexFormat(vertexformat) + , vbo(nullptr) + , vertexCount(0) + , vertexStride(0) + , vboUsedOffset(0) + , vboUsedSize(0) + , ibo(nullptr) + , elementCount(0) + , elementDataType(0) + , drawMode(drawmode) + , rangeMin(-1) + , rangeMax(-1) +{ + setupAttachedAttributes(); + calculateAttributeSizes(); - setVertices(verts); + vertexCount = datasize / vertexStride; + elementDataType = getGLDataTypeFromMax(vertexCount); + + if (vertexCount == 0) + throw love::Exception("Data size is too small for specified vertex attribute formats."); + + vbo = new GLBuffer(datasize, data, GL_ARRAY_BUFFER, getGLBufferUsage(usage)); + + vertexScratchBuffer = new char[vertexStride]; +} + +Mesh::Mesh(const std::vector &vertexformat, int vertexcount, DrawMode drawmode, Usage usage) + : vertexFormat(vertexformat) + , vbo(nullptr) + , vertexCount((size_t) vertexcount) + , vertexStride(0) + , vboUsedOffset(0) + , vboUsedSize(0) + , ibo(nullptr) + , elementCount(0) + , elementDataType(getGLDataTypeFromMax(vertexcount)) + , drawMode(drawmode) + , rangeMin(-1) + , rangeMax(-1) +{ + if (vertexcount <= 0) + throw love::Exception("Invalid number of vertices (%d).", vertexcount); + + setupAttachedAttributes(); + calculateAttributeSizes(); + + size_t buffersize = vertexCount * vertexStride; + + vbo = new GLBuffer(buffersize, nullptr, GL_ARRAY_BUFFER, getGLBufferUsage(usage)); + + // Initialize the buffer's contents to 0. + GLBuffer::Bind bind(*vbo); + memset(vbo->map(), 0, buffersize); + vbo->unmap(); + + vertexScratchBuffer = new char[vertexStride]; +} + +Mesh::Mesh(const std::vector &vertices, DrawMode drawmode, Usage usage) + : Mesh(getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage) +{ +} + +Mesh::Mesh(int vertexcount, DrawMode drawmode, Usage usage) + : Mesh(getDefaultVertexFormat(), vertexcount, drawmode, usage) +{ } Mesh::~Mesh() { delete vbo; delete ibo; + delete vertexScratchBuffer; + + for (const auto attrib : attachedAttributes) + { + if (attrib.second.mesh != this) + attrib.second.mesh->release(); + } } -void Mesh::setVertices(const std::vector &verts) +void Mesh::setupAttachedAttributes() { - if (verts.size() == 0) - throw love::Exception("At least one vertex is required."); - - size_t size = sizeof(Vertex) * verts.size(); - - if (vbo && size > vbo->getSize()) + for (size_t i = 0; i < vertexFormat.size(); i++) { - delete vbo; - vbo = nullptr; + const std::string &name = vertexFormat[i].name; + + if (attachedAttributes.find(name) != attachedAttributes.end()) + throw love::Exception("Duplicate vertex attribute name: %s", name.c_str()); + + attachedAttributes[name] = {this, i, true}; + } +} + +void Mesh::calculateAttributeSizes() +{ + size_t stride = 0; + + for (const AttribFormat &format : vertexFormat) + { + // Hardware really doesn't like attributes that aren't 32 bit-aligned. + if (format.type == DATA_BYTE && format.components != 4) + throw love::Exception("byte vertex attributes must have 4 components."); + + if (format.components <= 0 || format.components > 4) + throw love::Exception("Vertex attributes must have between 1 and 4 components."); + + // Total size in bytes of each attribute in a single vertex. + attributeSizes.push_back(getAttribFormatSize(format)); + stride += attributeSizes.back(); } - if (!vbo) - vbo = GLBuffer::Create(size, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW); - - vertex_count = verts.size(); - - GLBuffer::Bind vbo_bind(*vbo); - GLBuffer::Mapper vbo_mapper(*vbo); - - // Fill the buffer with the vertices. - memcpy(vbo_mapper.get(), &verts[0], size); + vertexStride = stride; } -const Vertex *Mesh::getVertices() const +size_t Mesh::getAttributeOffset(size_t attribindex) const { - if (vbo) + size_t offset = 0; + + for (size_t i = 0; i < attribindex; i++) + offset += attributeSizes[i]; + + return offset; +} + +void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize) +{ + if (vertindex >= vertexCount) + throw love::Exception("Invalid vertex index: %ld", vertindex + 1); + + size_t offset = vertindex * vertexStride; + size_t size = std::min(datasize, vertexStride); + + GLBuffer::Bind bind(*vbo); + uint8 *bufferdata = (uint8 *) vbo->map(); + + memcpy(bufferdata + offset, data, size); + + vboUsedOffset = std::min(vboUsedOffset, offset); + vboUsedSize = std::max(vboUsedSize, (offset + size) - vboUsedOffset); +} + +size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize) +{ + if (vertindex >= vertexCount) + throw love::Exception("Invalid vertex index: %ld", vertindex + 1); + + size_t offset = vertindex * vertexStride; + size_t size = std::min(datasize, vertexStride); + + // We're relying on vbo->map() returning read/write data... ew. + GLBuffer::Bind bind(*vbo); + const uint8 *bufferdata = (const uint8 *) vbo->map(); + + memcpy(data, bufferdata + offset, size); + + if (vboUsedSize == 0) { - GLBuffer::Bind vbo_bind(*vbo); - return (Vertex *) vbo->map(); + vboUsedOffset = std::min(vboUsedOffset, offset); + vboUsedSize = std::max(vboUsedSize, (offset + size) - vboUsedOffset); } - return nullptr; + return size; } -void Mesh::setVertex(size_t index, const Vertex &v) +void *Mesh::getVertexScratchBuffer() { - if (index >= vertex_count) - throw love::Exception("Invalid vertex index: %ld", index + 1); - - GLBuffer::Bind vbo_bind(*vbo); - - // We unmap the vertex buffer in Mesh::draw. This lets us coalesce the - // buffer transfer calls into just one. - Vertex *vertices = (Vertex *) vbo->map(); - vertices[index] = v; + return vertexScratchBuffer; } -Vertex Mesh::getVertex(size_t index) const +void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize) { - if (index >= vertex_count) - throw love::Exception("Invalid vertex index: %ld", index + 1); + if (vertindex >= vertexCount) + throw love::Exception("Invalid vertex index: %ld", vertindex + 1); - GLBuffer::Bind vbo_bind(*vbo); + if (attribindex >= (int) vertexFormat.size()) + throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1); - // We unmap the vertex buffer in Mesh::draw. - Vertex *vertices = (Vertex *) vbo->map(); - return vertices[index]; + size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex); + size_t size = std::min(datasize, attributeSizes[attribindex]); + + GLBuffer::Bind bind(*vbo); + uint8 *bufferdata = (uint8 *) vbo->map(); + + memcpy(bufferdata + offset, data, size); + + vboUsedOffset = std::min(vboUsedOffset, offset); + vboUsedSize = std::max(vboUsedSize, (offset + size) - vboUsedOffset); +} + +size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize) +{ + if (vertindex >= vertexCount) + throw love::Exception("Invalid vertex index: %ld", vertindex + 1); + + if (attribindex >= (int) vertexFormat.size()) + throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1); + + size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex); + size_t size = std::min(datasize, attributeSizes[attribindex]); + + // We're relying on vbo->map() returning read/write data... ew. + GLBuffer::Bind bind(*vbo); + const uint8 *bufferdata = (const uint8 *) vbo->map(); + + memcpy(data, bufferdata + offset, size); + + if (vboUsedSize == 0) + { + vboUsedOffset = std::min(vboUsedOffset, offset); + vboUsedSize = std::max(vboUsedSize, (offset + size) - vboUsedOffset); + } + + return size; } size_t Mesh::getVertexCount() const { - return vertex_count; + return vertexCount; +} + +size_t Mesh::getVertexStride() const +{ + return vertexStride; +} + +const std::vector &Mesh::getVertexFormat() const +{ + return vertexFormat; +} + +Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const +{ + if (attribindex < 0 || attribindex >= (int) vertexFormat.size()) + throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1); + + DataType type = vertexFormat[attribindex].type; + components = vertexFormat[attribindex].components; + return type; +} + +void Mesh::setAttributeEnabled(const std::string &name, bool enable) +{ + auto it = attachedAttributes.find(name); + + if (it == attachedAttributes.end()) + throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str()); + + it->second.enabled = enable; +} + +bool Mesh::isAttributeEnabled(const std::string &name) const +{ + const auto it = attachedAttributes.find(name); + + if (it == attachedAttributes.end()) + throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str()); + + return it->second.enabled; +} + +void Mesh::attachAttribute(const std::string &name, Mesh *mesh) +{ + if (mesh != this) + { + for (const auto &it : mesh->attachedAttributes) + { + // If the supplied Mesh has attached attributes of its own, then we + // prevent it from being attached to avoid reference cycles. + if (it.second.mesh != mesh) + throw love::Exception("Cannot attach a Mesh which has attached Meshes of its own."); + } + } + + AttachedAttribute oldattrib = {}; + AttachedAttribute newattrib = {}; + + auto it = attachedAttributes.find(name); + if (it != attachedAttributes.end()) + oldattrib = it->second; + + newattrib.mesh = mesh; + newattrib.index = std::numeric_limits::max(); + newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true; + + // Find the index of the attribute in the mesh. + for (size_t i = 0; i < mesh->vertexFormat.size(); i++) + { + if (mesh->vertexFormat[i].name == name) + { + newattrib.index = i; + break; + } + } + + if (newattrib.index == std::numeric_limits::max()) + throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str()); + + if (newattrib.mesh != this) + newattrib.mesh->retain(); + + attachedAttributes[name] = newattrib; + + if (oldattrib.mesh && oldattrib.mesh != this) + oldattrib.mesh->release(); +} + +void Mesh::flush() +{ + { + GLBuffer::Bind vbobind(*vbo); + vbo->unmap(vboUsedOffset, vboUsedSize); + vboUsedOffset = vboUsedSize = 0; + } + + if (ibo != nullptr) + { + GLBuffer::Bind ibobind(*ibo); + ibo->unmap(); + } } /** @@ -168,7 +401,9 @@ static void copyToIndexBuffer(const std::vector &indices, GLBuffer::Mapp void Mesh::setVertexMap(const std::vector &map) { - GLenum datatype = getGLDataTypeFromMax(vertex_count); + size_t maxval = getVertexCount(); + + GLenum datatype = getGLDataTypeFromMax(maxval); // Calculate the size in bytes of the index buffer data. size_t size = map.size() * getGLDataTypeSize(datatype); @@ -180,32 +415,29 @@ void Mesh::setVertexMap(const std::vector &map) } if (!ibo && size > 0) - ibo = GLBuffer::Create(size, GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW); + ibo = new GLBuffer(size, nullptr, GL_ELEMENT_ARRAY_BUFFER, vbo->getUsage()); - element_count = map.size(); + elementCount = map.size(); - if (!ibo || element_count == 0) + if (!ibo || elementCount == 0) return; - GLBuffer::Bind ibo_bind(*ibo); - GLBuffer::Mapper ibo_map(*ibo); + GLBuffer::Bind ibobind(*ibo); + GLBuffer::Mapper ibomap(*ibo); // Fill the buffer with the index values from the vector. switch (datatype) { - case GL_UNSIGNED_BYTE: - copyToIndexBuffer(map, ibo_map, vertex_count); - break; case GL_UNSIGNED_SHORT: - copyToIndexBuffer(map, ibo_map, vertex_count); + copyToIndexBuffer(map, ibomap, maxval); break; case GL_UNSIGNED_INT: default: - copyToIndexBuffer(map, ibo_map, vertex_count); + copyToIndexBuffer(map, ibomap, maxval); break; } - element_data_type = datatype; + elementDataType = datatype; } /** @@ -221,36 +453,33 @@ static void copyFromIndexBuffer(void *buffer, size_t count, std::vector void Mesh::getVertexMap(std::vector &map) const { - if (!ibo || element_count == 0) + if (!ibo || elementCount == 0) return; map.clear(); - map.reserve(element_count); + map.reserve(elementCount); - GLBuffer::Bind ibo_bind(*ibo); + GLBuffer::Bind ibobind(*ibo); - // We unmap the buffer in Mesh::draw and Mesh::setVertexMap. + // We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush. void *buffer = ibo->map(); // Fill the vector from the buffer. - switch (element_data_type) + switch (elementDataType) { - case GL_UNSIGNED_BYTE: - copyFromIndexBuffer(buffer, element_count, map); - break; case GL_UNSIGNED_SHORT: - copyFromIndexBuffer(buffer, element_count, map); + copyFromIndexBuffer(buffer, elementCount, map); break; case GL_UNSIGNED_INT: default: - copyFromIndexBuffer(buffer, element_count, map); + copyFromIndexBuffer(buffer, elementCount, map); break; } } size_t Mesh::getVertexMapCount() const { - return element_count; + return elementCount; } void Mesh::setTexture(Texture *tex) @@ -268,14 +497,14 @@ Texture *Mesh::getTexture() const return texture.get(); } -void Mesh::setDrawMode(Mesh::DrawMode mode) +void Mesh::setDrawMode(DrawMode mode) { - draw_mode = mode; + drawMode = mode; } Mesh::DrawMode Mesh::getDrawMode() const { - return draw_mode; + return drawMode; } void Mesh::setDrawRange(int min, int max) @@ -283,76 +512,95 @@ void Mesh::setDrawRange(int min, int max) if (min < 0 || max < 0 || min > max) throw love::Exception("Invalid draw range."); - range_min = min; - range_max = max; + rangeMin = min; + rangeMax = max; } void Mesh::setDrawRange() { - range_min = range_max = -1; + rangeMin = rangeMax = -1; } void Mesh::getDrawRange(int &min, int &max) const { - min = range_min; - max = range_max; -} - -void Mesh::setVertexColors(bool enable) -{ - colors_enabled = enable; -} - -bool Mesh::hasVertexColors() const -{ - return colors_enabled; + min = rangeMin; + max = rangeMax; } void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - const size_t pos_offset = offsetof(Vertex, x); - const size_t tex_offset = offsetof(Vertex, s); - const size_t color_offset = offsetof(Vertex, r); - - if (vertex_count == 0) - return; - OpenGL::TempDebugGroup debuggroup("Mesh draw"); + std::vector attriblocations; + attriblocations.reserve(attachedAttributes.size()); + + bool hasposattrib = false; + + for (const auto &attrib : attachedAttributes) + { + if (!attrib.second.enabled) + continue; + + Mesh *mesh = attrib.second.mesh; + const AttribFormat &format = mesh->vertexFormat[attrib.second.index]; + + GLint attriblocation = -1; + + // If the attribute is one of the LOVE-defined ones, use the constant + // attribute index for it, otherwise query the index from the shader. + VertexAttribID builtinattrib; + if (Shader::getConstant(format.name.c_str(), builtinattrib)) + attriblocation = (GLint) builtinattrib; + else if (Shader::current) + attriblocation = Shader::current->getAttribLocation(format.name); + + // The active shader might not use this vertex attribute name. + if (attriblocation < 0) + continue; + + // Needed for unmap and glVertexAttribPointer. + GLBuffer::Bind vbobind(*mesh->vbo); + + // Make sure the buffer isn't mapped (sends data to GPU if needed.) + mesh->vbo->unmap(mesh->vboUsedOffset, mesh->vboUsedSize); + mesh->vboUsedOffset = mesh->vboUsedSize = 0; + + size_t offset = mesh->getAttributeOffset(attrib.second.index); + const void *gloffset = mesh->vbo->getPointer(offset); + GLenum datatype = getGLDataType(format.type); + GLboolean normalized = (datatype == GL_UNSIGNED_BYTE); + + glEnableVertexAttribArray(attriblocation); + glVertexAttribPointer(attriblocation, format.components, datatype, normalized, mesh->vertexStride, gloffset); + + attriblocations.push_back(attriblocation); + + if (attriblocation == ATTRIB_POS) + hasposattrib = true; + } + + if (!hasposattrib) + { + for (GLint attrib : attriblocations) + glDisableVertexAttribArray(attrib); + + // Not supported on all platforms or GL versions at least, I believe. + throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn."); + } + if (texture.get()) gl.bindTexture(*(GLuint *) texture->getHandle()); else gl.bindTexture(gl.getDefaultTexture()); - Matrix m; - m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); + Matrix m(x, y, angle, sx, sy, ox, oy, kx, ky); OpenGL::TempTransform transform(gl); transform.get() *= m; - GLBuffer::Bind vbo_bind(*vbo); - - // Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.) - vbo->unmap(); - - glEnableVertexAttribArray(ATTRIB_POS); - glEnableVertexAttribArray(ATTRIB_TEXCOORD); - - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), vbo->getPointer(pos_offset)); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), vbo->getPointer(tex_offset)); - - if (hasVertexColors()) - { - // Per-vertex colors. - glEnableVertexAttribArray(ATTRIB_COLOR); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), vbo->getPointer(color_offset)); - } - - GLenum mode = getGLDrawMode(draw_mode); - gl.prepareDraw(); - if (ibo && element_count > 0) + if (ibo && elementCount > 0) { // Use the custom vertex map (index buffer) to draw the vertices. GLBuffer::Bind ibo_bind(*ibo); @@ -360,61 +608,84 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo // Make sure the index buffer isn't mapped (sends data to GPU if needed.) ibo->unmap(); - int max = (int) element_count - 1; - if (range_max >= 0) - max = std::min(range_max, max); + int max = (int) elementCount - 1; + if (rangeMax >= 0) + max = std::min(rangeMax, max); int min = 0; - if (range_min >= 0) - min = std::min(range_min, max); + if (rangeMin >= 0) + min = std::min(rangeMin, max); - GLenum type = element_data_type; + GLenum type = elementDataType; const void *indices = ibo->getPointer(min * getGLDataTypeSize(type)); - gl.drawElements(mode, max - min + 1, type, indices); + gl.drawElements(getGLDrawMode(drawMode), max - min + 1, type, indices); } else { - int max = (int) vertex_count - 1; - if (range_max >= 0) - max = std::min(range_max, max); + int max = (int) vertexCount - 1; + if (rangeMax >= 0) + max = std::min(rangeMax, max); int min = 0; - if (range_min >= 0) - min = std::min(range_min, max); + if (rangeMin >= 0) + min = std::min(rangeMin, max); // Normal non-indexed drawing (no custom vertex map.) - gl.drawArrays(mode, min, max - min + 1); + gl.drawArrays(getGLDrawMode(drawMode), min, max - min + 1); } - glDisableVertexAttribArray(ATTRIB_TEXCOORD); - glDisableVertexAttribArray(ATTRIB_POS); - - if (hasVertexColors()) + for (GLint attrib : attriblocations) { - glDisableVertexAttribArray(ATTRIB_COLOR); - // Using the color array leaves the GL constant color undefined. - gl.setColor(gl.getColor()); + glDisableVertexAttribArray(attrib); + if (attrib == ATTRIB_COLOR) + gl.setColor(gl.getColor()); } } -GLenum Mesh::getGLDrawMode(DrawMode mode) const +size_t Mesh::getAttribFormatSize(const AttribFormat &format) +{ + switch (format.type) + { + case DATA_BYTE: + return format.components * sizeof(uint8); + case DATA_FLOAT: + return format.components * sizeof(float); + default: + return 0; + } +} + +GLenum Mesh::getGLDrawMode(DrawMode mode) { switch (mode) { - case DRAW_MODE_FAN: + case DRAWMODE_FAN: return GL_TRIANGLE_FAN; - case DRAW_MODE_STRIP: + case DRAWMODE_STRIP: return GL_TRIANGLE_STRIP; - case DRAW_MODE_TRIANGLES: + case DRAWMODE_TRIANGLES: default: return GL_TRIANGLES; - case DRAW_MODE_POINTS: + case DRAWMODE_POINTS: return GL_POINTS; } } -GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) const +GLenum Mesh::getGLDataType(DataType type) +{ + switch (type) + { + case DATA_BYTE: + return GL_UNSIGNED_BYTE; + case DATA_FLOAT: + return GL_FLOAT; + default: + return 0; + } +} + +GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) { if (maxvalue > LOVE_UINT16_MAX) return GL_UNSIGNED_INT; @@ -422,7 +693,7 @@ GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) const return GL_UNSIGNED_SHORT; } -size_t Mesh::getGLDataTypeSize(GLenum datatype) const +size_t Mesh::getGLDataTypeSize(GLenum datatype) { switch (datatype) { @@ -437,6 +708,31 @@ size_t Mesh::getGLDataTypeSize(GLenum datatype) const } } +GLenum Mesh::getGLBufferUsage(Usage usage) +{ + switch (usage) + { + case USAGE_STREAM: + return GL_STREAM_DRAW; + case USAGE_DYNAMIC: + return GL_DYNAMIC_DRAW; + case USAGE_STATIC: + return GL_STATIC_DRAW; + default: + return 0; + } +} + +bool Mesh::getConstant(const char *in, Usage &out) +{ + return usages.find(in, out); +} + +bool Mesh::getConstant(Usage in, const char *&out) +{ + return usages.find(in, out); +} + bool Mesh::getConstant(const char *in, Mesh::DrawMode &out) { return drawModes.find(in, out); @@ -447,15 +743,42 @@ bool Mesh::getConstant(Mesh::DrawMode in, const char *&out) return drawModes.find(in, out); } -StringMap::Entry Mesh::drawModeEntries[] = +bool Mesh::getConstant(const char *in, DataType &out) { - {"fan", Mesh::DRAW_MODE_FAN}, - {"strip", Mesh::DRAW_MODE_STRIP}, - {"triangles", Mesh::DRAW_MODE_TRIANGLES}, - {"points", Mesh::DRAW_MODE_POINTS}, + return dataTypes.find(in, out); +} + +bool Mesh::getConstant(DataType in, const char *&out) +{ + return dataTypes.find(in, out); +} + +StringMap::Entry Mesh::usageEntries[] = +{ + {"stream", USAGE_STREAM}, + {"dynamic", USAGE_DYNAMIC}, + {"static", USAGE_STATIC}, }; -StringMap Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries)); +StringMap Mesh::usages(Mesh::usageEntries, sizeof(Mesh::usageEntries)); + +StringMap::Entry Mesh::drawModeEntries[] = +{ + {"fan", DRAWMODE_FAN}, + {"strip", DRAWMODE_STRIP}, + {"triangles", DRAWMODE_TRIANGLES}, + {"points", DRAWMODE_POINTS}, +}; + +StringMap Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries)); + +StringMap::Entry Mesh::dataTypeEntries[] = +{ + {"byte", DATA_BYTE}, + {"float", DATA_FLOAT}, +}; + +StringMap Mesh::dataTypes(Mesh::dataTypeEntries, sizeof(Mesh::dataTypeEntries)); } // opengl } // graphics diff --git a/src/modules/graphics/opengl/Mesh.h b/src/modules/graphics/opengl/Mesh.h index 0e170a531..9e3f4398f 100644 --- a/src/modules/graphics/opengl/Mesh.h +++ b/src/modules/graphics/opengl/Mesh.h @@ -32,6 +32,7 @@ // C++ #include +#include namespace love { @@ -42,64 +43,107 @@ namespace opengl /** * Holds and draws arbitrary vertex geometry. - * Each vertex in the Mesh has a position, texture coordinate, and color. + * Each vertex in the Mesh has a collection of vertex attributes specified on + * creation. **/ class Mesh : public Drawable { public: + // The expected usage pattern of the Mesh's vertex data. + enum Usage + { + USAGE_STREAM, + USAGE_DYNAMIC, + USAGE_STATIC, + USAGE_MAX_ENUM + }; + // How the Mesh's vertices are used when drawing. // http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png enum DrawMode { - DRAW_MODE_FAN, - DRAW_MODE_STRIP, - DRAW_MODE_TRIANGLES, - DRAW_MODE_POINTS, - DRAW_MODE_MAX_ENUM + DRAWMODE_FAN, + DRAWMODE_STRIP, + DRAWMODE_TRIANGLES, + DRAWMODE_POINTS, + DRAWMODE_MAX_ENUM }; - /** - * Constructor. - * @param verts The vertices to use in the Mesh. - * @param mode The draw mode to use when drawing the Mesh. - **/ - Mesh(const std::vector &verts, DrawMode mode = DRAW_MODE_FAN); + // The type of data a vertex attribute can store. + enum DataType + { + DATA_BYTE, + DATA_FLOAT, + DATA_MAX_ENUM + }; - /** - * Constructor. - * Creates a Mesh with a certain number of default-initialized (hidden) - * vertices. - * @param vertexcount The number of vertices to use in the Mesh. - * @param mode The draw mode to use when drawing the Mesh. - **/ - Mesh(int vertexcount, DrawMode mode = DRAW_MODE_FAN); + struct AttribFormat + { + std::string name; + DataType type; + int components; // max 4 + }; + + Mesh(const std::vector &vertexformat, const void *data, size_t datasize, DrawMode drawmode, Usage usage); + Mesh(const std::vector &vertexformat, int vertexcount, DrawMode drawmode, Usage usage); + + Mesh(const std::vector &vertices, DrawMode drawmode, Usage usage); + Mesh(int vertexcount, DrawMode drawmode, Usage usage); virtual ~Mesh(); /** - * Replaces all the vertices in the Mesh with a new set of vertices. + * Sets the values of all attributes at a specific vertex index in the Mesh. + * The size of the data must be less than or equal to the total size of all + * vertex attributes. **/ - void setVertices(const std::vector &verts); + void setVertex(size_t vertindex, const void *data, size_t datasize); + size_t getVertex(size_t vertindex, void *data, size_t datasize); + void *getVertexScratchBuffer(); /** - * Gets all of the vertices in the Mesh as an array. + * Sets the values for a single attribute at a specific vertex index in the + * Mesh. The size of the data must be less than or equal to the size of the + * attribute. **/ - const Vertex *getVertices() const; + void setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize); + size_t getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize); /** - * Sets an individual vertex in the Mesh. - * @param index The index into the list of vertices to use. - * @param v The new vertex. - **/ - void setVertex(size_t index, const Vertex &v); - Vertex getVertex(size_t index) const; - - /** - * Gets the total number of vertices in the Mesh. + * Gets the total number of vertices that can be used when drawing the Mesh. **/ size_t getVertexCount() const; + /** + * Gets the size in bytes of the start of one vertex to the start of the + * next, in the buffer. + **/ + size_t getVertexStride() const; + + /** + * Gets the format of each vertex attribute stored in the Mesh. + **/ + const std::vector &getVertexFormat() const; + DataType getAttributeInfo(int attribindex, int &components) const; + + /** + * Sets whether a specific vertex attribute is used when drawing the Mesh. + **/ + void setAttributeEnabled(const std::string &name, bool enable); + bool isAttributeEnabled(const std::string &name) const; + + /** + * Attaches a vertex attribute from another Mesh to this one. The attribute + * will be used when drawing this Mesh. + **/ + void attachAttribute(const std::string &name, Mesh *mesh); + + /** + * Flushes all modified data to the GPU. + **/ + void flush(); + /** * Sets the vertex map to use when drawing the Mesh. The vertex map * determines the order in which vertices are used by the draw mode. @@ -145,46 +189,79 @@ public: void setDrawRange(); void getDrawRange(int &min, int &max) const; - /** - * Sets whether per-vertex colors are enabled. If this is disabled, the - * global color (love.graphics.setColor) will be used for the entire Mesh. - **/ - void setVertexColors(bool enable); - bool hasVertexColors() const; - // Implements Drawable. - void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override; + + static GLenum getGLBufferUsage(Usage usage); + + static bool getConstant(const char *in, Usage &out); + static bool getConstant(Usage in, const char *&out); static bool getConstant(const char *in, DrawMode &out); static bool getConstant(DrawMode in, const char *&out); + static bool getConstant(const char *in, DataType &out); + static bool getConstant(DataType in, const char *&out); + private: - GLenum getGLDrawMode(DrawMode mode) const; - GLenum getGLDataTypeFromMax(size_t maxvalue) const; - size_t getGLDataTypeSize(GLenum datatype) const; + struct AttachedAttribute + { + Mesh *mesh; + size_t index; + bool enabled; + }; - // Vertex buffer. + void setupAttachedAttributes(); + void calculateAttributeSizes(); + size_t getAttributeOffset(size_t attribindex) const; + + static size_t getAttribFormatSize(const AttribFormat &format); + + static GLenum getGLDrawMode(DrawMode mode); + static GLenum getGLDataType(DataType type); + static GLenum getGLDataTypeFromMax(size_t maxvalue); + static size_t getGLDataTypeSize(GLenum datatype); + + std::vector vertexFormat; + std::vector attributeSizes; + + std::unordered_map attachedAttributes; + + // Vertex buffer, for the vertex data. GLBuffer *vbo; - size_t vertex_count; + size_t vertexCount; + size_t vertexStride; + + // Block of memory whose size is at least as large as a single vertex. Helps + // avoid memory allocations when using Mesh::setVertex etc. + char *vertexScratchBuffer; + + // Tracks the range of the vertex buffer that has been used, to make unmap() + // calls as efficient as possible. + size_t vboUsedOffset; + size_t vboUsedSize; // Element (vertex index) buffer, for the vertex map. GLBuffer *ibo; - size_t element_count; - GLenum element_data_type; + size_t elementCount; + GLenum elementDataType; - DrawMode draw_mode; + DrawMode drawMode; - int range_min; - int range_max; + int rangeMin; + int rangeMax; StrongRef texture; - // Whether the per-vertex colors are used when drawing. - bool colors_enabled; + static StringMap::Entry usageEntries[]; + static StringMap usages; - static StringMap::Entry drawModeEntries[]; - static StringMap drawModes; + static StringMap::Entry drawModeEntries[]; + static StringMap drawModes; + + static StringMap::Entry dataTypeEntries[]; + static StringMap dataTypes; }; // Mesh diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index f89701f37..d9e79637e 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -327,6 +327,8 @@ void Shader::unloadVolatile() activeTexUnits.clear(); activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0); + attributes.clear(); + // same with uniform location list uniforms.clear(); @@ -610,6 +612,18 @@ Shader::UniformType Shader::getExternVariable(const std::string &name, int &comp return it->second.baseType; } +GLint Shader::getAttribLocation(const std::string &name) +{ + auto it = attributes.find(name); + if (it != attributes.end()) + return it->second; + + GLint location = glGetAttribLocation(program, name.c_str()); + + attributes[name] = location; + return location; +} + bool Shader::hasVertexAttrib(VertexAttribID attrib) const { return builtinAttributes[int(attrib)] != -1; @@ -821,6 +835,16 @@ bool Shader::getConstant(UniformType in, const char *&out) return uniformTypes.find(in, out); } +bool Shader::getConstant(const char *in, VertexAttribID &out) +{ + return attribNames.find(in, out); +} + +bool Shader::getConstant(VertexAttribID in, const char *&out) +{ + return attribNames.find(in, out); +} + StringMap::Entry Shader::stageNameEntries[] = { {"vertex", Shader::STAGE_VERTEX}, diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index 729973042..e07c47601 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -174,6 +174,8 @@ public: **/ UniformType getExternVariable(const std::string &name, int &components, int &count); + GLint getAttribLocation(const std::string &name); + /** * Internal use only. **/ @@ -192,6 +194,9 @@ public: static bool getConstant(const char *in, UniformType &out); static bool getConstant(UniformType in, const char *&out); + static bool getConstant(const char *in, VertexAttribID &out); + static bool getConstant(VertexAttribID in, const char *&out); + private: // Represents a single uniform/extern shader variable. @@ -237,6 +242,8 @@ private: // Location values for any generic vertex attribute variables. GLint builtinAttributes[ATTRIB_MAX_ENUM]; + std::map attributes; + // Uniform location buffer map std::map uniforms; diff --git a/src/modules/graphics/opengl/SpriteBatch.cpp b/src/modules/graphics/opengl/SpriteBatch.cpp index bb919aa4c..cb9459fcc 100644 --- a/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/src/modules/graphics/opengl/SpriteBatch.cpp @@ -41,7 +41,7 @@ namespace graphics namespace opengl { -SpriteBatch::SpriteBatch(Texture *texture, int size, int usage) +SpriteBatch::SpriteBatch(Texture *texture, int size, Mesh::Usage usage) : texture(texture) , size(size) , next(0) @@ -54,26 +54,13 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage) if (size <= 0) throw love::Exception("Invalid SpriteBatch size."); - GLenum gl_usage; - switch (usage) - { - default: - case USAGE_DYNAMIC: - gl_usage = GL_DYNAMIC_DRAW; - break; - case USAGE_STATIC: - gl_usage = GL_STATIC_DRAW; - break; - case USAGE_STREAM: - gl_usage = GL_STREAM_DRAW; - break; - } + GLenum gl_usage = Mesh::getGLBufferUsage(usage); const size_t vertex_size = sizeof(Vertex) * 4 * size; try { - array_buf = GLBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage); + array_buf = new GLBuffer(vertex_size, nullptr, GL_ARRAY_BUFFER, gl_usage); } catch (love::Exception &) { @@ -162,7 +149,7 @@ void SpriteBatch::setColor(const Color &color) void SpriteBatch::setColor() { delete color; - color = 0; + color = nullptr; } const Color *SpriteBatch::getColor() const @@ -195,7 +182,7 @@ void SpriteBatch::setBufferSize(int newsize) try { - new_array_buf = GLBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage()); + new_array_buf = new GLBuffer(vertex_size, nullptr, array_buf->getTarget(), array_buf->getUsage()); // Copy as much of the old data into the new GLBuffer as can fit. GLBuffer::Bind bind(*new_array_buf); @@ -311,25 +298,6 @@ void SpriteBatch::setColorv(Vertex *v, const Color &color) } } -bool SpriteBatch::getConstant(const char *in, UsageHint &out) -{ - return usageHints.find(in, out); -} - -bool SpriteBatch::getConstant(UsageHint in, const char *&out) -{ - return usageHints.find(in, out); -} - -StringMap::Entry SpriteBatch::usageHintEntries[] = -{ - {"dynamic", SpriteBatch::USAGE_DYNAMIC}, - {"static", SpriteBatch::USAGE_STATIC}, - {"stream", SpriteBatch::USAGE_STREAM}, -}; - -StringMap SpriteBatch::usageHints(usageHintEntries, sizeof(usageHintEntries)); - } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/SpriteBatch.h b/src/modules/graphics/opengl/SpriteBatch.h index 28bd708de..da00a0cf4 100644 --- a/src/modules/graphics/opengl/SpriteBatch.h +++ b/src/modules/graphics/opengl/SpriteBatch.h @@ -27,12 +27,12 @@ // LOVE #include "common/math.h" #include "common/Matrix.h" -#include "common/StringMap.h" #include "graphics/Drawable.h" #include "graphics/Volatile.h" #include "graphics/Color.h" #include "graphics/Quad.h" #include "GLBuffer.h" +#include "Mesh.h" namespace love { @@ -49,15 +49,7 @@ class SpriteBatch : public Drawable { public: - enum UsageHint - { - USAGE_DYNAMIC, - USAGE_STATIC, - USAGE_STREAM, - USAGE_MAX_ENUM - }; - - SpriteBatch(Texture *texture, int size, int usage); + SpriteBatch(Texture *texture, int size, Mesh::Usage usage); virtual ~SpriteBatch(); int add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index = -1); @@ -109,9 +101,6 @@ public: // Implements Drawable. void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); - static bool getConstant(const char *in, UsageHint &out); - static bool getConstant(UsageHint in, const char *&out); - private: void addv(const Vertex *v, const Matrix &m, int index); @@ -144,9 +133,6 @@ private: size_t buffer_used_offset; size_t buffer_used_size; - static StringMap::Entry usageHintEntries[]; - static StringMap usageHints; - }; // SpriteBatch } // opengl diff --git a/src/modules/graphics/opengl/Text.cpp b/src/modules/graphics/opengl/Text.cpp index d6745c810..aad35d787 100644 --- a/src/modules/graphics/opengl/Text.cpp +++ b/src/modules/graphics/opengl/Text.cpp @@ -59,7 +59,7 @@ void Text::uploadVertices(const std::vector &vertices, size_t if (vbo != nullptr) newsize = std::max(size_t(vbo->getSize() * 1.5), newsize); - GLBuffer *new_vbo = GLBuffer::Create(newsize, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW); + GLBuffer *new_vbo = new GLBuffer(newsize, nullptr, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW); if (vbo != nullptr) { diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 814c54cd0..f3a31704c 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -258,14 +258,14 @@ int w_newImage(lua_State *L) { luax_catchexcept(L, [&]() { cdata = image->newCompressedData(fdata); }, - [&]() { fdata->release(); } + [&](bool) { fdata->release(); } ); } else { luax_catchexcept(L, [&]() { data = image->newImageData(fdata); }, - [&]() { fdata->release(); } + [&](bool) { fdata->release(); } ); } @@ -289,7 +289,7 @@ int w_newImage(lua_State *L) else if (data) image = instance()->newImage(data, flags); }, - [&]() { + [&](bool) { if (releasedata && data) data->release(); else if (releasedata && cdata) @@ -393,11 +393,11 @@ int w_newSpriteBatch(lua_State *L) { Texture *texture = luax_checktexture(L, 1); int size = luaL_optint(L, 2, 1000); - SpriteBatch::UsageHint usage = SpriteBatch::USAGE_DYNAMIC; + Mesh::Usage usage = Mesh::USAGE_DYNAMIC; if (lua_gettop(L) > 2) { const char *usagestr = luaL_checkstring(L, 3); - if (!SpriteBatch::getConstant(usagestr, usage)) + if (!Mesh::getConstant(usagestr, usage)) return luaL_error(L, "Invalid SpriteBatch usage hint: %s", usagestr); } @@ -558,43 +558,62 @@ int w_newShader(lua_State *L) return 1; } -int w_newMesh(lua_State *L) +static Mesh::Usage luax_optmeshusage(lua_State *L, int idx, Mesh::Usage def) { - // Check first argument: table of vertices or number of vertices. - int ttype = lua_type(L, 1); - if (ttype != LUA_TTABLE && ttype != LUA_TNUMBER) - luaL_argerror(L, 1, "table or number expected"); + const char *usagestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx); - // Second argument: optional texture. - Texture *tex = nullptr; - if (!lua_isnoneornil(L, 2)) - tex = luax_checktexture(L, 2); + if (usagestr && !Mesh::getConstant(usagestr, def)) + luaL_error(L, "Invalid mesh usage hint: %s", usagestr); - // Third argument: optional draw mode. - const char *str = 0; - Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN; - str = lua_isnoneornil(L, 3) ? 0 : luaL_checkstring(L, 3); + return def; +} - if (str && !Mesh::getConstant(str, mode)) - return luaL_error(L, "Invalid mesh draw mode: %s", str); +static Mesh::DrawMode luax_optmeshdrawmode(lua_State *L, int idx, Mesh::DrawMode def) +{ + const char *modestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx); + if (modestr && !Mesh::getConstant(modestr, def)) + luaL_error(L, "Invalid mesh draw mode: %s", modestr); + + return def; +} + +template +static inline size_t writeVertexData(lua_State *L, int startidx, int components, char *data) +{ + T *componentdata = (T *) data; + + for (int i = 0; i < components; i++) + componentdata[i] = (T) luaL_checknumber(L, startidx + i); + + return sizeof(T) * components; +} + +static Mesh *newStandardMesh(lua_State *L) +{ Mesh *t = nullptr; - if (ttype == LUA_TTABLE) - { - size_t vertex_count = lua_objlen(L, 1); - std::vector vertices; - vertices.reserve(vertex_count); + Mesh::DrawMode drawmode = luax_optmeshdrawmode(L, 2, Mesh::DRAWMODE_FAN); + Mesh::Usage usage = luax_optmeshusage(L, 3, Mesh::USAGE_DYNAMIC); - bool use_colors = false; + // First argument is a table of standard vertices, or the number of + // standard vertices. + if (lua_istable(L, 1)) + { + size_t vertexcount = lua_objlen(L, 1); + std::vector vertices; + vertices.reserve(vertexcount); // Get the vertices from the table. - for (size_t i = 1; i <= vertex_count; i++) + for (size_t i = 1; i <= vertexcount; i++) { lua_rawgeti(L, 1, (int) i); if (lua_type(L, -1) != LUA_TTABLE) - return luax_typerror(L, 1, "table of tables"); + { + luax_typerror(L, 1, "table of tables"); + return nullptr; + } for (int j = 1; j <= 8; j++) lua_rawgeti(L, -j, j); @@ -603,34 +622,178 @@ int w_newMesh(lua_State *L) v.x = (float) luaL_checknumber(L, -8); v.y = (float) luaL_checknumber(L, -7); - v.s = (float) luaL_optnumber(L, -6, 0.0); v.t = (float) luaL_optnumber(L, -5, 0.0); - v.r = (unsigned char) luaL_optinteger(L, -4, 255); v.g = (unsigned char) luaL_optinteger(L, -3, 255); v.b = (unsigned char) luaL_optinteger(L, -2, 255); v.a = (unsigned char) luaL_optinteger(L, -1, 255); - // Enable per-vertex coloring if any color is not the default. - if (!use_colors && (v.r != 255 || v.g != 255 || v.b != 255 || v.a != 255)) - use_colors = true; - lua_pop(L, 9); vertices.push_back(v); } - luax_catchexcept(L, [&](){ t = instance()->newMesh(vertices, mode); }); - t->setVertexColors(use_colors); + luax_catchexcept(L, [&](){ t = instance()->newMesh(vertices, drawmode, usage); }); } else { int count = luaL_checkint(L, 1); - luax_catchexcept(L, [&](){ t = instance()->newMesh(count, mode); }); + luax_catchexcept(L, [&](){ t = instance()->newMesh(count, drawmode, usage); }); } - if (tex) - t->setTexture(tex); + return t; +} + +static Mesh *newCustomMesh(lua_State *L) +{ + Mesh *t = nullptr; + + // First argument is the vertex format, second is a table of vertices or + // the number of vertices. + std::vector vertexformat; + + Mesh::DrawMode drawmode = luax_optmeshdrawmode(L, 3, Mesh::DRAWMODE_FAN); + Mesh::Usage usage = luax_optmeshusage(L, 4, Mesh::USAGE_DYNAMIC); + + lua_rawgeti(L, 1, 1); + if (!lua_istable(L, -1)) + { + luaL_argerror(L, 1, "table of tables expected"); + return nullptr; + } + lua_pop(L, 1); + + // Per-vertex attribute formats. + for (int i = 1; i <= (int) lua_objlen(L, 1); i++) + { + lua_rawgeti(L, 1, i); + + // {name, datatype, components} + for (int j = 1; j <= 3; j++) + lua_rawgeti(L, -j, j); + + Mesh::AttribFormat format; + format.name = luaL_checkstring(L, -3); + + const char *tname = luaL_checkstring(L, -2); + if (!Mesh::getConstant(tname, format.type)) + { + luaL_error(L, "Invalid Mesh vertex data type name: %s", tname); + return nullptr; + } + + format.components = luaL_checkint(L, -1); + if (format.components <= 0 || format.components > 4) + { + luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", format.components); + return nullptr; + } + + lua_pop(L, 4); + vertexformat.push_back(format); + } + + if (lua_isnumber(L, 2)) + { + int vertexcount = luaL_checkint(L, 2); + luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, vertexcount, drawmode, usage); }); + } + else if (luax_istype(L, 2, DATA_ID)) + { + // Vertex data comes directly from a Data object. + Data *data = luax_checktype(L, 2, DATA_ID); + luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, data->getData(), data->getSize(), drawmode, usage); }); + } + else + { + // Table of vertices. + lua_rawgeti(L, 2, 1); + if (!lua_istable(L, -1)) + { + luaL_argerror(L, 2, "expected table of tables"); + return nullptr; + } + lua_pop(L, 1); + + int vertexcomponents = 0; + for (const Mesh::AttribFormat &format : vertexformat) + vertexcomponents += format.components; + + size_t numvertices = lua_objlen(L, 2); + + luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); }); + + // Maximum possible data size for a single vertex attribute. + char data[sizeof(float) * 4]; + + for (size_t vertindex = 0; vertindex < numvertices; vertindex++) + { + // get vertices[vertindex] + lua_rawgeti(L, 2, vertindex + 1); + luaL_checktype(L, -1, LUA_TTABLE); + + if ((int) lua_objlen(L, -1) < vertexcomponents) + { + t->release(); + const char *err = "Invalid number of components in vertex #%d (expected %d components, got %d)"; + luaL_error(L, err, vertindex+1, vertexcomponents, lua_objlen(L, -1)); + return nullptr; + } + + int n = 0; + for (size_t i = 0; i < vertexformat.size(); i++) + { + int components = vertexformat[i].components; + + // get vertices[vertindex][n] + for (int c = 0; c < components; c++) + { + n++; + lua_rawgeti(L, -(c + 1), n); + } + + // Fetch the values from Lua and store them in data buffer. + switch (vertexformat[i].type) + { + case Mesh::DATA_BYTE: + writeVertexData(L, -components, components, data); + break; + case Mesh::DATA_FLOAT: + default: + writeVertexData(L, -components, components, data); + break; + } + + lua_pop(L, components); + + luax_catchexcept(L, + [&](){ t->setVertexAttribute(vertindex, i, data, sizeof(float) * 4); }, + [&](bool diderror){ if (diderror) t->release(); } + ); + } + + lua_pop(L, 1); // pop vertices[vertindex] + } + + t->flush(); + } + + return t; +} + +int w_newMesh(lua_State *L) +{ + // Check first argument: table or number of vertices. + int ttype = lua_type(L, 1); + if (ttype != LUA_TTABLE && ttype != LUA_TNUMBER) + luaL_argerror(L, 1, "table or number expected"); + + Mesh *t = nullptr; + + if (ttype == LUA_TTABLE && (lua_istable(L, 2) || lua_type(L, 2) == LUA_TNUMBER)) + t = newCustomMesh(L); + else + t = newStandardMesh(L); luax_pushtype(L, GRAPHICS_MESH_ID, t); t->release(); @@ -1086,7 +1249,6 @@ int w_setDefaultShaderCode(lua_State *L) return 0; } - int w_getSupported(lua_State *L) { lua_createtable(L, 0, (int) Graphics::SUPPORT_MAX_ENUM); @@ -1246,10 +1408,12 @@ int w_draw(lua_State *L) float kx = (float) luaL_optnumber(L, startidx + 7, 0.0); float ky = (float) luaL_optnumber(L, startidx + 8, 0.0); - if (texture && quad) - texture->drawq(quad, x, y, a, sx, sy, ox, oy, kx, ky); - else if (drawable) - drawable->draw(x, y, a, sx, sy, ox, oy, kx, ky); + luax_catchexcept(L, [&]() { + if (texture && quad) + texture->drawq(quad, x, y, a, sx, sy, ox, oy, kx, ky); + else if (drawable) + drawable->draw(x, y, a, sx, sy, ox, oy, kx, ky); + }); return 0; } diff --git a/src/modules/graphics/opengl/wrap_Mesh.cpp b/src/modules/graphics/opengl/wrap_Mesh.cpp index 1bfbc5999..1825d1774 100644 --- a/src/modules/graphics/opengl/wrap_Mesh.cpp +++ b/src/modules/graphics/opengl/wrap_Mesh.cpp @@ -39,152 +39,162 @@ Mesh *luax_checkmesh(lua_State *L, int idx) return luax_checktype(L, idx, GRAPHICS_MESH_ID); } +template +static inline size_t writeData(lua_State *L, int startidx, int components, char *data) +{ + T *componentdata = (T *) data; + for (int i = 0; i < components; i++) + componentdata[i] = (T) luaL_checknumber(L, startidx + i); + + return sizeof(T) * components; +} + +static inline char *writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, int components, char *data) +{ + switch (type) + { + case Mesh::DATA_BYTE: + data += writeData(L, startidx, components, data); + break; + case Mesh::DATA_FLOAT: + data += writeData(L, startidx, components, data); + break; + default: + break; + } + + return data; +} + +template +static inline size_t readData(lua_State *L, int components, const char *data) +{ + const T *componentdata = (const T *) data; + for (int i = 0; i < components; i++) + lua_pushnumber(L, (lua_Number) componentdata[i]); + + return sizeof(T) * components; +} + +static inline const char *readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data) +{ + switch (type) + { + case Mesh::DATA_BYTE: + data += readData(L, components, data); + break; + case Mesh::DATA_FLOAT: + data += readData(L, components, data); + break; + default: + break; + } + + return data; +} + int w_Mesh_setVertex(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); - size_t i = size_t(luaL_checkinteger(L, 2) - 1); + size_t index = (size_t) luaL_checkinteger(L, 2) - 1; - Vertex v; + bool istable = lua_istable(L, 3); - if (lua_istable(L, 3)) + const std::vector &vertexformat = t->getVertexFormat(); + + char *data = (char *) t->getVertexScratchBuffer(); + char *writtendata = data; + + int idx = istable ? 1 : 3; + + if (istable) { - for (int i = 1; i <= 8; i++) - lua_rawgeti(L, 3, i); + for (const Mesh::AttribFormat &format : vertexformat) + { + for (int i = idx; i < idx + format.components; i++) + lua_rawgeti(L, 3, i); - v.x = luaL_checknumber(L, -8); - v.y = luaL_checknumber(L, -7); - v.s = luaL_optnumber(L, -6, 0.0); - v.t = luaL_optnumber(L, -5, 0.0); - v.r = luaL_optinteger(L, -4, 255); - v.g = luaL_optinteger(L, -3, 255); - v.b = luaL_optinteger(L, -2, 255); - v.a = luaL_optinteger(L, -1, 255); + // Fetch the values from Lua and store them in data buffer. + writtendata = writeAttributeData(L, -format.components, format.type, format.components, writtendata); - lua_pop(L, 8); + idx += format.components; + lua_pop(L, format.components); + } } else { - v.x = luaL_checknumber(L, 3); - v.y = luaL_checknumber(L, 4); - v.s = luaL_optnumber(L, 5, 0.0); - v.t = luaL_optnumber(L, 6, 0.0); - v.r = luaL_optinteger(L, 7, 255); - v.g = luaL_optinteger(L, 8, 255); - v.b = luaL_optinteger(L, 9, 255); - v.a = luaL_optinteger(L, 10, 255); + for (const Mesh::AttribFormat &format : vertexformat) + { + // Fetch the values from Lua and store them in data buffer. + writtendata = writeAttributeData(L, idx, format.type, format.components, writtendata); + idx += format.components; + } } - luax_catchexcept(L, [&](){ t->setVertex(i, v); }); + luax_catchexcept(L, [&](){ t->setVertex(index, data, t->getVertexStride()); }); return 0; } int w_Mesh_getVertex(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); - size_t i = (size_t) (luaL_checkinteger(L, 2) - 1); + size_t index = (size_t) luaL_checkinteger(L, 2) - 1; - Vertex v; - luax_catchexcept(L, [&](){ v = t->getVertex(i); }); + const std::vector &vertexformat = t->getVertexFormat(); - lua_pushnumber(L, v.x); - lua_pushnumber(L, v.y); - lua_pushnumber(L, v.s); - lua_pushnumber(L, v.t); - lua_pushnumber(L, v.r); - lua_pushnumber(L, v.g); - lua_pushnumber(L, v.b); - lua_pushnumber(L, v.a); + char *data = (char *) t->getVertexScratchBuffer(); + const char *readdata = data; - return 8; -} + luax_catchexcept(L, [&](){ t->getVertex(index, data, t->getVertexStride()); }); -int w_Mesh_setVertices(lua_State *L) -{ - Mesh *t = luax_checkmesh(L, 1); + int n = 0; - int vertex_count = (int) lua_objlen(L, 2); - std::vector vertices; - vertices.reserve(vertex_count); - - // Get the vertices from the table. - for (int i = 1; i <= vertex_count; i++) + for (const Mesh::AttribFormat &format : vertexformat) { - lua_rawgeti(L, 2, i); - - if (lua_type(L, -1) != LUA_TTABLE) - return luax_typerror(L, 2, "table of tables"); - - for (int j = 1; j <= 8; j++) - lua_rawgeti(L, -j, j); - - Vertex v; - - v.x = (float) luaL_checknumber(L, -8); - v.y = (float) luaL_checknumber(L, -7); - - v.s = (float) luaL_optnumber(L, -6, 0.0); - v.t = (float) luaL_optnumber(L, -5, 0.0); - - v.r = (unsigned char) luaL_optinteger(L, -4, 255); - v.g = (unsigned char) luaL_optinteger(L, -3, 255); - v.b = (unsigned char) luaL_optinteger(L, -2, 255); - v.a = (unsigned char) luaL_optinteger(L, -1, 255); - - lua_pop(L, 9); - vertices.push_back(v); + readdata = readAttributeData(L, format.type, format.components, readdata); + n += format.components; } - luax_catchexcept(L, [&](){ t->setVertices(vertices); }); + return n; +} + +int w_Mesh_setVertexAttribute(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1; + int attribindex = (int) luaL_checkinteger(L, 3) - 1; + + Mesh::DataType type; + int components; + luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); }); + + // Maximum possible size for a single vertex attribute. + char data[sizeof(float) * 4]; + + // Fetch the values from Lua and store them in the data buffer. + writeAttributeData(L, 4, type, components, data); + + luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); }); return 0; } -int w_Mesh_getVertices(lua_State *L) +int w_Mesh_getVertexAttribute(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); + size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1; + int attribindex = (int) luaL_checkinteger(L, 3) - 1; - const Vertex *vertices = t->getVertices(); + Mesh::DataType type; + int components; + luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); }); - int count = (int) t->getVertexCount(); - lua_createtable(L, count, 0); + // Maximum possible size for a single vertex attribute. + char data[sizeof(float) * 4]; - if (count == 0 || vertices == nullptr) - return 1; + luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); }); - for (int i = 0; i < count; i++) - { - // Create vertex table. - lua_createtable(L, 8, 0); - - lua_pushnumber(L, vertices[i].x); - lua_rawseti(L, -2, 1); - - lua_pushnumber(L, vertices[i].y); - lua_rawseti(L, -2, 2); - - lua_pushnumber(L, vertices[i].s); - lua_rawseti(L, -2, 3); - - lua_pushnumber(L, vertices[i].t); - lua_rawseti(L, -2, 4); - - lua_pushnumber(L, vertices[i].r); - lua_rawseti(L, -2, 5); - - lua_pushnumber(L, vertices[i].g); - lua_rawseti(L, -2, 6); - - lua_pushnumber(L, vertices[i].b); - lua_rawseti(L, -2, 7); - - lua_pushnumber(L, vertices[i].a); - lua_rawseti(L, -2, 8); - - // Insert vertex table into vertices table. - lua_rawseti(L, -2, i + 1); - } - - // Return vertices table. - return 1; + readAttributeData(L, type, components, data); + return components; } int w_Mesh_getVertexCount(lua_State *L) @@ -194,6 +204,73 @@ int w_Mesh_getVertexCount(lua_State *L) return 1; } +int w_Mesh_getVertexFormat(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + + const std::vector &vertexformat = t->getVertexFormat(); + lua_createtable(L, (int) vertexformat.size(), 0); + + const char *tname = nullptr; + + for (size_t i = 0; i < vertexformat.size(); i++) + { + if (!Mesh::getConstant(vertexformat[i].type, tname)) + return luaL_error(L, "Unknown vertex attribute data type."); + + lua_createtable(L, 3, 0); + + lua_pushstring(L, vertexformat[i].name.c_str()); + lua_rawseti(L, -2, 1); + + lua_pushstring(L, tname); + lua_rawseti(L, -2, 2); + + lua_pushinteger(L, vertexformat[i].components); + lua_rawseti(L, -2, 3); + + // format[i] = {name, type, components} + lua_rawseti(L, -2, (int) i + 1); + } + + return 1; +} + +int w_Mesh_setAttributeEnabled(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + const char *name = luaL_checkstring(L, 2); + bool enable = luax_toboolean(L, 3); + luax_catchexcept(L, [&](){ t->setAttributeEnabled(name, enable); }); + return 0; +} + +int w_Mesh_isAttributeEnabled(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + const char *name = luaL_checkstring(L, 2); + bool enabled = false; + luax_catchexcept(L, [&](){ enabled = t->isAttributeEnabled(name); }); + lua_pushboolean(L, enabled); + return 1; +} + +int w_Mesh_attachAttribute(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + const char *name = luaL_checkstring(L, 2); + Mesh *mesh = luax_checkmesh(L, 3); + luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh); }); + return 0; +} + +int w_Mesh_flush(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + t->flush(); + return 0; +} + int w_Mesh_setVertexMap(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); @@ -204,15 +281,18 @@ int w_Mesh_setVertexMap(lua_State *L) std::vector vertexmap; vertexmap.reserve(nargs); - for (int i = 0; i < nargs; i++) + if (is_table) { - if (is_table) + for (int i = 0; i < nargs; i++) { lua_rawgeti(L, 2, i + 1); vertexmap.push_back(uint32(luaL_checkinteger(L, -1) - 1)); lua_pop(L, 1); } - else + } + else + { + for (int i = 0; i < nargs; i++) vertexmap.push_back(uint32(luaL_checkinteger(L, i + 2) - 1)); } @@ -332,27 +412,18 @@ int w_Mesh_getDrawRange(lua_State *L) return 2; } -int w_Mesh_setVertexColors(lua_State *L) -{ - Mesh *t = luax_checkmesh(L, 1); - t->setVertexColors(luax_toboolean(L, 2)); - return 0; -} - -int w_Mesh_hasVertexColors(lua_State *L) -{ - Mesh *t = luax_checkmesh(L, 1); - luax_pushboolean(L, t->hasVertexColors()); - return 1; -} - static const luaL_Reg functions[] = { { "setVertex", w_Mesh_setVertex }, { "getVertex", w_Mesh_getVertex }, - { "setVertices", w_Mesh_setVertices }, - { "getVertices", w_Mesh_getVertices }, + { "setVertexAttribute", w_Mesh_setVertexAttribute }, + { "getVertexAttribute", w_Mesh_getVertexAttribute }, { "getVertexCount", w_Mesh_getVertexCount }, + { "getVertexFormat", w_Mesh_getVertexFormat }, + { "setAttributeEnabled", w_Mesh_setAttributeEnabled }, + { "isAttributeEnabled", w_Mesh_isAttributeEnabled }, + { "attachAttribute", w_Mesh_attachAttribute }, + { "flush", w_Mesh_flush }, { "setVertexMap", w_Mesh_setVertexMap }, { "getVertexMap", w_Mesh_getVertexMap }, { "setTexture", w_Mesh_setTexture }, @@ -361,8 +432,6 @@ static const luaL_Reg functions[] = { "getDrawMode", w_Mesh_getDrawMode }, { "setDrawRange", w_Mesh_setDrawRange }, { "getDrawRange", w_Mesh_getDrawRange }, - { "setVertexColors", w_Mesh_setVertexColors }, - { "hasVertexColors", w_Mesh_hasVertexColors }, { 0, 0 } }; diff --git a/src/modules/graphics/opengl/wrap_Mesh.h b/src/modules/graphics/opengl/wrap_Mesh.h index 477927d3c..e63882e03 100644 --- a/src/modules/graphics/opengl/wrap_Mesh.h +++ b/src/modules/graphics/opengl/wrap_Mesh.h @@ -33,12 +33,16 @@ namespace opengl { Mesh *luax_checkmesh(lua_State *L, int idx); - int w_Mesh_setVertex(lua_State *L); int w_Mesh_getVertex(lua_State *L); -int w_Mesh_setVertices(lua_State *L); -int w_Mesh_getVertices(lua_State *L); +int w_Mesh_setVertexAttribute(lua_State *L); +int w_Mesh_getVertexAttribute(lua_State *L); int w_Mesh_getVertexCount(lua_State *L); +int w_Mesh_getVertexFormat(lua_State *L); +int w_Mesh_setAttributeEnabled(lua_State *L); +int w_Mesh_isAttributeEnabled(lua_State *L); +int w_Mesh_attachAttribute(lua_State *L); +int w_Mesh_flush(lua_State *L); int w_Mesh_setVertexMap(lua_State *L); int w_Mesh_getVertexMap(lua_State *L); int w_Mesh_setTexture(lua_State *L); @@ -47,9 +51,6 @@ int w_Mesh_setDrawMode(lua_State *L); int w_Mesh_getDrawMode(lua_State *L); int w_Mesh_setDrawRange(lua_State *L); int w_Mesh_getDrawRange(lua_State *L); -int w_Mesh_setVertexColors(lua_State *L); -int w_Mesh_hasVertexColors(lua_State *L); - extern "C" int luaopen_mesh(lua_State *L); } // opengl diff --git a/src/modules/graphics/opengl/wrap_Shader.cpp b/src/modules/graphics/opengl/wrap_Shader.cpp index cd5421b4a..a1078c639 100644 --- a/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/src/modules/graphics/opengl/wrap_Shader.cpp @@ -234,7 +234,7 @@ int w_Shader_sendMatrix(lua_State *L) luax_catchexcept(L, [&]() { shader->sendMatrix(name, dimension, values, count); }, - [&]() { delete[] values; } + [&](bool) { delete[] values; } ); return 0; diff --git a/src/modules/image/wrap_Image.cpp b/src/modules/image/wrap_Image.cpp index 09eac8c40..34c360ee9 100644 --- a/src/modules/image/wrap_Image.cpp +++ b/src/modules/image/wrap_Image.cpp @@ -58,7 +58,7 @@ int w_newImageData(lua_State *L) ImageData *t = nullptr; luax_catchexcept(L, [&]() { t = instance()->newImageData(data); }, - [&]() { data->release(); } + [&](bool) { data->release(); } ); luax_pushtype(L, IMAGE_IMAGE_DATA_ID, t); @@ -73,7 +73,7 @@ int w_newCompressedData(lua_State *L) CompressedImageData *t = nullptr; luax_catchexcept(L, [&]() { t = instance()->newCompressedData(data); }, - [&]() { data->release(); } + [&](bool) { data->release(); } ); luax_pushtype(L, IMAGE_COMPRESSED_IMAGE_DATA_ID, t); diff --git a/src/modules/sound/wrap_Sound.cpp b/src/modules/sound/wrap_Sound.cpp index 944083320..1b5819a5d 100644 --- a/src/modules/sound/wrap_Sound.cpp +++ b/src/modules/sound/wrap_Sound.cpp @@ -71,7 +71,7 @@ int w_newDecoder(lua_State *L) Decoder *t = nullptr; luax_catchexcept(L, [&]() { t = instance()->newDecoder(data, bufferSize); }, - [&]() { data->release(); } + [&](bool) { data->release(); } ); if (t == nullptr)