Cleanup octree and get started on chunk system
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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 ) )
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
|
||||
@@ -53,9 +53,11 @@
|
||||
#define FGL_STRICT_ALIGNMENT( bytesize ) [[gnu::warn_if_not_aligned( bytesize )]]
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
#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
|
||||
#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!" )
|
||||
|
||||
@@ -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 ) };
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
47
src/engine/tree/Chunk.cpp
Normal file
47
src/engine/tree/Chunk.cpp
Normal file
@@ -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
|
||||
70
src/engine/tree/Chunk.hpp
Normal file
70
src/engine/tree/Chunk.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// Created by kj16609 on 11/2/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user