Skip to content

Commit

Permalink
#5584: Refactor vertex and index storage to private class, to allow f…
Browse files Browse the repository at this point in the history
…or potential replacement with existing types like the IndexedVertexBuffer.
  • Loading branch information
codereader committed Nov 28, 2021
1 parent e14ad68 commit acc8ac5
Showing 1 changed file with 126 additions and 82 deletions.
208 changes: 126 additions & 82 deletions radiantcore/rendersystem/backend/SurfaceRenderer.h
Expand Up @@ -9,18 +9,117 @@ class SurfaceRenderer :
public ISurfaceRenderer
{
private:
struct VertexBuffer
class VertexBuffer
{
GLenum mode;
std::vector<ArbitraryMeshVertex> vertices;
std::vector<unsigned int> indices;
private:
GLenum _mode;

std::vector<ArbitraryMeshVertex> _vertices;
std::vector<unsigned int> _indices;
public:
VertexBuffer(GLenum mode) :
_mode(mode)
{}

bool empty() const
{
return _indices.empty();
}

void render() const
{
if (_indices.empty()) return;

glVertexPointer(3, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &_vertices.front().vertex);
glTexCoordPointer(2, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &_vertices.front().texcoord);
glNormalPointer(GL_DOUBLE, sizeof(ArbitraryMeshVertex), &_vertices.front().normal);

glDrawElements(_mode, static_cast<GLsizei>(_indices.size()), GL_UNSIGNED_INT, &_indices.front());
}

void renderIndexRange(std::size_t firstIndex, std::size_t numIndices) const
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);

glFrontFace(GL_CW);

glVertexPointer(3, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &_vertices.front().vertex);
glTexCoordPointer(2, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &_vertices.front().texcoord);
glNormalPointer(GL_DOUBLE, sizeof(ArbitraryMeshVertex), &_vertices.front().normal);

glDrawElements(_mode, static_cast<GLsizei>(numIndices), GL_UNSIGNED_INT, &_indices[firstIndex]);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

// Returns the vertex and index offsets in this buffer
std::pair<std::size_t, std::size_t> addSurface(const std::vector<ArbitraryMeshVertex>& vertices,
const std::vector<unsigned int>& indices)
{
auto vertexOffset = _vertices.size();
auto indexOffset = _indices.size();

std::copy(vertices.begin(), vertices.end(), std::back_inserter(_vertices));

for (auto index : indices)
{
_indices.push_back(index + static_cast<unsigned int>(vertexOffset));
}

return { vertexOffset, indexOffset };
}

void updateSurface(std::size_t firstVertex, std::size_t firstIndex,
const std::vector<ArbitraryMeshVertex>& vertices,
const std::vector<unsigned int>& indices)
{
// Copy the data to the correct slot in the array
std::copy(vertices.begin(), vertices.end(), _vertices.begin() + firstVertex);

// Before assignment, the indices need to be shifted to match the array offset of the vertices
auto targetIndex = _indices.begin() + firstIndex;
auto indexShift = static_cast<unsigned int>(firstVertex);

for (auto index : indices)
{
*targetIndex++ = index + indexShift;
}
}

// Cuts out the vertices and indices, adjusts all indices located to the right of the cut
void removeSurface(std::size_t firstVertex, std::size_t numVertices, std::size_t firstIndex, std::size_t numIndices)
{
// Cut out the vertices
auto firstVertexToRemove = _vertices.begin() + firstVertex;
_vertices.erase(firstVertexToRemove, firstVertexToRemove + numVertices);

// Shift all indices to the left, offsetting their values by the number of removed vertices
auto offsetToApply = -static_cast<int>(numVertices);

auto targetIndex = _indices.begin() + firstIndex;
auto indexToMove = targetIndex + numIndices;

auto indexEnd = _indices.end();
while (indexToMove != indexEnd)
{
*targetIndex++ = *indexToMove++ + offsetToApply;
}

// Cut off the tail of the indices
_indices.resize(_indices.size() - numIndices);
}
};

VertexBuffer _triangleBuffer;
VertexBuffer _quadBuffer;
std::vector<VertexBuffer> _buffers;

static constexpr std::size_t InvalidVertexIndex = std::numeric_limits<std::size_t>::max();

// Internal information about where the chunk of indexed vertex data is located:
// Which buffer they're in, and the data offset and count within the buffer.
// This is enough information to access, replace or remove the data at a later point.
struct SlotInfo
{
std::uint8_t bucketIndex;
Expand All @@ -38,13 +137,18 @@ class SurfaceRenderer :
SurfaceRenderer() :
_freeSlotMappingHint(InvalidSlotMapping)
{
_triangleBuffer.mode = GL_TRIANGLES;
_quadBuffer.mode = GL_QUADS;
_buffers.emplace_back(GL_TRIANGLES);
_buffers.emplace_back(GL_QUADS);
}

bool empty() const
{
return _triangleBuffer.vertices.empty() && _quadBuffer.vertices.empty();
for (const auto& buffer : _buffers)
{
if (!buffer.empty()) return false;
}

return true;
}

Slot addSurface(SurfaceIndexingType indexType, const std::vector<ArbitraryMeshVertex>& vertices,
Expand All @@ -54,26 +158,17 @@ class SurfaceRenderer :
auto& bucket = getBucketByIndex(bucketIndex);

// Allocate a slot
auto oldVertexSize = bucket.vertices.size();
auto oldIndexSize = bucket.indices.size();

auto newSlotIndex = getNextFreeSlotMapping();

auto& slot = _slots.at(newSlotIndex);


auto [vertexOffset, indexOffset] = bucket.addSurface(vertices, indices);

slot.bucketIndex = bucketIndex;
slot.firstVertex = oldVertexSize;
slot.firstVertex = vertexOffset;
slot.numVertices = vertices.size();
slot.firstIndex = oldIndexSize;
slot.firstIndex = indexOffset;
slot.numIndices = indices.size();

std::copy(vertices.begin(), vertices.end(), std::back_inserter(bucket.vertices));

for (auto index : indices)
{
bucket.indices.push_back(index + static_cast<unsigned int>(oldVertexSize));
}

return newSlotIndex;
}

Expand All @@ -82,24 +177,7 @@ class SurfaceRenderer :
auto& slotInfo = _slots.at(slot);
auto& bucket = getBucketByIndex(slotInfo.bucketIndex);

// Cut out the vertices
auto firstVertexToRemove = bucket.vertices.begin() + slotInfo.firstVertex;
bucket.vertices.erase(firstVertexToRemove, firstVertexToRemove + slotInfo.numVertices);

// Shift all indices to the left, offsetting their values by the number of removed vertices
auto offsetToApply = -static_cast<int>(slotInfo.numVertices);

auto targetIndex = bucket.indices.begin() + slotInfo.firstIndex;
auto indexToMove = targetIndex + slotInfo.numIndices;

auto indexEnd = bucket.indices.end();
while (indexToMove != indexEnd)
{
*targetIndex++ = *indexToMove++ + offsetToApply;
}

// Cut off the tail of the indices
bucket.indices.resize(bucket.indices.size() - slotInfo.numIndices);
bucket.removeSurface(slotInfo.firstVertex, slotInfo.numVertices, slotInfo.firstIndex, slotInfo.numIndices);

// Adjust all offsets in other slots
for (auto& slot : _slots)
Expand Down Expand Up @@ -129,68 +207,34 @@ class SurfaceRenderer :
auto& slotInfo = _slots.at(slot);
auto& bucket = getBucketByIndex(slotInfo.bucketIndex);

// Copy the data to the correct slot in the array
std::copy(vertices.begin(), vertices.end(), bucket.vertices.begin() + slotInfo.firstVertex);

// Before assignment, the indices need to be shifted to match the array offset of the vertices
auto targetIndex = bucket.indices.begin() + slotInfo.firstIndex;
auto indexShift = static_cast<unsigned int>(slotInfo.firstVertex);

for (auto index : indices)
{
*targetIndex++ = index + indexShift;
}
bucket.updateSurface(slotInfo.firstVertex, slotInfo.firstIndex, vertices, indices);
}

void render()
{
renderBuffer(_triangleBuffer);
renderBuffer(_quadBuffer);
for (auto& buffer : _buffers)
{
buffer.render();
}
}

void renderSurface(Slot slot) override
{
auto& slotInfo = _slots.at(slot);
auto& buffer = getBucketByIndex(slotInfo.bucketIndex);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);

glFrontFace(GL_CW);

glVertexPointer(3, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &buffer.vertices.front().vertex);
glTexCoordPointer(2, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &buffer.vertices.front().texcoord);
glNormalPointer(GL_DOUBLE, sizeof(ArbitraryMeshVertex), &buffer.vertices.front().normal);

glDrawElements(buffer.mode, static_cast<GLsizei>(slotInfo.numIndices), GL_UNSIGNED_INT, &buffer.indices[slotInfo.firstIndex]);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
buffer.renderIndexRange(slotInfo.firstIndex, slotInfo.numIndices);
}

private:

void renderBuffer(const VertexBuffer& buffer)
{
if (!buffer.indices.empty())
{
glVertexPointer(3, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &buffer.vertices.front().vertex);
glTexCoordPointer(2, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &buffer.vertices.front().texcoord);
glNormalPointer(GL_DOUBLE, sizeof(ArbitraryMeshVertex), &buffer.vertices.front().normal);

glDrawElements(buffer.mode, static_cast<GLsizei>(buffer.indices.size()), GL_UNSIGNED_INT, &buffer.indices.front());
}
}

constexpr static std::uint8_t GetBucketIndexForIndexType(SurfaceIndexingType indexType)
{
return indexType == SurfaceIndexingType::Triangles ? 0 : 1;
}

VertexBuffer& getBucketByIndex(std::uint8_t bucketIndex)
{
return bucketIndex == 0 ? _triangleBuffer : _quadBuffer;
return _buffers[bucketIndex];
}

ISurfaceRenderer::Slot getNextFreeSlotMapping()
Expand Down

0 comments on commit acc8ac5

Please sign in to comment.