diff --git a/libs/render/RenderableBox.h b/libs/render/RenderableBox.h new file mode 100644 index 0000000000..91bad3dcf2 --- /dev/null +++ b/libs/render/RenderableBox.h @@ -0,0 +1,187 @@ +#pragma once + +#include "render/RenderableGeometry.h" + +namespace render +{ + +namespace detail +{ + +inline std::vector getFillBoxVertices(const Vector3& min, const Vector3& max, const Vector4& colour) +{ + // Load the 6 times 4 = 24 corner points, each with the correct face normal + return + { + // Bottom quad + ArbitraryMeshVertex({ min[0], min[1], min[2] }, {0,0,-1}, {0,0}, colour), + ArbitraryMeshVertex({ max[0], min[1], min[2] }, {0,0,-1}, {1,0}, colour), + ArbitraryMeshVertex({ max[0], max[1], min[2] }, {0,0,-1}, {1,1}, colour), + ArbitraryMeshVertex({ min[0], max[1], min[2] }, {0,0,-1}, {0,1}, colour), + + // Top quad + ArbitraryMeshVertex({ min[0], min[1], max[2] }, {0,0,+1}, {0,0}, colour), + ArbitraryMeshVertex({ max[0], min[1], max[2] }, {0,0,+1}, {1,0}, colour), + ArbitraryMeshVertex({ max[0], max[1], max[2] }, {0,0,+1}, {1,1}, colour), + ArbitraryMeshVertex({ min[0], max[1], max[2] }, {0,0,+1}, {0,1}, colour), + + // Front quad + ArbitraryMeshVertex({ min[0], min[1], min[2] }, {0,-1,0}, {0,0}, colour), + ArbitraryMeshVertex({ max[0], min[1], min[2] }, {0,-1,0}, {1,0}, colour), + ArbitraryMeshVertex({ max[0], min[1], max[2] }, {0,-1,0}, {1,1}, colour), + ArbitraryMeshVertex({ min[0], min[1], max[2] }, {0,-1,0}, {0,1}, colour), + + // Back quad + ArbitraryMeshVertex({ max[0], max[1], min[2] }, {0,+1,0}, {0,0}, colour), + ArbitraryMeshVertex({ min[0], max[1], min[2] }, {0,+1,0}, {1,0}, colour), + ArbitraryMeshVertex({ min[0], max[1], max[2] }, {0,+1,0}, {1,1}, colour), + ArbitraryMeshVertex({ max[0], max[1], max[2] }, {0,+1,0}, {0,1}, colour), + + // Right quad + ArbitraryMeshVertex({ max[0], min[1], min[2] }, {+1,0,0}, {0,0}, colour), + ArbitraryMeshVertex({ max[0], max[1], min[2] }, {+1,0,0}, {1,0}, colour), + ArbitraryMeshVertex({ max[0], max[1], max[2] }, {+1,0,0}, {1,1}, colour), + ArbitraryMeshVertex({ min[0], max[1], max[2] }, {+1,0,0}, {0,1}, colour), + + // Left quad + ArbitraryMeshVertex({ min[0], max[1], min[2] }, {-1,0,0}, {0,0}, colour), + ArbitraryMeshVertex({ min[0], min[1], min[2] }, {-1,0,0}, {1,0}, colour), + ArbitraryMeshVertex({ min[0], min[1], max[2] }, {-1,0,0}, {1,1}, colour), + ArbitraryMeshVertex({ min[0], max[1], max[2] }, {-1,0,0}, {0,1}, colour), + }; +} + +inline std::vector getWireframeBoxVertices(const Vector3& min, const Vector3& max, const Vector4& colour) +{ + // Load the 8 corner points + return + { + // Bottom quad + ArbitraryMeshVertex({ min[0], min[1], min[2] }, {0,0,1}, {0,0}, colour), + ArbitraryMeshVertex({ max[0], min[1], min[2] }, {0,0,1}, {0,0}, colour), + ArbitraryMeshVertex({ max[0], max[1], min[2] }, {0,0,1}, {0,0}, colour), + ArbitraryMeshVertex({ min[0], max[1], min[2] }, {0,0,1}, {0,0}, colour), + + // Top quad + ArbitraryMeshVertex({ min[0], min[1], max[2] }, {0,0,1}, {0,0}, colour), + ArbitraryMeshVertex({ max[0], min[1], max[2] }, {0,0,1}, {0,0}, colour), + ArbitraryMeshVertex({ max[0], max[1], max[2] }, {0,0,1}, {0,0}, colour), + ArbitraryMeshVertex({ min[0], max[1], max[2] }, {0,0,1}, {0,0}, colour), + }; +} + +// Indices drawing a hollow box outline, corresponding to the order in getWireframeBoxVertices() +inline std::vector generateWireframeBoxIndices() +{ + return + { + 0, 1, // bottom rectangle + 1, 2, // + 2, 3, // + 3, 0, // + + 4, 5, // top rectangle + 5, 6, // + 6, 7, // + 7, 4, // + + 0, 4, // vertical edges + 1, 5, // + 2, 6, // + 3, 7, // + }; +}; + +// Indices drawing a hollow box outline, corresponding to the order in getFillBoxVertices() +inline std::vector generateFillBoxIndices() +{ + return + { + 3, 2, 1, 0, // bottom rectangle + 7, 6, 5, 4, // top rectangle + + 4, 5, 1, 0, // sides + 5, 6, 2, 1, + 6, 7, 3, 2, + 7, 4, 0, 3, + }; +}; + +} + +class RenderableBox : + public render::RenderableGeometry +{ +private: + const AABB& _bounds; + const Vector3& _worldPos; + bool _needsUpdate; + bool _filledBox; + +public: + RenderableBox(const AABB& bounds, const Vector3& worldPos) : + _bounds(bounds), + _worldPos(worldPos), + _needsUpdate(true), + _filledBox(true) + {} + + void queueUpdate() + { + _needsUpdate = true; + } + + void setFillMode(bool fill) + { + if (_filledBox != fill) + { + _filledBox = fill; + clear(); + queueUpdate(); + } + } + + virtual Vector4 getVertexColour() + { + return Vector4(1, 1, 1, 1); + } + + virtual void updateGeometry() override + { + if (!_needsUpdate) return; + + _needsUpdate = false; + + static Vector3 Origin(0, 0, 0); + + // Calculate the corner vertices of this bounding box, plus the mid-point + Vector3 max(Origin + _bounds.extents); + Vector3 min(Origin - _bounds.extents); + + auto colour = getVertexColour(); + + auto vertices = _filledBox ? + detail::getFillBoxVertices(min, max, colour) : + detail::getWireframeBoxVertices(min, max, colour); + + // Move the points to their world position + for (auto& vertex : vertices) + { + vertex.vertex += _worldPos; + } + + static auto FillBoxIndices = detail::generateFillBoxIndices(); + static auto WireframeBoxIndices = detail::generateWireframeBoxIndices(); + + if (_filledBox) + { + RenderableGeometry::updateGeometry(render::GeometryType::Quads, vertices, FillBoxIndices); + } + else + { + RenderableGeometry::updateGeometry(render::GeometryType::Lines, vertices, WireframeBoxIndices); + } + } +}; + +} diff --git a/radiantcore/entity/RenderableEntityBox.cpp b/radiantcore/entity/RenderableEntityBox.cpp index ae3c97243b..a9d4514dd2 100644 --- a/radiantcore/entity/RenderableEntityBox.cpp +++ b/radiantcore/entity/RenderableEntityBox.cpp @@ -5,157 +5,14 @@ namespace entity { -namespace -{ - -inline std::vector getFillBoxVertices(const Vector3& min, const Vector3& max, const Vector4& colour) -{ - // Load the 6 times 4 = 24 corner points, each with the correct face normal - return - { - // Bottom quad - ArbitraryMeshVertex({ min[0], min[1], min[2] }, {0,0,-1}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], min[1], min[2] }, {0,0,-1}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], max[1], min[2] }, {0,0,-1}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], max[1], min[2] }, {0,0,-1}, {0,0}, colour), - - // Top quad - ArbitraryMeshVertex({ min[0], min[1], max[2] }, {0,0,+1}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], min[1], max[2] }, {0,0,+1}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], max[1], max[2] }, {0,0,+1}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], max[1], max[2] }, {0,0,+1}, {0,0}, colour), - - // Front quad - ArbitraryMeshVertex({ min[0], min[1], min[2] }, {0,-1,0}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], min[1], min[2] }, {0,-1,0}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], min[1], max[2] }, {0,-1,0}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], min[1], max[2] }, {0,-1,0}, {0,0}, colour), - - // Back quad - ArbitraryMeshVertex({ max[0], max[1], min[2] }, {0,+1,0}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], max[1], min[2] }, {0,+1,0}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], max[1], max[2] }, {0,+1,0}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], max[1], max[2] }, {0,+1,0}, {0,0}, colour), - - // Right quad - ArbitraryMeshVertex({ max[0], min[1], min[2] }, {+1,0,0}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], max[1], min[2] }, {+1,0,0}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], max[1], max[2] }, {+1,0,0}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], max[1], max[2] }, {+1,0,0}, {0,0}, colour), - - // Left quad - ArbitraryMeshVertex({ min[0], max[1], min[2] }, {-1,0,0}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], min[1], min[2] }, {-1,0,0}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], min[1], max[2] }, {-1,0,0}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], max[1], max[2] }, {-1,0,0}, {0,0}, colour), - }; -} - -inline std::vector getWireframeBoxVertices(const Vector3& min, const Vector3& max, const Vector4& colour) -{ - // Load the 8 corner points - return - { - // Bottom quad - ArbitraryMeshVertex({ min[0], min[1], min[2] }, {0,0,1}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], min[1], min[2] }, {0,0,1}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], max[1], min[2] }, {0,0,1}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], max[1], min[2] }, {0,0,1}, {0,0}, colour), - - // Top quad - ArbitraryMeshVertex({ min[0], min[1], max[2] }, {0,0,1}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], min[1], max[2] }, {0,0,1}, {0,0}, colour), - ArbitraryMeshVertex({ max[0], max[1], max[2] }, {0,0,1}, {0,0}, colour), - ArbitraryMeshVertex({ min[0], max[1], max[2] }, {0,0,1}, {0,0}, colour), - }; -} - -// Indices drawing a hollow box outline, corresponding to the order in getWireframeBoxVertices() -static const std::vector WireframeBoxIndices -{ - 0, 1, // bottom rectangle - 1, 2, // - 2, 3, // - 3, 0, // - - 4, 5, // top rectangle - 5, 6, // - 6, 7, // - 7, 4, // - - 0, 4, // vertical edges - 1, 5, // - 2, 6, // - 3, 7, // -}; - -// Indices drawing a hollow box outline, corresponding to the order in getFillBoxVertices() -static const std::vector FillBoxIndices -{ - 3, 2, 1, 0, // bottom rectangle - 7, 6, 5, 4, // top rectangle - - 4, 5, 1, 0, // sides - 5, 6, 2, 1, - 6, 7, 3, 2, - 7, 4, 0, 3, -}; - -} - RenderableEntityBox::RenderableEntityBox(const IEntityNode& entity, const AABB& bounds, const Vector3& worldPos) : - _entity(entity), - _bounds(bounds), - _worldPos(worldPos), - _needsUpdate(true), - _filledBox(true) + RenderableBox(bounds, worldPos), + _entity(entity) {} -void RenderableEntityBox::queueUpdate() -{ - _needsUpdate = true; -} - -void RenderableEntityBox::setFillMode(bool fill) -{ - if (_filledBox != fill) - { - _filledBox = fill; - clear(); - queueUpdate(); - } -} - -void RenderableEntityBox::updateGeometry() +Vector4 RenderableEntityBox::getVertexColour() { - if (!_needsUpdate) return; - - _needsUpdate = false; - - static Vector3 Origin(0, 0, 0); - - // Calculate the corner vertices of this bounding box, plus the mid-point - Vector3 max(Origin + _bounds.extents); - Vector3 min(Origin - _bounds.extents); - - auto colour = _entity.getEntityColour(); - - auto vertices = _filledBox ? getFillBoxVertices(min, max, colour) : getWireframeBoxVertices(min, max, colour); - - // Move the points to their world position - for (auto& vertex : vertices) - { - vertex.vertex += _worldPos; - } - - if (_filledBox) - { - RenderableGeometry::updateGeometry(render::GeometryType::Quads, vertices, FillBoxIndices); - } - else - { - RenderableGeometry::updateGeometry(render::GeometryType::Lines, vertices, WireframeBoxIndices); - } + return _entity.getEntityColour(); } } diff --git a/radiantcore/entity/RenderableEntityBox.h b/radiantcore/entity/RenderableEntityBox.h index ee45666f0f..2ec82751e4 100644 --- a/radiantcore/entity/RenderableEntityBox.h +++ b/radiantcore/entity/RenderableEntityBox.h @@ -1,29 +1,23 @@ #pragma once -#include "render/RenderableGeometry.h" +#include "render/RenderableBox.h" namespace entity { class EntityNode; -class RenderableEntityBox : - public render::RenderableGeometry +class RenderableEntityBox final : + public render::RenderableBox { private: const IEntityNode& _entity; - const AABB& _bounds; - const Vector3& _worldPos; - bool _needsUpdate; - bool _filledBox; public: RenderableEntityBox(const IEntityNode& entity, const AABB& bounds, const Vector3& worldPos); - void queueUpdate(); - void setFillMode(bool fill); - - virtual void updateGeometry() override; +protected: + Vector4 getVertexColour() override; }; } diff --git a/radiantcore/model/NullModelNode.cpp b/radiantcore/model/NullModelNode.cpp index 6b48401b28..05bbc67068 100644 --- a/radiantcore/model/NullModelNode.cpp +++ b/radiantcore/model/NullModelNode.cpp @@ -5,11 +5,13 @@ namespace model { NullModelNode::NullModelNode() : - _nullModel(new NullModel) + _nullModel(new NullModel), + _renderableBox(localAABB(), worldAABB().getOrigin()) {} NullModelNode::NullModelNode(const NullModelPtr& nullModel) : - _nullModel(nullModel) + _nullModel(nullModel), + _renderableBox(localAABB(), worldAABB().getOrigin()) {} std::string NullModelNode::name() const diff --git a/radiantcore/model/NullModelNode.h b/radiantcore/model/NullModelNode.h index a18b34584b..a372e3c6e7 100644 --- a/radiantcore/model/NullModelNode.h +++ b/radiantcore/model/NullModelNode.h @@ -4,19 +4,22 @@ #include "irenderable.h" #include "NullModel.h" +#include "render/RenderableBox.h" namespace model { class NullModelNode; typedef std::shared_ptr NullModelNodePtr; -class NullModelNode : +class NullModelNode final : public scene::Node, public SelectionTestable, public ModelNode { private: NullModelPtr _nullModel; + render::RenderableBox _renderableBox; + public: // Default constructor, allocates a new NullModel NullModelNode(); @@ -48,7 +51,7 @@ class NullModelNode : } // Bounded implementation - virtual const AABB& localAABB() const override; + const AABB& localAABB() const override; }; } // namespace model diff --git a/tools/msvc/libs.vcxproj b/tools/msvc/libs.vcxproj index f0f96c48f0..324ce570f1 100644 --- a/tools/msvc/libs.vcxproj +++ b/tools/msvc/libs.vcxproj @@ -214,6 +214,7 @@ + diff --git a/tools/msvc/libs.vcxproj.filters b/tools/msvc/libs.vcxproj.filters index 0f36a04205..62c026b26e 100644 --- a/tools/msvc/libs.vcxproj.filters +++ b/tools/msvc/libs.vcxproj.filters @@ -347,6 +347,9 @@ render + + render +