Get traiin rendering working with random noise
This commit is contained in:
@@ -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 |
@@ -153,4 +153,4 @@ namespace fgl::engine
|
||||
return last_frustum_pos;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
} // namespace fgl::engine
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
113
src/engine/math/noise/perlin/generator.cpp
Normal file
113
src/engine/math/noise/perlin/generator.cpp
Normal 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
|
||||
15
src/engine/math/noise/perlin/generator.hpp
Normal file
15
src/engine/math/noise/perlin/generator.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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 >;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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" );
|
||||
|
||||
@@ -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 ) )
|
||||
{}
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user