Get traiin rendering working with random noise

This commit is contained in:
2024-03-30 10:00:09 -04:00
parent 9c50bea17b
commit c8ec3a741c
20 changed files with 351 additions and 129 deletions

View File

@@ -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()
CompilerPostSetup()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 KiB

View File

@@ -153,4 +153,4 @@ namespace fgl::engine
return last_frustum_pos;
}
} // namespace fgl::engine
} // namespace fgl::engine

View File

@@ -9,6 +9,7 @@
#include <array>
#include <chrono>
#include <iostream>
#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
} // namespace fgl::engine

View File

@@ -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();
}

View File

@@ -0,0 +1,113 @@
//
// Created by kj16609 on 3/27/24.
//
#include "generator.hpp"
#include <random>
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

View File

@@ -0,0 +1,15 @@
//
// Created by kj16609 on 3/27/24.
//
#pragma once
#include <bits/stdint-uintn.h>
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

View File

@@ -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

View File

@@ -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 >;

View File

@@ -5,7 +5,6 @@
#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/epsilon.hpp>
#include <ostream>
@@ -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;

View File

@@ -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 );
}

View File

@@ -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" );

View File

@@ -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 ) )
{}

View File

@@ -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();
};

View File

@@ -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;

View File

@@ -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();
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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