From 46464fa6d2d3fc633d2ae97a01ba07ad31ccfc3c Mon Sep 17 00:00:00 2001 From: kj16609 Date: Sun, 3 Nov 2024 12:27:43 -0500 Subject: [PATCH] Cleanup octree and get started on chunk system --- src/editor/src/gui/core.cpp | 12 +- src/editor/src/gui/preview.cpp | 2 +- src/engine/EngineContext.cpp | 6 +- src/engine/FGL_DEFINES.hpp | 14 +- .../assets/model/builders/SceneBuilder.cpp | 2 +- src/engine/constants.hpp | 2 +- src/engine/gameobjects/GameObject.hpp | 12 +- .../boxes/AxisAlignedBoundingBox.cpp | 40 ++---- .../boxes/AxisAlignedBoundingBox.hpp | 15 ++ .../primitives/boxes/OrientedBoundingBox.cpp | 9 +- src/engine/primitives/matricies/Matrix.hpp | 2 +- .../systems/render/EntityRendererSystem.cpp | 2 +- src/engine/tree/Chunk.cpp | 47 +++++++ src/engine/tree/Chunk.hpp | 70 +++++++++ src/engine/tree/octtree/OctTreeNode.cpp | 133 +++++++++++++----- src/engine/tree/octtree/OctTreeNode.hpp | 6 +- 16 files changed, 284 insertions(+), 90 deletions(-) create mode 100644 src/engine/tree/Chunk.cpp create mode 100644 src/engine/tree/Chunk.hpp diff --git a/src/editor/src/gui/core.cpp b/src/editor/src/gui/core.cpp index 46a4c3d..a598bc1 100644 --- a/src/editor/src/gui/core.cpp +++ b/src/editor/src/gui/core.cpp @@ -194,21 +194,23 @@ namespace fgl::engine::gui static GameObject* selected_object { nullptr }; - void itterateGameObjectNode( FrameInfo& info, const OctTreeNode& node ) + void itterateGameObjectNode( FrameInfo& info, OctTreeNode& node ) { if ( node.isLeaf() ) { if ( node.itemCount() == 0 ) return; - const auto& objects { node.getLeaf() }; - for ( const GameObject& object : objects ) + auto& objects { node.getLeaf() }; + for ( GameObject& object : objects ) { ImGui::PushID( object.getId() ); - debug::drawLine( object.getPosition(), node.getFitCenter() ); - if ( ImGui::TreeNodeEx( object.getName().c_str(), ImGuiTreeNodeFlags_Leaf ) ) { + if ( ImGui::IsItemClicked() ) + { + selected_object = &object; + } ImGui::TreePop(); } ImGui::PopID(); diff --git a/src/editor/src/gui/preview.cpp b/src/editor/src/gui/preview.cpp index 475575d..bb7e198 100644 --- a/src/editor/src/gui/preview.cpp +++ b/src/editor/src/gui/preview.cpp @@ -51,7 +51,7 @@ namespace fgl::engine::gui Model::createModel( data->path, info.model_vertex_buffer, info.model_index_buffer ) }; - obj.addFlag( IS_ENTITY | IS_VISIBLE ); + obj.addFlag( IsEntity | IsVisible ); auto component { std::make_unique< ModelComponent >( std::move( model ) ) diff --git a/src/engine/EngineContext.cpp b/src/engine/EngineContext.cpp index 9e36801..6a0638c 100644 --- a/src/engine/EngineContext.cpp +++ b/src/engine/EngineContext.cpp @@ -58,8 +58,8 @@ namespace fgl::engine initMaterialDataVec( m_material_data_pool ); - constexpr auto offset { 2.0f }; - constexpr std::size_t grid_size { 16 }; + constexpr auto offset { 8.0f }; + constexpr std::size_t grid_size { 6 }; constexpr float factor_offset { 1.0f / static_cast< float >( grid_size ) }; for ( std::size_t x = 0; x < grid_size; ++x ) @@ -92,7 +92,7 @@ namespace fgl::engine } } - obj.getTransform().translation = WorldCoordinate( x * offset, y * offset, 0.0f ); + obj.getTransform().translation = WorldCoordinate( 10.0 + x * offset, 10.0 + y * offset, 0.0f ); m_game_objects_root.addGameObject( std::move( obj ) ); } diff --git a/src/engine/FGL_DEFINES.hpp b/src/engine/FGL_DEFINES.hpp index 828a5d4..bcf5d23 100644 --- a/src/engine/FGL_DEFINES.hpp +++ b/src/engine/FGL_DEFINES.hpp @@ -53,9 +53,11 @@ #define FGL_STRICT_ALIGNMENT( bytesize ) [[gnu::warn_if_not_aligned( bytesize )]] #ifndef NDEBUG +#include #include -#define FGL_ASSERT( test, msg ) assert( ( test ) && "msg" ); -//if ( !( test ) ) throw std::runtime_error( msg ); +#define FGL_ASSERT( test, msg ) \ + if ( !( test ) ) \ + throw std::runtime_error( std::format( "{}:{}:{}: {}", __FILE__, __LINE__, __PRETTY_FUNCTION__, msg ) ); #else #define FGL_ASSERT( test, msg ) #endif @@ -69,4 +71,10 @@ std::unreachable() #else #define FGL_UNREACHABLE() std::unreachable() -#endif \ No newline at end of file +#endif + +#define FGL_NOTNAN( value ) FGL_ASSERT( !std::isnan( value ), "Value is NaN!" ) +#define FGL_NOTNANVEC3( vec3 ) \ + FGL_ASSERT( !std::isnan( ( vec3 ).x ), "X value was NaN!" ); \ + FGL_ASSERT( !std::isnan( ( vec3 ).y ), "Y value was NaN!" ); \ + FGL_ASSERT( !std::isnan( ( vec3 ).z ), "Z value was NaN!" ) diff --git a/src/engine/assets/model/builders/SceneBuilder.cpp b/src/engine/assets/model/builders/SceneBuilder.cpp index 12c8c2c..874e2ac 100644 --- a/src/engine/assets/model/builders/SceneBuilder.cpp +++ b/src/engine/assets/model/builders/SceneBuilder.cpp @@ -604,7 +604,7 @@ namespace fgl::engine std::unique_ptr< ModelComponent > component { std::make_unique< ModelComponent >( std::move( model ) ) }; obj.addComponent( std::move( component ) ); - obj.addFlag( IS_VISIBLE | IS_ENTITY ); + obj.addFlag( IsVisible | IsEntity ); const auto transform { loadTransform( node_idx, root ) }; diff --git a/src/engine/constants.hpp b/src/engine/constants.hpp index 588a1a9..4394f8a 100644 --- a/src/engine/constants.hpp +++ b/src/engine/constants.hpp @@ -15,7 +15,7 @@ namespace fgl::engine::constants { - constexpr glm::vec3 DEFAULT_VEC3 { std::numeric_limits< float >::max() }; + constexpr glm::vec3 DEFAULT_VEC3 { std::numeric_limits< float >::signaling_NaN() }; constexpr glm::vec3 WORLD_CENTER { 0.0f, 0.0f, 0.0f }; diff --git a/src/engine/gameobjects/GameObject.hpp b/src/engine/gameobjects/GameObject.hpp index 002c967..1ace9d7 100644 --- a/src/engine/gameobjects/GameObject.hpp +++ b/src/engine/gameobjects/GameObject.hpp @@ -25,11 +25,11 @@ namespace fgl::engine enum GameObjectFlagMask : GameObjectFlagType { - NONE_FLAG = 0, - IS_STATIC = 1 << 0, //! Object can not move - IS_VISIBLE = 1 << 1, //! Only return visible objects - IS_ENTITY = 1 << 2, - MASK_DEFAULT = IS_VISIBLE, + NoneFlag = 0, + IsStatic = 1 << 0, //! Object can not move + IsVisible = 1 << 1, //! Only return visible objects + IsEntity = 1 << 2, + MaskDefault = IsVisible, }; class GameObject @@ -44,7 +44,7 @@ namespace fgl::engine private: GameObjectID m_id { INVALID_ID }; - GameObjectFlagType object_flags { GameObjectFlagMask::MASK_DEFAULT }; + GameObjectFlagType object_flags { GameObjectFlagMask::MaskDefault }; GameObjectTransform m_transform {}; std::vector< GameObjectComponentPtr > components {}; diff --git a/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp b/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp index e1ed322..8de5704 100644 --- a/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp +++ b/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp @@ -93,6 +93,11 @@ namespace fgl::engine template < CoordinateSpace CType > AxisAlignedBoundingBox< CType >& AxisAlignedBoundingBox< CType >::combine( const AxisAlignedBoundingBox& other ) { + FGL_NOTNANVEC3( other.m_top_right_forward ); + FGL_NOTNANVEC3( other.m_bottom_left_back ); + FGL_NOTNANVEC3( this->m_top_right_forward ); + FGL_NOTNANVEC3( this->m_bottom_left_back ); + const Coordinate< CType > new_top_right_forward { std::max( this->m_top_right_forward.x, other.m_top_right_forward.x ), std::max( this->m_top_right_forward.y, other.m_top_right_forward.y ), @@ -118,6 +123,11 @@ namespace fgl::engine const auto other_trf { other.topRightForward() }; const auto other_blb { other.bottomLeftBack() }; + FGL_NOTNANVEC3( other.topRightForward() ); + FGL_NOTNANVEC3( other.bottomLeftBack() ); + FGL_NOTNANVEC3( this->m_top_right_forward ); + FGL_NOTNANVEC3( this->m_bottom_left_back ); + const Coordinate< CType > new_top_right_forward { std::max( this->m_top_right_forward.x, other_trf.x ), std::max( this->m_top_right_forward.y, other_trf.y ), @@ -141,17 +151,16 @@ namespace fgl::engine m_top_right_forward( constants::DEFAULT_VEC3 ), m_bottom_left_back( -constants::DEFAULT_VEC3 ) { + FGL_NOTNANVEC3( oobb.topRightForward() ); + FGL_NOTNANVEC3( oobb.bottomLeftBack() ); + FGL_NOTNANVEC3( this->m_top_right_forward ); + FGL_NOTNANVEC3( this->m_bottom_left_back ); + if ( oobb.m_transform.rotation == Rotation() ) // If default rotation then we can simply just take it as the box is { - assert( oobb.topRightForward().vec() != constants::DEFAULT_VEC3 ); - assert( oobb.bottomLeftBack().vec() != -constants::DEFAULT_VEC3 ); - m_top_right_forward = oobb.topRightForward(); m_bottom_left_back = oobb.bottomLeftBack(); - - assert( m_top_right_forward.vec() != constants::DEFAULT_VEC3 ); - assert( m_bottom_left_back.vec() != -constants::DEFAULT_VEC3 ); } else { @@ -166,28 +175,9 @@ namespace fgl::engine m_bottom_left_back.y = std::min( m_bottom_left_back.y, point.y ); m_bottom_left_back.z = std::min( m_bottom_left_back.z, point.z ); } - - assert( m_top_right_forward.vec() != constants::DEFAULT_VEC3 ); - assert( m_bottom_left_back.vec() != -constants::DEFAULT_VEC3 ); } } - /* - template < CoordinateSpace CType > - AxisAlignedBoundingBox< CType >& AxisAlignedBoundingBox< CType >::combine( const OrientedBoundingBox< CType >& - other ) - { - const AxisAlignedBoundingBox< CType > aabb { other }; - if ( this->m_top_right_forward == Coordinate< CType >( constants::DEFAULT_VEC3 ) - || this->m_bottom_left_back == Coordinate< CType >( constants::DEFAULT_VEC3 ) ) - return *this = aabb; - else - { - return this->combine( aabb ); - } - } - */ - template class AxisAlignedBoundingBox< CoordinateSpace::Model >; template class AxisAlignedBoundingBox< CoordinateSpace::World >; } // namespace fgl::engine diff --git a/src/engine/primitives/boxes/AxisAlignedBoundingBox.hpp b/src/engine/primitives/boxes/AxisAlignedBoundingBox.hpp index 1c3a2ca..5445488 100644 --- a/src/engine/primitives/boxes/AxisAlignedBoundingBox.hpp +++ b/src/engine/primitives/boxes/AxisAlignedBoundingBox.hpp @@ -5,6 +5,7 @@ #pragma once #include "BoundingBox.hpp" +#include "engine/FGL_DEFINES.hpp" #include "engine/primitives/Scale.hpp" #include "engine/primitives/points/Coordinate.hpp" #include "engine/primitives/vectors/NormalVector.hpp" @@ -33,6 +34,13 @@ namespace fgl::engine assert( m_top_right_forward.x > m_bottom_left_back.x ); assert( m_top_right_forward.y > m_bottom_left_back.y ); assert( m_top_right_forward.z > m_bottom_left_back.z ); + + FGL_NOTNAN( m_bottom_left_back.x ); + FGL_NOTNAN( m_bottom_left_back.y ); + FGL_NOTNAN( m_bottom_left_back.z ); + FGL_NOTNAN( m_bottom_left_back.x ); + FGL_NOTNAN( m_bottom_left_back.y ); + FGL_NOTNAN( m_bottom_left_back.z ); } AxisAlignedBoundingBox( const Coordinate< CType > midpoint, const Scale scale ) : @@ -41,6 +49,13 @@ namespace fgl::engine assert( m_top_right_forward.x > m_bottom_left_back.x ); assert( m_top_right_forward.y > m_bottom_left_back.y ); assert( m_top_right_forward.z > m_bottom_left_back.z ); + + FGL_NOTNAN( m_bottom_left_back.x ); + FGL_NOTNAN( m_bottom_left_back.y ); + FGL_NOTNAN( m_bottom_left_back.z ); + FGL_NOTNAN( m_bottom_left_back.x ); + FGL_NOTNAN( m_bottom_left_back.y ); + FGL_NOTNAN( m_bottom_left_back.z ); } //TODO: This should be removed from access in favor of OBB::alignToWorld() diff --git a/src/engine/primitives/boxes/OrientedBoundingBox.cpp b/src/engine/primitives/boxes/OrientedBoundingBox.cpp index 572bb63..eeefd5f 100644 --- a/src/engine/primitives/boxes/OrientedBoundingBox.cpp +++ b/src/engine/primitives/boxes/OrientedBoundingBox.cpp @@ -208,8 +208,10 @@ namespace fgl::engine AxisAlignedBoundingBox< CType > OrientedBoundingBox< CType >::alignToWorld() const { const auto points { this->points() }; - glm::vec3 max { std::numeric_limits< glm::vec3::type >::infinity() }; - glm::vec3 min { -std::numeric_limits< glm::vec3::type >::infinity() }; + static_assert( std::same_as< glm::vec3::value_type, float > ); + constexpr glm::vec3::value_type INF { std::numeric_limits< glm::vec3::value_type >::infinity() }; + glm::vec3 max { -INF }; + glm::vec3 min { INF }; for ( const auto& point : points ) { @@ -222,6 +224,9 @@ namespace fgl::engine min.z = glm::min( min.z, point.z ); } + FGL_ASSERT( glm::all( glm::notEqual( max, glm::vec3( -INF ) ) ), "Max was still infinity" ); + FGL_ASSERT( glm::all( glm::notEqual( min, glm::vec3( INF ) ) ), "Min was still infinity" ); + return AxisAlignedBoundingBox< CType >( Coordinate< CType >( max ), Coordinate< CType >( min ) ); } diff --git a/src/engine/primitives/matricies/Matrix.hpp b/src/engine/primitives/matricies/Matrix.hpp index 20a700f..f738fc9 100644 --- a/src/engine/primitives/matricies/Matrix.hpp +++ b/src/engine/primitives/matricies/Matrix.hpp @@ -46,7 +46,7 @@ namespace fgl::engine explicit Matrix( const glm::mat4& matrix ) : glm::mat4( matrix ) {} - Matrix operator*( const Matrix& other ) + Matrix operator*( const Matrix& other ) const { return Matrix( static_cast< glm::mat4 >( *this ) * static_cast< glm::mat4 >( other ) ); } diff --git a/src/engine/systems/render/EntityRendererSystem.cpp b/src/engine/systems/render/EntityRendererSystem.cpp index 17c1184..d387462 100644 --- a/src/engine/systems/render/EntityRendererSystem.cpp +++ b/src/engine/systems/render/EntityRendererSystem.cpp @@ -155,7 +155,7 @@ namespace fgl::engine TracyVkZone( info.tracy_ctx, *command_buffer, "Render textured entities" ); auto [ draw_commands, model_matricies ] = - getDrawCallsFromTree( info.game_objects, info.camera->getFrustumBounds(), IS_VISIBLE | IS_ENTITY ); + getDrawCallsFromTree( info.game_objects, info.camera->getFrustumBounds(), IsVisible | IsEntity ); if ( draw_commands.empty() ) return; diff --git a/src/engine/tree/Chunk.cpp b/src/engine/tree/Chunk.cpp new file mode 100644 index 0000000..cdd1dc6 --- /dev/null +++ b/src/engine/tree/Chunk.cpp @@ -0,0 +1,47 @@ +// +// Created by kj16609 on 11/3/24. +// + +#include "Chunk.hpp" + +#include "engine/utils.hpp" + +namespace fgl::engine::tree +{ + + std::shared_ptr< Chunk > ChunkManager::createChunk( const ChunkID id ) + { + std::shared_ptr< Chunk > chunk { std::make_shared< Chunk >( id ) }; + + m_chunks.insert( std::make_pair( id, chunk ) ); + + return chunk; + } + + std::shared_ptr< Chunk > ChunkManager::getChunk( const ChunkID id ) + {} + + ChunkID getID( const glm::vec3 point ) + { + // the inital chunk starts at 0,0,0. meaning that the bounds of the inital chunk goes from + // +/- CHUNK_SIZE / 2.0f + + // We need to start by dividing the point into the size of the chunks. + constexpr glm::vec3 CHUNK_VEC3 { Chunk::CHUNK_HALF }; + const glm::vec3 chunk_pos { point / CHUNK_VEC3 }; + + // chunk_pos now needs to be clamped to interegers, we always round DOWN. + const glm::vec3 round_offset { glm::sign( chunk_pos ) * glm::vec3( 0.5f ) }; + const glm::vec3 chunk_coords { glm::round( chunk_pos + round_offset ) }; + const glm::vec< 3, int > chunk_coords_i { chunk_coords }; + + ChunkID hash { 0 }; + engine::hashCombine( hash, chunk_coords_i[ 0 ], chunk_coords_i[ 1 ], chunk_coords_i[ 2 ] ); + + return hash; + } + + Chunk::Chunk( const ChunkID id ) : m_id( id ), m_center( getPosition( id ) ) + {} + +} // namespace fgl::engine::tree \ No newline at end of file diff --git a/src/engine/tree/Chunk.hpp b/src/engine/tree/Chunk.hpp new file mode 100644 index 0000000..1b4c222 --- /dev/null +++ b/src/engine/tree/Chunk.hpp @@ -0,0 +1,70 @@ +// +// Created by kj16609 on 11/2/24. +// + +#pragma once +#include +#include +#include + +#include "engine/gameobjects/GameObject.hpp" +#include "glm/vec3.hpp" + +namespace fgl::engine +{ + struct Frustum; +} + +namespace fgl::engine::tree +{ + class Chunk; + + using ChunkID = std::size_t; + + class ChunkManager + { + std::unordered_map< ChunkID, std::shared_ptr< Chunk > > m_chunks {}; + std::queue< std::shared_ptr< Chunk > > m_delete_list {}; + + std::shared_ptr< Chunk > createChunk( const ChunkID id ); + + //! Deletes any chunks pending deletion + void cleanup(); + + //! Returns a shared pointer to the chunk with the given ID. + std::shared_ptr< Chunk > getChunk( const ChunkID id ); + }; + + ChunkID getID( const glm::vec3 point ); + glm::vec3 getPosition( const ChunkID id ); + + class Chunk + { + //! Determines if the chunk is active to the rendering system + bool m_rendering_active { true }; + + const ChunkID m_id; + const glm::vec3 m_center; + + //! Contains a list of all objects within this chunk + std::unordered_map< GameObject::GameObjectID, std::shared_ptr< GameObject > > m_objects {}; + + public: + + Chunk() = delete; + Chunk( const ChunkID id ); + + void addGameObject( std::shared_ptr< GameObject > object ); + + // Size of a chunk from center to + constexpr static float CHUNK_SIZE { 100.0f }; + constexpr static float CHUNK_HALF { CHUNK_SIZE / 2.0f }; + + //! Returns the number of game objects within this chunk + std::size_t childCount(); + + //! Returns true if the bounds of this chunk are visible. + bool isVisible( const Frustum& frustum ) const; + }; + +} // namespace fgl::engine::tree diff --git a/src/engine/tree/octtree/OctTreeNode.cpp b/src/engine/tree/octtree/OctTreeNode.cpp index 3aab7c6..2627f28 100644 --- a/src/engine/tree/octtree/OctTreeNode.cpp +++ b/src/engine/tree/octtree/OctTreeNode.cpp @@ -28,6 +28,7 @@ namespace fgl::engine static bool draw_leaf_real_bounds { false }; static bool draw_inview_bounds { false }; static bool draw_branches { false }; + static bool draw_model_bounding_boxes { false }; static std::size_t number_moved { 0 }; static std::optional< std::chrono::microseconds > time { std::nullopt }; @@ -40,6 +41,7 @@ namespace fgl::engine ImGui::Checkbox( "Draw leaf real bounding boxes", &draw_leaf_real_bounds ); ImGui::Checkbox( "Draw ALL in view bounding boxes", &draw_inview_bounds ); ImGui::Checkbox( "Draw branches", &draw_branches ); + ImGui::Checkbox( "Draw all model bounding boxes", &draw_model_bounding_boxes ); if ( ImGui::Button( "Reorganize Octtree" ) ) { @@ -53,7 +55,7 @@ namespace fgl::engine if ( ImGui::Button( "Recalculate Bounds" ) ) { const auto start { fgl::Clock::now() }; - info.game_objects.recalculateBounds(); + info.game_objects.recalculateChildBounds(); const auto end { fgl::Clock::now() }; const auto time_diff { end - start }; time = std::chrono::duration_cast< std::chrono::microseconds >( time_diff ); @@ -120,23 +122,27 @@ namespace fgl::engine FGL_UNREACHABLE(); } - bool OctTreeNode::contains( const WorldCoordinate coord ) const - { - return this->m_bounds.contains( coord ); - } - - OctTreeNode& OctTreeNode::operator[]( const WorldCoordinate coord ) + OctTreeNode& OctTreeNode::operator[]( const WorldCoordinate coord ) const { assert( std::holds_alternative< OctTreeNodeArray >( m_node_data ) ); + // Bounding box center const auto bounds_center { this->m_bounds.getPosition().vec() }; - const auto test_dim { glm::greaterThanEqual( coord.vec(), bounds_center ) }; + const auto coordinate_center { coord.vec() }; - const auto& node_array { std::get< OctTreeNodeArray >( m_node_data ) }; - const auto& node { node_array[ test_dim.x ? 1 : 0 ][ test_dim.y ? 1 : 0 ][ test_dim.z ? 1 : 0 ] }; - assert( node ); - assert( node->canContain( coord ) ); + //const auto test_dim { glm::greaterThanEqual( coord.vec(), bounds_center ) }; + //const auto& node_array { std::get< OctTreeNodeArray >( m_node_data ) }; + //const auto& node { node_array[ test_dim.x ? 1 : 0 ][ test_dim.y ? 1 : 0 ][ test_dim.z ? 1 : 0 ] }; + + const std::size_t x_idx { coordinate_center.x > bounds_center.x ? 1ul : 0ul }; + const std::size_t y_idx { coordinate_center.y > bounds_center.y ? 1ul : 0ul }; + const std::size_t z_idx { coordinate_center.z > bounds_center.z ? 1ul : 0ul }; + + auto& node { std::get< OctTreeNodeArray >( m_node_data )[ x_idx ][ y_idx ][ z_idx ] }; + + FGL_ASSERT( node, "Node was invalid!" ); + FGL_ASSERT( node->canContain( coord ), "Node was not capable of containing the object!" ); return *node.get(); } @@ -210,7 +216,9 @@ namespace fgl::engine const bool is_forward { obj_coordinate.y > center.y }; const bool is_up { obj_coordinate.z > center.z }; - const std::unique_ptr< OctTreeNode >& node { new_nodes[ is_right ][ is_forward ][ is_up ] }; + const std::unique_ptr< OctTreeNode >& node { + new_nodes[ is_right ? 1 : 0 ][ is_forward ? 1 : 0 ][ is_up ? 1 : 0 ] + }; assert( std::holds_alternative< OctTreeNodeLeaf >( node->m_node_data ) ); std::get< OctTreeNodeLeaf >( node->m_node_data ).emplace_back( std::move( obj ) ); @@ -218,6 +226,8 @@ namespace fgl::engine this->m_node_data = std::move( new_nodes ); + recalculateChildBounds(); + if ( depth - 1 >= 1 ) { split( depth ); @@ -240,9 +250,11 @@ namespace fgl::engine return node; } + const bool should_recalc_bounds { obj.hasComponent< ModelComponent >() }; + objects.emplace_back( std::move( obj ) ); - recalculateBounds(); + if ( should_recalc_bounds ) recalculateLeafBounds(); return this; } @@ -272,15 +284,9 @@ namespace fgl::engine { std::size_t sum { 0 }; - for ( std::size_t x = 0; x < 2; ++x ) + FOR_EACH_OCTTREE_NODE { - for ( std::size_t y = 0; y < 2; ++y ) - { - for ( std::size_t z = 0; z < 2; ++z ) - { - sum += std::get< OctTreeNodeArray >( m_node_data )[ x ][ y ][ z ]->itemCount(); - } - } + sum += std::get< OctTreeNodeArray >( m_node_data )[ x ][ y ][ z ]->itemCount(); } return sum; @@ -299,6 +305,11 @@ namespace fgl::engine return std::get< OctTreeNodeLeaf >( m_node_data ); } + OctTreeNodeLeaf& OctTreeNode::getLeaf() + { + return std::get< OctTreeNodeLeaf >( m_node_data ); + } + bool OctTreeNode::isInFrustum( const Frustum& frustum ) const { #if ENABLE_IMGUI @@ -310,6 +321,23 @@ namespace fgl::engine debug::drawBoundingBox( m_fit_bounding_box ); if ( draw_leaf_real_bounds ) [[unlikely]] debug::drawBoundingBox( m_bounds ); + if ( draw_model_bounding_boxes ) [[unlikely]] + { + for ( const auto& obj : getLeaf() ) + { + const Matrix< MatrixType::ModelToWorld > obj_transform { obj.getTransform().mat() }; + + for ( const auto* model : obj.getComponents< ModelComponent >() ) + { + const auto model_bounds { ( *model )->getBoundingBox() }; + const Matrix< MatrixType::ModelToWorld > model_transform { model->m_transform.mat() }; + + const auto combined_transform { obj_transform * model_transform }; + + debug::drawBoundingBox( combined_transform * model_transform ); + } + } + } } return true; @@ -334,10 +362,17 @@ namespace fgl::engine */ void OctTreeNode::recalculateBounds() { - if ( isLeaf() ) - return recalculateLeafBounds(); - else if ( isBranch() ) - return recalculateNodeBounds(); + if ( isBranch() ) [[likely]] + { + recalculateNodeBounds(); + return; + } + else + { + FGL_ASSERT( isLeaf(), "Expected leaf, Got whatever the fuck this is instead" ); + recalculateLeafBounds(); + return; + } FGL_UNREACHABLE(); } @@ -390,6 +425,22 @@ namespace fgl::engine void OctTreeNode::drawDebug() const {} + void OctTreeNode::recalculateChildBounds() + { + if ( isBranch() ) + { + FOR_EACH_OCTTREE_NODE + { + auto& node { std::get< OctTreeNodeArray >( m_node_data )[ x ][ y ][ z ] }; + node->recalculateChildBounds(); + } + } + else + { + recalculateBounds(); + } + } + OctTreeNode* OctTreeNode::findID( const GameObject::GameObjectID id ) { ZoneScoped; @@ -435,9 +486,18 @@ namespace fgl::engine return canContain( obj.getTransform().translation ); } - bool OctTreeNode::canContain( const WorldCoordinate& obj ) const + bool OctTreeNode::canContain( const WorldCoordinate& coord ) const { - return m_bounds.contains( obj ); + const auto center { this->getCenter() }; + // top right forward + const auto high_center { center.vec() + glm::vec3( this->m_bounds.scale() ) }; + // bottom left back + const auto low_center { center.vec() - glm::vec3( this->m_bounds.scale() ) }; + + const bool under_high_center { glm::all( glm::lessThanEqual( coord.vec(), high_center ) ) }; + const bool above_low_center { glm::all( glm::greaterThan( coord.vec(), low_center ) ) }; + + return under_high_center && above_low_center; } GameObject OctTreeNode::extract( const GameObject::GameObjectID id ) @@ -547,7 +607,7 @@ namespace fgl::engine } // if ( isBoundsExpanded() && m_parent ) - if ( m_parent ) m_parent->recalculateBounds(); + if ( m_parent != nullptr ) m_parent->recalculateBounds(); } void OctTreeNode::recalculateLeafBounds() @@ -555,21 +615,18 @@ namespace fgl::engine FGL_ASSERT( std::holds_alternative< LeafDataT >( m_node_data ), "Node data was not a leaf!" ); const auto& data { std::get< LeafDataT >( m_node_data ) }; - if ( data.empty() ) - { - m_fit_bounding_box = static_cast< AxisAlignedBoundingBox< CoordinateSpace::World > >( m_bounds ); - return; - } + m_fit_bounding_box = static_cast< AxisAlignedBoundingBox< CoordinateSpace::World > >( m_bounds ); + + if ( data.empty() ) return; // If true, Then the fit has already been set, and we should combine with it bool fit_set { false }; for ( const auto& game_object : data ) { - auto model_components { game_object.getComponents< ModelComponent >() }; const Matrix< MatrixType::ModelToWorld > game_object_transform { game_object.getTransform().mat() }; - for ( const auto& model : model_components ) + for ( const ModelComponent* model : game_object.getComponents< ModelComponent >() ) { const OrientedBoundingBox< CS::Model > model_bounding_box { ( *model )->getBoundingBox() }; @@ -583,7 +640,7 @@ namespace fgl::engine const auto aligned_bounding_box { world_bounding_box.alignToWorld() }; - if ( fit_set ) + if ( fit_set ) [[likely]] m_fit_bounding_box = m_fit_bounding_box.combine( aligned_bounding_box ); else { @@ -595,7 +652,7 @@ namespace fgl::engine // Have our parent recalculate its bounds // if ( isBoundsExpanded() && m_parent ) - if ( m_parent ) m_parent->recalculateBounds(); + if ( m_parent != nullptr ) m_parent->recalculateBounds(); } } // namespace fgl::engine \ No newline at end of file diff --git a/src/engine/tree/octtree/OctTreeNode.hpp b/src/engine/tree/octtree/OctTreeNode.hpp index 52439fe..e6dae00 100644 --- a/src/engine/tree/octtree/OctTreeNode.hpp +++ b/src/engine/tree/octtree/OctTreeNode.hpp @@ -66,6 +66,7 @@ namespace fgl::engine WorldCoordinate getCenter() const; WorldCoordinate getFitCenter() const; void drawDebug() const; + void recalculateChildBounds(); private: @@ -97,9 +98,7 @@ namespace fgl::engine void getAllLeafs( std::vector< OctTreeNodeLeaf* >& out_leafs ); void getAllLeafsInFrustum( const Frustum& frustum, std::vector< OctTreeNodeLeaf* >& out_leafs ); - bool contains( WorldCoordinate coord ) const; - - OctTreeNode& operator[]( const WorldCoordinate coord ); + OctTreeNode& operator[]( const WorldCoordinate coord ) const; public: @@ -129,6 +128,7 @@ namespace fgl::engine std::size_t itemCount() const; const OctTreeNodeArray& getBranches() const; const OctTreeNodeLeaf& getLeaf() const; + OctTreeNodeLeaf& getLeaf(); }; #define FOR_EACH_OCTTREE_NODE \