Browse Source

Meshes expose vertex buffers, part 1

(compiles, but is currently half-implemented and broken)
Alex Szpakowski 5 years ago
parent
commit
25d9ae2ba0

+ 24 - 5
src/modules/graphics/Buffer.cpp

@@ -29,7 +29,9 @@ namespace graphics
 love::Type Buffer::type("GraphicsBuffer", &Object::type);
 
 Buffer::Buffer(size_t size, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags)
-	: size(size)
+	: arrayLength(0)
+	, arrayStride(0)
+	, size(size)
 	, typeFlags(typeflags)
 	, usage(usage)
 	, mapFlags(mapflags)
@@ -37,12 +39,14 @@ Buffer::Buffer(size_t size, BufferTypeFlags typeflags, BufferUsage usage, uint32
 {
 }
 
-Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMember> &format, size_t arraylength)
+Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMember> &dataMembers, size_t arraylength)
 	: Buffer(0, settings.typeFlags, settings.usage, settings.mapFlags)
 {
-	if (format.size() == 0)
+	if (dataMembers.size() == 0)
 		throw love::Exception("Data format must contain values.");
 
+	this->dataMembers = dataMembers;
+
 	bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
 
 	bool uniformbuffer = settings.typeFlags & BUFFERFLAG_UNIFORM;
@@ -50,13 +54,13 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMe
 	bool vertexbuffer = settings.typeFlags & BUFFERFLAG_VERTEX;
 	bool ssbuffer = settings.typeFlags & BUFFERFLAG_SHADER_STORAGE;
 
-	if (indexbuffer && format.size() > 1)
+	if (indexbuffer && dataMembers.size() > 1)
 		throw love::Exception("test");
 
 	size_t offset = 0;
 	size_t stride = 0;
 
-	for (const auto &member : format)
+	for (const auto &member : dataMembers)
 	{
 		DataFormat format = member.format;
 		const DataFormatInfo &info = getDataFormatInfo(format);
@@ -85,11 +89,26 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMe
 				throw love::Exception("");
 		}
 	}
+
+	this->arrayLength = arraylength;
+	this->arrayStride = stride;
+	this->size = stride * arraylength;
 }
 
 Buffer::~Buffer()
 {
 }
 
+int Buffer::getDataMemberIndex(const std::string &name) const
+{
+	for (size_t i = 0; i < dataMembers.size(); i++)
+	{
+		if (dataMembers[i].name == name)
+			return (int) i;
+	}
+
+	return -1;
+}
+
 } // graphics
 } // love

+ 10 - 2
src/modules/graphics/Buffer.h

@@ -69,7 +69,7 @@ public:
 	};
 
 	Buffer(size_t size, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags);
-	Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMember> &format, size_t arraylength);
+	Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMember> &members, size_t arraylength);
 	virtual ~Buffer();
 
 	size_t getSize() const { return size; }
@@ -78,6 +78,13 @@ public:
 	bool isMapped() const { return mapped; }
 	uint32 getMapFlags() const { return mapFlags; }
 
+	size_t getArrayLength() const { return arrayLength; }
+	size_t getArrayStride() const { return arrayStride; }
+	const std::vector<DataMember> &getDataMembers() const { return dataMembers; }
+	const DataMember &getDataMember(int index) const { return dataMembers[index]; }
+	size_t getMemberOffset(int index) const { return memberOffsets[index]; }
+	int getDataMemberIndex(const std::string &name) const;
+
 	/**
 	 * Map the Buffer to client memory.
 	 *
@@ -138,8 +145,9 @@ public:
 
 protected:
 
-	std::vector<DataMember> format;
+	std::vector<DataMember> dataMembers;
 	std::vector<size_t> memberOffsets;
+	size_t arrayLength;
 	size_t arrayStride;
 
 	// The size of the buffer, in bytes.

+ 29 - 34
src/modules/graphics/Mesh.cpp

@@ -125,8 +125,8 @@ Mesh::~Mesh()
 
 	for (const auto &attrib : attachedAttributes)
 	{
-		if (attrib.second.mesh != this)
-			attrib.second.mesh->release();
+		if (attrib.second.buffer != nullptr && attrib.second.buffer != vertexBuffer.get())
+			attrib.second.buffer->release();
 	}
 }
 
@@ -139,7 +139,7 @@ void Mesh::setupAttachedAttributes()
 		if (attachedAttributes.find(name) != attachedAttributes.end())
 			throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
 
-		attachedAttributes[name] = {this, (int) i, STEP_PER_VERTEX, true};
+		attachedAttributes[name] = {nullptr, (int) i, STEP_PER_VERTEX, true};
 	}
 }
 
@@ -260,6 +260,11 @@ size_t Mesh::getVertexStride() const
 	return vertexStride;
 }
 
+Buffer *Mesh::getVertexBuffer() const
+{
+	return vertexBuffer;
+}
+
 const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
 {
 	return vertexFormat;
@@ -305,23 +310,15 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
 	return it->second.enabled;
 }
 
-void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step)
+void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step)
 {
+	if ((buffer->getTypeFlags() & BUFFER_VERTEX) == 0)
+		throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
+
 	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 	if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
 		throw love::Exception("Vertex attribute instancing is not supported on this system.");
 
-	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 = {};
 
@@ -331,34 +328,33 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::strin
 	else if (attachedAttributes.size() + 1 > VertexAttributes::MAX)
 		throw love::Exception("A maximum of %d attributes can be attached at once.", VertexAttributes::MAX);
 
-	newattrib.mesh = mesh;
-	newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
-	newattrib.index = mesh->getAttributeIndex(attachname);
+	newattrib.buffer = buffer;
+	newattrib.enabled = oldattrib.buffer ? oldattrib.enabled : true;
+	newattrib.index = buffer->getDataMemberIndex(attachname);
 	newattrib.step = step;
 
 	if (newattrib.index < 0)
-		throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
+		throw love::Exception("The specified vertex buffer does not have a vertex attribute named '%s'", attachname.c_str());
 
-	if (newattrib.mesh != this)
-		newattrib.mesh->retain();
+	newattrib.buffer->retain();
 
 	attachedAttributes[name] = newattrib;
 
-	if (oldattrib.mesh && oldattrib.mesh != this)
-		oldattrib.mesh->release();
+	if (oldattrib.buffer)
+		oldattrib.buffer->release();
 }
 
 bool Mesh::detachAttribute(const std::string &name)
 {
 	auto it = attachedAttributes.find(name);
 
-	if (it != attachedAttributes.end() && it->second.mesh != this)
+	if (it != attachedAttributes.end() && it->second.buffer != vertexBuffer.get())
 	{
-		it->second.mesh->release();
+		it->second.buffer->release();
 		attachedAttributes.erase(it);
 
 		if (getAttributeIndex(name) != -1)
-			attachAttribute(name, this, name);
+			attachAttribute(name, vertexBuffer, name);
 
 		return true;
 	}
@@ -590,7 +586,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
 		if (!attrib.second.enabled)
 			continue;
 
-		Mesh *mesh = attrib.second.mesh;
+		Buffer *buffer = attrib.second.buffer;
 		int attributeindex = -1;
 
 		// If the attribute is one of the LOVE-defined ones, use the constant
@@ -604,19 +600,18 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
 		if (attributeindex >= 0)
 		{
 			// Make sure the buffer isn't mapped (sends data to GPU if needed.)
-			mesh->vertexBuffer->unmap();
+			buffer->unmap();
 
-			const auto &formats = mesh->getVertexFormat();
-			const auto &format = formats[attrib.second.index];
+			const auto &member = buffer->getDataMember(attrib.second.index);
 
-			uint16 offset = (uint16) mesh->getAttributeOffset(attrib.second.index);
-			uint16 stride = (uint16) mesh->getVertexStride();
+			uint16 offset = (uint16) buffer->getMemberOffset(attrib.second.index);
+			uint16 stride = (uint16) buffer->getArrayStride();
 
-			attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers);
+			attributes.set(attributeindex, member.format, offset, activebuffers);
 			attributes.setBufferLayout(activebuffers, stride, attrib.second.step);
 
 			// TODO: Ideally we want to reuse buffers with the same stride+step.
-			buffers.set(activebuffers, mesh->vertexBuffer, 0);
+			buffers.set(activebuffers, buffer, 0);
 			activebuffers++;
 		}
 	}

+ 6 - 4
src/modules/graphics/Mesh.h

@@ -92,6 +92,8 @@ public:
 	 **/
 	size_t getVertexStride() const;
 
+	Buffer *getVertexBuffer() const;
+
 	/**
 	 * Gets the format of each vertex attribute stored in the Mesh.
 	 **/
@@ -106,10 +108,10 @@ public:
 	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.
+	 * Attaches a vertex attribute from another vertex buffer to this Mesh. The
+	 * attribute will be used when drawing this Mesh.
 	 **/
-	void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
+	void attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
 	bool detachAttribute(const std::string &name);
 
 	void *mapVertexData();
@@ -180,7 +182,7 @@ private:
 
 	struct AttachedAttribute
 	{
-		Mesh *mesh;
+		Buffer *buffer;
 		int index;
 		AttributeStep step;
 		bool enabled;

+ 18 - 16
src/modules/graphics/SpriteBatch.cpp

@@ -244,24 +244,27 @@ int SpriteBatch::getBufferSize() const
 	return size;
 }
 
-void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
+void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
 {
+	if ((buffer->getTypeFlags() & BUFFER_VERTEX) == 0)
+		throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a SpriteBatch vertex attribute.");
+
 	AttachedAttribute oldattrib = {};
 	AttachedAttribute newattrib = {};
 
-	if (mesh->getVertexCount() < (size_t) next * 4)
-		throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4);
+	if (buffer->getArrayLength() < (size_t) next * 4)
+		throw love::Exception("Buffer has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4);
 
 	auto it = attached_attributes.find(name);
 	if (it != attached_attributes.end())
 		oldattrib = it->second;
 
-	newattrib.index = mesh->getAttributeIndex(name);
+	newattrib.index = buffer->getDataMemberIndex(name);
 
 	if (newattrib.index < 0)
-		throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
+		throw love::Exception("The specified Buffer does not have a vertex attribute named '%s'", name.c_str());
 
-	newattrib.mesh = mesh;
+	newattrib.buffer = buffer;
 
 	attached_attributes[name] = newattrib;
 }
@@ -327,12 +330,12 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 
 	for (const auto &it : attached_attributes)
 	{
-		Mesh *mesh = it.second.mesh.get();
+		Buffer *buffer = it.second.buffer.get();
 
 		// We have to do this check here as wll because setBufferSize can be
 		// called after attachAttribute.
-		if (mesh->getVertexCount() < (size_t) next * 4)
-			throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
+		if (buffer->getArrayLength() < (size_t) next * 4)
+			throw love::Exception("Buffer with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
 
 		int attributeindex = -1;
 
@@ -347,19 +350,18 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 		if (attributeindex >= 0)
 		{
 			// Make sure the buffer isn't mapped (sends data to GPU if needed.)
-			mesh->vertexBuffer->unmap();
+			buffer->unmap();
 
-			const auto &formats = mesh->getVertexFormat();
-			const auto &format = formats[it.second.index];
+			const auto &member = buffer->getDataMember(it.second.index);
 
-			uint16 offset = (uint16) mesh->getAttributeOffset(it.second.index);
-			uint16 stride = (uint16) mesh->getVertexStride();
+			uint16 offset = (uint16) buffer->getMemberOffset(it.second.index);
+			uint16 stride = (uint16) buffer->getArrayStride();
 
-			attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers);
+			attributes.set(attributeindex, member.format, offset, activebuffers);
 			attributes.setBufferLayout(activebuffers, stride);
 
 			// TODO: We should reuse buffer bindings with the same buffer+stride+step.
-			buffers.set(activebuffers, mesh->vertexBuffer, 0);
+			buffers.set(activebuffers, buffer, 0);
 			activebuffers++;
 		}
 	}

+ 2 - 2
src/modules/graphics/SpriteBatch.h

@@ -93,7 +93,7 @@ public:
 	 * Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
 	 * The vertex attribute will be used when drawing the SpriteBatch.
 	 **/
-	void attachAttribute(const std::string &name, Mesh *mesh);
+	void attachAttribute(const std::string &name, Buffer *buffer);
 
 	void setDrawRange(int start, int count);
 	void setDrawRange();
@@ -106,7 +106,7 @@ private:
 
 	struct AttachedAttribute
 	{
-		StrongRef<Mesh> mesh;
+		StrongRef<Buffer> buffer;
 		int index;
 	};
 

+ 126 - 21
src/modules/graphics/opengl/OpenGL.cpp

@@ -631,47 +631,151 @@ GLenum OpenGL::getGLIndexDataType(IndexDataType type)
 	}
 }
 
-GLenum OpenGL::getGLVertexDataType(DataType type, GLboolean &normalized, bool &intformat)
+GLenum OpenGL::getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat)
 {
 	normalized = GL_FALSE;
 	intformat = false;
+	components = 1;
 
-	switch (type)
+	switch (format)
 	{
-	case DATA_SNORM8:
+	case DATAFORMAT_FLOAT:
+		components = 1;
+		return GL_FLOAT;
+	case DATAFORMAT_FLOAT_VEC2:
+		components = 2;
+		return GL_FLOAT;
+	case DATAFORMAT_FLOAT_VEC3:
+		components = 3;
+		return GL_FLOAT;
+	case DATAFORMAT_FLOAT_VEC4:
+		components = 4;
+		return GL_FLOAT;
+
+	case DATAFORMAT_FLOAT_MAT2X2:
+	case DATAFORMAT_FLOAT_MAT2X3:
+	case DATAFORMAT_FLOAT_MAT2X4:
+	case DATAFORMAT_FLOAT_MAT3X2:
+	case DATAFORMAT_FLOAT_MAT3X3:
+	case DATAFORMAT_FLOAT_MAT3X4:
+	case DATAFORMAT_FLOAT_MAT4X2:
+	case DATAFORMAT_FLOAT_MAT4X3:
+	case DATAFORMAT_FLOAT_MAT4X4:
+		return GL_ZERO;
+
+	case DATAFORMAT_INT32:
+		components = 1;
+		intformat = true;
+		return GL_INT;
+	case DATAFORMAT_INT32_VEC2:
+		components = 2;
+		intformat = true;
+		return GL_INT;
+	case DATAFORMAT_INT32_VEC3:
+		components = 3;
+		intformat = true;
+		return GL_INT;
+	case DATAFORMAT_INT32_VEC4:
+		components = 4;
+		intformat = true;
+		return GL_INT;
+
+	case DATAFORMAT_UINT32:
+		components = 1;
+		intformat = true;
+		return GL_UNSIGNED_INT;
+	case DATAFORMAT_UINT32_VEC2:
+		components = 2;
+		intformat = true;
+		return GL_UNSIGNED_INT;
+	case DATAFORMAT_UINT32_VEC3:
+		components = 3;
+		intformat = true;
+		return GL_UNSIGNED_INT;
+	case DATAFORMAT_UINT32_VEC4:
+		components = 4;
+		intformat = true;
+		return GL_UNSIGNED_INT;
+
+	case DATAFORMAT_SNORM8_VEC4:
+		components = 4;
 		normalized = GL_TRUE;
 		return GL_BYTE;
-	case DATA_UNORM8:
+
+	case DATAFORMAT_UNORM8_VEC4:
+		components = 4;
 		normalized = GL_TRUE;
 		return GL_UNSIGNED_BYTE;
-	case DATA_INT8:
+
+	case DATAFORMAT_INT8_VEC4:
+		components = 4;
 		intformat = true;
 		return GL_BYTE;
-	case DATA_UINT8:
+
+	case DATAFORMAT_UINT8_VEC4:
+		components = 4;
 		intformat = true;
 		return GL_UNSIGNED_BYTE;
-	case DATA_SNORM16:
+
+	case DATAFORMAT_SNORM16:
+		components = 1;
 		normalized = GL_TRUE;
 		return GL_SHORT;
-	case DATA_UNORM16:
+	case DATAFORMAT_SNORM16_VEC2:
+		components = 2;
+		normalized = GL_TRUE;
+		return GL_BYTE;
+	case DATAFORMAT_SNORM16_VEC4:
+		components = 4;
+		normalized = GL_TRUE;
+		return GL_BYTE;
+
+	case DATAFORMAT_UNORM16:
+		components = 1;
+		normalized = GL_TRUE;
+		return GL_UNSIGNED_SHORT;
+	case DATAFORMAT_UNORM16_VEC2:
+		components = 2;
 		normalized = GL_TRUE;
 		return GL_UNSIGNED_SHORT;
-	case DATA_INT16:
+	case DATAFORMAT_UNORM16_VEC4:
+		components = 4;
+		normalized = GL_TRUE;
+		return GL_UNSIGNED_SHORT;
+
+	case DATAFORMAT_INT16:
+		components = 1;
 		intformat = true;
 		return GL_SHORT;
-	case DATA_UINT16:
+	case DATAFORMAT_INT16_VEC2:
+		components = 2;
+		intformat = true;
+		return GL_SHORT;
+	case DATAFORMAT_INT16_VEC4:
+		components = 4;
+		intformat = true;
+		return GL_SHORT;
+
+	case DATAFORMAT_UINT16:
+		components = 1;
 		intformat = true;
 		return GL_UNSIGNED_SHORT;
-	case DATA_INT32:
+	case DATAFORMAT_UINT16_VEC2:
+		components = 2;
 		intformat = true;
-		return GL_INT;
-	case DATA_UINT32:
+		return GL_UNSIGNED_SHORT;
+	case DATAFORMAT_UINT16_VEC4:
+		components = 4;
 		intformat = true;
-		return GL_UNSIGNED_INT;
-	case DATA_FLOAT:
-		normalized = GL_FALSE;
-		return GL_FLOAT;
-	case DATA_MAX_ENUM:
+		return GL_UNSIGNED_SHORT;
+
+	case DATAFORMAT_BOOL:
+	case DATAFORMAT_BOOL_VEC2:
+	case DATAFORMAT_BOOL_VEC3:
+	case DATAFORMAT_BOOL_VEC4:
+		return GL_ZERO;
+
+	case DATAFORMAT_MAX_ENUM:
 		return GL_ZERO;
 	}
 
@@ -746,18 +850,19 @@ void OpenGL::setVertexAttributes(const VertexAttributes &attributes, const Buffe
 			if ((state.instancedAttribArrays & bit) ^ divisorbit)
 				glVertexAttribDivisor(i, divisor);
 
+			int components = 0;
 			GLboolean normalized = GL_FALSE;
 			bool intformat = false;
-			GLenum gltype = getGLVertexDataType(attrib.type, normalized, intformat);
+			GLenum gltype = getGLVertexDataType(attrib.format, components, normalized, intformat);
 
 			const void *offsetpointer = reinterpret_cast<void*>(bufferinfo.offset + attrib.offsetFromVertex);
 
 			bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
 
 			if (intformat)
-				glVertexAttribIPointer(i, attrib.components, gltype, layout.stride, offsetpointer);
+				glVertexAttribIPointer(i, components, gltype, layout.stride, offsetpointer);
 			else
-				glVertexAttribPointer(i, attrib.components, gltype, normalized, layout.stride, offsetpointer);
+				glVertexAttribPointer(i, components, gltype, normalized, layout.stride, offsetpointer);
 		}
 
 		i++;

+ 1 - 1
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(DataType type, GLboolean &normalized, bool &intformat);
+	static GLenum getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat);
 	static GLenum getGLBufferUsage(BufferUsage usage);
 	static GLenum getGLTextureType(TextureType type);
 	static GLint getGLWrapMode(Texture::WrapMode wmode);

+ 20 - 20
src/modules/graphics/vertex.cpp

@@ -309,44 +309,44 @@ void VertexAttributes::setCommonFormat(CommonFormat format, uint8 bufferindex)
 	case CommonFormat::NONE:
 		break;
 	case CommonFormat::XYf:
-		set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
+		set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
 		break;
 	case CommonFormat::XYZf:
-		set(ATTRIB_POS, DATA_FLOAT, 3, 0, bufferindex);
+		set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC3, 0, bufferindex);
 		break;
 	case CommonFormat::RGBAub:
-		set(ATTRIB_COLOR, DATA_UNORM8, 4, 0, bufferindex);
+		set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, 0, bufferindex);
 		break;
 	case CommonFormat::STf_RGBAub:
-		set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, 0, bufferindex);
-		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2), bufferindex);
+		set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
+		set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 2), bufferindex);
 		break;
 	case CommonFormat::STPf_RGBAub:
-		set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, 0, bufferindex);
-		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 3), bufferindex);
+		set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, 0, bufferindex);
+		set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 3), bufferindex);
 		break;
 	case CommonFormat::XYf_STf:
-		set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
-		set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), bufferindex);
+		set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
+		set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, uint16(sizeof(float) * 2), bufferindex);
 		break;
 	case CommonFormat::XYf_STPf:
-		set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
-		set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), bufferindex);
+		set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
+		set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, uint16(sizeof(float) * 2), bufferindex);
 		break;
 	case CommonFormat::XYf_STf_RGBAub:
-		set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
-		set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), bufferindex);
-		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 4), bufferindex);
+		set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
+		set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, uint16(sizeof(float) * 2), bufferindex);
+		set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 4), bufferindex);
 		break;
 	case CommonFormat::XYf_STus_RGBAub:
-		set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
-		set(ATTRIB_TEXCOORD, DATA_UNORM16, 2, uint16(sizeof(float) * 2), bufferindex);
-		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), bufferindex);
+		set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
+		set(ATTRIB_TEXCOORD, DATAFORMAT_UNORM16_VEC2, uint16(sizeof(float) * 2), bufferindex);
+		set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), bufferindex);
 		break;
 	case CommonFormat::XYf_STPf_RGBAub:
-		set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex);
-		set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), bufferindex);
-		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 5), bufferindex);
+		set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex);
+		set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, uint16(sizeof(float) * 2), bufferindex);
+		set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 5), bufferindex);
 		break;
 	}
 }

+ 3 - 5
src/modules/graphics/vertex.h

@@ -320,8 +320,7 @@ struct BufferBindings
 struct VertexAttributeInfo
 {
 	uint8 bufferIndex;
-	DataType type : 4;
-	uint8 components : 4;
+	DataFormat format : 8;
 	uint16 offsetFromVertex;
 };
 
@@ -346,13 +345,12 @@ struct VertexAttributes
 		setCommonFormat(format, bufferindex);
 	}
 
-	void set(uint32 index, DataType type, uint8 components, uint16 offsetfromvertex, uint8 bufferindex)
+	void set(uint32 index, DataFormat format, uint16 offsetfromvertex, uint8 bufferindex)
 	{
 		enableBits |= (1u << index);
 
 		attribs[index].bufferIndex = bufferindex;
-		attribs[index].type = type;
-		attribs[index].components = components;
+		attribs[index].format = format;
 		attribs[index].offsetFromVertex = offsetfromvertex;
 	}
 

+ 12 - 2
src/modules/graphics/wrap_Mesh.cpp

@@ -416,7 +416,17 @@ 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);
+
+	Buffer *buffer = nullptr;
+	if (luax_istype(L, 3, Buffer::type))
+	{
+		buffer = luax_checktype<Buffer>(L, 3);
+	}
+	else
+	{
+		Mesh *mesh = luax_checkmesh(L, 3);
+		buffer = mesh->getVertexBuffer();
+	}
 
 	AttributeStep step = STEP_PER_VERTEX;
 	const char *stepstr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4);
@@ -425,7 +435,7 @@ int w_Mesh_attachAttribute(lua_State *L)
 
 	const char *attachname = luaL_optstring(L, 5, name);
 
-	luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname, step); });
+	luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, attachname, step); });
 	return 0;
 }
 

+ 12 - 2
src/modules/graphics/wrap_SpriteBatch.cpp

@@ -226,9 +226,19 @@ int w_SpriteBatch_attachAttribute(lua_State *L)
 {
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
 	const char *name = luaL_checkstring(L, 2);
-	Mesh *m = luax_checktype<Mesh>(L, 3);
 
-	luax_catchexcept(L, [&](){ t->attachAttribute(name, m); });
+	Buffer *buffer = nullptr;
+	if (luax_istype(L, 3, Buffer::type))
+	{
+		buffer = luax_checktype<Buffer>(L, 3);
+	}
+	else
+	{
+		Mesh *mesh = luax_checktype<Mesh>(L, 3);
+		buffer = mesh->getVertexBuffer();
+	}
+
+	luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer); });
 	return 0;
 }