diff --git a/include/igeometryrenderer.h b/include/igeometryrenderer.h index d7b05e33ac..1e252c618f 100644 --- a/include/igeometryrenderer.h +++ b/include/igeometryrenderer.h @@ -14,7 +14,6 @@ enum class GeometryType Triangles, Quads, Lines, - OrientedSurface, // Triangles with a per-surface transformation matrix }; /** @@ -38,8 +37,7 @@ class IGeometryRenderer // The indexType determines the primitive GLenum that is chosen to render this surface virtual Slot addGeometry(GeometryType indexType, const std::vector& vertices, - const std::vector& indices, - const std::function& getTransformCallback = std::function()) = 0; + const std::vector& indices) = 0; // Releases a previously allocated slot. This invalidates the handle. virtual void removeGeometry(Slot slot) = 0; diff --git a/include/irender.h b/include/irender.h index edeee722ea..83b663fbc4 100644 --- a/include/irender.h +++ b/include/irender.h @@ -3,6 +3,7 @@ #include "imodule.h" #include "iwindingrenderer.h" #include "igeometryrenderer.h" +#include "isurfacerenderer.h" #include #include @@ -407,7 +408,8 @@ typedef std::shared_ptr MaterialPtr; */ class Shader : public render::IWindingRenderer, - public render::IGeometryRenderer + public render::IGeometryRenderer, + public render::ISurfaceRenderer { public: // Observer interface to get notified on (un-)realisation diff --git a/include/isurfacerenderer.h b/include/isurfacerenderer.h new file mode 100644 index 0000000000..cf80a23f8e --- /dev/null +++ b/include/isurfacerenderer.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include "render/ArbitraryMeshVertex.h" +#include "math/Matrix4.h" + +namespace render +{ + +/** + * Surface type consisting of triangles, oriented by a transformation matrix. + * + * The ISurfaceRenderer will reacquire the transformation matrix each frame, + * whereas the vertices and indices won't be requested every time. + */ +class IRenderableSurface +{ +public: + virtual ~IRenderableSurface() {} + + // Returns the vertex array of this surface + virtual const std::vector& getVertices() = 0; + + // Returns the indices to render the triangle primitives + virtual const std::vector& getIndices() = 0; + + // The model view matrix used to render this surface + virtual const Matrix4& getSurfaceTransform() = 0; +}; + +/** + * A surface renderer accepts a variable number of IRenderableSurfaces + * each of which is oriented by its own transformation matrix. + * + * Surfaces are rendered separately, the transformation matrix is requested + * each frame before drawing it. + * The vertices and indices of the surface are buffered and won't be requested + * every frame. Invoke the updateSurface() method to schedule an update. + */ +class ISurfaceRenderer +{ +public: + virtual ~ISurfaceRenderer() {} + + using Slot = std::uint64_t; + static constexpr Slot InvalidSlot = std::numeric_limits::max(); + + // Allocate a slot to hold the given surface data, indexed to render triangles. + // Returns the handle which can be used to update or deallocate the data later + virtual Slot addSurface(IRenderableSurface& surface) = 0; + + // Releases a previously allocated slot. This invalidates the handle. + virtual void removeSurface(Slot slot) = 0; + + // Schedules an update of the vertex data contained in the surface. + virtual void updateSurface(Slot slot) = 0; + + // Submits the surface of a single slot to GL + virtual void renderSurface(Slot slot) = 0; +}; + +} diff --git a/libs/render/RenderableBox.h b/libs/render/RenderableBox.h index cb492f6e66..423f29bcf5 100644 --- a/libs/render/RenderableBox.h +++ b/libs/render/RenderableBox.h @@ -1,6 +1,8 @@ #pragma once +#include "isurfacerenderer.h" #include "render/RenderableGeometry.h" +#include "render/RenderableSurface.h" namespace render { @@ -124,7 +126,7 @@ inline std::vector generateTriangleBoxIndices() } class RenderableBox : - public render::RenderableGeometry + public RenderableGeometry { private: const AABB& _bounds; @@ -198,48 +200,40 @@ class RenderableBox : } }; -class RenderableBoxSurface : - public render::RenderableGeometry +class RenderableBoxSurface final : + public RenderableSurface { private: - const AABB& _bounds; const Matrix4& _orientation; - bool _needsUpdate; + + std::vector _vertices; + std::vector _indices; public: RenderableBoxSurface(const AABB& bounds, const Matrix4& orientation) : - _bounds(bounds), - _orientation(orientation), - _needsUpdate(true) - {} - - void queueUpdate() - { - _needsUpdate = true; - } - - virtual void updateGeometry() override + _orientation(orientation) { - if (!_needsUpdate) return; - - _needsUpdate = false; - static Vector3 Origin(0, 0, 0); // Calculate the corner vertices of this bounding box - Vector3 max(Origin + _bounds.extents); - Vector3 min(Origin - _bounds.extents); + Vector3 max(Origin + bounds.extents); + Vector3 min(Origin - bounds.extents); - auto vertices = detail::getFillBoxVertices(min, max, { 1, 1, 1, 1 }); + _vertices = detail::getFillBoxVertices(min, max, { 1, 1, 1, 1 }); + _indices = detail::generateTriangleBoxIndices(); + } - static auto Indices = detail::generateTriangleBoxIndices(); + const std::vector& getVertices() override + { + return _vertices; + } - RenderableGeometry::updateGeometry(render::GeometryType::OrientedSurface, vertices, Indices, - std::bind(&RenderableBoxSurface::getOrientation, this)); + const std::vector& getIndices() override + { + return _indices; } -private: - const Matrix4& getOrientation() const + const Matrix4& getSurfaceTransform() override { return _orientation; } diff --git a/libs/render/RenderableGeometry.h b/libs/render/RenderableGeometry.h index 7dbf7bcb2a..5037acddb5 100644 --- a/libs/render/RenderableGeometry.h +++ b/libs/render/RenderableGeometry.h @@ -103,8 +103,7 @@ class RenderableGeometry : // to ensure that the _shader reference is already up to date. void updateGeometry(GeometryType type, const std::vector& vertices, - const std::vector& indices, - const std::function& getTransformCallback = std::function()) + const std::vector& indices) { // Size changes require removal of the geometry before update if (_lastVertexSize != vertices.size() || _lastIndexSize != indices.size()) @@ -117,7 +116,7 @@ class RenderableGeometry : if (_surfaceSlot == IGeometryRenderer::InvalidSlot) { - _surfaceSlot = _shader->addGeometry(type, vertices, indices, getTransformCallback); + _surfaceSlot = _shader->addGeometry(type, vertices, indices); } else { diff --git a/libs/render/RenderableSurface.h b/libs/render/RenderableSurface.h new file mode 100644 index 0000000000..baa8706412 --- /dev/null +++ b/libs/render/RenderableSurface.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include "isurfacerenderer.h" +#include "irender.h" + +namespace render +{ + +/** + * Surface base type, handling vertex data updates in combination + * with an ISurfaceRenderer instance. + * + * It implements the OpenGLRenderable interface which will instruct + * the shader to render just the surface managed by this object. + * This is used to render highlights (such as selection overlays). + */ +class RenderableSurface : + public IRenderableSurface, + public OpenGLRenderable +{ +private: + ShaderPtr _shader; + ISurfaceRenderer::Slot _surfaceSlot; + + std::size_t _lastVertexSize; // To detect size changes when updating surfaces + std::size_t _lastIndexSize; // To detect size changes when updating surfaces + +protected: + RenderableSurface() : + _surfaceSlot(ISurfaceRenderer::InvalidSlot), + _lastVertexSize(0), + _lastIndexSize(0) + {} + +public: + // Noncopyable + RenderableSurface(const RenderableSurface& other) = delete; + RenderableSurface& operator=(const RenderableSurface& other) = delete; + + virtual ~RenderableSurface() + { + clear(); + } + + // (Non-virtual) update method handling any possible shader change + // The surface is withdrawn from the given shader if it turns out + // to be different from the last update. + void update(const ShaderPtr& shader) + { + bool shaderChanged = _shader != shader; + auto currentVertexSize = getVertices().size(); + auto currentIndexSize = getVertices().size(); + + bool sizeChanged = _lastIndexSize != currentIndexSize || _lastVertexSize != currentVertexSize; + + if (shaderChanged || sizeChanged) + { + clear(); + } + + // Update our local shader reference + _shader = shader; + + if (_shader) + { + _surfaceSlot = _shader->addSurface(*this); + + _lastVertexSize = currentVertexSize; + _lastIndexSize = currentIndexSize; + } + } + + // Removes the surface and clears the shader reference + void clear() + { + if (_shader && _surfaceSlot != ISurfaceRenderer::InvalidSlot) + { + _shader->removeSurface(_surfaceSlot); + } + + _surfaceSlot = ISurfaceRenderer::InvalidSlot; + _shader.reset(); + } + + // Renders the surface stored in our single slot + void render(const RenderInfo& info) const override + { + if (_surfaceSlot != ISurfaceRenderer::InvalidSlot && _shader) + { + _shader->renderSurface(_surfaceSlot); + } + } +}; + +} diff --git a/radiantcore/model/NullModelNode.cpp b/radiantcore/model/NullModelNode.cpp index 015027c8c7..1471dc2bcb 100644 --- a/radiantcore/model/NullModelNode.cpp +++ b/radiantcore/model/NullModelNode.cpp @@ -50,13 +50,6 @@ void NullModelNode::testSelect(Selector& selector, SelectionTest& test) _nullModel->testSelect(selector, test, localToWorld()); } -void NullModelNode::onPreRender(const VolumeTest& volume) -{ - Node::onPreRender(volume); - - _renderableBox.update(_shader); -} - void NullModelNode::renderSolid(IRenderableCollector& collector, const VolumeTest& volume) const { } @@ -75,10 +68,12 @@ void NullModelNode::setRenderSystem(const RenderSystemPtr& renderSystem) if (renderSystem) { _shader = renderSystem->capture(""); + _renderableBox.update(_shader); } else { _shader.reset(); + _renderableBox.clear(); } } @@ -91,7 +86,7 @@ void NullModelNode::onInsertIntoScene(scene::IMapRootNode& root) { Node::onInsertIntoScene(root); - _renderableBox.queueUpdate(); + _renderableBox.update(_shader); } void NullModelNode::onRemoveFromScene(scene::IMapRootNode& root) @@ -101,20 +96,13 @@ void NullModelNode::onRemoveFromScene(scene::IMapRootNode& root) _renderableBox.clear(); } -void NullModelNode::boundsChanged() -{ - Node::boundsChanged(); - - _renderableBox.queueUpdate(); -} - void NullModelNode::onVisibilityChanged(bool isVisibleNow) { Node::onVisibilityChanged(isVisibleNow); if (isVisibleNow) { - _renderableBox.queueUpdate(); + _renderableBox.update(_shader); } else { diff --git a/radiantcore/model/NullModelNode.h b/radiantcore/model/NullModelNode.h index f183988d3e..ccbb8fb824 100644 --- a/radiantcore/model/NullModelNode.h +++ b/radiantcore/model/NullModelNode.h @@ -39,7 +39,6 @@ class NullModelNode final : void testSelect(Selector& selector, SelectionTest& test) override; - void onPreRender(const VolumeTest& volume) override; void renderSolid(IRenderableCollector& collector, const VolumeTest& volume) const override; void renderWireframe(IRenderableCollector& collector, const VolumeTest& volume) const override; void renderHighlights(IRenderableCollector& collector, const VolumeTest& volume) override; @@ -56,8 +55,6 @@ class NullModelNode final : void onInsertIntoScene(scene::IMapRootNode& root) override; void onRemoveFromScene(scene::IMapRootNode& root) override; - void boundsChanged() override; - protected: void onVisibilityChanged(bool isVisibleNow) override; }; diff --git a/radiantcore/rendersystem/backend/GeometryRenderer.h b/radiantcore/rendersystem/backend/GeometryRenderer.h index d312763561..be0fad6123 100644 --- a/radiantcore/rendersystem/backend/GeometryRenderer.h +++ b/radiantcore/rendersystem/backend/GeometryRenderer.h @@ -119,26 +119,19 @@ class GeometryRenderer : std::size_t numVertices; std::size_t firstIndex; std::size_t numIndices; - - // Matrix getter used for oriented surfaces - std::function getTransform; }; std::vector _slots; static constexpr std::size_t InvalidSlotMapping = std::numeric_limits::max(); std::size_t _freeSlotMappingHint; - const std::uint8_t _orientedSurfaceBufferIndex; - public: GeometryRenderer() : - _freeSlotMappingHint(InvalidSlotMapping), - _orientedSurfaceBufferIndex(GetBucketIndexForIndexType(GeometryType::OrientedSurface)) + _freeSlotMappingHint(InvalidSlotMapping) { _buffers.emplace_back(GL_TRIANGLES); _buffers.emplace_back(GL_QUADS); _buffers.emplace_back(GL_LINES); - _buffers.emplace_back(GL_TRIANGLES); // oriented surfaces } bool empty() const @@ -152,8 +145,7 @@ class GeometryRenderer : } Slot addGeometry(GeometryType indexType, const std::vector& vertices, - const std::vector& indices, - const std::function& getTransformCallback) override + const std::vector& indices) override { auto bufferIndex = GetBucketIndexForIndexType(indexType); auto& buffer = getBucketByIndex(bufferIndex); @@ -169,7 +161,6 @@ class GeometryRenderer : slot.numVertices = vertices.size(); slot.firstIndex = indexOffset; slot.numIndices = indices.size(); - slot.getTransform = getTransformCallback; return newSlotIndex; } @@ -201,7 +192,6 @@ class GeometryRenderer : slotInfo.firstVertex = InvalidVertexIndex; slotInfo.firstIndex = 0; slotInfo.numIndices = 0; - slotInfo.getTransform = std::function(); if (slot < _freeSlotMappingHint) { @@ -236,33 +226,11 @@ class GeometryRenderer : glFrontFace(GL_CW); - for (auto indexType : { GeometryType::Triangles, GeometryType::Quads, GeometryType::Lines }) + for (auto& buffer : _buffers) { - auto bufferIndex = GetBucketIndexForIndexType(indexType); - auto& buffer = getBucketByIndex(bufferIndex); - buffer.render(); } - auto& orientedSurfaceBuffer = getBucketByIndex(_orientedSurfaceBufferIndex); - - for (auto& slot : _slots) - { - if (slot.bucketIndex != _orientedSurfaceBufferIndex) - { - continue; - } - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - - glMultMatrixd(slot.getTransform()); - - orientedSurfaceBuffer.renderIndexRange(slot.firstIndex, slot.numIndices); - - glPopMatrix(); - } - glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } @@ -281,21 +249,7 @@ class GeometryRenderer : auto& slotInfo = _slots.at(slot); auto& buffer = getBucketByIndex(slotInfo.bucketIndex); - if (slotInfo.getTransform) - { - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - - glMultMatrixd(slotInfo.getTransform()); - - buffer.renderIndexRange(slotInfo.firstIndex, slotInfo.numIndices); - - glPopMatrix(); - } - else - { - buffer.renderIndexRange(slotInfo.firstIndex, slotInfo.numIndices); - } + buffer.renderIndexRange(slotInfo.firstIndex, slotInfo.numIndices); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); diff --git a/radiantcore/rendersystem/backend/OpenGLShader.cpp b/radiantcore/rendersystem/backend/OpenGLShader.cpp index d52dc4ade2..eddb00b9e4 100644 --- a/radiantcore/rendersystem/backend/OpenGLShader.cpp +++ b/radiantcore/rendersystem/backend/OpenGLShader.cpp @@ -145,7 +145,8 @@ void OpenGLShader::drawSurfaces() { //_vertexBuffer->renderAllBatches(GL_TRIANGLES, false); - GeometryRenderer::render(); + _geometryRenderer.render(); + _surfaceRenderer.render(); #if 0 // Render all triangles @@ -209,30 +210,49 @@ void OpenGLShader::clearGeometry() bool OpenGLShader::hasSurfaces() const { - return !GeometryRenderer::empty() || _vertexBuffer && _vertexBuffer->getNumVertices() > 0; + return !_geometryRenderer.empty() || !_surfaceRenderer.empty() || _vertexBuffer && _vertexBuffer->getNumVertices() > 0; } IGeometryRenderer::Slot OpenGLShader::addGeometry(GeometryType indexType, - const std::vector& vertices, const std::vector& indices, - const std::function& getTransformCallback) + const std::vector& vertices, const std::vector& indices) { - return GeometryRenderer::addGeometry(indexType, vertices, indices, getTransformCallback); + return _geometryRenderer.addGeometry(indexType, vertices, indices); } void OpenGLShader::removeGeometry(IGeometryRenderer::Slot slot) { - GeometryRenderer::removeGeometry(slot); + _geometryRenderer.removeGeometry(slot); } void OpenGLShader::updateGeometry(IGeometryRenderer::Slot slot, const std::vector& vertices, const std::vector& indices) { - GeometryRenderer::updateGeometry(slot, vertices, indices); + _geometryRenderer.updateGeometry(slot, vertices, indices); } void OpenGLShader::renderGeometry(IGeometryRenderer::Slot slot) { - GeometryRenderer::renderGeometry(slot); + _geometryRenderer.renderGeometry(slot); +} + +ISurfaceRenderer::Slot OpenGLShader::addSurface(IRenderableSurface& surface) +{ + return _surfaceRenderer.addSurface(surface); +} + +void OpenGLShader::removeSurface(ISurfaceRenderer::Slot slot) +{ + _surfaceRenderer.removeSurface(slot); +} + +void OpenGLShader::updateSurface(ISurfaceRenderer::Slot slot) +{ + _surfaceRenderer.updateSurface(slot); +} + +void OpenGLShader::renderSurface(ISurfaceRenderer::Slot slot) +{ + _surfaceRenderer.renderSurface(slot); } IWindingRenderer::Slot OpenGLShader::addWinding(const std::vector& vertices) diff --git a/radiantcore/rendersystem/backend/OpenGLShader.h b/radiantcore/rendersystem/backend/OpenGLShader.h index 79039b9fe4..319e835497 100644 --- a/radiantcore/rendersystem/backend/OpenGLShader.h +++ b/radiantcore/rendersystem/backend/OpenGLShader.h @@ -8,6 +8,7 @@ #include "render/IndexedVertexBuffer.h" #include "render/WindingRenderer.h" #include "GeometryRenderer.h" +#include "SurfaceRenderer.h" #include #include @@ -20,9 +21,8 @@ class OpenGLRenderSystem; /** * Implementation of the Shader class. */ -class OpenGLShader final : - public Shader, - protected GeometryRenderer +class OpenGLShader final : + public Shader { private: // Name used to construct the shader @@ -55,6 +55,8 @@ class OpenGLShader final : #endif std::unique_ptr _windingRenderer; + GeometryRenderer _geometryRenderer; + SurfaceRenderer _surfaceRenderer; // Each shader can be used by either camera or orthoview, or both std::size_t _enabledViewTypes; @@ -116,12 +118,17 @@ class OpenGLShader final : void drawSurfaces(); IGeometryRenderer::Slot addGeometry(GeometryType indexType, - const std::vector& vertices, const std::vector& indices, - const std::function& getTransformCallback = std::function()) override; + const std::vector& vertices, const std::vector& indices) override; void removeGeometry(IGeometryRenderer::Slot slot) override; void updateGeometry(IGeometryRenderer::Slot slot, const std::vector& vertices, const std::vector& indices) override; void renderGeometry(IGeometryRenderer::Slot slot) override; + + ISurfaceRenderer::Slot addSurface(IRenderableSurface& surface) override; + void removeSurface(ISurfaceRenderer::Slot slot) override; + void updateSurface(ISurfaceRenderer::Slot slot) override; + void renderSurface(ISurfaceRenderer::Slot slot) override; + #ifdef RENDERABLE_GEOMETRY void addGeometry(RenderableGeometry& geometry) override; bool hasGeometry() const; diff --git a/radiantcore/rendersystem/backend/SurfaceRenderer.h b/radiantcore/rendersystem/backend/SurfaceRenderer.h new file mode 100644 index 0000000000..1a45384a69 --- /dev/null +++ b/radiantcore/rendersystem/backend/SurfaceRenderer.h @@ -0,0 +1,247 @@ +#pragma once + +#include "igeometryrenderer.h" + +namespace render +{ + +class SurfaceRenderer : + public ISurfaceRenderer +{ +private: + class VertexBuffer + { + private: + GLenum _mode; + + std::vector _vertices; + std::vector _indices; + public: + VertexBuffer(GLenum mode) : + _mode(mode) + {} + + bool empty() const + { + return _indices.empty(); + } + + void clear() + { + _vertices.clear(); + _indices.clear(); + } + + 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); + glColorPointer(4, GL_DOUBLE, sizeof(ArbitraryMeshVertex), &_vertices.front().colour); + + glDrawElements(_mode, static_cast(_indices.size()), GL_UNSIGNED_INT, &_indices.front()); + } + + void renderIndexRange(std::size_t firstIndex, std::size_t numIndices) const + { + 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(numIndices), GL_UNSIGNED_INT, &_indices[firstIndex]); + } + + // Returns the vertex and index offsets in this buffer + std::pair addSurface(const std::vector& vertices, + const std::vector& 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(vertexOffset)); + } + + return { vertexOffset, indexOffset }; + } + + void updateSurface(std::size_t firstVertex, std::size_t firstIndex, + const std::vector& vertices, + const std::vector& 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(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(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); + } + }; + + struct SurfaceInfo + { + std::reference_wrapper surface; + bool surfaceDataChanged; + VertexBuffer buffer; + + SurfaceInfo(IRenderableSurface& surface_) : + surface(surface_), + buffer(GL_TRIANGLES), + surfaceDataChanged(true) + {} + }; + std::map _surfaces; + + Slot _freeSlotMappingHint; + +public: + SurfaceRenderer() : + _freeSlotMappingHint(0) + {} + + bool empty() const + { + return _surfaces.empty(); + } + + Slot addSurface(IRenderableSurface& surface) override + { + // Find a free slot + auto newSlotIndex = getNextFreeSlotIndex(); + + _surfaces.emplace(newSlotIndex, surface); + + return newSlotIndex; + } + + void removeSurface(Slot slot) override + { + // Remove the surface + _surfaces.erase(slot); + + if (slot < _freeSlotMappingHint) + { + _freeSlotMappingHint = slot; + } + } + + void updateSurface(Slot slot) override + { + _surfaces.at(slot).surfaceDataChanged = true; + } + + void render() + { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + // Render this slot without any vertex colours + glDisableClientState(GL_COLOR_ARRAY); + + glFrontFace(GL_CW); + + for (auto& surface : _surfaces) + { + renderSlot(surface.second); + } + + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + void renderSurface(Slot slot) override + { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + // Render this slot without any vertex colours + glDisableClientState(GL_COLOR_ARRAY); + + glFrontFace(GL_CW); + + renderSlot(_surfaces.at(slot)); + + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + +private: + void renderSlot(SurfaceInfo& slot) + { + auto& surface = slot.surface.get(); + + // Update the vertex data now if necessary + if (slot.surfaceDataChanged) + { + slot.surfaceDataChanged = false; + + slot.buffer.clear(); + slot.buffer.addSurface(surface.getVertices(), surface.getIndices()); + } + + if (slot.buffer.empty()) + { + return; + } + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glMultMatrixd(surface.getSurfaceTransform()); + + slot.buffer.render(); + + glPopMatrix(); + } + + Slot getNextFreeSlotIndex() + { + for (auto i = _freeSlotMappingHint; i < std::numeric_limits::max(); ++i) + { + if (_surfaces.count(i) == 0) + { + _freeSlotMappingHint = i + 1; // start searching here next time + return i; + } + } + + throw std::runtime_error("SurfaceRenderer ran out of surface slot numbers"); + } +}; + +} diff --git a/tools/msvc/DarkRadiantCore.vcxproj b/tools/msvc/DarkRadiantCore.vcxproj index aa748ddd8c..49a595d3ab 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj +++ b/tools/msvc/DarkRadiantCore.vcxproj @@ -986,6 +986,7 @@ + diff --git a/tools/msvc/DarkRadiantCore.vcxproj.filters b/tools/msvc/DarkRadiantCore.vcxproj.filters index 4d48da3a57..9441070f1c 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj.filters +++ b/tools/msvc/DarkRadiantCore.vcxproj.filters @@ -2265,5 +2265,8 @@ src\entity + + src\rendersystem\backend + \ No newline at end of file diff --git a/tools/msvc/include.vcxproj b/tools/msvc/include.vcxproj index 638baecb24..eb85fd64c0 100644 --- a/tools/msvc/include.vcxproj +++ b/tools/msvc/include.vcxproj @@ -189,6 +189,7 @@ + diff --git a/tools/msvc/include.vcxproj.filters b/tools/msvc/include.vcxproj.filters index d5c05ef354..88ba0117ec 100644 --- a/tools/msvc/include.vcxproj.filters +++ b/tools/msvc/include.vcxproj.filters @@ -164,6 +164,7 @@ + diff --git a/tools/msvc/libs.vcxproj b/tools/msvc/libs.vcxproj index 324ce570f1..b2a8886402 100644 --- a/tools/msvc/libs.vcxproj +++ b/tools/msvc/libs.vcxproj @@ -220,6 +220,7 @@ + diff --git a/tools/msvc/libs.vcxproj.filters b/tools/msvc/libs.vcxproj.filters index 62c026b26e..1076cac78e 100644 --- a/tools/msvc/libs.vcxproj.filters +++ b/tools/msvc/libs.vcxproj.filters @@ -350,6 +350,9 @@ render + + render +