diff --git a/include/irender.h b/include/irender.h index 2bf9b5cec9..456e10c80f 100644 --- a/include/irender.h +++ b/include/irender.h @@ -174,14 +174,14 @@ class IRenderEntity * Associates the given object with this entity and the given shader. * It will be processed during the following lighting mode rendering passes. */ - virtual void addRenderable(const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) = 0; + virtual void addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader) = 0; /** * Removes the object from this entity. */ virtual void removeRenderable(const render::IRenderableObject::Ptr& object) = 0; - using ObjectVisitFunction = std::function; + using ObjectVisitFunction = std::function; /** * Enumerate all entity object (partially) intersecting with the given bounds. diff --git a/include/iwindingrenderer.h b/include/iwindingrenderer.h index 520b7d07bd..d9862974a5 100644 --- a/include/iwindingrenderer.h +++ b/include/iwindingrenderer.h @@ -5,6 +5,8 @@ #include #include "render/ArbitraryMeshVertex.h" +class IRenderEntity; + namespace render { @@ -27,14 +29,17 @@ class IWindingRenderer using Slot = std::uint64_t; static constexpr Slot InvalidSlot = std::numeric_limits::max(); - // Allocate a slot to hold the vertex data of a winding of the given size + // Allocate a slot to hold the vertex data of a winding of the given size. + // The winding will be associated to the given render entity (causing it to be grouped internally + // by the render entities when the surfaces are processed in lit render views). // Returns the handle which can be used to update or deallocate the data later - virtual Slot addWinding(const std::vector& vertices) = 0; + virtual Slot addWinding(const std::vector& vertices, IRenderEntity* entity) = 0; // Releases a previously allocated winding slot. This invalidates the handle. virtual void removeWinding(Slot slot) = 0; - // Sets the winding data + // Updates the winding data. An IRenderEntity change is not supported through updateWinding(), in case the + // winding has to be associated to a different entity, call removeWinding() first. virtual void updateWinding(Slot slot, const std::vector& vertices) = 0; // Mode used to specify how to render a single winding diff --git a/libs/render/RenderableGeometry.h b/libs/render/RenderableGeometry.h index 8a13187ee8..cb7b382348 100644 --- a/libs/render/RenderableGeometry.h +++ b/libs/render/RenderableGeometry.h @@ -173,7 +173,7 @@ class RenderableGeometry : _renderEntity = entity; ensureRenderAdapter(); - _renderEntity->addRenderable(_renderAdapter, _shader); + _renderEntity->addRenderable(_renderAdapter, _shader.get()); } void detachFromEntity() diff --git a/libs/render/RenderableSurface.h b/libs/render/RenderableSurface.h index 0f9444bd17..f1dfcbc9a9 100644 --- a/libs/render/RenderableSurface.h +++ b/libs/render/RenderableSurface.h @@ -106,7 +106,7 @@ class RenderableSurface : } _renderEntity = entity; - _renderEntity->addRenderable(shared_from_this(), shader); + _renderEntity->addRenderable(shared_from_this(), shader.get()); _storageLocation = shader->getSurfaceStorageLocation(_shaders[shader]); } diff --git a/libs/render/WindingRenderer.h b/libs/render/WindingRenderer.h index 41d5657697..c07ffba7d3 100644 --- a/libs/render/WindingRenderer.h +++ b/libs/render/WindingRenderer.h @@ -32,18 +32,24 @@ template<> struct RenderingTraits { constexpr static GLenum Mode() { return GL_LINES; } + + constexpr static bool SupportsEntitySurfaces() { return false; } }; template<> struct RenderingTraits { constexpr static GLenum Mode() { return GL_TRIANGLES; } + + constexpr static bool SupportsEntitySurfaces() { return true; } }; template<> struct RenderingTraits { constexpr static GLenum Mode() { return GL_POLYGON; } + + constexpr static bool SupportsEntitySurfaces() { return false; } }; template @@ -63,6 +69,9 @@ class WindingRenderer : std::vector pendingDeletions; }; + IGeometryStore& _geometryStore; + Shader* _owningShader; + // Maintain one bucket per winding size, allocated on demand std::vector _buckets; @@ -75,26 +84,168 @@ class WindingRenderer : { BucketIndex bucketIndex = InvalidBucketIndex; typename VertexBuffer::Slot slotNumber = InvalidVertexBufferSlot; + IRenderEntity* renderEntity = nullptr; }; std::vector _slots; static constexpr std::size_t InvalidSlotMapping = std::numeric_limits::max(); std::size_t _freeSlotMappingHint; - std::size_t _windings; + std::size_t _windingCount; + + class WindingGroup : + public IRenderableObject + { + private: + WindingRenderer& _owner; + + std::set _slotIndices; + bool _surfaceNeedsRebuild; + AABB _bounds; + bool _boundsNeedUpdate; + + IGeometryStore::Slot _geometrySlot; + + sigc::signal _sigBoundsChanged; + public: + WindingGroup(WindingRenderer& owner) : + _owner(owner), + _surfaceNeedsRebuild(true), + _boundsNeedUpdate(true), + _geometrySlot(std::numeric_limits::max()) + {} + + void addWinding(std::size_t slotMappingIndex) + { + _slotIndices.insert(slotMappingIndex); + _surfaceNeedsRebuild = true; + _sigBoundsChanged.emit(); + } + + void updateWinding(std::size_t slotMappingIndex) + { + _surfaceNeedsRebuild = true; + _sigBoundsChanged.emit(); + } + + void removeWinding(std::size_t slotMappingIndex) + { + _slotIndices.erase(slotMappingIndex); + _surfaceNeedsRebuild = true; + _sigBoundsChanged.emit(); + } + + bool empty() const + { + return _slotIndices.empty(); + } + + bool isVisible() override + { + return !empty(); + } + + const Matrix4& getObjectTransform() override + { + static Matrix4 _identity = Matrix4::getIdentity(); + return _identity; + } + + const AABB& getObjectBounds() override + { + if (_surfaceNeedsRebuild || _boundsNeedUpdate) + { + _boundsNeedUpdate = false; + _bounds = _owner._geometryStore.getBounds(_geometrySlot); + } + + return _bounds; + } + + sigc::signal& signal_boundsChanged() override + { + return _sigBoundsChanged; + } + + IGeometryStore::Slot getStorageLocation() override + { + return _geometrySlot; + } + }; + + class EntityWindings + { + private: + WindingRenderer& _owner; + + std::map> _windingsByEntity; + + public: + EntityWindings(WindingRenderer& owner) : + _owner(owner) + {} + + void addWinding(std::size_t slotMappingIndex) + { + const auto& slot = _owner._slots[slotMappingIndex]; + + // Find or create a surface for the entity + auto existing = _windingsByEntity.find(slot.renderEntity); + + if (existing != _windingsByEntity.end()) + { + existing = _windingsByEntity.emplace(slot.renderEntity, + std::make_shared(_owner)).first; + + // New surface, register this with the entity + slot.renderEntity->addRenderable(existing->second, _owner._owningShader); + } + + existing->second->addWinding(slotMappingIndex); + } + + void updateWinding(std::size_t slotMappingIndex) + { + const auto& slot = _owner._slots[slotMappingIndex]; + _windingsByEntity[slot.renderEntity]->updateWinding(slotMappingIndex); + } + + void removeWinding(std::size_t slotMappingIndex) + { + const auto& slot = _owner._slots[slotMappingIndex]; + + auto& group = _windingsByEntity[slot.renderEntity]; + group->removeWinding(slotMappingIndex); + + if (group->empty()) + { + slot.renderEntity->removeRenderable(group); + _windingsByEntity.erase(slot.renderEntity); + } + } + }; + + std::unique_ptr _entitySurfaces; public: - WindingRenderer() : - _windings(0), + WindingRenderer(IGeometryStore& geometryStore, Shader* owningShader) : + _geometryStore(geometryStore), + _owningShader(owningShader), + _windingCount(0), _freeSlotMappingHint(InvalidSlotMapping) - {} + { + if (RenderingTraits::SupportsEntitySurfaces()) + { + _entitySurfaces.reset(new EntityWindings(*this)); + } + } bool empty() const { - return _windings == 0; + return _windingCount == 0; } - Slot addWinding(const std::vector& vertices) override + Slot addWinding(const std::vector& vertices, IRenderEntity* entity) override { auto windingSize = vertices.size(); @@ -125,7 +276,15 @@ class WindingRenderer : slotMapping.slotNumber = bucket.buffer.pushWinding(vertices); } - ++_windings; + ++_windingCount; + + if (RenderingTraits::SupportsEntitySurfaces()) + { + slotMapping.renderEntity = entity; + + // Add this winding to the surface associated to the render entity + _entitySurfaces->addWinding(slotMappingIndex); + } return slotMappingIndex; } @@ -138,27 +297,18 @@ class WindingRenderer : auto bucketIndex = slotMapping.bucketIndex; assert(bucketIndex != InvalidBucketIndex); -#if 1 - // Mark this winding slot as pending for deletion - _buckets[bucketIndex].pendingDeletions.push_back(slotMapping.slotNumber); -#else - // Remove the winding from the bucket - _buckets[bucketIndex].removeWinding(slotMapping.slotNumber); - - // Update the value in other slot mappings, now that the bucket shrunk - for (auto& mapping : _slots) + if (RenderingTraits::SupportsEntitySurfaces()) { - // Every index in the same bucket beyond the removed winding needs to be shifted to left - if (mapping.bucketIndex == bucketIndex && mapping.slotNumber > slotMapping.slotNumber) - { - --mapping.slotNumber; - } + _entitySurfaces->removeWinding(slot); } -#endif + + // Mark this winding slot as pending for deletion + _buckets[bucketIndex].pendingDeletions.push_back(slotMapping.slotNumber); // Invalidate the slot mapping slotMapping.bucketIndex = InvalidBucketIndex; slotMapping.slotNumber = InvalidVertexBufferSlot; + slotMapping.renderEntity = nullptr; // Update the free slot hint, for the next round we allocate one if (slot < _freeSlotMappingHint) @@ -166,7 +316,7 @@ class WindingRenderer : _freeSlotMappingHint = slot; } - --_windings; + --_windingCount; } void updateWinding(Slot slot, const std::vector& vertices) override @@ -184,6 +334,11 @@ class WindingRenderer : } bucket.buffer.replaceWinding(slotMapping.slotNumber, vertices); + + if (RenderingTraits::SupportsEntitySurfaces()) + { + _entitySurfaces->updateWinding(slot); + } } void renderAllWindings(const RenderInfo& info) override @@ -276,7 +431,6 @@ class WindingRenderer : std::sort(bucket.pendingDeletions.begin(), bucket.pendingDeletions.end()); -#if 1 // Remove the winding from the bucket bucket.buffer.removeWindings(bucket.pendingDeletions); @@ -311,25 +465,7 @@ class WindingRenderer : mapping.slotNumber -= maxOffsetToApply; } } -#else - for (auto s = bucket.pendingDeletions.rbegin(); s != bucket.pendingDeletions.rend(); ++s) - { - auto slotNumber = *s; - - // Remove the winding from the bucket - bucket.buffer.removeWinding(slotNumber); - // Update the value in other slot mappings, now that the bucket shrank - for (auto& mapping : _slots) - { - // Every index in the same bucket beyond the removed winding needs to be shifted to left - if (mapping.bucketIndex == bucketIndex && mapping.slotNumber > slotNumber) - { - --mapping.slotNumber; - } - } - } -#endif bucket.pendingDeletions.clear(); } diff --git a/radiantcore/brush/RenderableWinding.h b/radiantcore/brush/RenderableWinding.h index ffe8672d9c..79b0cd4da3 100644 --- a/radiantcore/brush/RenderableWinding.h +++ b/radiantcore/brush/RenderableWinding.h @@ -13,6 +13,7 @@ class RenderableWinding : private: const Winding& _winding; ShaderPtr _shader; + IRenderEntity* _entity; bool _needsUpdate; IWindingRenderer::Slot _slot; @@ -21,6 +22,7 @@ class RenderableWinding : public: RenderableWinding(const Winding& winding) : _winding(winding), + _entity(nullptr), _needsUpdate(true), _slot(IWindingRenderer::InvalidSlot), _windingSize(0) @@ -31,13 +33,13 @@ class RenderableWinding : _needsUpdate = true; } - void update(const ShaderPtr& shader, const IRenderEntity& entity) + void update(const ShaderPtr& shader, IRenderEntity& entity) { if (!_needsUpdate) return; _needsUpdate = false; - if (_shader != shader) + if (_shader != shader || &entity != _entity) { clear(); } @@ -73,7 +75,7 @@ class RenderableWinding : if (_slot == IWindingRenderer::InvalidSlot) { - _slot = shader->addWinding(vertices); + _slot = shader->addWinding(vertices, &entity); } else { diff --git a/radiantcore/entity/EntityNode.cpp b/radiantcore/entity/EntityNode.cpp index 4ff4678376..fa72356aef 100644 --- a/radiantcore/entity/EntityNode.cpp +++ b/radiantcore/entity/EntityNode.cpp @@ -200,7 +200,7 @@ const Vector3& EntityNode::getDirection() const return _direction; } -void EntityNode::addRenderable(const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) +void EntityNode::addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader) { _renderObjects.addRenderable(object, shader); } diff --git a/radiantcore/entity/EntityNode.h b/radiantcore/entity/EntityNode.h index 1c86a6388a..600b9991f5 100644 --- a/radiantcore/entity/EntityNode.h +++ b/radiantcore/entity/EntityNode.h @@ -129,7 +129,7 @@ class EntityNode : virtual float getShaderParm(int parmNum) const override; virtual const Vector3& getDirection() const override; - virtual void addRenderable(const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) override; + virtual void addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader) override; virtual void removeRenderable(const render::IRenderableObject::Ptr& object) override; virtual void foreachRenderableTouchingBounds(const AABB& bounds, const ObjectVisitFunction& functor) override; diff --git a/radiantcore/entity/RenderableObjectCollection.h b/radiantcore/entity/RenderableObjectCollection.h index 353d4d944d..986f98ae90 100644 --- a/radiantcore/entity/RenderableObjectCollection.h +++ b/radiantcore/entity/RenderableObjectCollection.h @@ -20,7 +20,7 @@ class RenderableObjectCollection : struct ObjectData { - ShaderPtr shader; + Shader* shader; sigc::connection boundsChangedConnection; }; @@ -31,7 +31,7 @@ class RenderableObjectCollection : _collectionBoundsNeedUpdate(true) {} - void addRenderable(const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) + void addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader) { sigc::connection subscription = object->signal_boundsChanged().connect( sigc::mem_fun(*this, &RenderableObjectCollection::onObjectBoundsChanged)); diff --git a/radiantcore/rendersystem/backend/ColourShader.cpp b/radiantcore/rendersystem/backend/ColourShader.cpp index 31b6cf7f18..807e780faa 100644 --- a/radiantcore/rendersystem/backend/ColourShader.cpp +++ b/radiantcore/rendersystem/backend/ColourShader.cpp @@ -3,6 +3,7 @@ #include #include "string/convert.h" #include "fmt/format.h" +#include "../OpenGLRenderSystem.h" namespace render { @@ -73,7 +74,7 @@ void ColourShader::construct() // Don't touch a renderer that is not empty, this will break any client connections if (getWindingRenderer().empty()) { - setWindingRenderer(std::make_unique>()); + setWindingRenderer(std::make_unique>(getRenderSystem().getGeometryStore(), this)); } state.setRenderFlags(RENDER_DEPTHTEST | RENDER_DEPTHWRITE); diff --git a/radiantcore/rendersystem/backend/LightInteractions.cpp b/radiantcore/rendersystem/backend/LightInteractions.cpp index 0ed408a826..45a623b386 100644 --- a/radiantcore/rendersystem/backend/LightInteractions.cpp +++ b/radiantcore/rendersystem/backend/LightInteractions.cpp @@ -42,13 +42,13 @@ inline void submitObject(IRenderableObject& object, IGeometryStore& store) } -void LightInteractions::addObject(IRenderableObject& object, IRenderEntity& entity, OpenGLShader& shader) +void LightInteractions::addObject(IRenderableObject& object, IRenderEntity& entity, OpenGLShader* shader) { auto& objectsByMaterial = _objectsByEntity.emplace( &entity, ObjectsByMaterial{}).first->second; auto& surfaces = objectsByMaterial.emplace( - &shader, ObjectList{}).first->second; + shader, ObjectList{}).first->second; surfaces.emplace_back(std::ref(object)); @@ -66,7 +66,7 @@ void LightInteractions::collectSurfaces(const std::set& entiti for (const auto& entity : entities) { entity->foreachRenderableTouchingBounds(_lightBounds, - [&](const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) + [&](const render::IRenderableObject::Ptr& object, Shader* shader) { // Skip empty objects if (!object->isVisible()) return; @@ -74,7 +74,7 @@ void LightInteractions::collectSurfaces(const std::set& entiti // Don't collect invisible shaders if (!shader->isVisible()) return; - auto glShader = static_cast(shader.get()); + auto glShader = static_cast(shader); // We only consider materials designated for camera rendering if (!glShader->isApplicableTo(RenderViewType::Camera)) @@ -82,7 +82,7 @@ void LightInteractions::collectSurfaces(const std::set& entiti return; } - addObject(*object, *entity, *glShader); + addObject(*object, *entity, glShader); }); } } diff --git a/radiantcore/rendersystem/backend/LightInteractions.h b/radiantcore/rendersystem/backend/LightInteractions.h index 7b2a7af004..cc81488193 100644 --- a/radiantcore/rendersystem/backend/LightInteractions.h +++ b/radiantcore/rendersystem/backend/LightInteractions.h @@ -63,7 +63,7 @@ class LightInteractions return _objectsByEntity.size(); } - void addObject(IRenderableObject& object, IRenderEntity& entity, OpenGLShader& shader); + void addObject(IRenderableObject& object, IRenderEntity& entity, OpenGLShader* shader); bool isInView(const IRenderView& view); diff --git a/radiantcore/rendersystem/backend/OpenGLShader.cpp b/radiantcore/rendersystem/backend/OpenGLShader.cpp index 4f8bc5b5fb..f5cbb4435e 100644 --- a/radiantcore/rendersystem/backend/OpenGLShader.cpp +++ b/radiantcore/rendersystem/backend/OpenGLShader.cpp @@ -66,7 +66,7 @@ OpenGLShader::OpenGLShader(const std::string& name, OpenGLRenderSystem& renderSy _enabledViewTypes(0), _mergeModeActive(false) { - _windingRenderer.reset(new WindingRenderer()); + _windingRenderer.reset(new WindingRenderer(renderSystem.getGeometryStore(), this)); } OpenGLShader::~OpenGLShader() @@ -203,9 +203,9 @@ IGeometryStore::Slot OpenGLShader::getSurfaceStorageLocation(ISurfaceRenderer::S return _surfaceRenderer.getSurfaceStorageLocation(slot); } -IWindingRenderer::Slot OpenGLShader::addWinding(const std::vector& vertices) +IWindingRenderer::Slot OpenGLShader::addWinding(const std::vector& vertices, IRenderEntity* entity) { - return _windingRenderer->addWinding(vertices); + return _windingRenderer->addWinding(vertices, entity); } void OpenGLShader::removeWinding(IWindingRenderer::Slot slot) diff --git a/radiantcore/rendersystem/backend/OpenGLShader.h b/radiantcore/rendersystem/backend/OpenGLShader.h index 121d1742b1..09c4033d0f 100644 --- a/radiantcore/rendersystem/backend/OpenGLShader.h +++ b/radiantcore/rendersystem/backend/OpenGLShader.h @@ -124,7 +124,7 @@ class OpenGLShader : void renderSurface(ISurfaceRenderer::Slot slot) override; IGeometryStore::Slot getSurfaceStorageLocation(ISurfaceRenderer::Slot slot) override; - IWindingRenderer::Slot addWinding(const std::vector& vertices) override; + IWindingRenderer::Slot addWinding(const std::vector& vertices, IRenderEntity* entity) override; void removeWinding(IWindingRenderer::Slot slot) override; void updateWinding(IWindingRenderer::Slot slot, const std::vector& vertices) override; bool hasWindings() const;