diff --git a/CMakeLists.txt b/CMakeLists.txt index c2372e9..4ee801b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ project(Game LANGUAGES CXX C) enable_testing() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -86,5 +87,12 @@ add_custom_target(models ALL DEPENDS ${OBJ_MODELS}) add_dependencies(${PROJECT_NAME} shaders) add_dependencies(${PROJECT_NAME} models) + +add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/compile_commands.json + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/compile_commands.json ${CMAKE_SOURCE_DIR}/compile_commands.json + COMMENT "Copying build commands to src dir") + + SetVersionInfo() -CompilerPostSetup() \ No newline at end of file +CompilerPostSetup() diff --git a/models/textures/heightmap.png b/models/textures/heightmap.png deleted file mode 100644 index c25e26b..0000000 Binary files a/models/textures/heightmap.png and /dev/null differ diff --git a/src/engine/Camera.cpp b/src/engine/Camera.cpp index ca7cbe0..1ba43dd 100644 --- a/src/engine/Camera.cpp +++ b/src/engine/Camera.cpp @@ -153,4 +153,4 @@ namespace fgl::engine return last_frustum_pos; } -} // namespace fgl::engine \ No newline at end of file +} // namespace fgl::engine diff --git a/src/engine/EngineContext.cpp b/src/engine/EngineContext.cpp index f14a6f6..74b190e 100644 --- a/src/engine/EngineContext.cpp +++ b/src/engine/EngineContext.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "KeyboardMovementController.hpp" #include "engine/Average.hpp" @@ -38,7 +39,7 @@ namespace fgl::engine EngineContext::EngineContext() { using namespace fgl::literals::size_literals; - initGlobalStagingBuffer( 256_MiB ); + initGlobalStagingBuffer( 512_MiB ); #if ENABLE_IMGUI initImGui(); #endif @@ -293,7 +294,9 @@ namespace fgl::engine generateTerrainModel( m_terrain_system.getVertexBuffer(), m_terrain_system.getIndexBuffer() ) }; - Texture texture { Texture::loadFromFile( "models/textures/heightmap.png" ) }; + //Texture texture { Texture::loadFromFile( "models/Vally/textures/heightmap.png" ) }; + Texture texture { Texture::generateFromPerlinNoise( 1024, 1024 ) }; + Sampler sampler { vk::Filter::eLinear, vk::Filter::eLinear, vk::SamplerMipmapMode::eLinear, @@ -425,4 +428,4 @@ namespace fgl::engine ImGui::DestroyContext(); } -} // namespace fgl::engine \ No newline at end of file +} // namespace fgl::engine diff --git a/src/engine/buffers/vector/DeviceVector.hpp b/src/engine/buffers/vector/DeviceVector.hpp index 993111f..327c844 100644 --- a/src/engine/buffers/vector/DeviceVector.hpp +++ b/src/engine/buffers/vector/DeviceVector.hpp @@ -45,7 +45,7 @@ namespace fgl::engine void dropStaging() { - assert( staged ); + assert( staged && "Staging buffer has not been commanded to write yet!" ); m_staging_buffer.reset(); } diff --git a/src/engine/math/noise/perlin/generator.cpp b/src/engine/math/noise/perlin/generator.cpp new file mode 100644 index 0000000..fad1003 --- /dev/null +++ b/src/engine/math/noise/perlin/generator.cpp @@ -0,0 +1,113 @@ +// +// Created by kj16609 on 3/27/24. +// + +#include "generator.hpp" + +#include + +namespace fgl::engine +{ + glm::vec2 randomGradient( int ix, int iy ) + { + constexpr unsigned w { 8 * sizeof( unsigned ) }; + constexpr unsigned s { w / 2 }; + unsigned a { static_cast< unsigned >( ix ) }; + unsigned b { static_cast< unsigned >( iy ) }; + + a *= 3284157443; + + b ^= a << s | a >> ( w - s ); + b *= 1911520717; + + a ^= b << s | b >> ( w - s ); + a *= 2048419325; + const float val { static_cast< float >( a ) * ( std::numbers::pi_v< float > / ~( ~0u >> 1 ) ) }; + + return glm::vec2( sin( val ), cos( val ) ); + } + + float + dotGridGradiant( const glm::vec< 2, int > i_coord, const glm::vec2 coord, [[maybe_unused]] std::mt19937_64& mt ) + { + const glm::vec2 gradiant { randomGradient( i_coord.x, i_coord.y ) }; + + const glm::vec2 d { coord - static_cast< glm::vec2 >( i_coord ) }; + + return glm::dot( d, gradiant ); + } + + float interpolate( const float a0, const float a1, const float w ) + { + return ( a1 - a0 ) * ( 3.0f - w * 2.0f ) * w * w + a0; + } + + float perlin( const glm::vec2 coord, std::mt19937_64& mt ) + { + const glm::vec< 2, int > c0 { glm::round( coord - glm::vec2( 0.5f ) ) }; + const glm::vec< 2, int > c1 { c0 + glm::vec< 2, int >( 1 ) }; + + const float x_weight { coord.x - static_cast< float >( c0.x ) }; + const float y_weight { coord.y - static_cast< float >( c0.y ) }; + + const float n0 { dotGridGradiant( c0, coord, mt ) }; + const float n1 { dotGridGradiant( { c1.x, c0.y }, coord, mt ) }; + const float ix0 { interpolate( n0, n1, x_weight ) }; + + const float n2 { dotGridGradiant( { c0.x, c1.y }, coord, mt ) }; + const float n3 { dotGridGradiant( c1, coord, mt ) }; + const float ix1 { interpolate( n2, n3, x_weight ) }; + + return interpolate( ix0, ix1, y_weight ); + } + + std::vector< std::byte > + generatePerlinImage( const glm::vec< 2, std::size_t > size, const int octives, const std::size_t seed ) + { + constexpr std::size_t channel_count { 4 }; // RGBA + std::vector< std::byte > data { size.x * size.y * channel_count }; + + std::mt19937_64 mt { seed }; + + for ( std::size_t x = 0; x < size.x; ++x ) + { + for ( std::size_t y = 0; y < size.y; ++y ) + { + const auto index { ( y * size.x + x ) * 4 }; + + float val { 0.0f }; + + float freq { 1 }; + float amp { 1 }; + + constexpr float GRID_SIZE { 400.0f }; + + for ( int i = 0; i < octives; ++i ) + { + val += perlin( + { ( static_cast< float >( x ) * freq ) / GRID_SIZE, + ( static_cast< float >( y ) * freq ) / GRID_SIZE }, + mt ) + * amp; + + freq *= 2.0f; + amp /= 2.0f; + } + + val *= 1.2f; + + val = std::clamp( val, -1.0f, 1.0f ); + + const std::byte color { static_cast< std::byte >( ( val + 1.0f ) * 0.5f * 255.0f ) }; + + data[ index ] = color; + data[ index + 1 ] = color; + data[ index + 2 ] = color; + data[ index + 3 ] = std::byte( 255 ); + } + } + + return data; + } + +} // namespace fgl::engine diff --git a/src/engine/math/noise/perlin/generator.hpp b/src/engine/math/noise/perlin/generator.hpp new file mode 100644 index 0000000..6daccd9 --- /dev/null +++ b/src/engine/math/noise/perlin/generator.hpp @@ -0,0 +1,15 @@ +// +// Created by kj16609 on 3/27/24. +// + +#pragma once +#include + +namespace fgl::engine +{ + + float perlin( glm::vec2 coord, std::size_t seed = 0 ); + [[nodiscard]] std::vector< std::byte > + generatePerlinImage( const glm::vec< 2, std::size_t > size, const int octives, const std::size_t seed = 0 ); + +} // namespace fgl::engine diff --git a/src/engine/model/objLoading.cpp b/src/engine/model/objLoading.cpp index e859036..68ac598 100644 --- a/src/engine/model/objLoading.cpp +++ b/src/engine/model/objLoading.cpp @@ -89,21 +89,18 @@ namespace fgl::engine } } - for ( const auto idx : indicies ) + for ( [[maybe_unused]] const auto idx : indicies ) { assert( idx < verts.size() ); } const OrientedBoundingBox bounding_box { generateBoundingFromVerts( verts ) }; - auto& itter = m_primitives.emplace_back( + [[maybe_unused]] auto& itter = m_primitives.emplace_back( VertexBufferSuballocation( m_vertex_buffer, std::move( verts ) ), IndexBufferSuballocation( m_index_buffer, std::move( indicies ) ), bounding_box ); - itter.m_index_buffer.dropStaging(); - itter.m_vertex_buffer.dropStaging(); - std::cout << unique_verts.size() << " unique verts" << std::endl; } } // namespace fgl::engine diff --git a/src/engine/primitives/boxes/AxisAlignedBoundingCube.cpp b/src/engine/primitives/boxes/AxisAlignedBoundingCube.cpp index bb9e863..b666eb6 100644 --- a/src/engine/primitives/boxes/AxisAlignedBoundingCube.cpp +++ b/src/engine/primitives/boxes/AxisAlignedBoundingCube.cpp @@ -14,18 +14,10 @@ namespace fgl::engine { const Coordinate< CType > centered_coordinate { coordinate - this->getPosition() }; - const bool is_high_x { centered_coordinate.x > this->span() }; - const bool is_high_y { centered_coordinate.x > this->span() }; - const bool is_high_z { centered_coordinate.x > this->span() }; + const bool top_in_range { glm::all( glm::lessThanEqual( centered_coordinate.vec(), this->scale() ) ) }; + const bool bottom_in_range { glm::all( glm::greaterThanEqual( centered_coordinate.vec(), -this->scale() ) ) }; - const bool is_low_x { centered_coordinate.x < -this->span() }; - const bool is_low_y { centered_coordinate.x < -this->span() }; - const bool is_low_z { centered_coordinate.x < -this->span() }; - - const bool is_high { is_high_x || is_high_y || is_high_z }; - const bool is_low { is_low_x || is_low_y || is_low_z }; - - return !is_high && !is_low; + return top_in_range && bottom_in_range; } //template class AxisAlignedBoundingCube< CoordinateSpace::Model >; diff --git a/src/engine/primitives/points/Coordinate.hpp b/src/engine/primitives/points/Coordinate.hpp index 10c31c5..0b8d754 100644 --- a/src/engine/primitives/points/Coordinate.hpp +++ b/src/engine/primitives/points/Coordinate.hpp @@ -5,7 +5,6 @@ #pragma once #include -#include #include @@ -13,11 +12,9 @@ #include "engine/primitives/CoordinateSpace.hpp" #include "engine/primitives/Scale.hpp" #include "engine/primitives/matricies/Matrix.hpp" -#include "engine/primitives/matricies/MatrixEvolvedTypes.hpp" namespace fgl::engine { - class Vector; class NormalVector; diff --git a/src/engine/systems/EntityRendererSystem.cpp b/src/engine/systems/EntityRendererSystem.cpp index 5a09574..f9b9682 100644 --- a/src/engine/systems/EntityRendererSystem.cpp +++ b/src/engine/systems/EntityRendererSystem.cpp @@ -41,8 +41,8 @@ namespace fgl::engine using namespace fgl::literals::size_literals; - initVertexBuffer( 128_MiB ); - initIndexBuffer( 64_MiB ); + initVertexBuffer( 512_MiB ); + initIndexBuffer( 128_MiB ); initDrawParameterBuffer( 1_KiB ); } diff --git a/src/engine/systems/TerrainSystem.cpp b/src/engine/systems/TerrainSystem.cpp index f70a81b..1bb704d 100644 --- a/src/engine/systems/TerrainSystem.cpp +++ b/src/engine/systems/TerrainSystem.cpp @@ -31,7 +31,7 @@ namespace fgl::engine using namespace fgl::literals::size_literals; initVertexBuffer( 16_MiB ); - initIndexBuffer( 512_B ); + initIndexBuffer( 2_MiB ); this->m_index_buffer->setDebugName( "Terrain index buffer" ); this->m_vertex_buffer->setDebugName( "Terrain vertex buffer" ); diff --git a/src/engine/texture/Texture.cpp b/src/engine/texture/Texture.cpp index 1673dea..57bb800 100644 --- a/src/engine/texture/Texture.cpp +++ b/src/engine/texture/Texture.cpp @@ -18,6 +18,7 @@ #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wconversion" +#include "engine/math/noise/perlin/generator.hpp" #include "imgui/imgui_impl_vulkan.h" #pragma GCC diagnostic pop @@ -26,7 +27,7 @@ namespace fgl::engine inline static std::unordered_map< std::string, std::weak_ptr< TextureHandle > > texture_map; - std::tuple< std::vector< unsigned char >, int, int, int > loadTexture( const std::filesystem::path& path ) + std::tuple< std::vector< std::byte >, int, int, int > loadTexture( const std::filesystem::path& path ) { ZoneScoped; if ( !std::filesystem::exists( path ) ) throw std::runtime_error( "Failed to open file: " + path.string() ); @@ -39,7 +40,7 @@ namespace fgl::engine const auto data_c { stbi_load( path_str.data(), &x, &y, &channels, 4 ) }; - std::vector< unsigned char > data {}; + std::vector< std::byte > data {}; data.resize( x * y * 4 ); std::memcpy( data.data(), data_c, x * y * 4 ); @@ -67,7 +68,7 @@ namespace fgl::engine } } - auto data { loadTexture( path ) }; + const auto data { loadTexture( path ) }; Texture tex { data }; tex.m_handle->m_image_view->setName( path.string() ); @@ -76,18 +77,27 @@ namespace fgl::engine return std::move( tex ); } - Texture::Texture( std::shared_ptr< TextureHandle > handle ) : m_handle( handle ) + Texture Texture::generateFromPerlinNoise( int x_size, int y_size ) + { + const std::vector< std::byte > data { generatePerlinImage( { x_size, y_size }, 15 ) }; + + Texture tex { data, x_size, y_size, 4 }; + + return std::move( tex ); + } + + Texture::Texture( std::shared_ptr< TextureHandle > handle ) : m_handle( std::move( handle ) ) {} - Texture::Texture( std::tuple< std::vector< unsigned char >, int, int, int > tuple ) : + Texture::Texture( const std::tuple< std::vector< std::byte >, int, int, int >& tuple ) : Texture( std::get< 0 >( tuple ), std::get< 1 >( tuple ), std::get< 2 >( tuple ), std::get< 3 >( tuple ) ) {} - Texture::Texture( std::vector< unsigned char >& data, const int x, const int y, const int channels ) : + Texture::Texture( const std::vector< std::byte >& data, const int x, const int y, const int channels ) : Texture( data, vk::Extent2D( x, y ), channels ) {} - Texture::Texture( std::vector< unsigned char >& data, const vk::Extent2D extent, const int channels ) : + Texture::Texture( const std::vector< std::byte >& data, const vk::Extent2D extent, const int channels ) : m_handle( std::make_shared< TextureHandle >( data, extent, channels ) ) {} diff --git a/src/engine/texture/Texture.hpp b/src/engine/texture/Texture.hpp index 517a523..649123d 100644 --- a/src/engine/texture/Texture.hpp +++ b/src/engine/texture/Texture.hpp @@ -22,13 +22,13 @@ namespace fgl::engine //! Has this texture been submitted to the GPU? bool submitte_to_gpu { false }; - Texture( std::tuple< std::vector< unsigned char >, int, int, int > ); - Texture( std::shared_ptr< TextureHandle > handle ); + [[nodiscard]] Texture( const std::tuple< std::vector< std::byte >, int, int, int >& ); + [[nodiscard]] Texture( std::shared_ptr< TextureHandle > handle ); public: - Texture( std::vector< unsigned char >& data, const int x, const int y, const int channels ); - Texture( std::vector< unsigned char >& data, const vk::Extent2D extent, const int channels ); + [[nodiscard]] Texture( const std::vector< std::byte >& data, const int x, const int y, const int channels ); + [[nodiscard]] Texture( const std::vector< std::byte >& data, const vk::Extent2D extent, const int channels ); Texture( const Texture& ) = delete; Texture& operator=( const Texture& ) = delete; @@ -50,7 +50,8 @@ namespace fgl::engine void createImGuiSet(); - static Texture loadFromFile( const std::filesystem::path& path ); + [[nodiscard]] static Texture generateFromPerlinNoise( int x_size, int y_size ); + [[nodiscard]] static Texture loadFromFile( const std::filesystem::path& path ); static DescriptorSet& getTextureDescriptorSet(); }; diff --git a/src/engine/texture/TextureHandle.cpp b/src/engine/texture/TextureHandle.cpp index a4bf0af..28cad44 100644 --- a/src/engine/texture/TextureHandle.cpp +++ b/src/engine/texture/TextureHandle.cpp @@ -19,7 +19,7 @@ namespace fgl::engine { TextureHandle::TextureHandle( - const std::vector< unsigned char >& data, const vk::Extent2D extent, [[maybe_unused]] const int channels ) : + const std::vector< std::byte >& data, const vk::Extent2D extent, [[maybe_unused]] const int channels ) : m_extent( extent ) { ZoneScoped; diff --git a/src/engine/texture/TextureHandle.hpp b/src/engine/texture/TextureHandle.hpp index 3463c11..1487f50 100644 --- a/src/engine/texture/TextureHandle.hpp +++ b/src/engine/texture/TextureHandle.hpp @@ -33,7 +33,7 @@ namespace fgl::engine public: - TextureHandle( const std::vector< unsigned char >& data, const vk::Extent2D extent, const int channels ); + TextureHandle( const std::vector< std::byte >& data, const vk::Extent2D extent, const int channels ); ~TextureHandle(); }; diff --git a/src/engine/tree/octtree/OctTreeNode.cpp b/src/engine/tree/octtree/OctTreeNode.cpp index d9791ef..f3f1411 100644 --- a/src/engine/tree/octtree/OctTreeNode.cpp +++ b/src/engine/tree/octtree/OctTreeNode.cpp @@ -11,6 +11,7 @@ #include "engine/debug/drawers.hpp" #include "engine/model/Model.hpp" #include "engine/primitives/Frustum.hpp" +#include "engine/tree/quadtree/QuadTree.hpp" namespace fgl::engine { @@ -49,8 +50,8 @@ namespace fgl::engine #endif } - void OctTreeNode:: - getAllLeafsInFrustum( const Frustum< CoordinateSpace::World >& frustum, std::vector< NodeLeaf* >& out_leafs ) + void OctTreeNode::getAllLeafsInFrustum( + const Frustum< CoordinateSpace::World >& frustum, std::vector< OctTreeNodeLeaf* >& out_leafs ) { //Check if we are inside of the frustum. if ( !isInFrustum( frustum ) ) return; @@ -61,8 +62,8 @@ namespace fgl::engine { case 0: // NodeArray { - assert( std::holds_alternative< NodeArray >( m_node_data ) ); - NodeArray& node_array { std::get< NodeArray >( m_node_data ) }; + assert( std::holds_alternative< OctTreeNodeArray >( m_node_data ) ); + OctTreeNodeArray& node_array { std::get< OctTreeNodeArray >( m_node_data ) }; //Search deeper node_array[ LEFT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum, out_leafs ); @@ -80,8 +81,8 @@ namespace fgl::engine } case 1: // NodeLeaf { - assert( std::holds_alternative< NodeLeaf >( m_node_data ) ); - NodeLeaf& leaf { std::get< NodeLeaf >( m_node_data ) }; + assert( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) ); + OctTreeNodeLeaf& leaf { std::get< OctTreeNodeLeaf >( m_node_data ) }; leafs.reserve( LEAF_RESERVE_SIZE ); leafs.emplace_back( &leaf ); @@ -96,28 +97,45 @@ namespace fgl::engine std::unreachable(); } + bool OctTreeNode::contains( const WorldCoordinate coord ) const + { + return this->m_bounds.contains( coord ); + } + + OctTreeNode& OctTreeNode::operator[]( const WorldCoordinate coord ) + { + assert( std::holds_alternative< OctTreeNodeArray >( m_node_data ) ); + const auto test_dim { glm::greaterThanEqual( coord.vec(), this->m_bounds.getPosition().vec() ) }; + + auto& node_array { std::get< OctTreeNodeArray >( m_node_data ) }; + const auto& node { node_array[ test_dim.x ][ test_dim.y ][ test_dim.z ] }; + assert( node ); + + return *node.get(); + } + 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_node_data( OctTreeNodeLeaf() ), m_parent( parent ) { - assert( std::holds_alternative< NodeLeaf >( m_node_data ) ); - std::get< NodeLeaf >( m_node_data ).reserve( MAX_NODES_IN_LEAF ); + assert( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) ); + std::get< OctTreeNodeLeaf >( m_node_data ).reserve( MAX_NODES_IN_LEAF ); } void OctTreeNode::split( int depth ) { ZoneScoped; - if ( std::holds_alternative< NodeArray >( m_node_data ) ) return; - auto& game_objects { std::get< NodeLeaf >( m_node_data ) }; + if ( std::holds_alternative< OctTreeNodeArray >( m_node_data ) ) return; + auto& game_objects { std::get< OctTreeNodeLeaf >( m_node_data ) }; //Figure out the half span const float half_span { m_bounds.span() / 2.0f }; const Coordinate< CoordinateSpace::World > center { m_bounds.getPosition() }; - NodeArray new_nodes {}; + OctTreeNodeArray new_nodes {}; const float left_x { center.x - half_span }; const float right_x { center.x + half_span }; @@ -162,9 +180,9 @@ namespace fgl::engine const bool is_up { obj_coordinate.z > center.z }; std::unique_ptr< OctTreeNode >& node { new_nodes[ is_right ][ is_forward ][ is_up ] }; - assert( std::holds_alternative< NodeLeaf >( node->m_node_data ) ); + assert( std::holds_alternative< OctTreeNodeLeaf >( node->m_node_data ) ); - std::get< NodeLeaf >( node->m_node_data ).emplace_back( std::move( obj ) ); + std::get< OctTreeNodeLeaf >( node->m_node_data ).emplace_back( std::move( obj ) ); } this->m_node_data = std::move( new_nodes ); @@ -178,9 +196,9 @@ namespace fgl::engine OctTreeNode* OctTreeNode::addGameObject( GameObject&& obj ) { assert( this->canContain( obj ) ); - if ( std::holds_alternative< NodeLeaf >( m_node_data ) ) + if ( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) ) { - auto& objects { std::get< NodeLeaf >( m_node_data ) }; + auto& objects { std::get< OctTreeNodeLeaf >( m_node_data ) }; assert( objects.capacity() == MAX_NODES_IN_LEAF ); if ( objects.size() + 1 >= MAX_NODES_IN_LEAF ) { @@ -195,15 +213,7 @@ namespace fgl::engine } else { - const auto& center { m_bounds.getPosition() }; - const auto& obj_coordinate { obj.m_transform.translation }; - const bool is_right { obj_coordinate.x >= center.x }; - const bool is_forward { obj_coordinate.y >= center.y }; - const bool is_up { obj_coordinate.z >= center.z }; - - auto& nodes { std::get< NodeArray >( m_node_data ) }; - - return nodes[ is_right ][ is_forward ][ is_up ]->addGameObject( std::move( obj ) ); + return ( *this )[ obj.getPosition() ].addGameObject( std::forward< GameObject >( obj ) ); } } @@ -212,7 +222,7 @@ namespace fgl::engine #if ENABLE_IMGUI if ( !isEmpty() && frustum.intersects( m_fit_bounding_box ) ) { - if ( ( draw_inview_bounds || std::holds_alternative< NodeLeaf >( this->m_node_data ) ) && m_parent ) + if ( ( draw_inview_bounds || std::holds_alternative< OctTreeNodeLeaf >( this->m_node_data ) ) && m_parent ) { if ( draw_leaf_fit_bounds ) debug::world::drawBoundingBox( m_fit_bounding_box ); if ( draw_leaf_real_bounds ) debug::world::drawBoundingBox( m_bounds ); @@ -231,10 +241,10 @@ namespace fgl::engine OctTreeNode* OctTreeNode::findID( const GameObject::ID id ) { ZoneScoped; - if ( std::holds_alternative< NodeLeaf >( this->m_node_data ) ) + if ( std::holds_alternative< OctTreeNodeLeaf >( this->m_node_data ) ) { //We are the last node. Check if we have the ID - const auto& game_objects { std::get< NodeLeaf >( m_node_data ) }; + const auto& game_objects { std::get< OctTreeNodeLeaf >( m_node_data ) }; if ( std::find_if( game_objects.begin(), @@ -249,9 +259,9 @@ namespace fgl::engine return nullptr; } } - else if ( std::holds_alternative< NodeArray >( this->m_node_data ) ) + else if ( std::holds_alternative< OctTreeNodeArray >( this->m_node_data ) ) { - const auto& node_array { std::get< NodeArray >( this->m_node_data ) }; + const auto& node_array { std::get< OctTreeNodeArray >( this->m_node_data ) }; for ( std::size_t x = 0; x < 2; ++x ) { @@ -273,8 +283,8 @@ namespace fgl::engine auto OctTreeNode::getGameObjectItter( const GameObject::ID id ) { - assert( std::holds_alternative< NodeLeaf >( this->m_node_data ) ); - auto& game_objects { std::get< NodeLeaf >( this->m_node_data ) }; + assert( std::holds_alternative< OctTreeNodeLeaf >( this->m_node_data ) ); + auto& game_objects { std::get< OctTreeNodeLeaf >( this->m_node_data ) }; return std::find_if( game_objects.begin(), game_objects.end(), [ id ]( const GameObject& obj ) { return id == obj.getId(); } ); } @@ -288,7 +298,7 @@ namespace fgl::engine { auto itter { getGameObjectItter( id ) }; auto game_object { std::move( *itter ) }; - auto& game_objects { std::get< NodeLeaf >( this->m_node_data ) }; + auto& game_objects { std::get< OctTreeNodeLeaf >( this->m_node_data ) }; game_objects.erase( itter ); return game_object; } @@ -301,18 +311,18 @@ namespace fgl::engine return m_parent->getRoot(); } - void OctTreeNode::getAllLeafs( std::vector< NodeLeaf* >& objects ) + void OctTreeNode::getAllLeafs( std::vector< OctTreeNodeLeaf* >& objects ) { ZoneScoped; - if ( std::holds_alternative< NodeLeaf >( m_node_data ) ) + if ( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) ) { - auto& leaf { std::get< NodeLeaf >( m_node_data ) }; + auto& leaf { std::get< OctTreeNodeLeaf >( m_node_data ) }; //No point in us giving back an empy leaf if ( leaf.size() > 0 ) objects.emplace_back( &leaf ); } else { - auto& nodes { std::get< NodeArray >( m_node_data ) }; + auto& nodes { std::get< OctTreeNodeArray >( m_node_data ) }; for ( std::size_t x = 0; x < 2; ++x ) { @@ -332,11 +342,11 @@ namespace fgl::engine { ZoneScoped; const auto old_bounds { m_fit_bounding_box }; - if ( std::holds_alternative< NodeArray >( m_node_data ) ) + if ( std::holds_alternative< OctTreeNodeArray >( m_node_data ) ) { ZoneScopedN( "Process Array" ); bool bounding_box_changed { false }; - auto& nodes { std::get< NodeArray >( m_node_data ) }; + auto& nodes { std::get< OctTreeNodeArray >( m_node_data ) }; for ( std::size_t x = 0; x < 2; ++x ) { for ( std::size_t y = 0; y < 2; ++y ) @@ -380,10 +390,10 @@ namespace fgl::engine return false; } - else if ( std::holds_alternative< NodeLeaf >( m_node_data ) ) + else if ( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) ) { ZoneScopedN( "Process Leaf" ); - auto& game_objects { std::get< NodeLeaf >( m_node_data ) }; + auto& game_objects { std::get< OctTreeNodeLeaf >( m_node_data ) }; if ( game_objects.size() == 0 ) return false; diff --git a/src/engine/tree/octtree/OctTreeNode.hpp b/src/engine/tree/octtree/OctTreeNode.hpp index 4196b60..ef838df 100644 --- a/src/engine/tree/octtree/OctTreeNode.hpp +++ b/src/engine/tree/octtree/OctTreeNode.hpp @@ -29,38 +29,10 @@ namespace fgl::engine class OctTreeNode; class GameObject; - template < typename T > - class OctTreeAllocator : public std::allocator< T > - { - std::vector< std::vector< std::byte > > blocks; - using BlockIDX = int; + using OctTreeNodeArray = std::array< std::array< std::array< std::unique_ptr< OctTreeNode >, 2 >, 2 >, 2 >; + using OctTreeNodeLeaf = std::vector< GameObject >; - //! Map for each pointer to their respective blocks - std::unordered_map< T*, BlockIDX > m_block_map; - }; - - template < typename T > - using unique_alloc_ptr = std::unique_ptr< T, std::function< void( T* ) > >; - - template < typename T, typename... Ts > - unique_alloc_ptr< T > make_unique_from_allocator( OctTreeAllocator< T >& allocator, Ts... args ) - { - T* ptr = allocator.allocate( 1 ); - allocator.construct( ptr, args... ); - - auto deleter = [ &allocator ]( const auto* ptr_i ) -> void - { - allocator.destroy( ptr_i ); - allocator.deallocate( ptr_i, 1 ); - }; - - return std::unique_ptr< T, decltype( deleter ) >( ptr, deleter ); - } - - using NodeArray = std::array< std::array< std::array< std::unique_ptr< OctTreeNode >, 2 >, 2 >, 2 >; - using NodeLeaf = std::vector< GameObject >; - - static_assert( sizeof( NodeArray ) == sizeof( OctTreeNode* ) * 2 * 2 * 2 ); + static_assert( sizeof( OctTreeNodeArray ) == sizeof( OctTreeNode* ) * 2 * 2 * 2 ); static_assert( sizeof( OctTreeNode* ) == sizeof( std::uint64_t ) ); struct FrameInfo; @@ -75,8 +47,8 @@ namespace fgl::engine //! Real bounds of the node AxisAlignedBoundingCube< CoordinateSpace::World > m_bounds; - using NodeDataT = NodeArray; - using LeafDataT = NodeLeaf; + using NodeDataT = OctTreeNodeArray; + using LeafDataT = OctTreeNodeLeaf; std::variant< NodeDataT, LeafDataT > m_node_data; @@ -117,14 +89,19 @@ namespace fgl::engine bool isEmpty() const { - return std::holds_alternative< NodeLeaf >( m_node_data ) && std::get< NodeLeaf >( m_node_data ).empty(); + return std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) + && std::get< OctTreeNodeLeaf >( m_node_data ).empty(); } auto getGameObjectItter( const GameObject::ID id ); - void getAllLeafs( std::vector< NodeLeaf* >& out_leafs ); + void getAllLeafs( std::vector< OctTreeNodeLeaf* >& out_leafs ); void getAllLeafsInFrustum( - const Frustum< CoordinateSpace::World >& frustum, std::vector< NodeLeaf* >& out_leafs ); + const Frustum< CoordinateSpace::World >& frustum, std::vector< OctTreeNodeLeaf* >& out_leafs ); + + bool contains( const WorldCoordinate coord ) const; + + OctTreeNode& operator[]( const WorldCoordinate coord ); public: @@ -135,20 +112,20 @@ namespace fgl::engine constexpr static std::size_t LEAF_RESERVE_SIZE { 1024 }; - [[nodiscard]] inline std::vector< NodeLeaf* > getAllLeafs() + [[nodiscard]] inline std::vector< OctTreeNodeLeaf* > getAllLeafs() { ZoneScoped; - std::vector< NodeLeaf* > leafs; + std::vector< OctTreeNodeLeaf* > leafs; leafs.reserve( LEAF_RESERVE_SIZE ); this->getAllLeafs( leafs ); return leafs; } - [[nodiscard]] inline std::vector< NodeLeaf* > getAllLeafsInFrustum( const Frustum< CoordinateSpace::World >& - frustum ) + [[nodiscard]] inline std::vector< OctTreeNodeLeaf* > getAllLeafsInFrustum( const Frustum< + CoordinateSpace::World >& frustum ) { ZoneScoped; - std::vector< NodeLeaf* > leafs; + std::vector< OctTreeNodeLeaf* > leafs; leafs.reserve( LEAF_RESERVE_SIZE ); this->getAllLeafsInFrustum( frustum, leafs ); return leafs; diff --git a/src/engine/tree/quadtree/QuadTree.cpp b/src/engine/tree/quadtree/QuadTree.cpp index 7e719a5..846272b 100644 --- a/src/engine/tree/quadtree/QuadTree.cpp +++ b/src/engine/tree/quadtree/QuadTree.cpp @@ -3,3 +3,73 @@ // #include "QuadTree.hpp" + +namespace fgl::engine +{ + bool QuadTreeNode::contains( const WorldCoordinate coord ) const + { + const auto centered_coordinate { coord - m_center }; + + const bool top_in_range { glm::all( glm::lessThanEqual( centered_coordinate.vec(), m_node_bounds ) ) }; + const bool bottom_in_range { glm::all( glm::greaterThanEqual( centered_coordinate.vec(), -m_node_bounds ) ) }; + return top_in_range && bottom_in_range; + } + + QuadTreeNode& QuadTreeNode::operator[]( const WorldCoordinate pos ) + { + assert( std::holds_alternative< QuadTreeNodeArray >( m_node_data ) ); + const auto test_dim { glm::greaterThanEqual( pos.vec(), this->m_center.vec() ) }; + + auto& node_array { std::get< QuadTreeNodeArray >( m_node_data ) }; + const auto& node { node_array[ test_dim.x ][ test_dim.y ] }; + assert( node ); + + return *node.get(); + } + + void QuadTreeNode::split( const int depth ) + { + if ( std::holds_alternative< QuadTreeNodeArray >( m_node_data ) ) return; + + auto leaf_data { std::move( std::get< QuadTreeNodeLeaf >( m_node_data ) ) }; + + QuadTreeNodeArray new_nodes { + { { { std::make_unique< QuadTreeNode >(), std::make_unique< QuadTreeNode >() } }, + { { std::make_unique< QuadTreeNode >(), std::make_unique< QuadTreeNode >() } } } + }; + + m_node_data = std::move( new_nodes ); + + [[assume( leaf_data.size() <= MAX_QUAD_NODES_IN_LEAF )]]; + + for ( auto& item : leaf_data ) + { + const auto item_pos { item.getPosition() }; + + auto& node { ( *this )[ item_pos ] }; + assert( std::holds_alternative< QuadTreeNodeLeaf >( node.m_node_data ) ); + + node.addGameObject( std::move( item ) ); + } + + if ( depth > 1 ) split( depth - 1 ); + } + + void QuadTreeNode::addGameObject( GameObject&& obj ) + { + assert( contains( obj.getPosition() ) ); + + if ( std::holds_alternative< QuadTreeNodeLeaf >( m_node_data ) ) + { + std::get< QuadTreeNodeLeaf >( m_node_data ).emplace_back( std::forward< GameObject >( obj ) ); + return; + } + else + { + assert( std::holds_alternative< QuadTreeNodeArray >( m_node_data ) ); + ( *this )[ obj.getPosition() ].addGameObject( std::forward< GameObject >( obj ) ); + return; + } + } + +} // namespace fgl::engine diff --git a/src/engine/tree/quadtree/QuadTree.hpp b/src/engine/tree/quadtree/QuadTree.hpp index a0f8d69..d2368d0 100644 --- a/src/engine/tree/quadtree/QuadTree.hpp +++ b/src/engine/tree/quadtree/QuadTree.hpp @@ -2,10 +2,39 @@ // Created by kj16609 on 3/11/24. // -#ifndef GAME_QUADTREE_HPP -#define GAME_QUADTREE_HPP +#pragma once -class QuadTree -{}; +#include "engine/GameObject.hpp" -#endif //GAME_QUADTREE_HPP +namespace fgl::engine +{ + + enum class CoordinateSpace; + class QuadTreeNode; + + template < CoordinateSpace CType > + struct Frusutm; + + using QuadTreeNodeArray = std::array< std::array< std::unique_ptr< QuadTreeNode >, 2 >, 2 >; + using QuadTreeNodeLeaf = std::vector< GameObject >; + + constexpr std::size_t MAX_QUAD_NODES_IN_LEAF { 8 }; + + class QuadTreeNode + { + WorldCoordinate m_center { constants::WORLD_CENTER }; + Scale m_node_bounds { std::numeric_limits< float >::infinity() }; + std::variant< QuadTreeNodeArray, QuadTreeNodeLeaf > m_node_data { QuadTreeNodeLeaf() }; + + bool contains( const WorldCoordinate coord ) const; + + public: + + QuadTreeNode& operator[]( const WorldCoordinate pos ); + + void split( const int depth = 1 ); + + void addGameObject( GameObject&& obj ); + }; + +} // namespace fgl::engine