Skip to content

Commit

Permalink
#5584: Introduce ISurfaceRenderer interface to specifically deal with…
Browse files Browse the repository at this point in the history
… oriented surfaces
  • Loading branch information
codereader committed Dec 24, 2021
1 parent e1e6c65 commit f9151b6
Show file tree
Hide file tree
Showing 18 changed files with 493 additions and 117 deletions.
4 changes: 1 addition & 3 deletions include/igeometryrenderer.h
Expand Up @@ -14,7 +14,6 @@ enum class GeometryType
Triangles,
Quads,
Lines,
OrientedSurface, // Triangles with a per-surface transformation matrix
};

/**
Expand All @@ -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<ArbitraryMeshVertex>& vertices,
const std::vector<unsigned int>& indices,
const std::function<const Matrix4& ()>& getTransformCallback = std::function<const Matrix4&()>()) = 0;
const std::vector<unsigned int>& indices) = 0;

// Releases a previously allocated slot. This invalidates the handle.
virtual void removeGeometry(Slot slot) = 0;
Expand Down
4 changes: 3 additions & 1 deletion include/irender.h
Expand Up @@ -3,6 +3,7 @@
#include "imodule.h"
#include "iwindingrenderer.h"
#include "igeometryrenderer.h"
#include "isurfacerenderer.h"
#include <functional>
#include <vector>

Expand Down Expand Up @@ -407,7 +408,8 @@ typedef std::shared_ptr<Material> MaterialPtr;
*/
class Shader :
public render::IWindingRenderer,
public render::IGeometryRenderer
public render::IGeometryRenderer,
public render::ISurfaceRenderer
{
public:
// Observer interface to get notified on (un-)realisation
Expand Down
64 changes: 64 additions & 0 deletions include/isurfacerenderer.h
@@ -0,0 +1,64 @@
#pragma once

#include <vector>
#include <limits>
#include <cstdint>
#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<ArbitraryMeshVertex>& getVertices() = 0;

// Returns the indices to render the triangle primitives
virtual const std::vector<unsigned int>& 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<Slot>::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;
};

}
50 changes: 22 additions & 28 deletions libs/render/RenderableBox.h
@@ -1,6 +1,8 @@
#pragma once

#include "isurfacerenderer.h"
#include "render/RenderableGeometry.h"
#include "render/RenderableSurface.h"

namespace render
{
Expand Down Expand Up @@ -124,7 +126,7 @@ inline std::vector<unsigned int> generateTriangleBoxIndices()
}

class RenderableBox :
public render::RenderableGeometry
public RenderableGeometry
{
private:
const AABB& _bounds;
Expand Down Expand Up @@ -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<ArbitraryMeshVertex> _vertices;
std::vector<unsigned int> _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<ArbitraryMeshVertex>& getVertices() override
{
return _vertices;
}

RenderableGeometry::updateGeometry(render::GeometryType::OrientedSurface, vertices, Indices,
std::bind(&RenderableBoxSurface::getOrientation, this));
const std::vector<unsigned int>& getIndices() override
{
return _indices;
}

private:
const Matrix4& getOrientation() const
const Matrix4& getSurfaceTransform() override
{
return _orientation;
}
Expand Down
5 changes: 2 additions & 3 deletions libs/render/RenderableGeometry.h
Expand Up @@ -103,8 +103,7 @@ class RenderableGeometry :
// to ensure that the _shader reference is already up to date.
void updateGeometry(GeometryType type,
const std::vector<ArbitraryMeshVertex>& vertices,
const std::vector<unsigned int>& indices,
const std::function<const Matrix4& ()>& getTransformCallback = std::function<const Matrix4&()>())
const std::vector<unsigned int>& indices)
{
// Size changes require removal of the geometry before update
if (_lastVertexSize != vertices.size() || _lastIndexSize != indices.size())
Expand All @@ -117,7 +116,7 @@ class RenderableGeometry :

if (_surfaceSlot == IGeometryRenderer::InvalidSlot)
{
_surfaceSlot = _shader->addGeometry(type, vertices, indices, getTransformCallback);
_surfaceSlot = _shader->addGeometry(type, vertices, indices);
}
else
{
Expand Down
96 changes: 96 additions & 0 deletions libs/render/RenderableSurface.h
@@ -0,0 +1,96 @@
#pragma once

#include <vector>
#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);
}
}
};

}
20 changes: 4 additions & 16 deletions radiantcore/model/NullModelNode.cpp
Expand Up @@ -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
{
}
Expand All @@ -75,10 +68,12 @@ void NullModelNode::setRenderSystem(const RenderSystemPtr& renderSystem)
if (renderSystem)
{
_shader = renderSystem->capture("");
_renderableBox.update(_shader);
}
else
{
_shader.reset();
_renderableBox.clear();
}
}

Expand All @@ -91,7 +86,7 @@ void NullModelNode::onInsertIntoScene(scene::IMapRootNode& root)
{
Node::onInsertIntoScene(root);

_renderableBox.queueUpdate();
_renderableBox.update(_shader);
}

void NullModelNode::onRemoveFromScene(scene::IMapRootNode& root)
Expand All @@ -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
{
Expand Down
3 changes: 0 additions & 3 deletions radiantcore/model/NullModelNode.h
Expand Up @@ -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;
Expand All @@ -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;
};
Expand Down

0 comments on commit f9151b6

Please sign in to comment.