Cleanup some octree stuff

This commit is contained in:
2024-03-11 02:21:47 -04:00
parent bfe0da9c21
commit acf2416e6f
19 changed files with 337 additions and 94 deletions

View File

@@ -43,6 +43,15 @@ file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/bin/models")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/models" DESTINATION "${CMAKE_BINARY_DIR}/bin")
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_UPPER_BUILD_TYPE)
if (CMAKE_UPPER_BUILD_TYPE STREQUAL "DEBUG" OR CMAKE_UPPER_BUILD_TYPE STREQUAL "RELWITHDEBINFO")
set(SHADER_DEBUG_FLAGS "-g")
else ()
set(SHADER_DEBUG_FLAGS "")
endif ()
foreach (MODEL IN LISTS MODELS)
get_filename_component(FILENAME ${MODEL} NAME)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/models/${FILENAME}
@@ -54,7 +63,7 @@ endforeach ()
foreach (SHADER IN LISTS FRAG_SHADERS)
get_filename_component(FILENAME ${SHADER} NAME)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADER} -o ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv DEPENDS ${SHADER}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADER_DEBUG_FLAGS} ${SHADER} -o ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv DEPENDS ${SHADER}
COMMENT " Compiling ${SHADER} ")
list(APPEND SPV_SHADERS ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv)
endforeach ()
@@ -62,7 +71,7 @@ endforeach ()
foreach (SHADER IN LISTS VERT_SHADERS)
get_filename_component(FILENAME ${SHADER} NAME)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADER} -o ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv DEPENDS ${SHADER}
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADER_DEBUG_FLAGS} ${SHADER} -o ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv DEPENDS ${SHADER}
COMMENT " Compiling ${SHADER} ")
list(APPEND SPV_SHADERS ${CMAKE_BINARY_DIR}/bin/shaders/${FILENAME}.spv)
endforeach ()

View File

@@ -525,6 +525,7 @@ namespace fgl::engine
sponza.m_transform.rotation = Rotation( 0.0f, 0.0f, 0.0f );
m_game_objects_root.addGameObject( std::move( sponza ) );
m_game_objects_root.recalculateBoundingBoxes();
}
}

View File

@@ -4,7 +4,6 @@
#pragma once
#define FGL_FORCE_NOTHING
#ifndef FGL_FORCE_NOTHING
#ifdef __GNUC__
@@ -15,6 +14,8 @@
#define FGL_COLD __attribute__( ( cold ) )
#define FGL_FORCE_INLINE __attribute__( ( always_inline ) )
#define FGL_FORCE_INLINE_FLATTEN __attribute__( ( always_inline, flatten ) )
#define FGL_ASSUME( ... ) __attribute__( ( assume( __VA_ARGS__ ) ) )
#endif
#else

View File

@@ -4,6 +4,8 @@
#include "GameObject.hpp"
#include "engine/model/Model.hpp"
namespace fgl::engine
{
@@ -13,4 +15,9 @@ namespace fgl::engine
return { current_id++ };
}
OrientedBoundingBox< CoordinateSpace::World > GameObject::getBoundingBox() const
{
return this->m_transform.mat() * this->m_model->getBoundingBox();
}
} // namespace fgl::engine

View File

@@ -11,6 +11,8 @@
namespace fgl::engine
{
template < CoordinateSpace CType >
struct OrientedBoundingBox;
class Model;
@@ -51,6 +53,8 @@ namespace fgl::engine
inline const Rotation& getRotation() const { return m_transform.rotation; }
OrientedBoundingBox< CoordinateSpace::World > getBoundingBox() const;
static GameObject createGameObject();
inline ID getId() const { return m_id; }

View File

@@ -137,10 +137,24 @@ namespace fgl::engine::debug
const Coordinate< CoordinateSpace::Screen > end_screen { toScreenSpace( line.getEnd() ) };
if ( !inView( start_screen.vec() ) && !inView( end_screen.vec() ) ) return;
if ( isBehind( start_screen.vec() ) || isBehind( end_screen.vec() ) ) return;
ImGui::GetForegroundDrawList()->AddLine(
glmToImgui( start_screen ), glmToImgui( end_screen ), ImColor( color.x, color.y, color.z ), thickness );
if ( isBehind( start_screen.vec() ) )
{
const auto frustum { getDebugDrawingCamera().getFrustumBounds() };
const auto new_line { line.flip() };
const auto new_point { frustum.intersection( new_line ) };
}
else if ( isBehind( end_screen.vec() ) )
{}
else
{
ImGui::GetForegroundDrawList()->AddLine(
glmToImgui( start_screen ),
glmToImgui( end_screen ),
ImColor( color.x, color.y, color.z ),
thickness );
}
}
void drawLine(

View File

@@ -319,4 +319,32 @@ namespace fgl::engine
return true;
}
template <>
template <>
Coordinate< CoordinateSpace::World > Frustum<
CoordinateSpace::World >::intersection( const Line< CoordinateSpace::World >& line ) const
{
Coordinate< CoordinateSpace::World > coordinate { line.getEnd() };
//Test each. Whatever the line enters exists first is the point to return
auto testPlane = [ & ]( const Plane< CoordinateSpace::World >& plane )
{
const auto old_distance { signedDistance( line.getDirection(), coordinate, line.getPosition() ) };
const auto intersection { line.intersection( plane ) };
const auto intersection_distance {
signedDistance( line.getDirection(), intersection, line.getPosition() )
};
if ( old_distance < intersection_distance ) coordinate = intersection;
};
testPlane( this->right );
testPlane( this->left );
testPlane( this->near );
testPlane( this->far );
testPlane( this->top );
testPlane( this->bottom );
return coordinate;
}
} // namespace fgl::engine

View File

@@ -81,6 +81,9 @@ namespace fgl::engine
template < typename T >
bool intersects( const T& t ) const;
template < typename T >
Coordinate< CType > intersection( const T& t ) const;
std::array< Coordinate< CType >, 4 * 2 > points() const
{
const Vector pv0 { glm::cross( top.getDirection().vec(), left.getDirection().vec() ) };

View File

@@ -114,6 +114,8 @@ namespace fgl::engine
RotationMatrix mat() const;
Rotation operator*( const Rotation other ) const;
bool operator==( const Rotation& other ) const = default;
};
template < RotationModifierType ModifierType >

View File

@@ -4,6 +4,7 @@
#include "AxisAlignedBoundingBox.hpp"
#include "OrientedBoundingBox.hpp"
#include "engine/primitives/lines/LineSegment.hpp"
namespace fgl::engine
@@ -88,6 +89,67 @@ namespace fgl::engine
return lines;
}
template < CoordinateSpace CType >
AxisAlignedBoundingBox< CType >& AxisAlignedBoundingBox< CType >::combine( const AxisAlignedBoundingBox& other )
{
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 ),
std::max( this->m_top_right_forward.z, other.m_top_right_forward.z ),
};
const Coordinate< CType > new_bottom_left_back {
std::min( this->m_bottom_left_back.x, other.m_bottom_left_back.x ),
std::min( this->m_bottom_left_back.y, other.m_bottom_left_back.y ),
std::min( this->m_bottom_left_back.z, other.m_bottom_left_back.z ),
};
this->m_top_right_forward = new_top_right_forward;
this->m_bottom_left_back = new_bottom_left_back;
return *this;
}
template < CoordinateSpace CType >
AxisAlignedBoundingBox< CType >::AxisAlignedBoundingBox( const OrientedBoundingBox< CType >& oobb ) :
m_top_right_forward( -constants::DEFAULT_VEC3 ),
m_bottom_left_back( constants::DEFAULT_VEC3 )
{
if ( oobb.rotation == Rotation() ) // If default rotation then we can simply just take it as the box is
{
m_top_right_forward = oobb.middle + oobb.scale;
m_bottom_left_back = oobb.middle - oobb.scale;
}
else
{
//Rotation has been done. Need to use the points to figure it out
for ( const auto& point : oobb.points() )
{
m_top_right_forward.x = std::max( m_top_right_forward.x, point.x );
m_top_right_forward.y = std::max( m_top_right_forward.y, point.y );
m_top_right_forward.z = std::max( m_top_right_forward.z, point.z );
m_bottom_left_back.x = std::min( m_bottom_left_back.x, point.x );
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 );
}
}
}
template < CoordinateSpace CType >
AxisAlignedBoundingBox< CType >& AxisAlignedBoundingBox< CType >::combine( const OrientedBoundingBox< CType >&
other )
{
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

View File

@@ -11,6 +11,9 @@
namespace fgl::engine
{
template < CoordinateSpace CType >
struct OrientedBoundingBox;
//! Bounding box alligned with the world axis
template < CoordinateSpace CType >
class AxisAlignedBoundingBox : public interface::BoundingBox
@@ -22,11 +25,6 @@ namespace fgl::engine
constexpr static auto SpaceType { CType };
AxisAlignedBoundingBox() :
m_top_right_forward( constants::WORLD_CENTER ),
m_bottom_left_back( constants::WORLD_CENTER )
{}
explicit AxisAlignedBoundingBox(
const Coordinate< CType > top_right_forward, const Coordinate< CType > bottom_left_back ) :
m_top_right_forward( top_right_forward ),
@@ -37,6 +35,16 @@ namespace fgl::engine
AxisAlignedBoundingBox( midpoint + scale, midpoint - scale )
{}
explicit AxisAlignedBoundingBox( const OrientedBoundingBox< CType >& oobb );
AxisAlignedBoundingBox& combine( const AxisAlignedBoundingBox& other );
AxisAlignedBoundingBox& combine( const OrientedBoundingBox< CType >& other );
bool operator==( const AxisAlignedBoundingBox< CType >& other ) const
{
return m_top_right_forward == other.m_top_right_forward && m_bottom_left_back == other.m_bottom_left_back;
}
virtual Coordinate< CType > getPosition() const
{
return fgl::midpoint( m_top_right_forward, m_bottom_left_back );

View File

@@ -12,11 +12,14 @@ namespace fgl::engine
template < CoordinateSpace CType >
bool AxisAlignedBoundingCube< CType >::contains( const Coordinate< CType >& coordinate ) const
{
const Coordinate< CType > centered_coordinate { coordinate - m_middle };
const Coordinate< CType > centered_coordinate { coordinate - this->getPosition() };
return ( ( centered_coordinate.template x ) < m_span && ( centered_coordinate.template x ) > -m_span )
&& ( ( centered_coordinate.template y ) < m_span && ( centered_coordinate.template y ) > -m_span )
&& ( ( centered_coordinate.template z ) < m_span && ( centered_coordinate.template z ) > -m_span );
return ( ( centered_coordinate.template x ) < this->span()
&& ( centered_coordinate.template x ) > -this->span() )
&& ( ( centered_coordinate.template y ) < this->span()
&& ( centered_coordinate.template y ) > -this->span() )
&& ( ( centered_coordinate.template z ) < this->span()
&& ( centered_coordinate.template z ) > -this->span() );
}
//template class AxisAlignedBoundingCube< CoordinateSpace::Model >;

View File

@@ -16,31 +16,17 @@ namespace fgl::engine
template < CoordinateSpace CType >
class AxisAlignedBoundingCube final : public AxisAlignedBoundingBox< CType >
{
Coordinate< CType > m_middle;
float m_span;
public:
constexpr static auto SpaceType { CType };
AxisAlignedBoundingCube() : m_middle( constants::WORLD_CENTER ), m_span( 1.0f ) {}
explicit AxisAlignedBoundingCube( const Coordinate< CType > middle, const float span ) :
m_middle( middle ),
m_span( span )
AxisAlignedBoundingBox< CType >( middle, Scale( span, span, span ) )
{}
bool contains( const Coordinate< CType >& coordinate ) const;
float span() const { return m_span; }
Scale scale() const override { return Scale( m_span, m_span, m_span ); }
inline Coordinate< CType > topLeftForward() const override { return m_middle + scale(); }
inline Coordinate< CType > bottomLeftBack() const override { return m_middle - scale(); }
Coordinate< CType > getPosition() const override { return m_middle; }
float span() const { return this->scale().x; }
constexpr NormalVector right() const { return NormalVector::bypass( constants::WORLD_RIGHT ); }

View File

@@ -76,6 +76,8 @@ namespace fgl::engine
Coordinate& operator=( const Coordinate& other ) = default;
Coordinate& operator=( Coordinate&& other ) = default;
bool operator==( const Coordinate& other ) const = default;
};
using ModelCoordinate = Coordinate< CoordinateSpace::Model >;

View File

@@ -29,9 +29,7 @@ namespace fgl::engine
{
//Test if the object is in the frustum
const OrientedBoundingBox model_bounding_box {
obj.m_model->getBoundingBox( Matrix< MatrixType::ModelToWorld >( obj.m_transform.mat4() ) )
};
const OrientedBoundingBox model_bounding_box { obj.getBoundingBox() };
obj.m_is_visible = frustum.intersects( model_bounding_box );

View File

@@ -8,6 +8,7 @@
#include <glm/gtx/string_cast.hpp>
#include "engine/debug/drawers.hpp"
#include "engine/model/Model.hpp"
#include "engine/primitives/Frustum.hpp"
namespace fgl::engine
@@ -19,78 +20,76 @@ namespace fgl::engine
std::vector< NodeLeaf* > leafs {};
//Check if we are inside of the frustum.
if ( isInFrustum( frustum ) )
if ( !isInFrustum( frustum ) ) return leafs;
switch ( m_node_data.index() )
{
switch ( m_node_data.index() )
{
case 0: // NodeArray
{
assert( std::holds_alternative< NodeArray >( m_node_data ) );
NodeArray& node_array { std::get< NodeArray >( m_node_data ) };
//Search deeper
case 0: // NodeArray
{
assert( std::holds_alternative< NodeArray >( m_node_data ) );
NodeArray& node_array { std::get< NodeArray >( m_node_data ) };
//Search deeper
#ifndef NDEBUG
for ( int x = 0; x < 2; ++x )
for ( int y = 0; y < 2; ++y )
for ( int z = 0; z < 2; ++z ) assert( node_array[ x ][ y ][ z ] );
for ( int x = 0; x < 2; ++x )
for ( int y = 0; y < 2; ++y )
for ( int z = 0; z < 2; ++z ) assert( node_array[ x ][ y ][ z ] );
#endif
{
const auto ret { node_array[ LEFT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum ) };
leafs = std::move( ret );
}
{
const auto ret { node_array[ LEFT ][ FORWARD ][ BOTTOM ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ LEFT ][ BACK ][ TOP ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ LEFT ][ BACK ][ BOTTOM ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ RIGHT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret {
node_array[ RIGHT ][ FORWARD ][ BOTTOM ]->getAllLeafsInFrustum( frustum )
};
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ RIGHT ][ BACK ][ TOP ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ RIGHT ][ BACK ][ BOTTOM ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
return leafs;
}
case 1: // NodeLeaf
{
assert( std::holds_alternative< NodeLeaf >( m_node_data ) );
leafs.reserve( 4096 );
leafs.emplace_back( &std::get< NodeLeaf >( m_node_data ) );
//debug::world::drawBoundingBox( m_bounds );
return leafs;
const auto ret { node_array[ LEFT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum ) };
leafs = std::move( ret );
}
default:
throw std::runtime_error( "OctTreeNode::Index out of bounds" );
}
{
const auto ret { node_array[ LEFT ][ FORWARD ][ BOTTOM ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ LEFT ][ BACK ][ TOP ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ LEFT ][ BACK ][ BOTTOM ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ RIGHT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ RIGHT ][ FORWARD ][ BOTTOM ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ RIGHT ][ BACK ][ TOP ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
{
const auto ret { node_array[ RIGHT ][ BACK ][ BOTTOM ]->getAllLeafsInFrustum( frustum ) };
leafs.insert( leafs.end(), ret.begin(), ret.end() );
}
return leafs;
}
case 1: // NodeLeaf
{
assert( std::holds_alternative< NodeLeaf >( m_node_data ) );
leafs.reserve( 4096 );
leafs.emplace_back( &std::get< NodeLeaf >( m_node_data ) );
//debug::world::drawBoundingBox( m_bounds );
return leafs;
}
default:
throw std::runtime_error( "OctTreeNode::Index out of bounds" );
}
return leafs;
}
OctTreeNode::OctTreeNode( const WorldCoordinate center, float span, OctTreeNode* parent ) :
m_fit_bounding_box( center, glm::vec3( span, span, span ) ),
m_bounds( center, span ),
m_node_data( NodeLeaf() ),
m_parent( parent )
@@ -198,7 +197,19 @@ namespace fgl::engine
bool OctTreeNode::isInFrustum( const Frustum< CoordinateSpace::World >& frustum )
{
return !isEmpty() && frustum.intersects( m_bounds );
#if ENABLE_IMGUI
if ( isEmpty() ) return false;
if ( frustum.intersects( m_fit_bounding_box ) )
{
debug::world::drawBoundingBox( m_fit_bounding_box );
return true;
}
else
return false;
#else
return !isEmpty() && frustum.intersects( m_fit_bounding_box );
#endif
}
OctTreeNode* OctTreeNode::findID( const GameObject::ID id )
@@ -305,4 +316,85 @@ namespace fgl::engine
return objects;
}
bool OctTreeNode::recalculateBoundingBoxes()
{
ZoneScoped;
const auto old_bounds { m_fit_bounding_box };
if ( std::holds_alternative< NodeArray >( m_node_data ) )
{
ZoneScopedN( "Process Array" );
bool bounding_box_changed { false };
auto& nodes { std::get< NodeArray >( m_node_data ) };
for ( std::size_t x = 0; x < 2; ++x )
{
for ( std::size_t y = 0; y < 2; ++y )
{
for ( std::size_t z = 0; z < 2; ++z )
{
auto& node { nodes[ x ][ y ][ z ] };
bounding_box_changed |= node->recalculateBoundingBoxes();
}
}
}
if ( bounding_box_changed )
{
//We need to update our bounding box now.
auto new_bounds { nodes[ 0 ][ 0 ][ 0 ]->m_fit_bounding_box };
for ( std::size_t x = 0; x < 2; ++x )
{
for ( std::size_t y = 0; y < 2; ++y )
{
for ( std::size_t z = 0; z < 2; ++z )
{
auto& node { nodes[ x ][ y ][ z ] };
new_bounds.combine( node->m_fit_bounding_box );
}
}
}
if ( new_bounds == old_bounds )
{
return false;
}
else
{
this->m_fit_bounding_box = new_bounds;
return true;
}
}
return false;
}
else if ( std::holds_alternative< NodeLeaf >( m_node_data ) )
{
ZoneScopedN( "Process Leaf" );
auto& game_objects { std::get< NodeLeaf >( m_node_data ) };
if ( game_objects.size() == 0 ) return false;
AxisAlignedBoundingBox< CoordinateSpace::World > new_bounds { game_objects[ 0 ].getBoundingBox() };
[[assume( game_objects.size() <= MAX_NODES_IN_LEAF )]];
for ( const GameObject& obj : game_objects )
{
new_bounds.combine( obj.getBoundingBox() );
}
if ( new_bounds == old_bounds )
{
return false;
}
else
{
this->m_fit_bounding_box = new_bounds;
return true;
}
}
std::unreachable();
}
} // namespace fgl::engine

View File

@@ -36,7 +36,12 @@ namespace fgl::engine
class OctTreeNode
{
//! Fit to each model
AxisAlignedBoundingBox< CoordinateSpace::World > m_fit_bounding_box;
//! Real bounds of the node
AxisAlignedBoundingCube< CoordinateSpace::World > m_bounds;
std::variant< NodeArray, NodeLeaf > m_node_data;
OctTreeNode* m_parent;
@@ -81,6 +86,8 @@ namespace fgl::engine
public:
bool recalculateBoundingBoxes();
std::vector< NodeLeaf* > getAllLeafs();
std::vector< NodeLeaf* > getAllLeafsInFrustum( const Frustum< CoordinateSpace::World >& frustum );

View File

@@ -0,0 +1,5 @@
//
// Created by kj16609 on 3/11/24.
//
#include "QuadTree.hpp"

View File

@@ -0,0 +1,11 @@
//
// Created by kj16609 on 3/11/24.
//
#ifndef GAME_QUADTREE_HPP
#define GAME_QUADTREE_HPP
class QuadTree
{};
#endif //GAME_QUADTREE_HPP