diff --git a/src/common/runtime.h b/src/common/runtime.h index 620a00646..5c884ebcd 100644 --- a/src/common/runtime.h +++ b/src/common/runtime.h @@ -214,6 +214,16 @@ inline float luax_checkfloat(lua_State *L, int idx) return static_cast(luaL_checknumber(L, idx)); } +inline lua_Number luax_checknumberclamped(lua_State *L, int idx, double minv, double maxv) +{ + return std::min(std::max(luaL_checknumber(L, idx), minv), maxv); +} + +inline lua_Number luax_optnumberclamped(lua_State *L, int idx, double minv, double maxv, double def) +{ + return std::min(std::max(luaL_optnumber(L, idx, def), minv), maxv); +} + inline lua_Number luax_checknumberclamped01(lua_State *L, int idx) { return std::min(std::max(luaL_checknumber(L, idx), 0.0), 1.0); diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index 597edcd5c..7c3a42c95 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -73,7 +73,7 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexforma , rangeCount(-1) { setupAttachedAttributes(); - calculateAttributeSizes(); + calculateAttributeSizes(gfx); vertexCount = datasize / vertexStride; indexDataType = vertex::getIndexDataTypeFromMax(vertexCount); @@ -103,7 +103,7 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexforma throw love::Exception("Invalid number of vertices (%d).", vertexcount); setupAttachedAttributes(); - calculateAttributeSizes(); + calculateAttributeSizes(gfx); size_t buffersize = vertexCount * vertexStride; @@ -143,8 +143,10 @@ void Mesh::setupAttachedAttributes() } } -void Mesh::calculateAttributeSizes() +void Mesh::calculateAttributeSizes(Graphics *gfx) { + bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3]; + size_t stride = 0; for (const AttribFormat &format : vertexFormat) @@ -158,6 +160,9 @@ void Mesh::calculateAttributeSizes() if (size % 4 != 0) throw love::Exception("Vertex attributes must have enough components to be a multiple of 32 bits."); + if (vertex::isDataTypeInteger(format.type) && !supportsGLSL3) + throw love::Exception("Integer vertex attribute data types require GLSL 3 support."); + // Total size in bytes of each attribute in a single vertex. attributeSizes.push_back(size); stride += size; diff --git a/src/modules/graphics/Mesh.h b/src/modules/graphics/Mesh.h index cfbb2b6c5..148fc150d 100644 --- a/src/modules/graphics/Mesh.h +++ b/src/modules/graphics/Mesh.h @@ -187,7 +187,7 @@ private: }; void setupAttachedAttributes(); - void calculateAttributeSizes(); + void calculateAttributeSizes(Graphics *gfx); size_t getAttributeOffset(size_t attribindex) const; std::vector vertexFormat; diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 19acec97c..28862602e 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -627,18 +627,43 @@ GLenum OpenGL::getGLIndexDataType(IndexDataType type) } } -GLenum OpenGL::getGLVertexDataType(vertex::DataType type, GLboolean &normalized) +GLenum OpenGL::getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat) { normalized = GL_FALSE; + intformat = false; switch (type) { + case vertex::DATA_SNORM8: + normalized = GL_TRUE; + return GL_BYTE; case vertex::DATA_UNORM8: normalized = GL_TRUE; return GL_UNSIGNED_BYTE; + case vertex::DATA_INT8: + intformat = true; + return GL_BYTE; + case vertex::DATA_UINT8: + intformat = true; + return GL_UNSIGNED_BYTE; + case vertex::DATA_SNORM16: + normalized = GL_TRUE; + return GL_SHORT; case vertex::DATA_UNORM16: normalized = GL_TRUE; return GL_UNSIGNED_SHORT; + case vertex::DATA_INT16: + intformat = true; + return GL_SHORT; + case vertex::DATA_UINT16: + intformat = true; + return GL_UNSIGNED_SHORT; + case vertex::DATA_INT32: + intformat = true; + return GL_INT; + case vertex::DATA_UINT32: + intformat = true; + return GL_UNSIGNED_INT; case vertex::DATA_FLOAT: normalized = GL_FALSE; return GL_FLOAT; @@ -718,12 +743,17 @@ void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const ver glVertexAttribDivisor(i, divisor); GLboolean normalized = GL_FALSE; - GLenum gltype = getGLVertexDataType(attrib.type, normalized); + bool intformat = false; + GLenum gltype = getGLVertexDataType(attrib.type, normalized, intformat); const void *offsetpointer = reinterpret_cast(bufferinfo.offset + attrib.offsetFromVertex); bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle()); - glVertexAttribPointer(i, attrib.components, gltype, normalized, layout.stride, offsetpointer); + + if (intformat) + glVertexAttribIPointer(i, attrib.components, gltype, layout.stride, offsetpointer); + else + glVertexAttribPointer(i, attrib.components, gltype, normalized, layout.stride, offsetpointer); } i++; diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 88fda709e..8dd975cec 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -404,7 +404,7 @@ public: static GLenum getGLPrimitiveType(PrimitiveType type); static GLenum getGLBufferType(BufferType type); static GLenum getGLIndexDataType(IndexDataType type); - static GLenum getGLVertexDataType(vertex::DataType type, GLboolean &normalized); + static GLenum getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat); static GLenum getGLBufferUsage(vertex::Usage usage); static GLenum getGLTextureType(TextureType type); static GLint getGLWrapMode(Texture::WrapMode wmode); diff --git a/src/modules/graphics/vertex.cpp b/src/modules/graphics/vertex.cpp index af5d56a11..e979af478 100644 --- a/src/modules/graphics/vertex.cpp +++ b/src/modules/graphics/vertex.cpp @@ -41,28 +41,17 @@ size_t getFormatStride(CommonFormat format) { switch (format) { - case CommonFormat::NONE: - return 0; - case CommonFormat::XYf: - return sizeof(float) * 2; - case CommonFormat::XYZf: - return sizeof(float) * 3; - case CommonFormat::RGBAub: - return sizeof(uint8) * 4; - case CommonFormat::STf_RGBAub: - return sizeof(STf_RGBAub); - case CommonFormat::STPf_RGBAub: - return sizeof(STPf_RGBAub); - case CommonFormat::XYf_STf: - return sizeof(XYf_STf); - case CommonFormat::XYf_STPf: - return sizeof(XYf_STPf); - case CommonFormat::XYf_STf_RGBAub: - return sizeof(XYf_STf_RGBAub); - case CommonFormat::XYf_STus_RGBAub: - return sizeof(XYf_STus_RGBAub); - case CommonFormat::XYf_STPf_RGBAub: - return sizeof(XYf_STPf_RGBAub); + case CommonFormat::NONE: return 0; + case CommonFormat::XYf: return sizeof(float) * 2; + case CommonFormat::XYZf: return sizeof(float) * 3; + case CommonFormat::RGBAub: return sizeof(uint8) * 4; + case CommonFormat::STf_RGBAub: return sizeof(STf_RGBAub); + case CommonFormat::STPf_RGBAub: return sizeof(STPf_RGBAub); + case CommonFormat::XYf_STf: return sizeof(XYf_STf); + case CommonFormat::XYf_STPf: return sizeof(XYf_STPf); + case CommonFormat::XYf_STf_RGBAub: return sizeof(XYf_STf_RGBAub); + case CommonFormat::XYf_STus_RGBAub: return sizeof(XYf_STus_RGBAub); + case CommonFormat::XYf_STPf_RGBAub: return sizeof(XYf_STPf_RGBAub); } return 0; } @@ -118,12 +107,9 @@ size_t getIndexDataSize(IndexDataType type) { switch (type) { - case INDEX_UINT16: - return sizeof(uint16); - case INDEX_UINT32: - return sizeof(uint32); - default: - return 0; + case INDEX_UINT16: return sizeof(uint16); + case INDEX_UINT32: return sizeof(uint32); + default: return 0; } } @@ -131,15 +117,41 @@ size_t getDataTypeSize(DataType datatype) { switch (datatype) { + case DATA_SNORM8: case DATA_UNORM8: + case DATA_INT8: + case DATA_UINT8: return sizeof(uint8); + case DATA_SNORM16: case DATA_UNORM16: + case DATA_INT16: + case DATA_UINT16: return sizeof(uint16); + case DATA_INT32: + case DATA_UINT32: + return sizeof(uint32); case DATA_FLOAT: return sizeof(float); - default: + case DATA_MAX_ENUM: return 0; } + return 0; +} + +bool isDataTypeInteger(DataType datatype) +{ + switch (datatype) + { + case DATA_INT8: + case DATA_UINT8: + case DATA_INT16: + case DATA_UINT16: + case DATA_INT32: + case DATA_UINT32: + return true; + default: + return false; + } } IndexDataType getIndexDataTypeFromMax(size_t maxvalue) @@ -323,8 +335,16 @@ static StringMap attributeSteps(attributeStepEntri static StringMap::Entry dataTypeEntries[] = { - { "byte", DATA_UNORM8 }, // Legacy / more user-friendly name... + { "snorm8", DATA_SNORM8 }, + { "unorm8", DATA_UNORM8 }, + { "int8", DATA_INT8 }, + { "uint8", DATA_UINT8 }, + { "snorm16", DATA_SNORM16 }, { "unorm16", DATA_UNORM16 }, + { "int16", DATA_INT16 }, + { "uint16", DATA_UINT16 }, + { "int32", DATA_INT32 }, + { "uint32", DATA_UINT32 }, { "float", DATA_FLOAT }, }; diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index 7a8dfda0f..8d4e75dcc 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -106,9 +106,21 @@ enum Usage enum DataType { + DATA_SNORM8, DATA_UNORM8, + DATA_INT8, + DATA_UINT8, + + DATA_SNORM16, DATA_UNORM16, + DATA_INT16, + DATA_UINT16, + + DATA_INT32, + DATA_UINT32, + DATA_FLOAT, + DATA_MAX_ENUM }; @@ -296,6 +308,7 @@ inline CommonFormat getSinglePositionFormat(bool is2D) size_t getIndexDataSize(IndexDataType type); size_t getDataTypeSize(DataType datatype); +bool isDataTypeInteger(DataType datatype); IndexDataType getIndexDataTypeFromMax(size_t maxvalue); diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 9c9b147d4..f42938b1f 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -1529,7 +1529,9 @@ static Mesh *newCustomMesh(lua_State *L) format.name = luaL_checkstring(L, -3); const char *tname = luaL_checkstring(L, -2); - if (!vertex::getConstant(tname, format.type)) + if (strcmp(tname, "byte") == 0) // Legacy name. + format.type = vertex::DATA_UNORM8; + else if (!vertex::getConstant(tname, format.type)) { luax_enumerror(L, "Mesh vertex data type name", vertex::getConstants(format.type), tname); return nullptr; diff --git a/src/modules/graphics/wrap_Mesh.cpp b/src/modules/graphics/wrap_Mesh.cpp index 6e9c6a308..9e238dec1 100644 --- a/src/modules/graphics/wrap_Mesh.cpp +++ b/src/modules/graphics/wrap_Mesh.cpp @@ -37,91 +37,135 @@ Mesh *luax_checkmesh(lua_State *L, int idx) return luax_checktype(L, idx); } -static inline size_t writeUnorm8Data(lua_State *L, int startidx, int components, char *data) +static const double defaultComponents[] = {0.0, 0.0, 0.0, 1.0}; + +template +static inline size_t writeData(lua_State *L, int startidx, int components, char *data) { - uint8 *componentdata = (uint8 *) data; + auto componentdata = (T *) data; for (int i = 0; i < components; i++) - componentdata[i] = (uint8) (luax_optnumberclamped01(L, startidx + i, 1.0) * 255.0); + componentdata[i] = (T) (luaL_optnumber(L, startidx + i, defaultComponents[i])); - return sizeof(uint8) * components; + return sizeof(T) * components; } -static inline size_t writeUnorm16Data(lua_State *L, int startidx, int components, char *data) +template +static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data) { - uint16 *componentdata = (uint16 *) data; + auto componentdata = (T *) data; + auto maxval = std::numeric_limits::max(); for (int i = 0; i < components; i++) - componentdata[i] = (uint16) (luax_optnumberclamped01(L, startidx + i, 1.0) * 65535.0); + componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval); - return sizeof(uint16) * components; + return sizeof(T) * components; } -static inline size_t writeFloatData(lua_State *L, int startidx, int components, char *data) +template +static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data) { - float *componentdata = (float *) data; + auto componentdata = (T *) data; + auto maxval = std::numeric_limits::max(); for (int i = 0; i < components; i++) - componentdata[i] = (float) luaL_optnumber(L, startidx + i, 0); + componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval); - return sizeof(float) * components; + return sizeof(T) * components; } char *luax_writeAttributeData(lua_State *L, int startidx, vertex::DataType type, int components, char *data) { switch (type) { + case vertex::DATA_SNORM8: + return data + writeSNormData(L, startidx, components, data); case vertex::DATA_UNORM8: - return data + writeUnorm8Data(L, startidx, components, data); + return data + writeUNormData(L, startidx, components, data); + case vertex::DATA_INT8: + return data + writeData(L, startidx, components, data); + case vertex::DATA_UINT8: + return data + writeData(L, startidx, components, data); + case vertex::DATA_SNORM16: + return data + writeSNormData(L, startidx, components, data); case vertex::DATA_UNORM16: - return data + writeUnorm16Data(L, startidx, components, data); + return data + writeUNormData(L, startidx, components, data); + case vertex::DATA_INT16: + return data + writeData(L, startidx, components, data); + case vertex::DATA_UINT16: + return data + writeData(L, startidx, components, data); + case vertex::DATA_INT32: + return data + writeData(L, startidx, components, data); + case vertex::DATA_UINT32: + return data + writeData(L, startidx, components, data); case vertex::DATA_FLOAT: - return data + writeFloatData(L, startidx, components, data); + return data + writeData(L, startidx, components, data); default: return data; } } -static inline size_t readUnorm8Data(lua_State *L, int components, const char *data) +template +static inline size_t readData(lua_State *L, int components, const char *data) { - const uint8 *componentdata = (const uint8 *) data; + auto componentdata = (const T *) data; for (int i = 0; i < components; i++) - lua_pushnumber(L, (lua_Number) componentdata[i] / 255.0); + lua_pushnumber(L, (lua_Number) componentdata[i]); - return sizeof(uint8) * components; + return sizeof(T) * components; } -static inline size_t readUnorm16Data(lua_State *L, int components, const char *data) +template +static inline size_t readSNormData(lua_State *L, int components, const char *data) { - const uint16 *componentdata = (const uint16 *) data; + auto componentdata = (const T *) data; + auto maxval = std::numeric_limits::max(); for (int i = 0; i < components; i++) - lua_pushnumber(L, (lua_Number) componentdata[i] / 65535.0); + lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval)); - return sizeof(uint16) * components; + return sizeof(T) * components; } -static inline size_t readFloatData(lua_State *L, int components, const char *data) +template +static inline size_t readUNormData(lua_State *L, int components, const char *data) { - const float *componentdata = (const float *) data; + auto componentdata = (const T *) data; + auto maxval = std::numeric_limits::max(); for (int i = 0; i < components; i++) - lua_pushnumber(L, componentdata[i]); + lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval); - return sizeof(float) * components; + return sizeof(T) * components; } const char *luax_readAttributeData(lua_State *L, vertex::DataType type, int components, const char *data) { switch (type) { + case vertex::DATA_SNORM8: + return data + readSNormData(L, components, data); case vertex::DATA_UNORM8: - return data + readUnorm8Data(L, components, data); + return data + readUNormData(L, components, data); + case vertex::DATA_INT8: + return data + readData(L, components, data); + case vertex::DATA_UINT8: + return data + readData(L, components, data); + case vertex::DATA_SNORM16: + return data + readSNormData(L, components, data); case vertex::DATA_UNORM16: - return data + readUnorm16Data(L, components, data); + return data + readUNormData(L, components, data); + case vertex::DATA_INT16: + return data + readData(L, components, data); + case vertex::DATA_UINT16: + return data + readData(L, components, data); + case vertex::DATA_INT32: + return data + readData(L, components, data); + case vertex::DATA_UINT32: + return data + readData(L, components, data); case vertex::DATA_FLOAT: - return data + readFloatData(L, components, data); + return data + readData(L, components, data); default: return data; }