mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
Move backend-agnostic Mesh and SpriteBatch code out of the opengl implementation folder.
--HG-- branch : minor
This commit is contained in:
@@ -694,6 +694,11 @@ void Graphics::draw(Texture *texture, Quad *quad, const Matrix4 &m)
|
||||
texture->drawq(this, quad, m);
|
||||
}
|
||||
|
||||
void Graphics::drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount)
|
||||
{
|
||||
mesh->drawInstanced(this, m, instancecount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Primitives (points, shapes, lines).
|
||||
**/
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "Font.h"
|
||||
#include "Shader.h"
|
||||
#include "Quad.h"
|
||||
#include "Mesh.h"
|
||||
#include "math/Transform.h"
|
||||
#include "font/Rasterizer.h"
|
||||
#include "video/VideoStream.h"
|
||||
@@ -50,6 +51,8 @@ class Reference;
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
class SpriteBatch;
|
||||
class ParticleSystem;
|
||||
class Text;
|
||||
class Video;
|
||||
class Buffer;
|
||||
@@ -314,6 +317,10 @@ public:
|
||||
|
||||
Quad *newQuad(Quad::Viewport v, double sw, double sh);
|
||||
|
||||
virtual SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage) = 0;
|
||||
|
||||
virtual ParticleSystem *newParticleSystem(Texture *texture, int size) = 0;
|
||||
|
||||
virtual Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::defaultFilter) = 0;
|
||||
|
||||
virtual Canvas *newCanvas(int width, int height, const Canvas::Settings &settings) = 0;
|
||||
@@ -324,6 +331,12 @@ public:
|
||||
|
||||
virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
|
||||
|
||||
virtual Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
|
||||
virtual Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
|
||||
|
||||
virtual Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
|
||||
virtual Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage) = 0;
|
||||
|
||||
virtual Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {}) = 0;
|
||||
|
||||
bool validateShader(bool gles, const Shader::ShaderSource &source, std::string &err);
|
||||
@@ -546,6 +559,7 @@ public:
|
||||
|
||||
void draw(Drawable *drawable, const Matrix4 &m);
|
||||
void draw(Texture *texture, Quad *quad, const Matrix4 &m);
|
||||
void drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount);
|
||||
|
||||
/**
|
||||
* Draws text at the specified coordinates
|
||||
|
||||
@@ -0,0 +1,642 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2017 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "Mesh.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "common/Exception.h"
|
||||
#include "Shader.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
static const char *getBuiltinAttribName(VertexAttribID attribid)
|
||||
{
|
||||
const char *name = "";
|
||||
vertex::getConstant(attribid, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
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, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
|
||||
|
||||
std::vector<Mesh::AttribFormat> Mesh::getDefaultVertexFormat()
|
||||
{
|
||||
// Corresponds to the love::Vertex struct.
|
||||
std::vector<Mesh::AttribFormat> vertexformat = {
|
||||
{getBuiltinAttribName(ATTRIB_POS), Mesh::DATA_FLOAT, 2},
|
||||
{getBuiltinAttribName(ATTRIB_TEXCOORD), Mesh::DATA_FLOAT, 2},
|
||||
{getBuiltinAttribName(ATTRIB_COLOR), Mesh::DATA_BYTE, 4},
|
||||
};
|
||||
|
||||
return vertexformat;
|
||||
}
|
||||
|
||||
love::Type Mesh::type("Mesh", &Drawable::type);
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage)
|
||||
: vertexFormat(vertexformat)
|
||||
, vbo(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, ibo(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, elementCount(0)
|
||||
, elementDataType(INDEX_UINT16)
|
||||
, drawMode(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
{
|
||||
setupAttachedAttributes();
|
||||
calculateAttributeSizes();
|
||||
|
||||
vertexCount = datasize / vertexStride;
|
||||
elementDataType = vertex::getIndexDataTypeFromMax(vertexCount);
|
||||
|
||||
if (vertexCount == 0)
|
||||
throw love::Exception("Data size is too small for specified vertex attribute formats.");
|
||||
|
||||
vbo = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage)
|
||||
: vertexFormat(vertexformat)
|
||||
, vbo(nullptr)
|
||||
, vertexCount((size_t) vertexcount)
|
||||
, vertexStride(0)
|
||||
, ibo(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, elementCount(0)
|
||||
, elementDataType(vertex::getIndexDataTypeFromMax(vertexcount))
|
||||
, drawMode(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
{
|
||||
if (vertexcount <= 0)
|
||||
throw love::Exception("Invalid number of vertices (%d).", vertexcount);
|
||||
|
||||
setupAttachedAttributes();
|
||||
calculateAttributeSizes();
|
||||
|
||||
size_t buffersize = vertexCount * vertexStride;
|
||||
|
||||
vbo = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
|
||||
|
||||
// Initialize the buffer's contents to 0.
|
||||
memset(vbo->map(), 0, buffersize);
|
||||
vbo->setMappedRangeModified(0, vbo->getSize());
|
||||
vbo->unmap();
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
delete vbo;
|
||||
delete ibo;
|
||||
delete vertexScratchBuffer;
|
||||
|
||||
for (const auto &attrib : attachedAttributes)
|
||||
{
|
||||
if (attrib.second.mesh != this)
|
||||
attrib.second.mesh->release();
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setupAttachedAttributes()
|
||||
{
|
||||
for (size_t i = 0; i < vertexFormat.size(); i++)
|
||||
{
|
||||
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, (int) i, STEP_PER_VERTEX, 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();
|
||||
}
|
||||
|
||||
vertexStride = stride;
|
||||
}
|
||||
|
||||
size_t Mesh::getAttributeOffset(size_t attribindex) const
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
for (size_t i = 0; i < attribindex; i++)
|
||||
offset += attributeSizes[i];
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vbo->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vbo->setMappedRangeModified(offset, size);
|
||||
}
|
||||
|
||||
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.
|
||||
const uint8 *bufferdata = (const uint8 *) vbo->map();
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void *Mesh::getVertexScratchBuffer()
|
||||
{
|
||||
return vertexScratchBuffer;
|
||||
}
|
||||
|
||||
void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
|
||||
size_t size = std::min(datasize, attributeSizes[attribindex]);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vbo->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vbo->setMappedRangeModified(offset, size);
|
||||
}
|
||||
|
||||
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.
|
||||
const uint8 *bufferdata = (const uint8 *) vbo->map();
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexCount() const
|
||||
{
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexStride() const
|
||||
{
|
||||
return vertexStride;
|
||||
}
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
|
||||
{
|
||||
return vertexFormat;
|
||||
}
|
||||
|
||||
Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
|
||||
{
|
||||
if (attribindex < 0 || attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
DataType type = vertexFormat[attribindex].type;
|
||||
components = vertexFormat[attribindex].components;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
int Mesh::getAttributeIndex(const std::string &name) const
|
||||
{
|
||||
for (int i = 0; i < (int) vertexFormat.size(); i++)
|
||||
{
|
||||
if (vertexFormat[i].name == name)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
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, const std::string &attachname, AttributeStep step)
|
||||
{
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
if (step == STEP_PER_INSTANCE && !gfx->isSupported(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 = {};
|
||||
|
||||
auto it = attachedAttributes.find(name);
|
||||
if (it != attachedAttributes.end())
|
||||
oldattrib = it->second;
|
||||
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
||||
newattrib.index = mesh->getAttributeIndex(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());
|
||||
|
||||
if (newattrib.mesh != this)
|
||||
newattrib.mesh->retain();
|
||||
|
||||
attachedAttributes[name] = newattrib;
|
||||
|
||||
if (oldattrib.mesh && oldattrib.mesh != this)
|
||||
oldattrib.mesh->release();
|
||||
}
|
||||
|
||||
bool Mesh::detachAttribute(const std::string &name)
|
||||
{
|
||||
auto it = attachedAttributes.find(name);
|
||||
|
||||
if (it != attachedAttributes.end() && it->second.mesh != this)
|
||||
{
|
||||
it->second.mesh->release();
|
||||
attachedAttributes.erase(it);
|
||||
|
||||
if (getAttributeIndex(name) != -1)
|
||||
attachAttribute(name, this, name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void *Mesh::mapVertexData()
|
||||
{
|
||||
return vbo->map();
|
||||
}
|
||||
|
||||
void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
|
||||
{
|
||||
vbo->setMappedRangeModified(modifiedoffset, modifiedsize);
|
||||
vbo->unmap();
|
||||
}
|
||||
|
||||
void Mesh::flush()
|
||||
{
|
||||
vbo->unmap();
|
||||
|
||||
if (ibo != nullptr)
|
||||
ibo->unmap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies index data from a vector to a mapped index buffer.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
|
||||
{
|
||||
T *elems = (T *) buffermap.get();
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
if (indices[i] >= maxval)
|
||||
throw love::Exception("Invalid vertex map value: %d", indices[i] + 1);
|
||||
|
||||
elems[i] = (T) indices[i];
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
{
|
||||
size_t maxval = getVertexCount();
|
||||
|
||||
IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval);
|
||||
|
||||
// Calculate the size in bytes of the index buffer data.
|
||||
size_t size = map.size() * vertex::getIndexDataSize(datatype);
|
||||
|
||||
if (ibo && size > ibo->getSize())
|
||||
{
|
||||
delete ibo;
|
||||
ibo = nullptr;
|
||||
}
|
||||
|
||||
if (!ibo && size > 0)
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
ibo = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
|
||||
}
|
||||
|
||||
useIndexBuffer = true;
|
||||
elementCount = map.size();
|
||||
|
||||
if (!ibo || elementCount == 0)
|
||||
return;
|
||||
|
||||
Buffer::Mapper ibomap(*ibo);
|
||||
|
||||
// Fill the buffer with the index values from the vector.
|
||||
switch (datatype)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
copyToIndexBuffer<uint16>(map, ibomap, maxval);
|
||||
break;
|
||||
case INDEX_UINT32:
|
||||
default:
|
||||
copyToIndexBuffer<uint32>(map, ibomap, maxval);
|
||||
break;
|
||||
}
|
||||
|
||||
elementDataType = datatype;
|
||||
}
|
||||
|
||||
void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize)
|
||||
{
|
||||
if (ibo && datasize > ibo->getSize())
|
||||
{
|
||||
delete ibo;
|
||||
ibo = nullptr;
|
||||
}
|
||||
|
||||
if (!ibo && datasize > 0)
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
ibo = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
|
||||
}
|
||||
|
||||
elementCount = datasize / vertex::getIndexDataSize(datatype);
|
||||
|
||||
if (!ibo || elementCount == 0)
|
||||
return;
|
||||
|
||||
Buffer::Mapper ibomap(*ibo);
|
||||
memcpy(ibomap.get(), data, datasize);
|
||||
|
||||
useIndexBuffer = true;
|
||||
elementDataType = datatype;
|
||||
}
|
||||
|
||||
void Mesh::setVertexMap()
|
||||
{
|
||||
useIndexBuffer = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies index data from a mapped buffer to a vector.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32> &indices)
|
||||
{
|
||||
T *elems = (T *) buffer;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
indices.push_back((uint32) elems[i]);
|
||||
}
|
||||
|
||||
bool Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
{
|
||||
if (!useIndexBuffer)
|
||||
return false;
|
||||
|
||||
map.clear();
|
||||
map.reserve(elementCount);
|
||||
|
||||
if (!ibo || elementCount == 0)
|
||||
return true;
|
||||
|
||||
// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
|
||||
void *buffer = ibo->map();
|
||||
|
||||
// Fill the vector from the buffer.
|
||||
switch (elementDataType)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
copyFromIndexBuffer<uint16>(buffer, elementCount, map);
|
||||
break;
|
||||
case INDEX_UINT32:
|
||||
default:
|
||||
copyFromIndexBuffer<uint32>(buffer, elementCount, map);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexMapCount() const
|
||||
{
|
||||
return elementCount;
|
||||
}
|
||||
|
||||
void Mesh::setTexture(Texture *tex)
|
||||
{
|
||||
texture.set(tex);
|
||||
}
|
||||
|
||||
void Mesh::setTexture()
|
||||
{
|
||||
texture.set(nullptr);
|
||||
}
|
||||
|
||||
Texture *Mesh::getTexture() const
|
||||
{
|
||||
return texture.get();
|
||||
}
|
||||
|
||||
void Mesh::setDrawMode(DrawMode mode)
|
||||
{
|
||||
drawMode = mode;
|
||||
}
|
||||
|
||||
Mesh::DrawMode Mesh::getDrawMode() const
|
||||
{
|
||||
return drawMode;
|
||||
}
|
||||
|
||||
void Mesh::setDrawRange(int start, int count)
|
||||
{
|
||||
if (start < 0 || count <= 0)
|
||||
throw love::Exception("Invalid draw range.");
|
||||
|
||||
rangeStart = start;
|
||||
rangeCount = count;
|
||||
}
|
||||
|
||||
void Mesh::setDrawRange()
|
||||
{
|
||||
rangeStart = rangeCount = -1;
|
||||
}
|
||||
|
||||
bool Mesh::getDrawRange(int &start, int &count) const
|
||||
{
|
||||
if (rangeStart < 0 || rangeCount <= 0)
|
||||
return false;
|
||||
|
||||
start = rangeStart;
|
||||
count = rangeCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mesh::draw(love::graphics::Graphics *gfx, const love::Matrix4 &m)
|
||||
{
|
||||
drawInstanced(gfx, m, 1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
|
||||
{
|
||||
return drawModes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(Mesh::DrawMode in, const char *&out)
|
||||
{
|
||||
return drawModes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, DataType &out)
|
||||
{
|
||||
return dataTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(DataType in, const char *&out)
|
||||
{
|
||||
return dataTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, AttributeStep &out)
|
||||
{
|
||||
return attributeSteps.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(AttributeStep in, const char *&out)
|
||||
{
|
||||
return attributeSteps.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
|
||||
{
|
||||
{ "fan", DRAWMODE_FAN },
|
||||
{ "strip", DRAWMODE_STRIP },
|
||||
{ "triangles", DRAWMODE_TRIANGLES },
|
||||
{ "points", DRAWMODE_POINTS },
|
||||
};
|
||||
|
||||
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
|
||||
|
||||
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM>::Entry Mesh::dataTypeEntries[] =
|
||||
{
|
||||
{ "byte", DATA_BYTE },
|
||||
{ "float", DATA_FLOAT },
|
||||
};
|
||||
|
||||
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM> Mesh::dataTypes(Mesh::dataTypeEntries, sizeof(Mesh::dataTypeEntries));
|
||||
|
||||
StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM>::Entry Mesh::attributeStepEntries[] =
|
||||
{
|
||||
{ "pervertex", STEP_PER_VERTEX },
|
||||
{ "perinstance", STEP_PER_INSTANCE },
|
||||
};
|
||||
|
||||
StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM> Mesh::attributeSteps(Mesh::attributeStepEntries, sizeof(Mesh::attributeStepEntries));
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2017 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/int.h"
|
||||
#include "common/math.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "Drawable.h"
|
||||
#include "Texture.h"
|
||||
#include "vertex.h"
|
||||
#include "Buffer.h"
|
||||
|
||||
// C++
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
class Graphics;
|
||||
|
||||
/**
|
||||
* Holds and draws arbitrary vertex geometry.
|
||||
* Each vertex in the Mesh has a collection of vertex attributes specified on
|
||||
* creation.
|
||||
**/
|
||||
class Mesh : public Drawable
|
||||
{
|
||||
public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
// How the Mesh's vertices are used when drawing.
|
||||
// http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png
|
||||
enum DrawMode
|
||||
{
|
||||
DRAWMODE_FAN,
|
||||
DRAWMODE_STRIP,
|
||||
DRAWMODE_TRIANGLES,
|
||||
DRAWMODE_POINTS,
|
||||
DRAWMODE_MAX_ENUM
|
||||
};
|
||||
|
||||
// The type of data a vertex attribute can store.
|
||||
enum DataType
|
||||
{
|
||||
DATA_BYTE,
|
||||
DATA_FLOAT,
|
||||
DATA_MAX_ENUM
|
||||
};
|
||||
|
||||
enum AttributeStep
|
||||
{
|
||||
STEP_PER_VERTEX,
|
||||
STEP_PER_INSTANCE,
|
||||
STEP_MAX_ENUM
|
||||
};
|
||||
|
||||
struct AttribFormat
|
||||
{
|
||||
std::string name;
|
||||
DataType type;
|
||||
int components; // max 4
|
||||
};
|
||||
|
||||
Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage);
|
||||
Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage);
|
||||
|
||||
virtual ~Mesh();
|
||||
|
||||
/**
|
||||
* 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 setVertex(size_t vertindex, const void *data, size_t datasize);
|
||||
size_t getVertex(size_t vertindex, void *data, size_t datasize);
|
||||
void *getVertexScratchBuffer();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Gets the total number of vertices that can be used when drawing the Mesh.
|
||||
**/
|
||||
size_t getVertexCount() const;
|
||||
|
||||
/**
|
||||
* Gets the size in bytes of the start of one vertex to the start of the
|
||||
* next, in the buffer.
|
||||
**/
|
||||
size_t getVertexStride() const;
|
||||
|
||||
/**
|
||||
* Gets the format of each vertex attribute stored in the Mesh.
|
||||
**/
|
||||
const std::vector<AttribFormat> &getVertexFormat() const;
|
||||
DataType getAttributeInfo(int attribindex, int &components) const;
|
||||
int getAttributeIndex(const std::string &name) 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, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||
bool detachAttribute(const std::string &name);
|
||||
|
||||
void *mapVertexData();
|
||||
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* A 0-element vector is equivalent to the default vertex map:
|
||||
* {0, 1, 2, 3, 4, ...}
|
||||
**/
|
||||
void setVertexMap(const std::vector<uint32> &map);
|
||||
void setVertexMap(IndexDataType datatype, const void *data, size_t datasize);
|
||||
void setVertexMap();
|
||||
|
||||
/**
|
||||
* Fills the uint32 vector passed into the method with the previously set
|
||||
* vertex map (index buffer) values.
|
||||
**/
|
||||
bool getVertexMap(std::vector<uint32> &map) const;
|
||||
|
||||
/**
|
||||
* Gets the total number of elements in the vertex map array.
|
||||
**/
|
||||
size_t getVertexMapCount() const;
|
||||
|
||||
/**
|
||||
* Sets the texture used when drawing the Mesh.
|
||||
**/
|
||||
void setTexture(Texture *texture);
|
||||
|
||||
/**
|
||||
* Disables any texture from being used when drawing the Mesh.
|
||||
**/
|
||||
void setTexture();
|
||||
|
||||
/**
|
||||
* Gets the texture used when drawing the Mesh. May return null if no
|
||||
* texture is set.
|
||||
**/
|
||||
Texture *getTexture() const;
|
||||
|
||||
/**
|
||||
* Sets the draw mode used when drawing the Mesh.
|
||||
**/
|
||||
void setDrawMode(DrawMode mode);
|
||||
DrawMode getDrawMode() const;
|
||||
|
||||
void setDrawRange(int start, int count);
|
||||
void setDrawRange();
|
||||
bool getDrawRange(int &start, int &count) const;
|
||||
|
||||
virtual int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) = 0;
|
||||
|
||||
virtual void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) = 0;
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
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);
|
||||
|
||||
static bool getConstant(const char *in, AttributeStep &out);
|
||||
static bool getConstant(AttributeStep in, const char *&out);
|
||||
|
||||
protected:
|
||||
|
||||
struct AttachedAttribute
|
||||
{
|
||||
Mesh *mesh;
|
||||
int index;
|
||||
AttributeStep step;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
void setupAttachedAttributes();
|
||||
void calculateAttributeSizes();
|
||||
size_t getAttributeOffset(size_t attribindex) const;
|
||||
|
||||
static size_t getAttribFormatSize(const AttribFormat &format);
|
||||
static std::vector<AttribFormat> getDefaultVertexFormat();
|
||||
|
||||
std::vector<AttribFormat> vertexFormat;
|
||||
std::vector<size_t> attributeSizes;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
|
||||
|
||||
// Vertex buffer, for the vertex data.
|
||||
Buffer *vbo;
|
||||
size_t vertexCount;
|
||||
size_t vertexStride;
|
||||
|
||||
// Block of memory whose size is at least as large as a single vertex. Helps
|
||||
// avoid memory allocations when using Mesh::setVertex etc.
|
||||
char *vertexScratchBuffer;
|
||||
|
||||
// Element (vertex index) buffer, for the vertex map.
|
||||
Buffer *ibo;
|
||||
bool useIndexBuffer;
|
||||
size_t elementCount;
|
||||
IndexDataType elementDataType;
|
||||
|
||||
DrawMode drawMode;
|
||||
|
||||
int rangeStart;
|
||||
int rangeCount;
|
||||
|
||||
StrongRef<Texture> texture;
|
||||
|
||||
private:
|
||||
|
||||
static StringMap<DrawMode, DRAWMODE_MAX_ENUM>::Entry drawModeEntries[];
|
||||
static StringMap<DrawMode, DRAWMODE_MAX_ENUM> drawModes;
|
||||
|
||||
static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[];
|
||||
static StringMap<DataType, DATA_MAX_ENUM> dataTypes;
|
||||
|
||||
static StringMap<AttributeStep, STEP_MAX_ENUM>::Entry attributeStepEntries[];
|
||||
static StringMap<AttributeStep, STEP_MAX_ENUM> attributeSteps;
|
||||
|
||||
}; // Mesh
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2017 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "common/config.h"
|
||||
#include "SpriteBatch.h"
|
||||
|
||||
// LOVE
|
||||
#include "Texture.h"
|
||||
#include "Quad.h"
|
||||
#include "Graphics.h"
|
||||
#include "Buffer.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
|
||||
// C
|
||||
#include <stddef.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
love::Type SpriteBatch::type("SpriteBatch", &Drawable::type);
|
||||
|
||||
SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage)
|
||||
: texture(texture)
|
||||
, size(size)
|
||||
, next(0)
|
||||
, color(0)
|
||||
, array_buf(nullptr)
|
||||
, quad_indices(gfx, size)
|
||||
, range_start(-1)
|
||||
, range_count(-1)
|
||||
{
|
||||
if (size <= 0)
|
||||
throw love::Exception("Invalid SpriteBatch size.");
|
||||
|
||||
size_t vertex_size = sizeof(Vertex) * 4 * size;
|
||||
|
||||
array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
|
||||
}
|
||||
|
||||
SpriteBatch::~SpriteBatch()
|
||||
{
|
||||
delete color;
|
||||
delete array_buf;
|
||||
}
|
||||
|
||||
int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/)
|
||||
{
|
||||
if (index < -1 || index >= size)
|
||||
throw love::Exception("Invalid sprite index: %d", index + 1);
|
||||
|
||||
if (index == -1 && next >= size)
|
||||
setBufferSize(size * 2);
|
||||
|
||||
addv(texture->getVertices(), m, (index == -1) ? next : index);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
return next++;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int SpriteBatch::addq(Quad *quad, const Matrix4 &m, int index /*= -1*/)
|
||||
{
|
||||
if (index < -1 || index >= size)
|
||||
throw love::Exception("Invalid sprite index: %d", index + 1);
|
||||
|
||||
if (index == -1 && next >= size)
|
||||
setBufferSize(size * 2);
|
||||
|
||||
addv(quad->getVertices(), m, (index == -1) ? next : index);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
return next++;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void SpriteBatch::clear()
|
||||
{
|
||||
// Reset the position of the next index.
|
||||
next = 0;
|
||||
}
|
||||
|
||||
void SpriteBatch::flush()
|
||||
{
|
||||
array_buf->unmap();
|
||||
}
|
||||
|
||||
void SpriteBatch::setTexture(Texture *newtexture)
|
||||
{
|
||||
texture.set(newtexture);
|
||||
}
|
||||
|
||||
Texture *SpriteBatch::getTexture() const
|
||||
{
|
||||
return texture.get();
|
||||
}
|
||||
|
||||
void SpriteBatch::setColor(const Color &color)
|
||||
{
|
||||
if (!this->color)
|
||||
this->color = new Color(color);
|
||||
else
|
||||
*(this->color) = color;
|
||||
}
|
||||
|
||||
void SpriteBatch::setColor()
|
||||
{
|
||||
delete color;
|
||||
color = nullptr;
|
||||
}
|
||||
|
||||
const Color *SpriteBatch::getColor() const
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
int SpriteBatch::getCount() const
|
||||
{
|
||||
return next;
|
||||
}
|
||||
|
||||
void SpriteBatch::setBufferSize(int newsize)
|
||||
{
|
||||
if (newsize <= 0)
|
||||
throw love::Exception("Invalid SpriteBatch size.");
|
||||
|
||||
if (newsize == size)
|
||||
return;
|
||||
|
||||
size_t vertex_size = sizeof(Vertex) * 4 * newsize;
|
||||
love::graphics::Buffer *new_array_buf = nullptr;
|
||||
|
||||
int new_next = std::min(next, newsize);
|
||||
|
||||
try
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
|
||||
|
||||
// Copy as much of the old data into the new GLBuffer as can fit.
|
||||
size_t copy_size = sizeof(Vertex) * 4 * new_next;
|
||||
array_buf->copyTo(0, copy_size, new_array_buf, 0);
|
||||
|
||||
quad_indices = QuadIndices(gfx, newsize);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
delete new_array_buf;
|
||||
throw;
|
||||
}
|
||||
|
||||
// We don't need to unmap the old GLBuffer since we're deleting it.
|
||||
delete array_buf;
|
||||
|
||||
array_buf = new_array_buf;
|
||||
size = newsize;
|
||||
|
||||
next = new_next;
|
||||
}
|
||||
|
||||
int SpriteBatch::getBufferSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
|
||||
{
|
||||
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);
|
||||
|
||||
auto it = attached_attributes.find(name);
|
||||
if (it != attached_attributes.end())
|
||||
oldattrib = it->second;
|
||||
|
||||
newattrib.index = mesh->getAttributeIndex(name);
|
||||
|
||||
if (newattrib.index < 0)
|
||||
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
|
||||
|
||||
newattrib.mesh = mesh;
|
||||
|
||||
attached_attributes[name] = newattrib;
|
||||
}
|
||||
|
||||
void SpriteBatch::setDrawRange(int start, int count)
|
||||
{
|
||||
if (start < 0 || count <= 0)
|
||||
throw love::Exception("Invalid draw range.");
|
||||
|
||||
range_start = start;
|
||||
range_count = count;
|
||||
}
|
||||
|
||||
void SpriteBatch::setDrawRange()
|
||||
{
|
||||
range_start = range_count = -1;
|
||||
}
|
||||
|
||||
bool SpriteBatch::getDrawRange(int &start, int &count) const
|
||||
{
|
||||
if (range_start < 0 || range_count <= 0)
|
||||
return false;
|
||||
|
||||
start = range_start;
|
||||
count = range_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index)
|
||||
{
|
||||
// Needed for colors.
|
||||
Vertex sprite[4] = {v[0], v[1], v[2], v[3]};
|
||||
const size_t sprite_size = 4 * sizeof(Vertex); // bytecount
|
||||
|
||||
m.transform(sprite, sprite, 4);
|
||||
|
||||
if (color != nullptr)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
sprite[i].color = *color;
|
||||
}
|
||||
|
||||
// Always keep the VBO mapped when adding data for now (it'll be unmapped
|
||||
// on draw.)
|
||||
array_buf->map();
|
||||
|
||||
array_buf->fill(index * sprite_size, sprite_size, sprite);
|
||||
}
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2017 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
// C
|
||||
#include <cstring>
|
||||
|
||||
// C++
|
||||
#include <unordered_map>
|
||||
|
||||
// LOVE
|
||||
#include "common/math.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "Drawable.h"
|
||||
#include "Color.h"
|
||||
#include "Mesh.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
class Graphics;
|
||||
class Texture;
|
||||
class Quad;
|
||||
class Buffer;
|
||||
|
||||
class SpriteBatch : public Drawable
|
||||
{
|
||||
public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
|
||||
virtual ~SpriteBatch();
|
||||
|
||||
int add(const Matrix4 &m, int index = -1);
|
||||
int addq(Quad *quad, const Matrix4 &m, int index = -1);
|
||||
void clear();
|
||||
|
||||
void flush();
|
||||
|
||||
void setTexture(Texture *newtexture);
|
||||
Texture *getTexture() const;
|
||||
|
||||
/**
|
||||
* Set the current color for this SpriteBatch. The sprites added
|
||||
* after this call will use this color. Note that global color
|
||||
* will not longer apply to the SpriteBatch if this is used.
|
||||
*
|
||||
* @param color The color to use for the following sprites.
|
||||
*/
|
||||
void setColor(const Color &color);
|
||||
|
||||
/**
|
||||
* Disable per-sprite colors for this SpriteBatch. The next call to
|
||||
* draw will use the global color for all sprites.
|
||||
*/
|
||||
void setColor();
|
||||
|
||||
/**
|
||||
* Get the current color for this SpriteBatch. Returns NULL if no color is
|
||||
* set.
|
||||
**/
|
||||
const Color *getColor() const;
|
||||
|
||||
/**
|
||||
* Get the number of sprites currently in this SpriteBatch.
|
||||
**/
|
||||
int getCount() const;
|
||||
|
||||
/**
|
||||
* Get the total number of sprites this SpriteBatch can currently hold.
|
||||
**/
|
||||
int getBufferSize() const;
|
||||
|
||||
/**
|
||||
* 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 setDrawRange(int start, int count);
|
||||
void setDrawRange();
|
||||
bool getDrawRange(int &start, int &count) const;
|
||||
|
||||
protected:
|
||||
|
||||
struct AttachedAttribute
|
||||
{
|
||||
StrongRef<Mesh> mesh;
|
||||
int index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the total number of sprites this SpriteBatch can hold.
|
||||
* Leaves existing sprite data intact when possible.
|
||||
**/
|
||||
void setBufferSize(int newsize);
|
||||
|
||||
void addv(const Vertex *v, const Matrix4 &m, int index);
|
||||
|
||||
StrongRef<Texture> texture;
|
||||
|
||||
// Max number of sprites in the batch.
|
||||
int size;
|
||||
|
||||
// The next free element.
|
||||
int next;
|
||||
|
||||
// Current color. This color, if present, will be applied to the next
|
||||
// added sprite.
|
||||
Color *color;
|
||||
|
||||
love::graphics::Buffer *array_buf;
|
||||
QuadIndices quad_indices;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
|
||||
|
||||
int range_start;
|
||||
int range_count;
|
||||
|
||||
}; // SpriteBatch
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -118,12 +118,12 @@ graphics::Font *Graphics::newFont(love::font::Rasterizer *r, const Texture::Filt
|
||||
return new Font(r, filter);
|
||||
}
|
||||
|
||||
SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
|
||||
love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
|
||||
{
|
||||
return new SpriteBatch(this, texture, size, usage);
|
||||
}
|
||||
|
||||
ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
|
||||
love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
|
||||
{
|
||||
return new ParticleSystem(this, texture, size);
|
||||
}
|
||||
@@ -167,22 +167,22 @@ love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, Buffe
|
||||
return new Buffer(size, data, type, usage, mapflags);
|
||||
}
|
||||
|
||||
Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage)
|
||||
love::graphics::Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage)
|
||||
{
|
||||
return new Mesh(this, vertices, drawmode, usage);
|
||||
}
|
||||
|
||||
Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
|
||||
love::graphics::Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
|
||||
{
|
||||
return new Mesh(this, vertexcount, drawmode, usage);
|
||||
}
|
||||
|
||||
Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
|
||||
love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
|
||||
{
|
||||
return new Mesh(this, vertexformat, vertexcount, drawmode, usage);
|
||||
}
|
||||
|
||||
Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage)
|
||||
love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage)
|
||||
{
|
||||
return new Mesh(this, vertexformat, data, datasize, drawmode, usage);
|
||||
}
|
||||
@@ -498,11 +498,6 @@ void Graphics::flushStreamDraws()
|
||||
streamBufferState.indexCount = 0;
|
||||
}
|
||||
|
||||
void Graphics::drawInstanced(Mesh *mesh, const love::Matrix4 &m, int instancecount)
|
||||
{
|
||||
mesh->drawInstanced(this, m, instancecount);
|
||||
}
|
||||
|
||||
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
|
||||
{
|
||||
// Human-readable strings for the debug info.
|
||||
|
||||
@@ -66,9 +66,9 @@ public:
|
||||
|
||||
love::graphics::Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::defaultFilter) override;
|
||||
|
||||
SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage);
|
||||
love::graphics::SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage) override;
|
||||
|
||||
ParticleSystem *newParticleSystem(Texture *texture, int size);
|
||||
love::graphics::ParticleSystem *newParticleSystem(Texture *texture, int size) override;
|
||||
|
||||
love::graphics::Canvas *newCanvas(int width, int height, const Canvas::Settings &settings) override;
|
||||
|
||||
@@ -76,11 +76,11 @@ public:
|
||||
|
||||
love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
|
||||
|
||||
Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage);
|
||||
Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage);
|
||||
love::graphics::Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage) override;
|
||||
love::graphics::Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage) override;
|
||||
|
||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage);
|
||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage);
|
||||
love::graphics::Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage) override;
|
||||
love::graphics::Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage) override;
|
||||
|
||||
love::graphics::Text *newText(love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {}) override;
|
||||
|
||||
@@ -94,8 +94,6 @@ public:
|
||||
|
||||
void flushStreamDraws() override;
|
||||
|
||||
void drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount);
|
||||
|
||||
void clear(Colorf color) override;
|
||||
void clear(const std::vector<OptionalColorf> &colors) override;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
// LOVE
|
||||
#include "Mesh.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "common/Exception.h"
|
||||
#include "Shader.h"
|
||||
#include "graphics/Graphics.h"
|
||||
@@ -36,87 +35,14 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
static const char *getBuiltinAttribName(VertexAttribID attribid)
|
||||
{
|
||||
const char *name = "";
|
||||
vertex::getConstant(attribid, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
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, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
|
||||
|
||||
static std::vector<Mesh::AttribFormat> getDefaultVertexFormat()
|
||||
{
|
||||
// Corresponds to the love::Vertex struct.
|
||||
std::vector<Mesh::AttribFormat> vertexformat = {
|
||||
{getBuiltinAttribName(ATTRIB_POS), Mesh::DATA_FLOAT, 2},
|
||||
{getBuiltinAttribName(ATTRIB_TEXCOORD), Mesh::DATA_FLOAT, 2},
|
||||
{getBuiltinAttribName(ATTRIB_COLOR), Mesh::DATA_BYTE, 4},
|
||||
};
|
||||
|
||||
return vertexformat;
|
||||
}
|
||||
|
||||
love::Type Mesh::type("Mesh", &Drawable::type);
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage)
|
||||
: vertexFormat(vertexformat)
|
||||
, vbo(nullptr)
|
||||
, vertexCount(0)
|
||||
, vertexStride(0)
|
||||
, ibo(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, elementCount(0)
|
||||
, elementDataType(INDEX_UINT16)
|
||||
, drawMode(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
: love::graphics::Mesh(gfx, vertexformat, data, datasize, drawmode, usage)
|
||||
{
|
||||
setupAttachedAttributes();
|
||||
calculateAttributeSizes();
|
||||
|
||||
vertexCount = datasize / vertexStride;
|
||||
elementDataType = vertex::getIndexDataTypeFromMax(vertexCount);
|
||||
|
||||
if (vertexCount == 0)
|
||||
throw love::Exception("Data size is too small for specified vertex attribute formats.");
|
||||
|
||||
vbo = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage)
|
||||
: vertexFormat(vertexformat)
|
||||
, vbo(nullptr)
|
||||
, vertexCount((size_t) vertexcount)
|
||||
, vertexStride(0)
|
||||
, ibo(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, elementCount(0)
|
||||
, elementDataType(vertex::getIndexDataTypeFromMax(vertexcount))
|
||||
, drawMode(drawmode)
|
||||
, rangeStart(-1)
|
||||
, rangeCount(-1)
|
||||
: love::graphics::Mesh(gfx, vertexformat, vertexcount, drawmode, usage)
|
||||
{
|
||||
if (vertexcount <= 0)
|
||||
throw love::Exception("Invalid number of vertices (%d).", vertexcount);
|
||||
|
||||
setupAttachedAttributes();
|
||||
calculateAttributeSizes();
|
||||
|
||||
size_t buffersize = vertexCount * vertexStride;
|
||||
|
||||
vbo = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
|
||||
|
||||
// Initialize the buffer's contents to 0.
|
||||
memset(vbo->map(), 0, buffersize);
|
||||
vbo->setMappedRangeModified(0, vbo->getSize());
|
||||
vbo->unmap();
|
||||
|
||||
vertexScratchBuffer = new char[vertexStride];
|
||||
}
|
||||
|
||||
Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Vertex> &vertices, DrawMode drawmode, vertex::Usage usage)
|
||||
@@ -131,448 +57,6 @@ Mesh::Mesh(graphics::Graphics *gfx, int vertexcount, DrawMode drawmode, vertex::
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
delete vbo;
|
||||
delete ibo;
|
||||
delete vertexScratchBuffer;
|
||||
|
||||
for (const auto &attrib : attachedAttributes)
|
||||
{
|
||||
if (attrib.second.mesh != this)
|
||||
attrib.second.mesh->release();
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setupAttachedAttributes()
|
||||
{
|
||||
for (size_t i = 0; i < vertexFormat.size(); i++)
|
||||
{
|
||||
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, (int) i, STEP_PER_VERTEX, 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();
|
||||
}
|
||||
|
||||
vertexStride = stride;
|
||||
}
|
||||
|
||||
size_t Mesh::getAttributeOffset(size_t attribindex) const
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
for (size_t i = 0; i < attribindex; i++)
|
||||
offset += attributeSizes[i];
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride;
|
||||
size_t size = std::min(datasize, vertexStride);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vbo->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vbo->setMappedRangeModified(offset, size);
|
||||
}
|
||||
|
||||
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.
|
||||
const uint8 *bufferdata = (const uint8 *) vbo->map();
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void *Mesh::getVertexScratchBuffer()
|
||||
{
|
||||
return vertexScratchBuffer;
|
||||
}
|
||||
|
||||
void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize)
|
||||
{
|
||||
if (vertindex >= vertexCount)
|
||||
throw love::Exception("Invalid vertex index: %ld", vertindex + 1);
|
||||
|
||||
if (attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
|
||||
size_t size = std::min(datasize, attributeSizes[attribindex]);
|
||||
|
||||
uint8 *bufferdata = (uint8 *) vbo->map();
|
||||
memcpy(bufferdata + offset, data, size);
|
||||
|
||||
vbo->setMappedRangeModified(offset, size);
|
||||
}
|
||||
|
||||
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.
|
||||
const uint8 *bufferdata = (const uint8 *) vbo->map();
|
||||
memcpy(data, bufferdata + offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexCount() const
|
||||
{
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexStride() const
|
||||
{
|
||||
return vertexStride;
|
||||
}
|
||||
|
||||
const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
|
||||
{
|
||||
return vertexFormat;
|
||||
}
|
||||
|
||||
Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
|
||||
{
|
||||
if (attribindex < 0 || attribindex >= (int) vertexFormat.size())
|
||||
throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
|
||||
|
||||
DataType type = vertexFormat[attribindex].type;
|
||||
components = vertexFormat[attribindex].components;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
int Mesh::getAttributeIndex(const std::string &name) const
|
||||
{
|
||||
for (int i = 0; i < (int) vertexFormat.size(); i++)
|
||||
{
|
||||
if (vertexFormat[i].name == name)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
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, const std::string &attachname, AttributeStep step)
|
||||
{
|
||||
if (step == STEP_PER_INSTANCE && !gl.isInstancingSupported())
|
||||
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 = {};
|
||||
|
||||
auto it = attachedAttributes.find(name);
|
||||
if (it != attachedAttributes.end())
|
||||
oldattrib = it->second;
|
||||
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
||||
newattrib.index = mesh->getAttributeIndex(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());
|
||||
|
||||
if (newattrib.mesh != this)
|
||||
newattrib.mesh->retain();
|
||||
|
||||
attachedAttributes[name] = newattrib;
|
||||
|
||||
if (oldattrib.mesh && oldattrib.mesh != this)
|
||||
oldattrib.mesh->release();
|
||||
}
|
||||
|
||||
bool Mesh::detachAttribute(const std::string &name)
|
||||
{
|
||||
auto it = attachedAttributes.find(name);
|
||||
|
||||
if (it != attachedAttributes.end() && it->second.mesh != this)
|
||||
{
|
||||
it->second.mesh->release();
|
||||
attachedAttributes.erase(it);
|
||||
|
||||
if (getAttributeIndex(name) != -1)
|
||||
attachAttribute(name, this, name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void *Mesh::mapVertexData()
|
||||
{
|
||||
return vbo->map();
|
||||
}
|
||||
|
||||
void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
|
||||
{
|
||||
vbo->setMappedRangeModified(modifiedoffset, modifiedsize);
|
||||
vbo->unmap();
|
||||
}
|
||||
|
||||
void Mesh::flush()
|
||||
{
|
||||
vbo->unmap();
|
||||
|
||||
if (ibo != nullptr)
|
||||
ibo->unmap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies index data from a vector to a mapped index buffer.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
|
||||
{
|
||||
T *elems = (T *) buffermap.get();
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
if (indices[i] >= maxval)
|
||||
throw love::Exception("Invalid vertex map value: %d", indices[i] + 1);
|
||||
|
||||
elems[i] = (T) indices[i];
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
{
|
||||
size_t maxval = getVertexCount();
|
||||
|
||||
IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval);
|
||||
|
||||
// Calculate the size in bytes of the index buffer data.
|
||||
size_t size = map.size() * vertex::getIndexDataSize(datatype);
|
||||
|
||||
if (ibo && size > ibo->getSize())
|
||||
{
|
||||
delete ibo;
|
||||
ibo = nullptr;
|
||||
}
|
||||
|
||||
if (!ibo && size > 0)
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
ibo = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
|
||||
}
|
||||
|
||||
useIndexBuffer = true;
|
||||
elementCount = map.size();
|
||||
|
||||
if (!ibo || elementCount == 0)
|
||||
return;
|
||||
|
||||
Buffer::Mapper ibomap(*ibo);
|
||||
|
||||
// Fill the buffer with the index values from the vector.
|
||||
switch (datatype)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
copyToIndexBuffer<uint16>(map, ibomap, maxval);
|
||||
break;
|
||||
case INDEX_UINT32:
|
||||
default:
|
||||
copyToIndexBuffer<uint32>(map, ibomap, maxval);
|
||||
break;
|
||||
}
|
||||
|
||||
elementDataType = datatype;
|
||||
}
|
||||
|
||||
void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize)
|
||||
{
|
||||
if (ibo && datasize > ibo->getSize())
|
||||
{
|
||||
delete ibo;
|
||||
ibo = nullptr;
|
||||
}
|
||||
|
||||
if (!ibo && datasize > 0)
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
ibo = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
|
||||
}
|
||||
|
||||
elementCount = datasize / vertex::getIndexDataSize(datatype);
|
||||
|
||||
if (!ibo || elementCount == 0)
|
||||
return;
|
||||
|
||||
Buffer::Mapper ibomap(*ibo);
|
||||
memcpy(ibomap.get(), data, datasize);
|
||||
|
||||
useIndexBuffer = true;
|
||||
elementDataType = datatype;
|
||||
}
|
||||
|
||||
void Mesh::setVertexMap()
|
||||
{
|
||||
useIndexBuffer = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies index data from a mapped buffer to a vector.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32> &indices)
|
||||
{
|
||||
T *elems = (T *) buffer;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
indices.push_back((uint32) elems[i]);
|
||||
}
|
||||
|
||||
bool Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
{
|
||||
if (!useIndexBuffer)
|
||||
return false;
|
||||
|
||||
map.clear();
|
||||
map.reserve(elementCount);
|
||||
|
||||
if (!ibo || elementCount == 0)
|
||||
return true;
|
||||
|
||||
// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
|
||||
void *buffer = ibo->map();
|
||||
|
||||
// Fill the vector from the buffer.
|
||||
switch (elementDataType)
|
||||
{
|
||||
case INDEX_UINT16:
|
||||
copyFromIndexBuffer<uint16>(buffer, elementCount, map);
|
||||
break;
|
||||
case INDEX_UINT32:
|
||||
default:
|
||||
copyFromIndexBuffer<uint32>(buffer, elementCount, map);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexMapCount() const
|
||||
{
|
||||
return elementCount;
|
||||
}
|
||||
|
||||
void Mesh::setTexture(Texture *tex)
|
||||
{
|
||||
texture.set(tex);
|
||||
}
|
||||
|
||||
void Mesh::setTexture()
|
||||
{
|
||||
texture.set(nullptr);
|
||||
}
|
||||
|
||||
Texture *Mesh::getTexture() const
|
||||
{
|
||||
return texture.get();
|
||||
}
|
||||
|
||||
void Mesh::setDrawMode(DrawMode mode)
|
||||
{
|
||||
drawMode = mode;
|
||||
}
|
||||
|
||||
Mesh::DrawMode Mesh::getDrawMode() const
|
||||
{
|
||||
return drawMode;
|
||||
}
|
||||
|
||||
void Mesh::setDrawRange(int start, int count)
|
||||
{
|
||||
if (start < 0 || count <= 0)
|
||||
throw love::Exception("Invalid draw range.");
|
||||
|
||||
rangeStart = start;
|
||||
rangeCount = count;
|
||||
}
|
||||
|
||||
void Mesh::setDrawRange()
|
||||
{
|
||||
rangeStart = rangeCount = -1;
|
||||
}
|
||||
|
||||
bool Mesh::getDrawRange(int &start, int &count) const
|
||||
{
|
||||
if (rangeStart < 0 || rangeCount <= 0)
|
||||
return false;
|
||||
|
||||
start = rangeStart;
|
||||
count = rangeCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inputname)
|
||||
@@ -627,7 +111,7 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
|
||||
if (!attrib.second.enabled)
|
||||
continue;
|
||||
|
||||
Mesh *mesh = attrib.second.mesh;
|
||||
love::graphics::Mesh *mesh = attrib.second.mesh;
|
||||
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
|
||||
|
||||
if (location >= 0)
|
||||
@@ -692,24 +176,6 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::draw(love::graphics::Graphics *gfx, const love::Matrix4 &m)
|
||||
{
|
||||
drawInstanced(gfx, m, 1);
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -739,62 +205,6 @@ GLenum Mesh::getGLDataType(DataType type)
|
||||
}
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
|
||||
{
|
||||
return drawModes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(Mesh::DrawMode in, const char *&out)
|
||||
{
|
||||
return drawModes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, DataType &out)
|
||||
{
|
||||
return dataTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(DataType in, const char *&out)
|
||||
{
|
||||
return dataTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, AttributeStep &out)
|
||||
{
|
||||
return attributeSteps.find(in, out);
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(AttributeStep in, const char *&out)
|
||||
{
|
||||
return attributeSteps.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
|
||||
{
|
||||
{ "fan", DRAWMODE_FAN },
|
||||
{ "strip", DRAWMODE_STRIP },
|
||||
{ "triangles", DRAWMODE_TRIANGLES },
|
||||
{ "points", DRAWMODE_POINTS },
|
||||
};
|
||||
|
||||
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
|
||||
|
||||
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM>::Entry Mesh::dataTypeEntries[] =
|
||||
{
|
||||
{ "byte", DATA_BYTE },
|
||||
{ "float", DATA_FLOAT },
|
||||
};
|
||||
|
||||
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM> Mesh::dataTypes(Mesh::dataTypeEntries, sizeof(Mesh::dataTypeEntries));
|
||||
|
||||
StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM>::Entry Mesh::attributeStepEntries[] =
|
||||
{
|
||||
{ "pervertex", STEP_PER_VERTEX },
|
||||
{ "perinstance", STEP_PER_INSTANCE },
|
||||
};
|
||||
|
||||
StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM> Mesh::attributeSteps(Mesh::attributeStepEntries, sizeof(Mesh::attributeStepEntries));
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -18,78 +18,23 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_MESH_H
|
||||
#define LOVE_GRAPHICS_OPENGL_MESH_H
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/int.h"
|
||||
#include "common/math.h"
|
||||
#include "common/StringMap.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/vertex.h"
|
||||
#include "graphics/Buffer.h"
|
||||
#include "graphics/Mesh.h"
|
||||
#include "OpenGL.h"
|
||||
|
||||
// C++
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
class Graphics;
|
||||
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
/**
|
||||
* Holds and draws arbitrary vertex geometry.
|
||||
* Each vertex in the Mesh has a collection of vertex attributes specified on
|
||||
* creation.
|
||||
**/
|
||||
class Mesh : public Drawable
|
||||
class Mesh : public love::graphics::Mesh
|
||||
{
|
||||
public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
// How the Mesh's vertices are used when drawing.
|
||||
// http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png
|
||||
enum DrawMode
|
||||
{
|
||||
DRAWMODE_FAN,
|
||||
DRAWMODE_STRIP,
|
||||
DRAWMODE_TRIANGLES,
|
||||
DRAWMODE_POINTS,
|
||||
DRAWMODE_MAX_ENUM
|
||||
};
|
||||
|
||||
// The type of data a vertex attribute can store.
|
||||
enum DataType
|
||||
{
|
||||
DATA_BYTE,
|
||||
DATA_FLOAT,
|
||||
DATA_MAX_ENUM
|
||||
};
|
||||
|
||||
enum AttributeStep
|
||||
{
|
||||
STEP_PER_VERTEX,
|
||||
STEP_PER_INSTANCE,
|
||||
STEP_MAX_ENUM
|
||||
};
|
||||
|
||||
struct AttribFormat
|
||||
{
|
||||
std::string name;
|
||||
DataType type;
|
||||
int components; // max 4
|
||||
};
|
||||
|
||||
Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage);
|
||||
Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage);
|
||||
|
||||
@@ -98,184 +43,16 @@ public:
|
||||
|
||||
virtual ~Mesh();
|
||||
|
||||
/**
|
||||
* 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 setVertex(size_t vertindex, const void *data, size_t datasize);
|
||||
size_t getVertex(size_t vertindex, void *data, size_t datasize);
|
||||
void *getVertexScratchBuffer();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Gets the total number of vertices that can be used when drawing the Mesh.
|
||||
**/
|
||||
size_t getVertexCount() const;
|
||||
|
||||
/**
|
||||
* Gets the size in bytes of the start of one vertex to the start of the
|
||||
* next, in the buffer.
|
||||
**/
|
||||
size_t getVertexStride() const;
|
||||
|
||||
/**
|
||||
* Gets the format of each vertex attribute stored in the Mesh.
|
||||
**/
|
||||
const std::vector<AttribFormat> &getVertexFormat() const;
|
||||
DataType getAttributeInfo(int attribindex, int &components) const;
|
||||
int getAttributeIndex(const std::string &name) 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, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||
bool detachAttribute(const std::string &name);
|
||||
|
||||
void *mapVertexData();
|
||||
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* A 0-element vector is equivalent to the default vertex map:
|
||||
* {0, 1, 2, 3, 4, ...}
|
||||
**/
|
||||
void setVertexMap(const std::vector<uint32> &map);
|
||||
void setVertexMap(IndexDataType datatype, const void *data, size_t datasize);
|
||||
void setVertexMap();
|
||||
|
||||
/**
|
||||
* Fills the uint32 vector passed into the method with the previously set
|
||||
* vertex map (index buffer) values.
|
||||
**/
|
||||
bool getVertexMap(std::vector<uint32> &map) const;
|
||||
|
||||
/**
|
||||
* Gets the total number of elements in the vertex map array.
|
||||
**/
|
||||
size_t getVertexMapCount() const;
|
||||
|
||||
/**
|
||||
* Sets the texture used when drawing the Mesh.
|
||||
**/
|
||||
void setTexture(Texture *texture);
|
||||
|
||||
/**
|
||||
* Disables any texture from being used when drawing the Mesh.
|
||||
**/
|
||||
void setTexture();
|
||||
|
||||
/**
|
||||
* Gets the texture used when drawing the Mesh. May return null if no
|
||||
* texture is set.
|
||||
**/
|
||||
Texture *getTexture() const;
|
||||
|
||||
/**
|
||||
* Sets the draw mode used when drawing the Mesh.
|
||||
**/
|
||||
void setDrawMode(DrawMode mode);
|
||||
DrawMode getDrawMode() const;
|
||||
|
||||
void setDrawRange(int start, int count);
|
||||
void setDrawRange();
|
||||
bool getDrawRange(int &start, int &count) const;
|
||||
|
||||
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname);
|
||||
|
||||
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
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);
|
||||
|
||||
static bool getConstant(const char *in, AttributeStep &out);
|
||||
static bool getConstant(AttributeStep in, const char *&out);
|
||||
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) override;
|
||||
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) override;
|
||||
|
||||
private:
|
||||
|
||||
struct AttachedAttribute
|
||||
{
|
||||
Mesh *mesh;
|
||||
int index;
|
||||
AttributeStep step;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
std::vector<AttribFormat> vertexFormat;
|
||||
std::vector<size_t> attributeSizes;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
|
||||
|
||||
// Vertex buffer, for the vertex data.
|
||||
Buffer *vbo;
|
||||
size_t vertexCount;
|
||||
size_t vertexStride;
|
||||
|
||||
// Block of memory whose size is at least as large as a single vertex. Helps
|
||||
// avoid memory allocations when using Mesh::setVertex etc.
|
||||
char *vertexScratchBuffer;
|
||||
|
||||
// Element (vertex index) buffer, for the vertex map.
|
||||
Buffer *ibo;
|
||||
bool useIndexBuffer;
|
||||
size_t elementCount;
|
||||
IndexDataType elementDataType;
|
||||
|
||||
DrawMode drawMode;
|
||||
|
||||
int rangeStart;
|
||||
int rangeCount;
|
||||
|
||||
StrongRef<Texture> texture;
|
||||
|
||||
static StringMap<DrawMode, DRAWMODE_MAX_ENUM>::Entry drawModeEntries[];
|
||||
static StringMap<DrawMode, DRAWMODE_MAX_ENUM> drawModes;
|
||||
|
||||
static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[];
|
||||
static StringMap<DataType, DATA_MAX_ENUM> dataTypes;
|
||||
|
||||
static StringMap<AttributeStep, STEP_MAX_ENUM>::Entry attributeStepEntries[];
|
||||
static StringMap<AttributeStep, STEP_MAX_ENUM> attributeSteps;
|
||||
|
||||
}; // Mesh
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_MESH_H
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "OpenGL.h"
|
||||
|
||||
// LOVE
|
||||
#include "graphics/Buffer.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/Graphics.h"
|
||||
|
||||
@@ -41,199 +42,13 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
love::Type SpriteBatch::type("SpriteBatch", &Drawable::type);
|
||||
|
||||
SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage)
|
||||
: texture(texture)
|
||||
, size(size)
|
||||
, next(0)
|
||||
, color(0)
|
||||
, array_buf(nullptr)
|
||||
, quad_indices(gfx, size)
|
||||
, range_start(-1)
|
||||
, range_count(-1)
|
||||
: love::graphics::SpriteBatch(gfx, texture, size, usage)
|
||||
{
|
||||
if (size <= 0)
|
||||
throw love::Exception("Invalid SpriteBatch size.");
|
||||
|
||||
size_t vertex_size = sizeof(Vertex) * 4 * size;
|
||||
|
||||
array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
|
||||
}
|
||||
|
||||
SpriteBatch::~SpriteBatch()
|
||||
{
|
||||
delete color;
|
||||
delete array_buf;
|
||||
}
|
||||
|
||||
int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/)
|
||||
{
|
||||
if (index < -1 || index >= size)
|
||||
throw love::Exception("Invalid sprite index: %d", index + 1);
|
||||
|
||||
if (index == -1 && next >= size)
|
||||
setBufferSize(size * 2);
|
||||
|
||||
addv(texture->getVertices(), m, (index == -1) ? next : index);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
return next++;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int SpriteBatch::addq(Quad *quad, const Matrix4 &m, int index /*= -1*/)
|
||||
{
|
||||
if (index < -1 || index >= size)
|
||||
throw love::Exception("Invalid sprite index: %d", index + 1);
|
||||
|
||||
if (index == -1 && next >= size)
|
||||
setBufferSize(size * 2);
|
||||
|
||||
addv(quad->getVertices(), m, (index == -1) ? next : index);
|
||||
|
||||
// Increment counter.
|
||||
if (index == -1)
|
||||
return next++;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void SpriteBatch::clear()
|
||||
{
|
||||
// Reset the position of the next index.
|
||||
next = 0;
|
||||
}
|
||||
|
||||
void SpriteBatch::flush()
|
||||
{
|
||||
array_buf->unmap();
|
||||
}
|
||||
|
||||
void SpriteBatch::setTexture(Texture *newtexture)
|
||||
{
|
||||
texture.set(newtexture);
|
||||
}
|
||||
|
||||
Texture *SpriteBatch::getTexture() const
|
||||
{
|
||||
return texture.get();
|
||||
}
|
||||
|
||||
void SpriteBatch::setColor(const Color &color)
|
||||
{
|
||||
if (!this->color)
|
||||
this->color = new Color(color);
|
||||
else
|
||||
*(this->color) = color;
|
||||
}
|
||||
|
||||
void SpriteBatch::setColor()
|
||||
{
|
||||
delete color;
|
||||
color = nullptr;
|
||||
}
|
||||
|
||||
const Color *SpriteBatch::getColor() const
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
int SpriteBatch::getCount() const
|
||||
{
|
||||
return next;
|
||||
}
|
||||
|
||||
void SpriteBatch::setBufferSize(int newsize)
|
||||
{
|
||||
if (newsize <= 0)
|
||||
throw love::Exception("Invalid SpriteBatch size.");
|
||||
|
||||
if (newsize == size)
|
||||
return;
|
||||
|
||||
size_t vertex_size = sizeof(Vertex) * 4 * newsize;
|
||||
love::graphics::Buffer *new_array_buf = nullptr;
|
||||
|
||||
int new_next = std::min(next, newsize);
|
||||
|
||||
try
|
||||
{
|
||||
auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
|
||||
new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
|
||||
|
||||
// Copy as much of the old data into the new GLBuffer as can fit.
|
||||
size_t copy_size = sizeof(Vertex) * 4 * new_next;
|
||||
array_buf->copyTo(0, copy_size, new_array_buf, 0);
|
||||
|
||||
quad_indices = QuadIndices(gfx, newsize);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
delete new_array_buf;
|
||||
throw;
|
||||
}
|
||||
|
||||
// We don't need to unmap the old GLBuffer since we're deleting it.
|
||||
delete array_buf;
|
||||
|
||||
array_buf = new_array_buf;
|
||||
size = newsize;
|
||||
|
||||
next = new_next;
|
||||
}
|
||||
|
||||
int SpriteBatch::getBufferSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
|
||||
{
|
||||
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);
|
||||
|
||||
auto it = attached_attributes.find(name);
|
||||
if (it != attached_attributes.end())
|
||||
oldattrib = it->second;
|
||||
|
||||
newattrib.index = mesh->getAttributeIndex(name);
|
||||
|
||||
if (newattrib.index < 0)
|
||||
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
|
||||
|
||||
newattrib.mesh = mesh;
|
||||
|
||||
attached_attributes[name] = newattrib;
|
||||
}
|
||||
|
||||
void SpriteBatch::setDrawRange(int start, int count)
|
||||
{
|
||||
if (start < 0 || count <= 0)
|
||||
throw love::Exception("Invalid draw range.");
|
||||
|
||||
range_start = start;
|
||||
range_count = count;
|
||||
}
|
||||
|
||||
void SpriteBatch::setDrawRange()
|
||||
{
|
||||
range_start = range_count = -1;
|
||||
}
|
||||
|
||||
bool SpriteBatch::getDrawRange(int &start, int &count) const
|
||||
{
|
||||
if (range_start < 0 || range_count <= 0)
|
||||
return false;
|
||||
|
||||
start = range_start;
|
||||
count = range_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
@@ -308,27 +123,6 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index)
|
||||
{
|
||||
// Needed for colors.
|
||||
Vertex sprite[4] = {v[0], v[1], v[2], v[3]};
|
||||
const size_t sprite_size = 4 * sizeof(Vertex); // bytecount
|
||||
|
||||
m.transform(sprite, sprite, 4);
|
||||
|
||||
if (color != nullptr)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
sprite[i].color = *color;
|
||||
}
|
||||
|
||||
// Always keep the VBO mapped when adding data for now (it'll be unmapped
|
||||
// on draw.)
|
||||
array_buf->map();
|
||||
|
||||
array_buf->fill(index * sprite_size, sprite_size, sprite);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -18,139 +18,30 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
|
||||
#define LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
|
||||
|
||||
// C
|
||||
#include <cstring>
|
||||
|
||||
// C++
|
||||
#include <unordered_map>
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/math.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "graphics/Color.h"
|
||||
#include "graphics/Quad.h"
|
||||
#include "graphics/Buffer.h"
|
||||
#include "Mesh.h"
|
||||
#include "graphics/SpriteBatch.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
class Texture;
|
||||
class Graphics;
|
||||
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class SpriteBatch : public Drawable
|
||||
class SpriteBatch : public love::graphics::SpriteBatch
|
||||
{
|
||||
public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
|
||||
virtual ~SpriteBatch();
|
||||
|
||||
int add(const Matrix4 &m, int index = -1);
|
||||
int addq(Quad *quad, const Matrix4 &m, int index = -1);
|
||||
void clear();
|
||||
|
||||
void flush();
|
||||
|
||||
void setTexture(Texture *newtexture);
|
||||
Texture *getTexture() const;
|
||||
|
||||
/**
|
||||
* Set the current color for this SpriteBatch. The sprites added
|
||||
* after this call will use this color. Note that global color
|
||||
* will not longer apply to the SpriteBatch if this is used.
|
||||
*
|
||||
* @param color The color to use for the following sprites.
|
||||
*/
|
||||
void setColor(const Color &color);
|
||||
|
||||
/**
|
||||
* Disable per-sprite colors for this SpriteBatch. The next call to
|
||||
* draw will use the global color for all sprites.
|
||||
*/
|
||||
void setColor();
|
||||
|
||||
/**
|
||||
* Get the current color for this SpriteBatch. Returns NULL if no color is
|
||||
* set.
|
||||
**/
|
||||
const Color *getColor() const;
|
||||
|
||||
/**
|
||||
* Get the number of sprites currently in this SpriteBatch.
|
||||
**/
|
||||
int getCount() const;
|
||||
|
||||
/**
|
||||
* Get the total number of sprites this SpriteBatch can currently hold.
|
||||
**/
|
||||
int getBufferSize() const;
|
||||
|
||||
/**
|
||||
* 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 setDrawRange(int start, int count);
|
||||
void setDrawRange();
|
||||
bool getDrawRange(int &start, int &count) const;
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
private:
|
||||
|
||||
struct AttachedAttribute
|
||||
{
|
||||
StrongRef<Mesh> mesh;
|
||||
int index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the total number of sprites this SpriteBatch can hold.
|
||||
* Leaves existing sprite data intact when possible.
|
||||
**/
|
||||
void setBufferSize(int newsize);
|
||||
|
||||
void addv(const Vertex *v, const Matrix4 &m, int index);
|
||||
|
||||
StrongRef<Texture> texture;
|
||||
|
||||
// Max number of sprites in the batch.
|
||||
int size;
|
||||
|
||||
// The next free element.
|
||||
int next;
|
||||
|
||||
// Current color. This color, if present, will be applied to the next
|
||||
// added sprite.
|
||||
Color *color;
|
||||
|
||||
love::graphics::Buffer *array_buf;
|
||||
QuadIndices quad_indices;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
|
||||
|
||||
int range_start;
|
||||
int range_count;
|
||||
|
||||
}; // SpriteBatch
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
|
||||
|
||||
@@ -651,7 +651,7 @@ int w_newSpriteBatch(lua_State *L)
|
||||
return luaL_error(L, "Invalid usage hint: %s", usagestr);
|
||||
}
|
||||
|
||||
SpriteBatch *t = nullptr;
|
||||
love::graphics::SpriteBatch *t = nullptr;
|
||||
luax_catchexcept(L,
|
||||
[&](){ t = instance()->newSpriteBatch(texture, size, usage); }
|
||||
);
|
||||
@@ -667,7 +667,7 @@ int w_newParticleSystem(lua_State *L)
|
||||
|
||||
Texture *texture = luax_checktexture(L, 1);
|
||||
lua_Number size = luaL_optnumber(L, 2, 1000);
|
||||
ParticleSystem *t = 0;
|
||||
love::graphics::ParticleSystem *t = nullptr;
|
||||
if (size < 1.0 || size > ParticleSystem::MAX_PARTICLES)
|
||||
return luaL_error(L, "Invalid ParticleSystem size");
|
||||
|
||||
@@ -882,9 +882,9 @@ static Mesh::DrawMode luax_optmeshdrawmode(lua_State *L, int idx, Mesh::DrawMode
|
||||
return def;
|
||||
}
|
||||
|
||||
static Mesh *newStandardMesh(lua_State *L)
|
||||
static love::graphics::Mesh *newStandardMesh(lua_State *L)
|
||||
{
|
||||
Mesh *t = nullptr;
|
||||
love::graphics::Mesh *t = nullptr;
|
||||
|
||||
Mesh::DrawMode drawmode = luax_optmeshdrawmode(L, 2, Mesh::DRAWMODE_FAN);
|
||||
vertex::Usage usage = luax_optmeshusage(L, 3, vertex::USAGE_DYNAMIC);
|
||||
@@ -938,9 +938,9 @@ static Mesh *newStandardMesh(lua_State *L)
|
||||
return t;
|
||||
}
|
||||
|
||||
static Mesh *newCustomMesh(lua_State *L)
|
||||
static love::graphics::Mesh *newCustomMesh(lua_State *L)
|
||||
{
|
||||
Mesh *t = nullptr;
|
||||
love::graphics::Mesh *t = nullptr;
|
||||
|
||||
// First argument is the vertex format, second is a table of vertices or
|
||||
// the number of vertices.
|
||||
@@ -1067,7 +1067,7 @@ int w_newMesh(lua_State *L)
|
||||
if (arg1type != LUA_TTABLE && arg1type != LUA_TNUMBER)
|
||||
luaL_argerror(L, 1, "table or number expected");
|
||||
|
||||
Mesh *t = nullptr;
|
||||
love::graphics::Mesh *t = nullptr;
|
||||
|
||||
int arg2type = lua_type(L, 2);
|
||||
if (arg1type == LUA_TTABLE && (arg2type == LUA_TTABLE || arg2type == LUA_TNUMBER || arg2type == LUA_TUSERDATA))
|
||||
@@ -1671,7 +1671,7 @@ int w_draw(lua_State *L)
|
||||
|
||||
int w_drawInstanced(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
love::graphics::Mesh *t = luax_checkmesh(L, 1);
|
||||
int instancecount = (int) luaL_checkinteger(L, 2);
|
||||
|
||||
if (luax_istype(L, 3, math::Transform::type))
|
||||
|
||||
@@ -26,11 +26,11 @@
|
||||
#include "graphics/wrap_Font.h"
|
||||
#include "graphics/wrap_Image.h"
|
||||
#include "graphics/wrap_Quad.h"
|
||||
#include "wrap_SpriteBatch.h"
|
||||
#include "wrap_ParticleSystem.h"
|
||||
#include "graphics/wrap_SpriteBatch.h"
|
||||
#include "graphics/wrap_ParticleSystem.h"
|
||||
#include "graphics/wrap_Canvas.h"
|
||||
#include "graphics/wrap_Shader.h"
|
||||
#include "wrap_Mesh.h"
|
||||
#include "graphics/wrap_Mesh.h"
|
||||
#include "graphics/wrap_Text.h"
|
||||
#include "graphics/wrap_Video.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
@@ -22,18 +22,15 @@
|
||||
#include "wrap_Mesh.h"
|
||||
#include "Image.h"
|
||||
#include "Canvas.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
#include "wrap_Texture.h"
|
||||
|
||||
// C++
|
||||
#include <typeinfo>
|
||||
#include <algorithm>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Mesh *luax_checkmesh(lua_State *L, int idx)
|
||||
{
|
||||
@@ -570,6 +567,5 @@ extern "C" int luaopen_mesh(lua_State *L)
|
||||
return luax_register_type(L, &Mesh::type, w_Mesh_functions, nullptr);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -18,8 +18,7 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
|
||||
#define LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
@@ -30,8 +29,6 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
char *luax_writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, int components, char *data);
|
||||
const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data);
|
||||
@@ -39,8 +36,5 @@ const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int compon
|
||||
Mesh *luax_checkmesh(lua_State *L, int idx);
|
||||
extern "C" int luaopen_mesh(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
|
||||
+1
-4
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "Image.h"
|
||||
#include "Canvas.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
#include "wrap_Texture.h"
|
||||
|
||||
// C
|
||||
#include <cstring>
|
||||
@@ -33,8 +33,6 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx)
|
||||
{
|
||||
@@ -772,6 +770,5 @@ extern "C" int luaopen_particlesystem(lua_State *L)
|
||||
return luax_register_type(L, &ParticleSystem::type, w_ParticleSystem_functions, nullptr);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
+1
-7
@@ -18,8 +18,7 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H
|
||||
#define LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/runtime.h"
|
||||
@@ -29,14 +28,9 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx);
|
||||
extern "C" int luaopen_particlesystem(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_WRAP_PARTICLE_SYSTEM_H
|
||||
+1
-7
@@ -22,18 +22,13 @@
|
||||
#include "wrap_SpriteBatch.h"
|
||||
#include "Image.h"
|
||||
#include "Canvas.h"
|
||||
#include "graphics/wrap_Texture.h"
|
||||
#include "wrap_Texture.h"
|
||||
#include "math/wrap_Transform.h"
|
||||
|
||||
// C++
|
||||
#include <typeinfo>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
SpriteBatch *luax_checkspritebatch(lua_State *L, int idx)
|
||||
{
|
||||
@@ -274,6 +269,5 @@ extern "C" int luaopen_spritebatch(lua_State *L)
|
||||
return luax_register_type(L, &SpriteBatch::type, w_SpriteBatch_functions, nullptr);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
+1
-7
@@ -18,8 +18,7 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H
|
||||
#define LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H
|
||||
#pragma once
|
||||
|
||||
#include "common/runtime.h"
|
||||
#include "SpriteBatch.h"
|
||||
@@ -28,14 +27,9 @@ namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
SpriteBatch *luax_checkspritebatch(lua_State *L, int idx);
|
||||
extern "C" int luaopen_spritebatch(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_WRAP_SPRITE_BATCH_H
|
||||
Reference in New Issue
Block a user