GPU culling work
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -58,3 +58,6 @@
|
||||
[submodule "dependencies/slang"]
|
||||
path = dependencies/slang
|
||||
url = https://github.com/shader-slang/slang.git
|
||||
[submodule "dependencies/json"]
|
||||
path = dependencies/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
|
||||
@@ -31,7 +31,8 @@ include(dependencies/glm)
|
||||
include(cmake_modules/dependencies/tracy.cmake)
|
||||
include(dependencies/vulkan)
|
||||
include(dependencies/catch2)
|
||||
includE(dependencies/slang)
|
||||
include(dependencies/slang)
|
||||
include(dependencies/json)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
||||
1
cmake_modules/dependencies/json.cmake
Normal file
1
cmake_modules/dependencies/json.cmake
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dependencies/json)
|
||||
1
dependencies/json
vendored
Submodule
1
dependencies/json
vendored
Submodule
Submodule dependencies/json added at f3dc4684b4
BIN
src/assets/TestScene.bin
LFS
Normal file
BIN
src/assets/TestScene.bin
LFS
Normal file
Binary file not shown.
BIN
src/assets/TestScene.gltf
LFS
Normal file
BIN
src/assets/TestScene.gltf
LFS
Normal file
Binary file not shown.
BIN
src/assets/blender/2-91/blender-2-91.bin
LFS
Normal file
BIN
src/assets/blender/2-91/blender-2-91.bin
LFS
Normal file
Binary file not shown.
BIN
src/assets/blender/2-91/blender-2-91.gltf
LFS
Normal file
BIN
src/assets/blender/2-91/blender-2-91.gltf
LFS
Normal file
Binary file not shown.
BIN
src/assets/blender/2-91/textures/Flower_Color Image.png
LFS
Normal file
BIN
src/assets/blender/2-91/textures/Flower_Color Image.png
LFS
Normal file
Binary file not shown.
@@ -5,15 +5,14 @@
|
||||
#include "engine/gameobjects/components/ModelComponent.hpp"
|
||||
|
||||
#include "engine/assets/model/Model.hpp"
|
||||
#include "engine/gameobjects/components/drawers.hpp"
|
||||
#include "gui/safe_include.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
namespace fgl::engine::components
|
||||
{
|
||||
|
||||
void ModelComponent::drawImGui()
|
||||
{
|
||||
drawComponentTransform( m_transform );
|
||||
// drawComponentTransform( m_transform );
|
||||
|
||||
// TODO: If the model is not set then we should be able to set it to one from the file selection
|
||||
if ( this->m_model == nullptr )
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "engine/debug/DEBUG_NAMES.hpp"
|
||||
#include "engine/descriptors/DescriptorPool.hpp"
|
||||
#include "engine/rendering/Renderer.hpp"
|
||||
#include "engine/tree/octtree/OctTreeNode.hpp"
|
||||
#include "gui_window_names.hpp"
|
||||
#include "safe_include.hpp"
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace fgl::engine::gui
|
||||
ImGui::InputText( "Name", &name_input_temp );
|
||||
if ( game_object.getName() != name_input_temp ) game_object.setName( name_input_temp );
|
||||
|
||||
/*
|
||||
auto& transform { game_object.getTransform() };
|
||||
|
||||
// Transform - Position
|
||||
@@ -29,6 +30,7 @@ namespace fgl::engine::gui
|
||||
}
|
||||
|
||||
dragFloat3( "Scale", transform.scale );
|
||||
*/
|
||||
}
|
||||
|
||||
static GameObjectComponentPtr SELECTED_COMPONENT { nullptr };
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "engine/flags.hpp"
|
||||
#include "engine/math/literals/size.hpp"
|
||||
#include "engine/memory/buffers/Buffer.hpp"
|
||||
#include "engine/tree/octtree/OctTreeNode.hpp"
|
||||
#include "safe_include.hpp"
|
||||
|
||||
namespace fgl::engine::gui
|
||||
@@ -128,8 +127,6 @@ namespace fgl::engine::gui
|
||||
debug::timing::render();
|
||||
}
|
||||
|
||||
imGuiOctTreeSettings( info );
|
||||
|
||||
if ( ImGui::Button( "Reload shaders" ) )
|
||||
{
|
||||
flags::triggerShaderReload();
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
#include "engine/camera/Camera.hpp"
|
||||
#include "engine/filesystem/scanner/FileScanner.hpp"
|
||||
#include "engine/filesystem/types.hpp"
|
||||
#include "engine/gameobjects/components/ModelComponent.hpp"
|
||||
#include "engine/rendering/PresentSwapChain.hpp"
|
||||
#include "engine/tree/octtree/OctTreeNode.hpp"
|
||||
#include "safe_include.hpp"
|
||||
|
||||
namespace fgl::engine::gui
|
||||
@@ -47,12 +45,16 @@ namespace fgl::engine::gui
|
||||
GameObject obj { GameObject::createGameObject() };
|
||||
|
||||
std::shared_ptr< Model > model {
|
||||
Model::createModel( data->m_path, info.model_vertex_buffer, info.model_index_buffer )
|
||||
Model::
|
||||
createModel( data->m_path, info.model_vertex_buffer, info.model_index_buffer )
|
||||
};
|
||||
|
||||
obj.addFlag( IsEntity | IsVisible );
|
||||
|
||||
auto component { std::make_unique< ModelComponent >( std::move( model ) ) };
|
||||
info.context.models()
|
||||
.loadModel( data->m_path, info.model_vertex_buffer, info.model_index_buffer );
|
||||
|
||||
auto component { std::make_unique< components::ModelComponent >( std::move( model ) ) };
|
||||
|
||||
obj.addComponent( std::move( component ) );
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "engine/debug/timing/FlameGraph.hpp"
|
||||
#include "engine/gameobjects/components/CameraComponent.hpp"
|
||||
#include "gui/EditorGuiContext.hpp"
|
||||
#include "gui/core.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
@@ -63,12 +62,22 @@ int main()
|
||||
|
||||
editor_camera->setFOV( glm::radians( 90.0f ) );
|
||||
|
||||
// Create a default world to assign to the engine before we load or create a new one.
|
||||
World world {};
|
||||
|
||||
constexpr bool playing { false };
|
||||
|
||||
//! Will be true until the window says it wants to close.
|
||||
while ( engine_ctx.good() )
|
||||
{
|
||||
debug::timing::reset();
|
||||
|
||||
engine_ctx.tickDeltaTime();
|
||||
|
||||
engine_ctx.setWorld( world );
|
||||
|
||||
if ( playing ) world = engine_ctx.tickSimulation();
|
||||
|
||||
engine_ctx.handleTransfers();
|
||||
|
||||
// Process input
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "KeyboardMovementController.hpp"
|
||||
#include "assets/material/Material.hpp"
|
||||
#include "camera/Camera.hpp"
|
||||
#include "camera/CameraManager.hpp"
|
||||
#include "camera/GBufferRenderer.hpp"
|
||||
@@ -48,52 +47,6 @@ namespace fgl::engine
|
||||
|
||||
m_matrix_info_pool.setDebugName( "Matrix info pool" );
|
||||
m_draw_parameter_pool.setDebugName( "Draw parameter pool" );
|
||||
|
||||
m_vertex_buffer->setDebugName( "Vertex buffer" );
|
||||
m_index_buffer->setDebugName( "Index buffer" );
|
||||
|
||||
constexpr float offset { 4.0f };
|
||||
constexpr std::size_t grid_size { 6 };
|
||||
constexpr float factor_offset { 1.0f / static_cast< float >( grid_size ) };
|
||||
|
||||
for ( std::size_t x = 0; x < grid_size; ++x )
|
||||
for ( std::size_t y = 0; y < grid_size; ++y )
|
||||
{
|
||||
const std::filesystem::path path {
|
||||
"/home/kj16609/Desktop/Projects/cxx/Mecha/src/assets/PBRSphere.gltf"
|
||||
};
|
||||
|
||||
SceneBuilder builder { *m_vertex_buffer, *m_index_buffer };
|
||||
|
||||
builder.loadScene( path );
|
||||
|
||||
std::vector< GameObject > objs { builder.getGameObjects() };
|
||||
|
||||
for ( auto& obj : objs )
|
||||
{
|
||||
auto model_components { obj.getComponents< ModelComponent >() };
|
||||
|
||||
for ( const auto& model_component : model_components )
|
||||
{
|
||||
auto& prims = ( *model_component )->m_primitives;
|
||||
|
||||
for ( auto& prim : prims )
|
||||
{
|
||||
auto& pbr { prim.m_material->properties.pbr };
|
||||
pbr.roughness_factor = static_cast< float >( x ) * factor_offset;
|
||||
pbr.metallic_factor = static_cast< float >( y ) * factor_offset;
|
||||
prim.m_material->update();
|
||||
}
|
||||
}
|
||||
|
||||
obj.getTransform().translation = WorldCoordinate(
|
||||
-5.0f + ( static_cast< float >( x ) * offset ),
|
||||
-5.0f + ( static_cast< float >( y ) * offset ),
|
||||
0.0f );
|
||||
|
||||
m_game_objects_root.addGameObject( std::move( obj ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Average< float, 60 * 15 > rolling_ms_average;
|
||||
@@ -116,7 +69,7 @@ namespace fgl::engine
|
||||
m_delta_time = time_diff.count();
|
||||
}
|
||||
|
||||
void EngineContext::tickSimulation()
|
||||
World EngineContext::tickSimulation()
|
||||
{
|
||||
ZoneScoped;
|
||||
auto timer = debug::timing::push( "Tick Simulation" );
|
||||
@@ -124,6 +77,8 @@ namespace fgl::engine
|
||||
// The first step here should be culling things that aren't needed to be ticked.
|
||||
// Perhaps implementing a tick system that doesn't care about the refresh rate might be good?
|
||||
// That way we can still tick consistantly without actually needing to render anything.
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void EngineContext::renderCameras( FrameInfo frame_info )
|
||||
@@ -160,12 +115,10 @@ namespace fgl::engine
|
||||
nullptr, // Camera
|
||||
m_camera_manager.getCameras(),
|
||||
// global_descriptor_sets[ frame_index ],
|
||||
m_game_objects_root,
|
||||
m_model_manager,
|
||||
m_renderer.getCurrentTracyCTX(),
|
||||
m_matrix_info_pool,
|
||||
m_draw_parameter_pool,
|
||||
*this->m_vertex_buffer,
|
||||
*this->m_index_buffer,
|
||||
// m_renderer.getSwapChain().getInputDescriptor( present_idx ),
|
||||
this->m_renderer.getSwapChain() };
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "engine/assets/transfer/TransferManager.hpp"
|
||||
#include "engine/math/literals/size.hpp"
|
||||
#include "engine/rendering/Renderer.hpp"
|
||||
#include "engine/tree/octtree/OctTreeNode.hpp"
|
||||
#include "scene/World.hpp"
|
||||
#include "systems/composition/GuiSystem.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
@@ -43,17 +43,10 @@ namespace fgl::engine
|
||||
|
||||
Renderer m_renderer { m_window, m_device.phyDevice() };
|
||||
|
||||
std::unique_ptr< memory::Buffer > m_vertex_buffer { std::make_unique< memory::Buffer >(
|
||||
1_GiB,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal ) };
|
||||
std::unique_ptr< memory::Buffer > m_index_buffer { std::make_unique< memory::Buffer >(
|
||||
512_MiB,
|
||||
vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
||||
vk::MemoryPropertyFlagBits::eDeviceLocal ) };
|
||||
ModelManager m_model_manager {};
|
||||
|
||||
//GameObject::Map game_objects {};
|
||||
OctTreeNode m_game_objects_root { WorldCoordinate( constants::WORLD_CENTER ) };
|
||||
// GameObject::Map game_objects {};
|
||||
// OctTreeNode m_game_objects_root { WorldCoordinate( constants::WORLD_CENTER ) };
|
||||
|
||||
// SubPass 0
|
||||
GuiSystem m_gui_system {};
|
||||
@@ -88,8 +81,12 @@ namespace fgl::engine
|
||||
std::chrono::time_point< Clock > m_last_tick { Clock::now() };
|
||||
DeltaTime m_delta_time;
|
||||
|
||||
World m_world;
|
||||
|
||||
public:
|
||||
|
||||
ModelManager& models() { return m_model_manager; }
|
||||
|
||||
FGL_FORCE_INLINE_FLATTEN void hookInitImGui( const std::function< void( Window&, Renderer& ) >& func )
|
||||
{
|
||||
func( m_window, m_renderer );
|
||||
@@ -107,6 +104,8 @@ namespace fgl::engine
|
||||
|
||||
void hookDestruction( const std::function< void() >& func ) { m_destruction_hooks.emplace_back( func ); }
|
||||
|
||||
void setWorld( const World& world );
|
||||
|
||||
public:
|
||||
|
||||
EngineContext();
|
||||
@@ -126,7 +125,7 @@ namespace fgl::engine
|
||||
void processInput();
|
||||
|
||||
void tickDeltaTime();
|
||||
void tickSimulation();
|
||||
World tickSimulation();
|
||||
void renderCameras( FrameInfo frame_info );
|
||||
|
||||
void renderFrame();
|
||||
|
||||
@@ -83,16 +83,13 @@ namespace fgl::engine
|
||||
std::vector< std::weak_ptr< Camera > >& m_camera_list;
|
||||
|
||||
// descriptors::DescriptorSet& global_descriptor_set;
|
||||
OctTreeNode& game_objects;
|
||||
// OctTreeNode& game_objects;
|
||||
TracyVkCtx tracy_ctx;
|
||||
|
||||
//Buffers
|
||||
memory::Buffer& model_matrix_info_buffer;
|
||||
memory::Buffer& draw_parameter_buffer;
|
||||
|
||||
memory::Buffer& model_vertex_buffer;
|
||||
memory::Buffer& model_index_buffer;
|
||||
|
||||
// descriptors::DescriptorSet& gui_input_descriptor;
|
||||
|
||||
descriptors::DescriptorSet& getGBufferDescriptor() const;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "engine/primitives/vectors/Vector.hpp"
|
||||
#include "gameobjects/GameObject.hpp"
|
||||
#include "gameobjects/components/TransformComponent.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
@@ -31,6 +32,18 @@ namespace fgl::engine
|
||||
void KeyboardMovementController::moveInPlaneXZ( GLFWwindow* window, float dt, GameObject& target )
|
||||
{
|
||||
assert( window );
|
||||
|
||||
auto components { target.getComponents< components::TransformComponent >() };
|
||||
|
||||
if ( components.empty() )
|
||||
{
|
||||
log::warn( "KeyboardMovementController: Attempted to move object with no transform" );
|
||||
return;
|
||||
}
|
||||
|
||||
components::TransformComponent& component { *components[ 0 ] };
|
||||
WorldTransform& transform { *component };
|
||||
|
||||
constexpr float rotation_rate { 1.0f };
|
||||
constexpr float pitch_modifier { 1.0f };
|
||||
constexpr float yaw_modifier { 1.0f };
|
||||
@@ -86,11 +99,11 @@ namespace fgl::engine
|
||||
|
||||
if ( pitch_change > std::numeric_limits< float >::epsilon()
|
||||
|| pitch_change < -std::numeric_limits< float >::epsilon() )
|
||||
target.getTransform().rotation.addX( dt * pitch_change );
|
||||
transform.rotation.addX( dt * pitch_change );
|
||||
|
||||
if ( yaw_change > std::numeric_limits< float >::epsilon()
|
||||
|| yaw_change < -std::numeric_limits< float >::epsilon() )
|
||||
target.getTransform().rotation.addY( dt * yaw_change );
|
||||
transform.rotation.addY( dt * yaw_change );
|
||||
}
|
||||
else // No cursor
|
||||
{
|
||||
@@ -98,7 +111,7 @@ namespace fgl::engine
|
||||
const float xpos { pos.x };
|
||||
const float ypos { pos.y };
|
||||
|
||||
UniversalRotation& target_rotation { target.getTransform().rotation };
|
||||
UniversalRotation& target_rotation { transform.rotation };
|
||||
|
||||
target_rotation.addZ( ( xpos * 0.006f ) * look_speed );
|
||||
target_rotation.addX( ( ypos * 0.006f ) * look_speed );
|
||||
@@ -106,9 +119,9 @@ namespace fgl::engine
|
||||
setCursorPos( window, { 0, 0 } );
|
||||
}
|
||||
|
||||
const Vector forward_dir { target.getTransform().rotation.forward() };
|
||||
const Vector up_dir { target.getTransform().rotation.up() };
|
||||
const Vector right_dir { target.getTransform().rotation.right() };
|
||||
const Vector forward_dir { transform.rotation.forward() };
|
||||
const Vector up_dir { transform.rotation.up() };
|
||||
const Vector right_dir { transform.rotation.right() };
|
||||
|
||||
Vector move_dir { 0.0f };
|
||||
if ( glfwGetKey( window, key_mappings.move_forward ) == GLFW_PRESS ) move_dir += forward_dir;
|
||||
@@ -121,7 +134,7 @@ namespace fgl::engine
|
||||
const NormalVector n_move_dir { move_dir };
|
||||
|
||||
if ( glm::dot( move_dir.vec(), move_dir.vec() ) > std::numeric_limits< float >::epsilon() )
|
||||
target.getTransform().translation += n_move_dir * ( move_speed * dt );
|
||||
transform.translation += n_move_dir * ( move_speed * dt );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -53,8 +53,8 @@ namespace fgl::engine
|
||||
//! Key type given by T
|
||||
using KeyT = typename T::UIDKeyT;
|
||||
|
||||
std::unordered_map< KeyT, std::weak_ptr< T > > active_map {};
|
||||
std::mutex map_mtx {};
|
||||
std::unordered_map< KeyT, std::weak_ptr< T > > m_active_map {};
|
||||
std::mutex m_map_mtx {};
|
||||
|
||||
public:
|
||||
|
||||
@@ -71,9 +71,9 @@ namespace fgl::engine
|
||||
ZoneScoped;
|
||||
const auto key { T::extractKey( std::forward< T_Args >( args )... ) };
|
||||
|
||||
std::lock_guard guard { map_mtx };
|
||||
std::lock_guard guard { m_map_mtx };
|
||||
|
||||
if ( auto itter = active_map.find( key ); itter != active_map.end() )
|
||||
if ( auto itter = m_active_map.find( key ); itter != m_active_map.end() )
|
||||
{
|
||||
// We've found the item in the map. We can now check if it's still active
|
||||
|
||||
@@ -83,13 +83,13 @@ namespace fgl::engine
|
||||
}
|
||||
|
||||
//Item was expired. Remove it from the map and continue
|
||||
active_map.erase( itter );
|
||||
m_active_map.erase( itter );
|
||||
}
|
||||
|
||||
std::shared_ptr< T > s_ptr { new T( std::forward< T_Args >( args )... ) };
|
||||
|
||||
// Add the weak pointer to the map so we can find it later.
|
||||
active_map.insert( std::make_pair( key, s_ptr ) );
|
||||
m_active_map.insert( std::make_pair( key, s_ptr ) );
|
||||
|
||||
return s_ptr;
|
||||
}
|
||||
|
||||
25
src/engine/assets/model/InstanceManager.cpp
Normal file
25
src/engine/assets/model/InstanceManager.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by kj16609 on 3/17/25.
|
||||
//
|
||||
|
||||
#include "InstanceManager.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
using namespace fgl::literals::size_literals;
|
||||
|
||||
inline InstanceArray createInstances( memory::Buffer& buffer )
|
||||
{
|
||||
InstanceArray instances {};
|
||||
|
||||
std::ranges::generate( instances, [ & ]() -> InstanceVector { return InstanceVector( buffer, 0 ); } );
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
inline InstanceManager::InstanceManager() :
|
||||
m_buffer( 128_MiB, vk::BufferUsageFlagBits::eStorageBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal ),
|
||||
m_instances( createInstances( m_buffer ) )
|
||||
{}
|
||||
|
||||
} // namespace fgl::engine
|
||||
31
src/engine/assets/model/InstanceManager.hpp
Normal file
31
src/engine/assets/model/InstanceManager.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Created by kj16609 on 3/17/25.
|
||||
//
|
||||
#pragma once
|
||||
#include "memory/buffers/Buffer.hpp"
|
||||
#include "memory/buffers/vector/DeviceVector.hpp"
|
||||
#include "rendering/PresentSwapChain.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct ModelGPUInstance;
|
||||
|
||||
using InstanceVector = DeviceVector< ModelGPUInstance >;
|
||||
using InstanceArray = PerFrameArray< InstanceVector >;
|
||||
|
||||
class InstanceManager
|
||||
{
|
||||
//! Buffer for each instance to use
|
||||
memory::Buffer m_buffer;
|
||||
|
||||
InstanceArray m_instances;
|
||||
|
||||
public:
|
||||
|
||||
//! Constructor to initialize m_buffer and m_instances
|
||||
[[nodiscard]] InstanceManager();
|
||||
|
||||
~InstanceManager() = default;
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -54,6 +54,11 @@ namespace fgl::engine
|
||||
return box;
|
||||
}
|
||||
|
||||
std::shared_ptr< ModelRenderInfo > Model::getRenderHandle() const
|
||||
{
|
||||
return m_render_handle.lock();
|
||||
}
|
||||
|
||||
bool Model::ready() const
|
||||
{
|
||||
//Return true if even a single primitive is ready
|
||||
|
||||
@@ -15,11 +15,16 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Primitive.hpp"
|
||||
#include "assets/AssetManager.hpp"
|
||||
#include "engine/assets/material/Material.hpp"
|
||||
#include "engine/primitives/boxes/OrientedBoundingBox.hpp"
|
||||
#include "memory/buffers/vector/IndexedVector.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct ModelRenderInfo;
|
||||
class ModelRenderHandle;
|
||||
|
||||
namespace memory
|
||||
{
|
||||
class Buffer;
|
||||
@@ -33,30 +38,31 @@ namespace fgl::engine
|
||||
MaterialID material_id { constants::INVALID_TEXTURE_ID };
|
||||
};
|
||||
|
||||
class Model
|
||||
class Model final : public AssetInterface< Model >
|
||||
{
|
||||
static std::vector< vk::DrawIndexedIndirectCommand > buildParameters( const std::vector< Primitive >&
|
||||
primitives );
|
||||
static OrientedBoundingBox< CoordinateSpace::Model > buildBoundingBox( const std::vector< Primitive >&
|
||||
primitives );
|
||||
|
||||
std::vector< vk::DrawIndexedIndirectCommand > m_draw_parameters;
|
||||
static OrientedBoundingBox< CoordinateSpace::Model >
|
||||
buildBoundingBox( const std::vector< Primitive >& primitives );
|
||||
|
||||
std::string m_name { "Unnamed model" };
|
||||
|
||||
//! Bounding box of the model
|
||||
OrientedBoundingBox< CoordinateSpace::Model > m_bounding_box;
|
||||
|
||||
IndexedVector< ModelMatrixInfo >::Index m_model_info {};
|
||||
|
||||
public:
|
||||
|
||||
bool ready() const;
|
||||
[[nodiscard]] bool ready() const;
|
||||
|
||||
//! Returns the bounding box in model space
|
||||
const OrientedBoundingBox< CoordinateSpace::Model >& getBoundingBox() const { return m_bounding_box; }
|
||||
[[nodiscard]] const OrientedBoundingBox< CoordinateSpace::Model >& getBoundingBox() const
|
||||
{
|
||||
return m_bounding_box;
|
||||
}
|
||||
|
||||
std::vector< Primitive > m_primitives {};
|
||||
|
||||
std::vector< vk::DrawIndexedIndirectCommand > getDrawCommand( std::uint32_t index ) const;
|
||||
[[nodiscard]] std::vector< vk::DrawIndexedIndirectCommand > getDrawCommand( std::uint32_t index ) const;
|
||||
|
||||
//TODO: Switch to using shared_ptr instead of unique_ptr
|
||||
static std::shared_ptr< Model > createModel(
|
||||
|
||||
8
src/engine/assets/model/ModelInstance.cpp
Normal file
8
src/engine/assets/model/ModelInstance.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by kj16609 on 3/17/25.
|
||||
//
|
||||
#include "ModelInstance.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
} // namespace fgl::engine
|
||||
64
src/engine/assets/model/ModelInstance.hpp
Normal file
64
src/engine/assets/model/ModelInstance.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Created by kj16609 on 3/17/25.
|
||||
//
|
||||
#pragma once
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "memory/buffers/vector/DeviceVector.hpp"
|
||||
#include "primitives/Transform.hpp"
|
||||
#include "rendering/PresentSwapChain.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class Model;
|
||||
|
||||
struct ModelGPUInstance
|
||||
{
|
||||
//! Index of the model
|
||||
std::uint32_t m_model_index;
|
||||
glm::mat4x4 m_transform;
|
||||
};
|
||||
|
||||
using InstanceIndex = std::uint32_t;
|
||||
|
||||
using namespace fgl::literals::size_literals;
|
||||
|
||||
class ModelInstance
|
||||
{
|
||||
std::shared_ptr< Model > m_model;
|
||||
InstanceIndex m_index;
|
||||
|
||||
//! CPU side data to be modified
|
||||
ModelGPUInstance m_cpu_data;
|
||||
|
||||
//! True if the last frame changed this instance in any way
|
||||
bool m_updated { false };
|
||||
|
||||
public:
|
||||
|
||||
//! Returns GPU instance data
|
||||
ModelGPUInstance gpuInstanceData() const
|
||||
{
|
||||
ModelGPUInstance data {};
|
||||
|
||||
data.m_model_index = m_model->getGPUID();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//! Returns the current update state and sets it to false if it was true.
|
||||
bool acquireNeedsUpdate()
|
||||
{
|
||||
if ( m_updated ) [[unlikely]]
|
||||
{
|
||||
m_updated = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
10
src/engine/assets/model/ModelManager.cpp
Normal file
10
src/engine/assets/model/ModelManager.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
//
|
||||
// Created by kj16609 on 3/12/25.
|
||||
//
|
||||
#include "ModelManager.hpp"
|
||||
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
}
|
||||
24
src/engine/assets/model/ModelManager.hpp
Normal file
24
src/engine/assets/model/ModelManager.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Created by kj16609 on 3/12/25.
|
||||
//
|
||||
#pragma once
|
||||
#include "memory/buffers/vector/DeviceVector.hpp"
|
||||
#include "rendering/PresentSwapChain.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class ModelManager
|
||||
{
|
||||
//! The managed draw command buffer.
|
||||
/**
|
||||
* Contains all draw commands for the rendering process. Updated by the CPU whenever a new model is created
|
||||
*/
|
||||
DeviceVector< vk::DrawIndexedIndirectCommand > m_draw_commands;
|
||||
|
||||
//! Draw buffers the GPU compute shader writes into
|
||||
PerFrameArray< DeviceVector< vk::DrawIndexedIndirectCommand > > m_gpu_draw_commands {};
|
||||
|
||||
//! Contains extra information such as model instance indexes by the compute shader
|
||||
PerFrameArray<> m_gpu_draw_info {};
|
||||
};
|
||||
} // namespace fgl::engine
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
#include "engine/primitives/Transform.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
@@ -595,7 +595,10 @@ namespace fgl::engine
|
||||
|
||||
assert( model );
|
||||
|
||||
std::unique_ptr< ModelComponent > component { std::make_unique< ModelComponent >( std::move( model ) ) };
|
||||
std::unique_ptr< components::ModelComponent > component {
|
||||
std::make_unique< components::ModelComponent >( std::move( model ) )
|
||||
};
|
||||
|
||||
obj.addComponent( std::move( component ) );
|
||||
|
||||
obj.addFlag( IsVisible | IsEntity );
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "engine/assets/material/Material.hpp"
|
||||
#include "engine/gameobjects/GameObject.hpp"
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
#include "engine/primitives/Transform.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
@@ -197,12 +197,12 @@ namespace fgl::engine
|
||||
return m_imgui_set;
|
||||
}
|
||||
|
||||
Texture::Texture( Image& image, Sampler sampler ) :
|
||||
Texture::Texture( const std::shared_ptr< Image >& image, Sampler sampler ) :
|
||||
m_texture_id( texture_id_pool.getID() ),
|
||||
m_image(),
|
||||
m_image_view( image.getView() ),
|
||||
m_image( image ),
|
||||
m_image_view( image->getView() ),
|
||||
//TODO: Figure out how to get extents from images.
|
||||
m_extent(),
|
||||
m_extent( image->getExtent() ),
|
||||
m_name( "Default Texture Name" )
|
||||
{
|
||||
m_image_view->getSampler() = std::move( sampler );
|
||||
|
||||
@@ -91,6 +91,7 @@ namespace fgl::engine
|
||||
Texture() = delete;
|
||||
|
||||
~Texture();
|
||||
Texture( const std::shared_ptr<Image>& image, Sampler sampler = Sampler() );
|
||||
|
||||
Image& getImageRef();
|
||||
|
||||
@@ -100,8 +101,6 @@ namespace fgl::engine
|
||||
Texture( Texture&& other ) = delete;
|
||||
Texture& operator=( Texture&& ) = delete;
|
||||
|
||||
Texture( Image& image, Sampler sampler = Sampler() );
|
||||
|
||||
bool ready() const;
|
||||
|
||||
[[nodiscard]] TextureID getID() const;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "engine/memory/buffers/HostSingleT.hpp"
|
||||
#include "engine/memory/buffers/UniqueFrameSuballocation.hpp"
|
||||
#include "engine/primitives/Frustum.hpp"
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
#include "engine/primitives/Transform.hpp"
|
||||
#include "engine/rendering/types.hpp"
|
||||
|
||||
namespace vk::raii
|
||||
@@ -54,11 +54,13 @@ namespace fgl::engine
|
||||
|
||||
vk::Extent2D m_target_extent;
|
||||
|
||||
std::unique_ptr< CompositeSwapchain > m_old_composite_swapchain { nullptr };
|
||||
std::unique_ptr< CompositeSwapchain > m_composite_swapchain;
|
||||
std::unique_ptr< GBufferSwapchain > m_old_gbuffer_swapchain { nullptr };
|
||||
std::unique_ptr< GBufferSwapchain > m_gbuffer_swapchain;
|
||||
|
||||
//TODO: Move to deffered deleter
|
||||
std::unique_ptr< CompositeSwapchain > m_old_composite_swapchain { nullptr };
|
||||
std::unique_ptr< GBufferSwapchain > m_old_gbuffer_swapchain { nullptr };
|
||||
|
||||
std::shared_ptr< GBufferRenderer > m_camera_renderer;
|
||||
|
||||
//! True if the camera is active and to be rendered
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace fgl::engine
|
||||
|
||||
for ( const auto& image : m_buffer.m_target.m_attachment_resources.m_images )
|
||||
{
|
||||
m_gbuffer_target.emplace_back( std::make_unique< Texture >( *image ) );
|
||||
m_gbuffer_target.emplace_back( std::make_unique< Texture >( image ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace fgl::engine
|
||||
if ( m_id != INVALID_ID )
|
||||
{
|
||||
log::debug( "Destroyed game object {}", this->m_id );
|
||||
for ( const auto& component : components ) delete component;
|
||||
for ( const auto& component : m_components ) delete component;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ namespace fgl::engine
|
||||
{
|
||||
m_id = other.m_id;
|
||||
object_flags = other.object_flags;
|
||||
m_transform = other.m_transform;
|
||||
components = std::move( other.components );
|
||||
m_components = std::move( other.m_components );
|
||||
m_name = std::move( other.m_name );
|
||||
|
||||
other.m_id = INVALID_ID;
|
||||
@@ -32,8 +31,7 @@ namespace fgl::engine
|
||||
GameObject::GameObject( GameObject&& other ) noexcept :
|
||||
m_id( other.m_id ),
|
||||
object_flags( other.object_flags ),
|
||||
m_transform( other.m_transform ),
|
||||
components( std::move( other.components ) ),
|
||||
m_components( std::move( other.m_components ) ),
|
||||
m_name( other.m_name )
|
||||
{
|
||||
other.m_id = INVALID_ID;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#include "components/ModelComponent.hpp"
|
||||
#include "engine/gameobjects/components/interface/GameObjectComponent.hpp"
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
#include "engine/primitives/Transform.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
@@ -45,9 +45,11 @@ namespace fgl::engine
|
||||
|
||||
GameObjectID m_id { INVALID_ID };
|
||||
GameObjectFlagType object_flags { GameObjectFlagMask::MaskDefault };
|
||||
GameObjectTransform m_transform {};
|
||||
|
||||
std::vector< GameObjectComponentPtr > components {};
|
||||
std::vector< GameObjectComponentPtr > m_components {};
|
||||
|
||||
GameObject* m_parent { nullptr };
|
||||
std::vector< GameObject > m_children {};
|
||||
|
||||
std::string m_name {};
|
||||
|
||||
@@ -67,17 +69,15 @@ namespace fgl::engine
|
||||
requires is_component< T >
|
||||
void addComponent( std::unique_ptr< T >&& ptr )
|
||||
{
|
||||
components.emplace_back( ptr.release() );
|
||||
m_components.emplace_back( ptr.release() );
|
||||
}
|
||||
|
||||
Scale& getScale() { return m_transform.scale; }
|
||||
|
||||
template < typename T >
|
||||
requires is_component< T >
|
||||
bool hasComponent() const
|
||||
{
|
||||
ZoneScoped;
|
||||
for ( const GameObjectComponentPtr comp : components )
|
||||
for ( const GameObjectComponentPtr comp : m_components )
|
||||
{
|
||||
if ( comp->id() == T::ID ) return true;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace fgl::engine
|
||||
ZoneScopedN( "Get components" );
|
||||
std::vector< const T* > temp {};
|
||||
|
||||
for ( const ComponentEngineInterface* comp : components )
|
||||
for ( const ComponentEngineInterface* comp : m_components )
|
||||
{
|
||||
if ( comp->id() == T::ID ) temp.emplace_back( static_cast< const T* >( comp ) );
|
||||
}
|
||||
@@ -107,7 +107,7 @@ namespace fgl::engine
|
||||
ZoneScopedN( "Get components" );
|
||||
std::vector< T* > temp {};
|
||||
|
||||
for ( ComponentEngineInterface* comp : components )
|
||||
for ( ComponentEngineInterface* comp : m_components )
|
||||
{
|
||||
if ( comp->id() == T::ID ) temp.emplace_back( static_cast< T* >( comp ) );
|
||||
}
|
||||
@@ -115,9 +115,9 @@ namespace fgl::engine
|
||||
return temp;
|
||||
}
|
||||
|
||||
std::vector< GameObjectComponentPtr >& getComponents() { return components; }
|
||||
std::vector< GameObjectComponentPtr >& getComponents() { return m_components; }
|
||||
|
||||
const std::vector< GameObjectComponentPtr >& getComponents() const { return components; }
|
||||
const std::vector< GameObjectComponentPtr >& getComponents() const { return m_components; }
|
||||
|
||||
//Flags
|
||||
GameObjectFlagType flags() const { return object_flags; }
|
||||
@@ -126,17 +126,6 @@ namespace fgl::engine
|
||||
|
||||
void removeFlag( GameObjectFlagType flag ) { object_flags &= ( ~flag ); }
|
||||
|
||||
//Transform
|
||||
GameObjectTransform& getTransform() { return m_transform; }
|
||||
|
||||
const GameObjectTransform& getTransform() const { return m_transform; }
|
||||
|
||||
const WorldCoordinate& getPosition() const { return m_transform.translation; }
|
||||
|
||||
// const Rotation& getRotation() const { return m_transform.rotation; }
|
||||
|
||||
QuatRotation getRotation() const { return m_transform.rotation.forcedQuat(); }
|
||||
|
||||
//Misc
|
||||
static GameObject createGameObject();
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace fgl::engine
|
||||
{
|
||||
using ComponentID = std::uint32_t;
|
||||
|
||||
ASSIGN_COMPONENT_ID( TransformComponentID, 0 );
|
||||
|
||||
ASSIGN_COMPONENT_ID( ModelComponentID, 1 );
|
||||
|
||||
ASSIGN_COMPONENT_ID( CameraComponentID, 2 );
|
||||
|
||||
17
src/engine/gameobjects/components/ModelComponent.cpp
Normal file
17
src/engine/gameobjects/components/ModelComponent.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by kj16609 on 3/1/25.
|
||||
//
|
||||
|
||||
#include "ModelComponent.hpp"
|
||||
|
||||
#include "engine/assets/model/Model.hpp"
|
||||
|
||||
namespace fgl::engine::components
|
||||
{
|
||||
|
||||
ModelComponent::ModelComponent( std::shared_ptr< Model >&& model ) :
|
||||
m_model( std::forward< decltype( m_model ) >( model ) ),
|
||||
m_render_handle( m_model->getRenderHandle() )
|
||||
{}
|
||||
|
||||
} // namespace fgl::engine::components
|
||||
@@ -6,20 +6,28 @@
|
||||
#include <memory>
|
||||
|
||||
#include "ComponentIDS.hpp"
|
||||
#include "assets/model/ModelRenderHandle.hpp"
|
||||
#include "engine/gameobjects/components/interface/GameObjectComponent.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class ModelRenderHandle;
|
||||
}
|
||||
|
||||
namespace fgl::engine::components
|
||||
{
|
||||
struct GameModel;
|
||||
class Model;
|
||||
|
||||
COMPONENT_CLASS( ModelComponent, ModelComponentID )
|
||||
{
|
||||
std::shared_ptr< Model > m_model;
|
||||
ModelRenderHandle m_render_handle;
|
||||
// In the future this should also contain a handle to the pipeline that is going to be used for rendering this model.
|
||||
|
||||
public:
|
||||
|
||||
ModelComponent( std::shared_ptr< Model > && model ) : m_model( std::forward< decltype( m_model ) >( model ) )
|
||||
{}
|
||||
explicit ModelComponent( std::shared_ptr< Model > && model );
|
||||
|
||||
void drawImGui() override;
|
||||
|
||||
@@ -36,4 +44,4 @@ namespace fgl::engine
|
||||
|
||||
static_assert( is_component< ModelComponent > );
|
||||
|
||||
} // namespace fgl::engine
|
||||
} // namespace fgl::engine::components
|
||||
|
||||
32
src/engine/gameobjects/components/TransformComponent.hpp
Normal file
32
src/engine/gameobjects/components/TransformComponent.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Created by kj16609 on 3/3/25.
|
||||
//
|
||||
#pragma once
|
||||
#include "ComponentIDS.hpp"
|
||||
#include "interface/GameObjectComponent.hpp"
|
||||
|
||||
namespace fgl::engine::components
|
||||
{
|
||||
|
||||
COMPONENT_CLASS( TransformComponent, TransformComponentID )
|
||||
{
|
||||
WorldTransform m_transform;
|
||||
|
||||
public:
|
||||
|
||||
explicit TransformComponent( WorldTransform & transform );
|
||||
|
||||
void drawImGui() override;
|
||||
|
||||
std::string_view humanName() const override;
|
||||
std::string_view className() const override;
|
||||
|
||||
WorldTransform& operator*()
|
||||
{
|
||||
return m_transform;
|
||||
}
|
||||
|
||||
virtual ~TransformComponent() override = default;
|
||||
};
|
||||
|
||||
} // namespace fgl::engine::components
|
||||
@@ -7,14 +7,14 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ComponentEngineInterface.hpp"
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
#include "engine/primitives/Transform.hpp"
|
||||
|
||||
#define COMPONENT_CLASS( class_name, id_name ) class class_name final : public GameObjectComponent< id_name >
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
struct ComponentTransform final : public TransformComponent< CoordinateSpace::World >
|
||||
struct ComponentTransform final : public Transform< CoordinateSpace::World >
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
@@ -29,7 +29,7 @@ namespace fgl::engine
|
||||
ComponentTransform() = default;
|
||||
|
||||
ComponentTransform( const WorldCoordinate position_i, const Scale scale_i, const QuatRotation rotation_i ) :
|
||||
TransformComponent( position_i, scale_i, rotation_i )
|
||||
Transform( position_i, scale_i, rotation_i )
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -50,7 +50,6 @@ namespace fgl::engine
|
||||
struct GameObjectComponent : public GameObjectComponentBase
|
||||
{
|
||||
constexpr static ComponentID ID { T_ID };
|
||||
ComponentTransform m_transform { WorldCoordinate( 0.0f ), Scale( 1.0 ), QuatRotation() };
|
||||
|
||||
virtual ComponentID id() const override final { return ID; }
|
||||
};
|
||||
|
||||
12
src/engine/lighting/lights/GlobalIlluminator.cpp
Normal file
12
src/engine/lighting/lights/GlobalIlluminator.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// Created by kj16609 on 2/17/25.
|
||||
//
|
||||
#include "GlobalIlluminator.hpp"
|
||||
|
||||
|
||||
namespace fgl::engine::lights
|
||||
{
|
||||
|
||||
GlobalIlluminator::GlobalIlluminator( const NormalVector direction ) : m_direction( direction )
|
||||
{}
|
||||
} // namespace fgl::engine::lights
|
||||
26
src/engine/lighting/lights/GlobalIlluminator.hpp
Normal file
26
src/engine/lighting/lights/GlobalIlluminator.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Created by kj16609 on 2/17/25.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "primitives/vectors/NormalVector.hpp"
|
||||
|
||||
namespace fgl::engine::lights
|
||||
{
|
||||
|
||||
//! Sun illumation.
|
||||
class GlobalIlluminator
|
||||
{
|
||||
NormalVector m_direction;
|
||||
|
||||
public:
|
||||
|
||||
explicit GlobalIlluminator( NormalVector direction );
|
||||
};
|
||||
|
||||
} // namespace fgl::engine::lights
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
using namespace lights;
|
||||
}
|
||||
62
src/engine/lighting/shadows/ShadowMap.cpp
Normal file
62
src/engine/lighting/shadows/ShadowMap.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Created by kj16609 on 2/17/25.
|
||||
//
|
||||
#include "ShadowMap.hpp"
|
||||
|
||||
#include "assets/image/Image.hpp"
|
||||
#include "assets/texture/Texture.hpp"
|
||||
|
||||
namespace fgl::engine::shadows
|
||||
{
|
||||
|
||||
std::shared_ptr< Image > getDepthImage( const vk::Extent2D extent )
|
||||
{
|
||||
constexpr auto format { vk::Format::eR16Unorm };
|
||||
constexpr vk::ImageUsageFlags usage_flags { vk::ImageUsageFlagBits::eSampled
|
||||
| vk::ImageUsageFlagBits::eDepthStencilAttachment };
|
||||
constexpr auto inital_layout { vk::ImageLayout::eUndefined };
|
||||
constexpr auto final_layout { vk::ImageLayout::eDepthReadOnlyOptimal };
|
||||
|
||||
return std::make_shared< Image >( extent, format, usage_flags, inital_layout, final_layout );
|
||||
}
|
||||
|
||||
PerFrameArray< std::shared_ptr< Image > > createDepthImages( vk::Extent2D extent )
|
||||
{
|
||||
PerFrameArray< std::shared_ptr< Image > > array {};
|
||||
|
||||
for ( std::size_t i = 0; i < array.size(); ++i ) array[ i ] = getDepthImage( extent );
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
PerFrameArray< std::shared_ptr< Texture > > ShadowMap::createDepthTargets()
|
||||
{
|
||||
PerFrameArray< std::shared_ptr< Texture > > array {};
|
||||
|
||||
for ( std::size_t i = 0; i < array.size(); ++i ) array[ i ] = std::make_shared< Texture >( m_image[ i ] );
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
void ShadowMap::renderForCamera( const Camera& camera )
|
||||
{
|
||||
// model -> world -> camera (identity) -> screen (shadow)
|
||||
// since the camera in this case is the shadow map we just need to convert the screen space to world space. So we can just use an identity matrix
|
||||
|
||||
const Matrix< MatrixType::WorldToCamera > camera_matrix { m_transform.mat() };
|
||||
const Matrix< MatrixType::CameraToScreen > identity { 1.0f };
|
||||
|
||||
const Matrix< MatrixType::WorldToScreen > matrix { camera_matrix * identity };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
ShadowMap::ShadowMap( const vk::Extent2D extent ) :
|
||||
m_image( createDepthImages( extent ) ),
|
||||
m_target( createDepthTargets() )
|
||||
{}
|
||||
|
||||
} // namespace fgl::engine::shadows
|
||||
47
src/engine/lighting/shadows/ShadowMap.hpp
Normal file
47
src/engine/lighting/shadows/ShadowMap.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Created by kj16609 on 2/17/25.
|
||||
//
|
||||
#pragma once
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "primitives/Transform.hpp"
|
||||
#include "primitives/matricies/Matrix.hpp"
|
||||
#include "primitives/matricies/MatrixEvolvedTypes.hpp"
|
||||
#include "rendering/PresentSwapChain.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class Image;
|
||||
class Texture;
|
||||
} // namespace fgl::engine
|
||||
|
||||
namespace fgl::engine::shadows
|
||||
{
|
||||
|
||||
class ShadowMap
|
||||
{
|
||||
PerFrameArray< std::shared_ptr< Image > > m_image;
|
||||
PerFrameArray< std::shared_ptr< Texture > > m_target;
|
||||
|
||||
Matrix< MatrixType::WorldToScreen > m_matrix { 1.0f };
|
||||
|
||||
Transform< CoordinateSpace::World > m_transform {};
|
||||
|
||||
PerFrameArray< std::shared_ptr< Texture > > createDepthTargets();
|
||||
|
||||
public:
|
||||
|
||||
void renderForCamera( const Camera& camera );
|
||||
|
||||
ShadowMap( vk::Extent2D extent );
|
||||
~ShadowMap();
|
||||
};
|
||||
|
||||
} // namespace fgl::engine::shadows
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
using namespace shadows;
|
||||
}
|
||||
@@ -13,7 +13,8 @@ namespace fgl::engine::memory
|
||||
[[nodiscard]] BufferVector::BufferVector( Buffer& buffer, std::uint32_t count, std::uint32_t stride ) :
|
||||
BufferSuballocation( buffer.allocate( count * stride ) ),
|
||||
m_count( count ),
|
||||
m_stride( stride )
|
||||
m_stride( stride ),
|
||||
m_capacity( count )
|
||||
{}
|
||||
|
||||
//! Returns the offset count from the start of the buffer to the first element
|
||||
@@ -41,21 +42,38 @@ namespace fgl::engine::memory
|
||||
return m_count;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint32_t BufferVector::capacity() const noexcept
|
||||
{
|
||||
assert( !std::isnan( m_count ) );
|
||||
assert( m_count * m_stride <= this->bytesize() );
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
void BufferVector::resize( const std::uint32_t count )
|
||||
{
|
||||
assert( count > 0 );
|
||||
assert( !std::isnan( m_stride ) );
|
||||
assert( !std::isnan( m_count ) );
|
||||
|
||||
//If the capacity is higher then what we are requesting then we simply just ignore the request.
|
||||
// TODO: Maybe this is bad? I'm unsure. But reducing the number of allocations is always good
|
||||
if ( count < m_count ) return;
|
||||
// we are reclaiming size
|
||||
//TODO: Figure out a way to truely reclaim any size
|
||||
if ( count < capacity() )
|
||||
{
|
||||
m_count = count;
|
||||
return;
|
||||
}
|
||||
|
||||
BufferVector other { this->getBuffer(), count, m_stride };
|
||||
// the capacity is not enough for the new size, we must reallocate.
|
||||
if ( count > capacity() )
|
||||
{
|
||||
BufferVector other { this->getBuffer(), count, m_stride };
|
||||
|
||||
TransferManager::getInstance().copyToVector( *this, other, 0 );
|
||||
TransferManager::getInstance().copyToVector( *this, other, 0 );
|
||||
|
||||
*this = std::move( other );
|
||||
*this = std::move( other );
|
||||
}
|
||||
|
||||
this->m_count = count;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine::memory
|
||||
@@ -19,6 +19,7 @@ namespace fgl::engine::memory
|
||||
|
||||
//! Number of items in the vector
|
||||
std::uint32_t m_count { std::numeric_limits< std::uint32_t >::quiet_NaN() };
|
||||
std::uint32_t m_capacity { std::numeric_limits< std::uint32_t >::quiet_NaN() };
|
||||
|
||||
//! Bytes for each item
|
||||
std::uint32_t m_stride { std::numeric_limits< std::uint32_t >::quiet_NaN() };
|
||||
@@ -43,7 +44,9 @@ namespace fgl::engine::memory
|
||||
std::uint32_t getOffsetCount() const;
|
||||
std::uint32_t stride() const noexcept;
|
||||
std::uint32_t size() const noexcept;
|
||||
std::uint32_t capacity() const noexcept;
|
||||
void resize( std::uint32_t count );
|
||||
// void reserve( std::uint32_t count );
|
||||
};
|
||||
|
||||
} // namespace fgl::engine::memory
|
||||
|
||||
@@ -45,8 +45,7 @@ namespace fgl::engine
|
||||
memory::TransferManager::getInstance().copyToVector< T, DeviceVector< T > >( data, *this );
|
||||
}
|
||||
|
||||
//TODO: This
|
||||
void resize( const std::size_t new_size );
|
||||
void resize( const std::size_t new_size ) { BufferVector::resize( new_size ); }
|
||||
|
||||
void updateData( const std::size_t idx, const T& data )
|
||||
{
|
||||
|
||||
69
src/engine/memory/buffers/vector/IndexedVector.hpp
Normal file
69
src/engine/memory/buffers/vector/IndexedVector.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Created by kj16609 on 3/17/25.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "DeviceVector.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
template < typename T >
|
||||
class IndexedVector : DeviceVector< T >
|
||||
{
|
||||
std::queue< std::uint32_t > m_free_indexes {};
|
||||
|
||||
public:
|
||||
|
||||
class Index
|
||||
{
|
||||
IndexedVector< T >& m_vector;
|
||||
std::uint32_t m_idx;
|
||||
|
||||
public:
|
||||
|
||||
void update( const T& t )
|
||||
{
|
||||
memory::TransferManager::getInstance()
|
||||
.copyToVector< T, DeviceVector< T > >( m_vector.m_data, m_idx, t );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Index( IndexedVector< T >& vector, const std::uint32_t idx ) : m_vector( vector ), m_idx( idx ) {}
|
||||
|
||||
// Privated to force returning to the IndexedVector
|
||||
~Index() = default;
|
||||
};
|
||||
|
||||
IndexedVector() = delete;
|
||||
|
||||
IndexedVector( memory::Buffer& buffer, const std::uint32_t desired_count = 0 ) :
|
||||
DeviceVector< T >( buffer, desired_count )
|
||||
{
|
||||
for ( std::uint32_t i = 0; i < desired_count; ++i ) m_free_indexes.push( i );
|
||||
}
|
||||
|
||||
Index< T > acquire( const T& t )
|
||||
{
|
||||
if ( m_free_indexes.empty() )
|
||||
{
|
||||
resize( this->size() + 1 );
|
||||
m_free_indexes.push( this->size() - 1 );
|
||||
}
|
||||
|
||||
Index< T > index { *this, m_free_indexes.front() };
|
||||
m_free_indexes.pop();
|
||||
|
||||
index.update( t );
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void release( Index< T >&& index_i )
|
||||
{
|
||||
Index< T > index { std::forward< Index< T > >( index_i ) };
|
||||
m_free_indexes.push( index.m_idx );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -2,13 +2,13 @@
|
||||
// Created by kj16609 on 2/27/24.
|
||||
//
|
||||
|
||||
#include "TransformComponent.hpp"
|
||||
#include "Transform.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
glm::mat4 TransformComponent< CType >::mat4() const
|
||||
glm::mat4 Transform< CType >::mat4() const
|
||||
{
|
||||
const glm::mat3 rotation_mat { rotation.forcedQuat().mat() };
|
||||
|
||||
@@ -21,30 +21,30 @@ namespace fgl::engine
|
||||
}
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
Matrix< MatrixTransformType< CType >() > TransformComponent< CType >::mat() const
|
||||
Matrix< MatrixTransformType< CType >() > Transform< CType >::mat() const
|
||||
{
|
||||
return Matrix< MatrixTransformType< CType >() >( mat4() );
|
||||
}
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
NormalVector TransformComponent< CType >::forward() const
|
||||
NormalVector Transform< CType >::forward() const
|
||||
{
|
||||
return rotation.forward();
|
||||
}
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
NormalVector TransformComponent< CType >::right() const
|
||||
NormalVector Transform< CType >::right() const
|
||||
{
|
||||
return rotation.right();
|
||||
}
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
NormalVector TransformComponent< CType >::up() const
|
||||
NormalVector Transform< CType >::up() const
|
||||
{
|
||||
return rotation.up();
|
||||
}
|
||||
|
||||
template struct TransformComponent< CoordinateSpace::World >;
|
||||
template struct TransformComponent< CoordinateSpace::Model >;
|
||||
template struct Transform< CoordinateSpace::World >;
|
||||
template struct Transform< CoordinateSpace::Model >;
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -35,15 +35,15 @@ namespace fgl::engine
|
||||
FGL_UNREACHABLE();
|
||||
};
|
||||
|
||||
//TransformComponent is always in world space
|
||||
//Transform is always in world space
|
||||
template < CoordinateSpace CType = CoordinateSpace::World >
|
||||
struct TransformComponent
|
||||
struct Transform
|
||||
{
|
||||
Coordinate< CType > translation { constants::WORLD_CENTER };
|
||||
Scale scale { 1.0f, 1.0f, 1.0f };
|
||||
UniversalRotation rotation { constants::DEFAULT_ROTATION };
|
||||
|
||||
//TODO: Figure this out and replace TransformComponent with a template of CType instead
|
||||
//TODO: Figure this out and replace Transform with a template of CType instead
|
||||
[[nodiscard]] glm::mat4 mat4() const;
|
||||
|
||||
[[nodiscard]] Matrix< MatrixTransformType< CType >() > mat() const;
|
||||
@@ -61,8 +61,17 @@ namespace fgl::engine
|
||||
[[nodiscard]] NormalVector down() const { return -up(); }
|
||||
};
|
||||
|
||||
template < CoordinateSpace CType = CoordinateSpace::World >
|
||||
struct UniversalTransform
|
||||
{
|
||||
std::variant< Transform< CType >, glm::mat4 > m_variant;
|
||||
|
||||
//! Converts the transform into a matrix
|
||||
void decay() { m_variant = std::get< Transform< CType > >( m_variant ).mat4(); }
|
||||
};
|
||||
|
||||
template < CoordinateSpace CType, MatrixType MType >
|
||||
TransformComponent< CType > decompose( const glm::mat4 matrix )
|
||||
Transform< CType > decompose( const glm::mat4 matrix )
|
||||
{
|
||||
glm::mat4 localMatrix = matrix;
|
||||
|
||||
@@ -133,8 +142,8 @@ namespace fgl::engine
|
||||
}
|
||||
|
||||
// A game object will be going from world to camera space
|
||||
using GameObjectTransform = TransformComponent< CoordinateSpace::World >;
|
||||
// using ModelTransform = TransformComponent< CoordinateSpace::Model >;
|
||||
using WorldTransform = TransformComponent< CoordinateSpace::World >;
|
||||
using GameObjectTransform = Transform< CoordinateSpace::World >;
|
||||
// using ModelTransform = Transform< CoordinateSpace::Model >;
|
||||
using WorldTransform = Transform< CoordinateSpace::World >;
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "BoundingBox.hpp"
|
||||
#include "engine/constants.hpp"
|
||||
#include "engine/primitives/Scale.hpp"
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
#include "engine/primitives/Transform.hpp"
|
||||
#include "engine/primitives/matricies/Matrix.hpp"
|
||||
#include "engine/primitives/points/Coordinate.hpp"
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace fgl::engine
|
||||
|
||||
union
|
||||
{
|
||||
TransformComponent< CType > m_transform;
|
||||
Transform< CType > m_transform;
|
||||
glm::mat4 m_matrix;
|
||||
};
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace fgl::engine
|
||||
assert( inital_scale != constants::DEFAULT_VEC3 );
|
||||
}
|
||||
|
||||
[[nodiscard]] OrientedBoundingBox( const TransformComponent< CType >& transform ) :
|
||||
[[nodiscard]] OrientedBoundingBox( const Transform< CType >& transform ) :
|
||||
transform_mode( TransformMode::Transform ),
|
||||
m_transform( transform )
|
||||
{
|
||||
@@ -131,14 +131,14 @@ namespace fgl::engine
|
||||
|
||||
TransformMode getMode() const { return transform_mode; }
|
||||
|
||||
[[nodiscard]] TransformComponent< CType >& getTransform()
|
||||
[[nodiscard]] Transform< CType >& getTransform()
|
||||
{
|
||||
FGL_ASSERT(
|
||||
transform_mode == TransformMode::Transform, "FIXME: Transform not initalized. Mode switch needed" );
|
||||
return m_transform;
|
||||
}
|
||||
|
||||
[[nodiscard]] const TransformComponent< CType >& getTransform() const
|
||||
[[nodiscard]] const Transform< CType >& getTransform() const
|
||||
{
|
||||
FGL_ASSERT(
|
||||
transform_mode == TransformMode::Transform, "FIXME: Transform not initalized. Mode switch needed" );
|
||||
|
||||
@@ -47,6 +47,12 @@ namespace fgl::engine
|
||||
return point - ( this->getDirection() * distance );
|
||||
}
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
SimplePlane< CType > PointPlane< CType >::toSimple() const
|
||||
{
|
||||
return SimplePlane< CType >( m_vector, -glm::dot( m_vector.vec(), m_coordinate.vec() ) );
|
||||
}
|
||||
|
||||
template class PointPlane< CoordinateSpace::World >;
|
||||
template class PointPlane< CoordinateSpace::Model >;
|
||||
template class PointPlane< CoordinateSpace::Screen >;
|
||||
|
||||
@@ -14,6 +14,43 @@ namespace fgl::engine
|
||||
|
||||
class Vector;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @tparam CType
|
||||
*/
|
||||
template < CoordinateSpace CType >
|
||||
class FGL_PACKED SimplePlane
|
||||
{
|
||||
NormalVector m_vector;
|
||||
float m_distance;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @param vector
|
||||
* @param distance
|
||||
* @return A new instance of a simple plane with the given dimensions.
|
||||
*/
|
||||
[[nodiscard]] SimplePlane( NormalVector vector, float distance ) : m_vector( vector ), m_distance( distance ) {}
|
||||
|
||||
[[nodiscard]] NormalVector getDirection() const { return m_vector; }
|
||||
|
||||
[[nodiscard]] float distance() const { return m_distance; }
|
||||
|
||||
[[nodiscard]] float distanceFrom( const Coordinate< CType >& coord ) const
|
||||
{
|
||||
return glm::dot( m_vector.vec(), coord.vec() ) + m_distance;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isForward( const Coordinate< CType >& coord ) const { return distanceFrom( coord ) > 0.0f; }
|
||||
|
||||
[[nodiscard]] Coordinate< CType > mapToPlane( const Coordinate< CType >& point ) const
|
||||
{
|
||||
const float dist = distanceFrom( point );
|
||||
return Coordinate< CType >( point.vec() - dist * m_vector.vec() );
|
||||
}
|
||||
};
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
class PointPlane
|
||||
{
|
||||
@@ -31,19 +68,24 @@ namespace fgl::engine
|
||||
|
||||
PointPlane( Coordinate< CType > pos, NormalVector vec );
|
||||
|
||||
FGL_FORCE_INLINE NormalVector getDirection() const { return m_vector; }
|
||||
FGL_FORCE_INLINE [[nodiscard]] NormalVector getDirection() const { return m_vector; }
|
||||
|
||||
float distance() const;
|
||||
[[nodiscard]] float distance() const;
|
||||
|
||||
FGL_FORCE_INLINE Coordinate< CType > getPosition() const { return m_coordinate; }
|
||||
|
||||
float distanceFrom( const Coordinate< CType > coord ) const;
|
||||
float distanceFrom( Coordinate< CType > coord ) const;
|
||||
|
||||
bool isForward( const Coordinate< CType > coord ) const { return distanceFrom( coord ) > 0.0f; }
|
||||
|
||||
Coordinate< CType > mapToPlane( const Coordinate< CType > point ) const;
|
||||
[[nodiscard]] Coordinate< CType > mapToPlane( Coordinate< CType > point ) const;
|
||||
|
||||
[[nodiscard]] SimplePlane< CType > toSimple() const;
|
||||
};
|
||||
|
||||
static_assert(
|
||||
sizeof( glm::vec4 ) == sizeof( SimplePlane< CoordinateSpace::World > ), "SimplePlane was not 4 floats" );
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
using Plane = PointPlane< CType >;
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ namespace fgl::engine
|
||||
Vector operator*( const float scalar ) const;
|
||||
|
||||
NormalVector operator-() const { return NormalVector( -static_cast< glm::vec3 >( *this ) ); }
|
||||
|
||||
explicit operator glm::vec3() const { return static_cast< glm::vec3 >( *this ); }
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
|
||||
@@ -132,7 +132,8 @@ namespace fgl::engine
|
||||
|
||||
rendering_info.setColorAttachmentFormats( state.formats.colors );
|
||||
|
||||
rendering_info.setDepthAttachmentFormat( state.formats.depth );
|
||||
if ( state.formats.depth != vk::Format::eUndefined )
|
||||
rendering_info.setDepthAttachmentFormat( state.formats.depth );
|
||||
|
||||
if ( state.m_dynamic_state.size() > 0 ) info.setPDynamicState( &dynamic_state_create_info );
|
||||
|
||||
@@ -209,7 +210,7 @@ namespace fgl::engine
|
||||
m_state->push_constant.stageFlags = flags;
|
||||
}
|
||||
|
||||
PipelineBuilder::BuilderState::Formats::Formats() : depth( pickDepthFormat() )
|
||||
PipelineBuilder::BuilderState::Formats::Formats()
|
||||
{}
|
||||
|
||||
[[nodiscard]] vk::PipelineColorBlendAttachmentState& PipelineBuilder::BuilderState::addColorAttachment()
|
||||
@@ -292,6 +293,11 @@ namespace fgl::engine
|
||||
m_state->rasterization_info.cullMode = vk::CullModeFlagBits::eNone;
|
||||
}
|
||||
|
||||
void PipelineBuilder::addDepthAttachment()
|
||||
{
|
||||
m_state->formats.depth = pickDepthFormat();
|
||||
}
|
||||
|
||||
AttachmentBuilder PipelineBuilder::addAttachment()
|
||||
{
|
||||
return { *this };
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace fgl::engine
|
||||
struct Formats
|
||||
{
|
||||
std::vector< vk::Format > colors {};
|
||||
vk::Format depth;
|
||||
vk::Format depth { vk::Format::eUndefined };
|
||||
|
||||
Formats();
|
||||
} formats {};
|
||||
@@ -106,6 +106,7 @@ namespace fgl::engine
|
||||
|
||||
void disableCulling();
|
||||
|
||||
void addDepthAttachment();
|
||||
[[nodiscard]] AttachmentBuilder addAttachment();
|
||||
[[nodiscard]] AttachmentBuilder addColorAttachment();
|
||||
|
||||
|
||||
10
src/engine/scene/Scene.cpp
Normal file
10
src/engine/scene/Scene.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
//
|
||||
// Created by kj16609 on 2/28/25.
|
||||
//
|
||||
#include "Scene.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
Scene::Scene( const std::string& name ) : m_name( name )
|
||||
{}
|
||||
} // namespace fgl::engine
|
||||
33
src/engine/scene/Scene.hpp
Normal file
33
src/engine/scene/Scene.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Created by kj16609 on 2/28/25.
|
||||
//
|
||||
#pragma once
|
||||
#include "camera/Camera.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class GameObject;
|
||||
class World;
|
||||
|
||||
class Scene
|
||||
{
|
||||
std::string m_name {};
|
||||
|
||||
std::vector< std::shared_ptr< GameObject > > m_objects {};
|
||||
|
||||
public:
|
||||
|
||||
Scene() = delete;
|
||||
|
||||
FGL_DELETE_MOVE( Scene );
|
||||
FGL_DELETE_COPY( Scene );
|
||||
|
||||
std::string& getName() { return m_name; };
|
||||
|
||||
const std::string& getName() const { return m_name; };
|
||||
|
||||
//!! Loads a scene from a gltf file
|
||||
Scene( const std::string& name );
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
44
src/engine/scene/World.cpp
Normal file
44
src/engine/scene/World.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Created by kj16609 on 3/2/25.
|
||||
//
|
||||
#include "World.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
bool World::sceneExists( const std::string& name )
|
||||
{
|
||||
return std::ranges::
|
||||
find_if( m_scenes, [ &name ]( const auto& scene ) -> bool { return scene.getName() == name; } )
|
||||
!= m_scenes.end();
|
||||
}
|
||||
|
||||
std::shared_ptr< Scene > World::createScene()
|
||||
{
|
||||
constexpr std::string default_scene { "Scene" };
|
||||
|
||||
std::string name { default_scene };
|
||||
|
||||
// keep finding new name until we find one not used yet.
|
||||
if ( sceneExists( name ) )
|
||||
{
|
||||
std::uint8_t i { 1 };
|
||||
name = std::format( "Scene {}", i );
|
||||
|
||||
while ( sceneExists( name ) )
|
||||
{
|
||||
name = std::format( "Scene {}", ++i );
|
||||
}
|
||||
}
|
||||
|
||||
auto ptr { std::make_shared< Scene >( name ) };
|
||||
|
||||
m_scenes.push_back( ptr );
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
World::World( const std::filesystem::path& path )
|
||||
{}
|
||||
|
||||
} // namespace fgl::engine
|
||||
39
src/engine/scene/World.hpp
Normal file
39
src/engine/scene/World.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Created by kj16609 on 3/2/25.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Scene.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
/** A world is the highest representation of scenes in an engine,
|
||||
* a world is comprised up of multiple scenes. Only one world can be running at any given time
|
||||
*
|
||||
*/
|
||||
class World
|
||||
{
|
||||
std::vector< std::shared_ptr< Scene > > m_scenes {};
|
||||
|
||||
std::shared_ptr< ModelManager > m_model_manager { std::make_shared< ModelManager >() };
|
||||
|
||||
public:
|
||||
|
||||
bool sceneExists( const std::string& name );
|
||||
|
||||
std::shared_ptr< Scene > createScene();
|
||||
|
||||
FGL_DEFAULT_ALL_RO5( World );
|
||||
explicit World( const std::filesystem::path& path );
|
||||
explicit World( std::vector< std::byte >& data );
|
||||
|
||||
//! Dumps the world save info to a vector
|
||||
std::vector< std::byte > dump();
|
||||
|
||||
~World() = default;
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "engine/FrameInfo.hpp"
|
||||
#include "engine/camera/Camera.hpp"
|
||||
#include "engine/tree/octtree/OctTreeNode.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 3/14/24.
|
||||
//
|
||||
|
||||
#include "DrawPair.hpp"
|
||||
|
||||
#include <tracy/TracyC.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "engine/assets/model/Model.hpp"
|
||||
#include "engine/debug/drawers.hpp"
|
||||
#include "engine/debug/profiling/counters.hpp"
|
||||
#include "engine/gameobjects/components/ModelComponent.hpp"
|
||||
#include "engine/math/intersections.hpp"
|
||||
#include "engine/tree/octtree/OctTreeNode.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
std::pair< std::vector< vk::DrawIndexedIndirectCommand >, std::vector< ModelMatrixInfo > > getDrawCallsFromTree(
|
||||
OctTreeNode& root,
|
||||
const Frustum& frustum,
|
||||
const GameObjectFlagType game_object_flags,
|
||||
const TreeFilterFlags tree_flags,
|
||||
std::function< bool( const GameObject& ) > filterFunc )
|
||||
{
|
||||
ZoneScoped;
|
||||
std::unordered_map< DrawKey, DrawPair > draw_pairs {};
|
||||
draw_pairs.reserve( 512 );
|
||||
|
||||
const auto nodes { root.getAllLeafsInFrustum( frustum ) };
|
||||
|
||||
for ( auto* node : nodes )
|
||||
{
|
||||
ZoneScopedN( "Process leaf" );
|
||||
for ( const auto& obj : *node )
|
||||
{
|
||||
ZoneScopedN( "Process object" );
|
||||
|
||||
if ( ( obj.flags() & game_object_flags ) != game_object_flags ) continue;
|
||||
if ( !filterFunc( obj ) ) continue;
|
||||
|
||||
//Check if we have a renderable component
|
||||
if ( !obj.hasComponent< ModelComponent >() ) continue;
|
||||
|
||||
const auto model_components { obj.getComponents< ModelComponent >() };
|
||||
|
||||
const Matrix< MatrixType::ModelToWorld > obj_matrix { obj.getTransform().mat() };
|
||||
|
||||
for ( const auto* model_component_ptr : model_components )
|
||||
{
|
||||
const auto& model_transform { model_component_ptr->m_transform };
|
||||
|
||||
const Matrix< MatrixType::ModelToWorld > world_matrix { model_transform.mat() * obj_matrix };
|
||||
|
||||
const auto& comp { *model_component_ptr };
|
||||
for ( const Primitive& primitive : comp->m_primitives )
|
||||
{
|
||||
if ( !primitive.ready() ) continue;
|
||||
|
||||
// Does this primitive pass the bounds check
|
||||
const OrientedBoundingBox< CoordinateSpace::World > world_bounding_box {
|
||||
world_matrix * primitive.getBoundingBox()
|
||||
};
|
||||
|
||||
// No. Skip it
|
||||
if ( !intersects( frustum, world_bounding_box ) ) continue;
|
||||
|
||||
//assert( primitive.m_texture );
|
||||
const ModelMatrixInfo matrix_info { .model_matrix = world_matrix,
|
||||
.material_id = primitive.m_material->getID() };
|
||||
|
||||
// If the textureless flag is on and we have a texture then skip the primitive.c
|
||||
if ( tree_flags & IsTextureless )
|
||||
{
|
||||
if ( primitive.m_material != nullptr ) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flag is not present
|
||||
if ( primitive.m_material == nullptr ) continue;
|
||||
}
|
||||
|
||||
const auto key {
|
||||
std::make_pair( matrix_info.material_id, primitive.m_index_buffer.getOffset() )
|
||||
};
|
||||
|
||||
assert( primitive.m_index_buffer.size() > 0 );
|
||||
|
||||
profiling::addVertexDrawn( primitive.m_index_buffer.size() );
|
||||
|
||||
if ( auto itter = draw_pairs.find( key ); itter != draw_pairs.end() )
|
||||
{
|
||||
ZoneScopedN( "Accumulate for draw pair" );
|
||||
//Draw command for this mesh already exists. Simply add a count to it
|
||||
auto& [ itter_key, pair ] = *itter;
|
||||
auto& [ existing_cmd, model_matrix ] = pair;
|
||||
|
||||
existing_cmd.instanceCount += 1;
|
||||
model_matrix.emplace_back( matrix_info );
|
||||
assert( model_matrix.size() == existing_cmd.instanceCount );
|
||||
}
|
||||
else
|
||||
{
|
||||
ZoneScopedN( "Create new draw pair" );
|
||||
vk::DrawIndexedIndirectCommand cmd {};
|
||||
|
||||
cmd.firstIndex = primitive.m_index_buffer.getOffsetCount();
|
||||
cmd.indexCount = primitive.m_index_buffer.size();
|
||||
|
||||
cmd.vertexOffset = static_cast< int32_t >( primitive.m_vertex_buffer.getOffsetCount() );
|
||||
|
||||
cmd.instanceCount = 1;
|
||||
|
||||
std::vector< ModelMatrixInfo > matrix_infos {};
|
||||
matrix_infos.reserve( 128 );
|
||||
matrix_infos.emplace_back( matrix_info );
|
||||
draw_pairs.emplace( key, std::make_pair( cmd, std::move( matrix_infos ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( draw_pairs.empty() )
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector< vk::DrawIndexedIndirectCommand > draw_commands {};
|
||||
std::vector< ModelMatrixInfo > model_matrices {};
|
||||
|
||||
draw_commands.reserve( draw_pairs.size() );
|
||||
model_matrices.reserve( draw_pairs.size() * 2 );
|
||||
|
||||
TracyCZoneN( filter_zone_TRACY, "Reorganize draw commands", true );
|
||||
for ( auto& [ key, pair ] : draw_pairs )
|
||||
{
|
||||
auto cmd { pair.first };
|
||||
assert( cmd != vk::DrawIndexedIndirectCommand() );
|
||||
cmd.firstInstance = static_cast< std::uint32_t >( model_matrices.size() );
|
||||
|
||||
assert( cmd.instanceCount == pair.second.size() );
|
||||
|
||||
assert( pair.second.size() > 0 );
|
||||
|
||||
draw_commands.emplace_back( cmd );
|
||||
model_matrices.insert( model_matrices.end(), pair.second.begin(), pair.second.end() );
|
||||
}
|
||||
|
||||
TracyCZoneEnd( filter_zone_TRACY );
|
||||
|
||||
return { std::move( draw_commands ), std::move( model_matrices ) };
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,67 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 3/14/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "engine/gameobjects/GameObject.hpp"
|
||||
#include "engine/utils.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct Frustum;
|
||||
class OctTreeNode;
|
||||
struct ModelMatrixInfo;
|
||||
// <TextureID, MemoryOffset>
|
||||
using DrawKey = std::pair< TextureID, vk::DeviceSize >;
|
||||
|
||||
using DrawPair = std::pair< vk::DrawIndexedIndirectCommand, std::vector< ModelMatrixInfo > >;
|
||||
|
||||
inline bool operator<( const DrawPair& left, const DrawPair& right )
|
||||
{
|
||||
return left.first.firstIndex < right.first.firstIndex;
|
||||
}
|
||||
|
||||
inline bool operator==( const DrawPair& left, const DrawPair& right )
|
||||
{
|
||||
return left.first.firstIndex == right.first.firstIndex && left.first.indexCount && right.first.indexCount;
|
||||
}
|
||||
|
||||
inline bool defaultTrueFunc( [[maybe_unused]] const GameObject& )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
enum TreeFilterFlags
|
||||
{
|
||||
IsTextureless = 1 << 0,
|
||||
DefaultFlags = 0,
|
||||
};
|
||||
|
||||
std::pair< std::vector< vk::DrawIndexedIndirectCommand >, std::vector< ModelMatrixInfo > > getDrawCallsFromTree(
|
||||
OctTreeNode& root,
|
||||
const Frustum& frustum,
|
||||
GameObjectFlagType game_object_flags,
|
||||
TreeFilterFlags tree_flags = DefaultFlags,
|
||||
std::function< bool( const GameObject& ) > filterFunc = &defaultTrueFunc );
|
||||
|
||||
} // namespace fgl::engine
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash< fgl::engine::DrawKey >
|
||||
{
|
||||
inline size_t operator()( const fgl::engine::DrawKey& key ) const noexcept
|
||||
{
|
||||
const auto id_hash { std::hash< fgl::engine::TextureID >()( key.first ) };
|
||||
const auto offset_hash { std::hash< vk::DeviceSize >()( key.second ) };
|
||||
|
||||
size_t seed { 0 };
|
||||
fgl::engine::hashCombine( seed, id_hash, offset_hash );
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
@@ -7,14 +7,12 @@
|
||||
#include <tracy/TracyC.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "DrawPair.hpp"
|
||||
#include "engine/assets/material/Material.hpp"
|
||||
#include "engine/camera/Camera.hpp"
|
||||
#include "engine/debug/profiling/counters.hpp"
|
||||
#include "engine/debug/timing/FlameGraph.hpp"
|
||||
#include "engine/rendering/pipelines/v2/Pipeline.hpp"
|
||||
#include "engine/rendering/pipelines/v2/PipelineBuilder.hpp"
|
||||
#include "engine/tree/octtree/OctTreeNode.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
@@ -22,50 +20,25 @@ namespace fgl::engine
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
/*
|
||||
{
|
||||
// PipelineConfigInfo standard_info { render_pass };
|
||||
// PipelineConfigInfo::addGBufferAttachmentsConfig( standard_info );
|
||||
// PipelineConfigInfo textured_info { render_pass };
|
||||
// PipelineConfigInfo::addGBufferAttachmentsConfig( textured_info );
|
||||
|
||||
PipelineBuilder builder { 0 };
|
||||
PipelineBuilder builder { 0 };
|
||||
|
||||
builder.addDescriptorSet( Camera::getDescriptorLayout() );
|
||||
addGBufferAttachments( builder );
|
||||
|
||||
addGBufferAttachments( builder );
|
||||
builder.addDescriptorSet( Camera::getDescriptorLayout() );
|
||||
builder.addDescriptorSet( Texture::getDescriptorLayout() );
|
||||
builder.addDescriptorSet( Material::getDescriptorLayout() );
|
||||
|
||||
builder.setFragmentShader( Shader::loadFragment( "shaders/textureless-gbuffer.frag" ) );
|
||||
builder.setVertexShader( Shader::loadVertex( "shaders/textureless-gbuffer.vert" ) );
|
||||
builder.setFragmentShader( Shader::loadFragment( "shaders/textured.slang" ) );
|
||||
builder.setVertexShader( Shader::loadVertex( "shaders/textured.slang" ) );
|
||||
|
||||
builder.setAttributeDescriptions( ModelVertex::getAttributeDescriptions() );
|
||||
builder.setBindingDescriptions( ModelVertex::getBindingDescriptions() );
|
||||
builder.setAttributeDescriptions( ModelVertex::getAttributeDescriptions() );
|
||||
builder.setBindingDescriptions( ModelVertex::getBindingDescriptions() );
|
||||
|
||||
m_standard_pipeline = builder.create();
|
||||
|
||||
m_standard_pipeline->setDebugName( "Standard entity pipeline" );
|
||||
}
|
||||
*/
|
||||
|
||||
{
|
||||
// PipelineConfigInfo textured_info { render_pass };
|
||||
// PipelineConfigInfo::addGBufferAttachmentsConfig( textured_info );
|
||||
|
||||
PipelineBuilder builder { 0 };
|
||||
|
||||
addGBufferAttachments( builder );
|
||||
|
||||
builder.addDescriptorSet( Camera::getDescriptorLayout() );
|
||||
builder.addDescriptorSet( Texture::getDescriptorLayout() );
|
||||
builder.addDescriptorSet( Material::getDescriptorLayout() );
|
||||
|
||||
builder.setFragmentShader( Shader::loadFragment( "shaders/textured.slang" ) );
|
||||
builder.setVertexShader( Shader::loadVertex( "shaders/textured.slang" ) );
|
||||
|
||||
builder.setAttributeDescriptions( ModelVertex::getAttributeDescriptions() );
|
||||
builder.setBindingDescriptions( ModelVertex::getBindingDescriptions() );
|
||||
|
||||
m_textured_pipeline = builder.create();
|
||||
m_textured_pipeline->setDebugName( "Textured entity pipeline" );
|
||||
}
|
||||
m_textured_pipeline = builder.create();
|
||||
m_textured_pipeline->setDebugName( "Textured entity pipeline" );
|
||||
}
|
||||
|
||||
EntityRendererSystem::~EntityRendererSystem()
|
||||
@@ -97,60 +70,13 @@ namespace fgl::engine
|
||||
texturedPass( info );
|
||||
}
|
||||
|
||||
void EntityRendererSystem::texturelessPass( [[maybe_unused]] const FrameInfo& info )
|
||||
{
|
||||
/*
|
||||
ZoneScopedN( "Textureless pass" );
|
||||
auto& command_buffer { info.command_buffer };
|
||||
TracyVkZone( info.tracy_ctx, *command_buffer, "Render textureless entities" );
|
||||
|
||||
//Bind pipeline
|
||||
m_standard_pipeline->bind( command_buffer );
|
||||
|
||||
m_standard_pipeline->bindDescriptor( command_buffer, info.getCameraDescriptor() );
|
||||
|
||||
//Get all commands for drawing anything without a texture
|
||||
auto [ draw_commands, model_matricies ] = getDrawCallsFromTree(
|
||||
info.game_objects, info.camera->getFrustumBounds(), IS_VISIBLE | IS_ENTITY, IS_TEXTURELESS );
|
||||
|
||||
//TODO: Filter Textureless models (#6)
|
||||
|
||||
if ( draw_commands.size() == 0 ) return;
|
||||
|
||||
auto& model_matrix_info_buffer { m_simple_model_matrix_info_buffers[ info.frame_idx ] };
|
||||
|
||||
model_matrix_info_buffer =
|
||||
std::make_unique< ModelMatrixInfoBufferSuballocation >( info.model_matrix_info_buffer, model_matricies );
|
||||
|
||||
auto& draw_parameter_buffer { m_draw_simple_parameter_buffers[ info.frame_idx ] };
|
||||
|
||||
draw_parameter_buffer =
|
||||
std::make_unique< DrawParameterBufferSuballocation >( info.draw_parameter_buffer, draw_commands );
|
||||
|
||||
const std::vector< vk::Buffer > vert_buffers { info.model_vertex_buffer.getVkBuffer(),
|
||||
model_matrix_info_buffer->getVkBuffer() };
|
||||
|
||||
command_buffer.bindVertexBuffers( 0, vert_buffers, { 0, model_matrix_info_buffer->getOffset() } );
|
||||
command_buffer.bindIndexBuffer( info.model_index_buffer.getVkBuffer(), 0, vk::IndexType::eUint32 );
|
||||
|
||||
command_buffer.drawIndexedIndirect(
|
||||
draw_parameter_buffer->getVkBuffer(),
|
||||
draw_parameter_buffer->getOffset(),
|
||||
draw_parameter_buffer->size(),
|
||||
draw_parameter_buffer->stride() );
|
||||
*/
|
||||
}
|
||||
|
||||
void EntityRendererSystem::texturedPass( const FrameInfo& info )
|
||||
{
|
||||
ZoneScopedN( "Textured pass" );
|
||||
auto& command_buffer { info.command_buffer.render_cb };
|
||||
TracyVkZone( info.tracy_ctx, **command_buffer, "Render textured entities" );
|
||||
|
||||
auto [ draw_commands, model_matricies ] =
|
||||
getDrawCallsFromTree( info.game_objects, info.camera->getFrustumBounds(), IsVisible | IsEntity );
|
||||
|
||||
if ( draw_commands.empty() ) return;
|
||||
// compute shader
|
||||
|
||||
m_textured_pipeline->bind( command_buffer );
|
||||
|
||||
@@ -158,9 +84,6 @@ namespace fgl::engine
|
||||
m_textured_pipeline->bindDescriptor( command_buffer, Texture::getDescriptorSet() );
|
||||
m_textured_pipeline->bindDescriptor( command_buffer, Material::getDescriptorSet() );
|
||||
|
||||
profiling::addModelDrawn( model_matricies.size() );
|
||||
profiling::addInstances( draw_commands.size() );
|
||||
|
||||
auto& model_matrix_info_buffer { m_textured_model_matrix_info_buffers[ info.frame_idx ] };
|
||||
model_matrix_info_buffer =
|
||||
std::make_unique< ModelMatrixInfoBufferSuballocation >( info.model_matrix_info_buffer, model_matricies );
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace fgl::engine
|
||||
|
||||
//! Pipeline for basic textured models (Single texture)
|
||||
std::unique_ptr< Pipeline > m_textured_pipeline {};
|
||||
// std::unique_ptr< ComputePipeline > m_cull_pipeline {};
|
||||
|
||||
using DrawParameterBufferSuballocation = HostVector< vk::DrawIndexedIndirectCommand >;
|
||||
|
||||
|
||||
34
src/engine/systems/render/ShadowRenderer.cpp
Normal file
34
src/engine/systems/render/ShadowRenderer.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by kj16609 on 2/28/25.
|
||||
//
|
||||
#include "ShadowRenderer.hpp"
|
||||
|
||||
#include "FrameInfo.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
CommandBuffer& ShadowRenderer::setupSystem( const FrameInfo& info )
|
||||
{}
|
||||
|
||||
void ShadowRenderer::pass( FrameInfo& info )
|
||||
{
|
||||
// Render any shadowmaps attach to the camera
|
||||
auto& command_buffer { setupSystem( info ) };
|
||||
|
||||
//TODO: Implement object culling for shadowmaps
|
||||
if ( draw_commands.empty() ) return;
|
||||
|
||||
m_pipeline->bind( command_buffer );
|
||||
}
|
||||
|
||||
ShadowRenderer::ShadowRenderer()
|
||||
{
|
||||
PipelineBuilder builder { 0 };
|
||||
|
||||
builder.setVertexShader( Shader::loadVertex( "shaders/shadowmap.slang" ) );
|
||||
|
||||
m_pipeline = builder.create();
|
||||
m_pipeline->setDebugName( "Shadow map pipeline" );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
30
src/engine/systems/render/ShadowRenderer.hpp
Normal file
30
src/engine/systems/render/ShadowRenderer.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Created by kj16609 on 2/28/25.
|
||||
//
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
#include "memory/buffers/vector/HostVector.hpp"
|
||||
#include "rendering/pipelines/v2/Pipeline.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct FrameInfo;
|
||||
struct ModelMatrixInfo;
|
||||
|
||||
class ShadowRenderer
|
||||
{
|
||||
std::unique_ptr< Pipeline > m_pipeline {};
|
||||
|
||||
using DrawParameterBuffer = HostVector< vk::DrawIndexedIndirectCommand >;
|
||||
using DrawIndexedIndirectCommand = HostVector< ModelMatrixInfo >;
|
||||
|
||||
public:
|
||||
|
||||
CommandBuffer& setupSystem( const FrameInfo& info );
|
||||
void pass( FrameInfo& info );
|
||||
|
||||
ShadowRenderer();
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,99 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 11/3/24.
|
||||
//
|
||||
|
||||
#include "Chunk.hpp"
|
||||
|
||||
#include "engine/math/intersections.hpp"
|
||||
#include "engine/primitives/boxes/AxisAlignedBoundingCube.hpp"
|
||||
#include "engine/utils.hpp"
|
||||
|
||||
/*
|
||||
namespace fgl::engine::tree
|
||||
{
|
||||
|
||||
std::shared_ptr< Chunk > ChunkManager::createChunk( const ChunkID id )
|
||||
{
|
||||
std::shared_ptr< Chunk > chunk { std::make_shared< Chunk >( id ) };
|
||||
|
||||
m_chunks.insert( std::make_pair( id, chunk ) );
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void ChunkManager::cleanup()
|
||||
{
|
||||
std::lock_guard guard { m_delete_mtx };
|
||||
while ( m_delete_list.size() > 0 )
|
||||
{
|
||||
const auto item { m_delete_list.front() };
|
||||
m_delete_list.pop();
|
||||
|
||||
auto itter { m_chunks.find( item->getID() ) };
|
||||
|
||||
m_chunks.erase( itter );
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr< Chunk > ChunkManager::getChunk( const ChunkID id )
|
||||
{}
|
||||
|
||||
void ChunkManager::markForDeletion( std::shared_ptr< Chunk >& chunk )
|
||||
{
|
||||
m_delete_list.push( chunk );
|
||||
}
|
||||
|
||||
ChunkManager& ChunkManager::getInstance()
|
||||
{
|
||||
static ChunkManager manager {};
|
||||
return manager;
|
||||
}
|
||||
|
||||
ChunkID getID( const glm::vec3 point )
|
||||
{
|
||||
// the inital chunk starts at 0,0,0. meaning that the bounds of the inital chunk goes from
|
||||
// +/- CHUNK_SIZE / 2.0f
|
||||
|
||||
// We need to start by dividing the point into the size of the chunks.
|
||||
constexpr glm::vec3 CHUNK_VEC3 { Chunk::CHUNK_HALF };
|
||||
const glm::vec3 chunk_pos { point / CHUNK_VEC3 };
|
||||
|
||||
// chunk_pos now needs to be clamped to interegers, we always round DOWN.
|
||||
const glm::vec3 round_offset { glm::sign( chunk_pos ) * glm::vec3( 0.5f ) };
|
||||
const glm::vec3 chunk_coords { glm::round( chunk_pos + round_offset ) };
|
||||
const glm::vec< 3, int > chunk_coords_i { chunk_coords };
|
||||
|
||||
ChunkID hash { 0 };
|
||||
engine::hashCombine( hash, chunk_coords_i[ 0 ], chunk_coords_i[ 1 ], chunk_coords_i[ 2 ] );
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::shared_ptr< Chunk > Chunk::getShared()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
Chunk::Chunk( const ChunkID id ) : m_id( id ), m_center( getPosition( id ) )
|
||||
{}
|
||||
|
||||
ChunkID Chunk::getID() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
bool Chunk::isVisible( const Frustum& frustum ) const
|
||||
{
|
||||
// Create a bounding box of this chunk
|
||||
const AxisAlignedBoundingCube< CS::World > bounds { WorldCoordinate( m_center ), CHUNK_HALF };
|
||||
|
||||
return intersects( frustum, bounds );
|
||||
}
|
||||
|
||||
void Chunk::deleteLater()
|
||||
{
|
||||
// ChunkManager::getInstance().markForDeletion( this->getShared() );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine::tree
|
||||
*/
|
||||
@@ -1,85 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 11/2/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "engine/gameobjects/GameObject.hpp"
|
||||
|
||||
/*
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct Frustum;
|
||||
}
|
||||
|
||||
namespace fgl::engine::tree
|
||||
{
|
||||
class Chunk;
|
||||
|
||||
using ChunkID = std::size_t;
|
||||
|
||||
class ChunkManager
|
||||
{
|
||||
std::unordered_map< ChunkID, std::shared_ptr< Chunk > > m_chunks {};
|
||||
std::mutex m_delete_mtx {};
|
||||
std::queue< std::shared_ptr< Chunk > > m_delete_list {};
|
||||
|
||||
std::shared_ptr< Chunk > createChunk( ChunkID id );
|
||||
|
||||
//! Deletes any chunks pending deletion
|
||||
void cleanup();
|
||||
|
||||
//! Returns a shared pointer to the chunk with the given ID.
|
||||
std::shared_ptr< Chunk > getChunk( ChunkID id );
|
||||
|
||||
public:
|
||||
|
||||
void markForDeletion( std::shared_ptr< Chunk >& chunk );
|
||||
|
||||
static ChunkManager& getInstance();
|
||||
};
|
||||
|
||||
ChunkID getID( const glm::vec3 point );
|
||||
glm::vec3 getPosition( const ChunkID id );
|
||||
|
||||
class Chunk : public std::enable_shared_from_this< Chunk >
|
||||
{
|
||||
//! Determines if the chunk is active to the rendering system
|
||||
bool m_rendering_active { true };
|
||||
|
||||
const ChunkID m_id;
|
||||
const glm::vec3 m_center;
|
||||
|
||||
//! Contains a list of all objects within this chunk
|
||||
std::unordered_map< GameObject::GameObjectID, std::shared_ptr< GameObject > > m_objects {};
|
||||
|
||||
std::shared_ptr< Chunk > getShared();
|
||||
|
||||
public:
|
||||
|
||||
Chunk() = delete;
|
||||
Chunk( const ChunkID id );
|
||||
|
||||
ChunkID getID() const;
|
||||
|
||||
void addGameObject( std::shared_ptr< GameObject > object );
|
||||
|
||||
// Size of a chunk from center to
|
||||
constexpr static float CHUNK_SIZE { 100.0f };
|
||||
constexpr static float CHUNK_HALF { CHUNK_SIZE / 2.0f };
|
||||
|
||||
//! Returns the number of game objects within this chunk
|
||||
std::size_t childCount();
|
||||
|
||||
//! Returns true if the bounds of this chunk are visible.
|
||||
bool isVisible( const Frustum& frustum ) const;
|
||||
|
||||
//! Marks this node to be deleted later
|
||||
void deleteLater();
|
||||
};
|
||||
|
||||
} // namespace fgl::engine::tree
|
||||
*/
|
||||
@@ -1,9 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 1/24/25.
|
||||
//
|
||||
#include "BVHTree.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 1/24/25.
|
||||
//
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#include "glm/vec3.hpp"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
using BVHIndex = std::uint32_t;
|
||||
|
||||
class BVHTree
|
||||
{
|
||||
struct GameObjectInfo
|
||||
{};
|
||||
|
||||
struct BVHNode
|
||||
{
|
||||
using FlagType = std::uint8_t;
|
||||
|
||||
enum Masks : FlagType
|
||||
{
|
||||
Flag_None = 0,
|
||||
//! This node was visible during the last test
|
||||
Flag_Previously_Visible = 1 << 0,
|
||||
//! This node was not visible during the last test
|
||||
Flag_Previously_Invisible = 1 << 1,
|
||||
};
|
||||
|
||||
FlagType m_flags { Masks::Flag_None };
|
||||
glm::vec3 m_centerpoint;
|
||||
|
||||
//! Index to try next if we hit this index.
|
||||
BVHIndex m_hit;
|
||||
//! Index t otry if we fail this index.
|
||||
BVHIndex m_miss;
|
||||
};
|
||||
|
||||
public:
|
||||
};
|
||||
} // namespace fgl::engine
|
||||
@@ -1,732 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 3/1/24.
|
||||
//
|
||||
|
||||
#include "OctTreeNode.hpp"
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <engine/FrameInfo.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#include <imgui.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include "engine/assets/model/Model.hpp"
|
||||
#include "engine/clock.hpp"
|
||||
#include "engine/debug/drawers.hpp"
|
||||
#include "engine/math/intersections.hpp"
|
||||
#include "engine/primitives/Frustum.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
struct FrameInfo;
|
||||
|
||||
static bool draw_leaf_fit_bounds { false };
|
||||
static bool draw_leaf_real_bounds { false };
|
||||
static bool draw_inview_bounds { false };
|
||||
static bool draw_branches { false };
|
||||
static bool draw_model_bounding_boxes { false };
|
||||
static std::size_t number_moved { 0 };
|
||||
static std::optional< std::chrono::microseconds > time { std::nullopt };
|
||||
|
||||
void imGuiOctTreeSettings( const FrameInfo& info )
|
||||
{
|
||||
if ( ImGui::CollapsingHeader( "OctTree debug settings" ) )
|
||||
{
|
||||
ImGui::Checkbox( "Draw leaf fitted bounding boxes", &draw_leaf_fit_bounds );
|
||||
ImGui::Checkbox( "Draw leaf real bounding boxes", &draw_leaf_real_bounds );
|
||||
ImGui::Checkbox( "Draw ALL in view bounding boxes", &draw_inview_bounds );
|
||||
ImGui::Checkbox( "Draw branches", &draw_branches );
|
||||
ImGui::Checkbox( "Draw all model bounding boxes", &draw_model_bounding_boxes );
|
||||
|
||||
if ( ImGui::Button( "Reorganize Octtree" ) )
|
||||
{
|
||||
const auto start { fgl::Clock::now() };
|
||||
number_moved = info.game_objects.reorganize();
|
||||
const auto end { fgl::Clock::now() };
|
||||
const auto time_diff { end - start };
|
||||
time = std::chrono::duration_cast< std::chrono::microseconds >( time_diff );
|
||||
}
|
||||
|
||||
if ( ImGui::Button( "Recalculate Bounds" ) )
|
||||
{
|
||||
const auto start { fgl::Clock::now() };
|
||||
info.game_objects.recalculateChildBounds();
|
||||
const auto end { fgl::Clock::now() };
|
||||
const auto time_diff { end - start };
|
||||
time = std::chrono::duration_cast< std::chrono::microseconds >( time_diff );
|
||||
}
|
||||
|
||||
if ( ImGui::Button( "Optimize Octree Travel" ) )
|
||||
{
|
||||
info.game_objects.optimizePath();
|
||||
}
|
||||
|
||||
if ( time.has_value() )
|
||||
{
|
||||
ImGui::Text( "Time spent reorganizing: %.2ldus", time.value().count() );
|
||||
ImGui::Text( "Moved %ld objects", number_moved );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OctTreeNode::getAllLeafsInFrustum( const Frustum& frustum, std::vector< OctTreeNodeLeaf* >& out_leafs )
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
auto& leafs { out_leafs };
|
||||
|
||||
switch ( m_node_data.index() )
|
||||
{
|
||||
case 0: // NodeArray
|
||||
{
|
||||
//Check if we are inside the frustum.
|
||||
if ( !isInFrustum( frustum ) ) return;
|
||||
|
||||
assert( std::holds_alternative< OctTreeNodeArray >( m_node_data ) );
|
||||
const OctTreeNodeArray& node_array { std::get< OctTreeNodeArray >( m_node_data ) };
|
||||
//Search deeper
|
||||
|
||||
if ( m_skip != nullptr )
|
||||
{
|
||||
m_skip->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
return;
|
||||
}
|
||||
|
||||
node_array[ LEFT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
node_array[ LEFT ][ FORWARD ][ BOTTOM ]->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
|
||||
node_array[ LEFT ][ BACK ][ TOP ]->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
node_array[ LEFT ][ BACK ][ BOTTOM ]->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
|
||||
node_array[ RIGHT ][ FORWARD ][ TOP ]->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
node_array[ RIGHT ][ FORWARD ][ BOTTOM ]->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
|
||||
node_array[ RIGHT ][ BACK ][ TOP ]->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
node_array[ RIGHT ][ BACK ][ BOTTOM ]->getAllLeafsInFrustum( frustum, out_leafs );
|
||||
return;
|
||||
}
|
||||
case 1: // NodeLeaf
|
||||
{
|
||||
OctTreeNodeLeaf& leaf { std::get< OctTreeNodeLeaf >( m_node_data ) };
|
||||
if ( leaf.empty() ) return;
|
||||
|
||||
//Check if we are inside the frustum.
|
||||
if ( !isInFrustum( frustum ) ) return;
|
||||
|
||||
assert( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) );
|
||||
leafs.emplace_back( &leaf );
|
||||
|
||||
//debug::world::drawBoundingBox( m_bounds );
|
||||
|
||||
return;
|
||||
}
|
||||
default:
|
||||
FGL_UNREACHABLE();
|
||||
}
|
||||
|
||||
FGL_UNREACHABLE();
|
||||
}
|
||||
|
||||
OctTreeNode& OctTreeNode::operator[]( const WorldCoordinate coord ) const
|
||||
{
|
||||
assert( std::holds_alternative< OctTreeNodeArray >( m_node_data ) );
|
||||
|
||||
// Bounding box center
|
||||
const auto bounds_center { this->m_bounds.getPosition().vec() };
|
||||
|
||||
const auto coordinate_center { coord.vec() };
|
||||
|
||||
//const auto test_dim { glm::greaterThanEqual( coord.vec(), bounds_center ) };
|
||||
//const auto& node_array { std::get< OctTreeNodeArray >( m_node_data ) };
|
||||
//const auto& node { node_array[ test_dim.x ? 1 : 0 ][ test_dim.y ? 1 : 0 ][ test_dim.z ? 1 : 0 ] };
|
||||
|
||||
const std::size_t x_idx { coordinate_center.x > bounds_center.x ? 1ul : 0ul };
|
||||
const std::size_t y_idx { coordinate_center.y > bounds_center.y ? 1ul : 0ul };
|
||||
const std::size_t z_idx { coordinate_center.z > bounds_center.z ? 1ul : 0ul };
|
||||
|
||||
const auto& node { std::get< OctTreeNodeArray >( m_node_data )[ x_idx ][ y_idx ][ z_idx ] };
|
||||
|
||||
FGL_ASSERT( node, "Node was invalid!" );
|
||||
FGL_ASSERT( node->canContain( coord ), "Node was not capable of containing the object!" );
|
||||
|
||||
return *node.get();
|
||||
}
|
||||
|
||||
OctTreeNode::OctTreeNode( const WorldCoordinate center, const float span, OctTreeNode* parent ) :
|
||||
m_fit_bounding_box( center, glm::vec3( glm::abs( span ) ) ),
|
||||
m_bounds( center, glm::abs( span ) ),
|
||||
m_node_data( OctTreeNodeLeaf() ),
|
||||
m_parent( parent )
|
||||
{
|
||||
assert( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) );
|
||||
std::get< OctTreeNodeLeaf >( m_node_data ).reserve( MAX_NODES_IN_LEAF );
|
||||
}
|
||||
|
||||
OctTreeNode::~OctTreeNode()
|
||||
{}
|
||||
|
||||
void OctTreeNode::split( const int depth )
|
||||
{
|
||||
ZoneScoped;
|
||||
if ( std::holds_alternative< OctTreeNodeArray >( m_node_data ) ) return;
|
||||
auto game_objects { std::get< OctTreeNodeLeaf >( std::move( 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() };
|
||||
|
||||
OctTreeNodeArray new_nodes {};
|
||||
|
||||
const float left_x { center.x - half_span };
|
||||
const float right_x { center.x + half_span };
|
||||
|
||||
const float forward_y { center.y + half_span };
|
||||
const float backward_y { center.y - half_span };
|
||||
|
||||
const float top_z { center.z + half_span };
|
||||
const float bottom_z { center.z - half_span };
|
||||
|
||||
assert( !std::isinf( left_x ) && !std::isinf( right_x ) );
|
||||
assert( !std::isinf( forward_y ) && !std::isinf( backward_y ) );
|
||||
assert( !std::isinf( top_z ) && !std::isinf( bottom_z ) );
|
||||
|
||||
new_nodes[ LEFT ][ FORWARD ][ TOP ] =
|
||||
std::make_unique< OctTreeNode >( WorldCoordinate( left_x, forward_y, top_z ), half_span, this );
|
||||
|
||||
new_nodes[ LEFT ][ FORWARD ][ BOTTOM ] =
|
||||
std::make_unique< OctTreeNode >( WorldCoordinate( left_x, forward_y, bottom_z ), half_span, this );
|
||||
|
||||
new_nodes[ LEFT ][ BACK ][ TOP ] =
|
||||
std::make_unique< OctTreeNode >( WorldCoordinate( left_x, backward_y, top_z ), half_span, this );
|
||||
|
||||
new_nodes[ LEFT ][ BACK ][ BOTTOM ] =
|
||||
std::make_unique< OctTreeNode >( WorldCoordinate( left_x, backward_y, bottom_z ), half_span, this );
|
||||
|
||||
new_nodes[ RIGHT ][ FORWARD ][ TOP ] =
|
||||
std::make_unique< OctTreeNode >( WorldCoordinate( right_x, forward_y, top_z ), half_span, this );
|
||||
|
||||
new_nodes[ RIGHT ][ FORWARD ][ BOTTOM ] =
|
||||
std::make_unique< OctTreeNode >( WorldCoordinate( right_x, forward_y, bottom_z ), half_span, this );
|
||||
|
||||
new_nodes[ RIGHT ][ BACK ][ TOP ] =
|
||||
std::make_unique< OctTreeNode >( WorldCoordinate( right_x, backward_y, top_z ), half_span, this );
|
||||
|
||||
new_nodes[ RIGHT ][ BACK ][ BOTTOM ] =
|
||||
std::make_unique< OctTreeNode >( WorldCoordinate( right_x, backward_y, bottom_z ), half_span, this );
|
||||
|
||||
FGL_ASSUME( game_objects.size() <= MAX_NODES_IN_LEAF )
|
||||
|
||||
for ( GameObject& obj : game_objects )
|
||||
{
|
||||
const auto& obj_coordinate { obj.getTransform().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 };
|
||||
|
||||
const std::unique_ptr< OctTreeNode >& node {
|
||||
new_nodes[ is_right ? 1 : 0 ][ is_forward ? 1 : 0 ][ is_up ? 1 : 0 ]
|
||||
};
|
||||
assert( std::holds_alternative< OctTreeNodeLeaf >( node->m_node_data ) );
|
||||
|
||||
std::get< OctTreeNodeLeaf >( node->m_node_data ).emplace_back( std::move( obj ) );
|
||||
}
|
||||
|
||||
this->m_node_data = std::move( new_nodes );
|
||||
|
||||
recalculateChildBounds();
|
||||
|
||||
if ( depth - 1 >= 1 )
|
||||
{
|
||||
split( depth );
|
||||
}
|
||||
}
|
||||
|
||||
OctTreeNode* OctTreeNode::addGameObject( GameObject&& obj )
|
||||
{
|
||||
assert( this->canContain( obj ) );
|
||||
|
||||
if ( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) ) // This node is a leaf
|
||||
{
|
||||
auto& objects { std::get< OctTreeNodeLeaf >( m_node_data ) };
|
||||
assert( objects.capacity() == MAX_NODES_IN_LEAF );
|
||||
// If the amount of nodes is about to exceed the number of leafs, Then split the nodes
|
||||
if ( objects.size() + 1 > MAX_NODES_IN_LEAF )
|
||||
{
|
||||
split();
|
||||
auto* node { this->addGameObject( std::move( obj ) ) };
|
||||
return node;
|
||||
}
|
||||
|
||||
const bool should_recalc_bounds { obj.hasComponent< ModelComponent >() };
|
||||
|
||||
objects.emplace_back( std::move( obj ) );
|
||||
|
||||
if ( should_recalc_bounds ) recalculateLeafBounds();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if ( std::holds_alternative< OctTreeNodeArray >( m_node_data ) )
|
||||
{
|
||||
auto* node { ( *this )[ obj.getPosition() ].addGameObject( std::forward< GameObject >( obj ) ) };
|
||||
return node;
|
||||
}
|
||||
|
||||
FGL_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool OctTreeNode::isLeaf() const
|
||||
{
|
||||
return std::holds_alternative< OctTreeNodeLeaf >( m_node_data );
|
||||
}
|
||||
|
||||
bool OctTreeNode::isBranch() const
|
||||
{
|
||||
return std::holds_alternative< OctTreeNodeArray >( m_node_data );
|
||||
}
|
||||
|
||||
std::size_t OctTreeNode::itemCount() const
|
||||
{
|
||||
//TODO: Store this value in the nodes itself
|
||||
if ( !isLeaf() )
|
||||
{
|
||||
std::size_t sum { 0 };
|
||||
|
||||
FOR_EACH_OCTTREE_NODE
|
||||
{
|
||||
sum += std::get< OctTreeNodeArray >( m_node_data )[ x ][ y ][ z ]->itemCount();
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
return std::get< OctTreeNodeLeaf >( m_node_data ).size();
|
||||
}
|
||||
|
||||
const OctTreeNodeArray& OctTreeNode::getBranches() const
|
||||
{
|
||||
return std::get< OctTreeNodeArray >( m_node_data );
|
||||
}
|
||||
|
||||
const OctTreeNodeLeaf& OctTreeNode::getLeaf() const
|
||||
{
|
||||
return std::get< OctTreeNodeLeaf >( m_node_data );
|
||||
}
|
||||
|
||||
OctTreeNodeLeaf& OctTreeNode::getLeaf()
|
||||
{
|
||||
return std::get< OctTreeNodeLeaf >( m_node_data );
|
||||
}
|
||||
|
||||
bool OctTreeNode::isInFrustum( const Frustum& frustum ) const
|
||||
{
|
||||
#if ENABLE_IMGUI
|
||||
if ( !isEmpty() && intersects( frustum, m_fit_bounding_box ) )
|
||||
{
|
||||
if ( isLeaf() && itemCount() > 0 ) [[unlikely]]
|
||||
{
|
||||
if ( draw_leaf_fit_bounds ) [[unlikely]]
|
||||
debug::drawBoundingBox( m_fit_bounding_box );
|
||||
if ( draw_leaf_real_bounds ) [[unlikely]]
|
||||
debug::drawBoundingBox( m_bounds );
|
||||
if ( draw_model_bounding_boxes ) [[unlikely]]
|
||||
{
|
||||
for ( const auto& obj : getLeaf() )
|
||||
{
|
||||
const Matrix< MatrixType::ModelToWorld > obj_transform { obj.getTransform().mat() };
|
||||
|
||||
for ( const auto* model : obj.getComponents< ModelComponent >() )
|
||||
{
|
||||
const auto model_bounds { ( *model )->getBoundingBox() };
|
||||
const Matrix< MatrixType::ModelToWorld > model_transform { model->m_transform.mat() };
|
||||
|
||||
const auto combined_transform { obj_transform * model_transform };
|
||||
|
||||
debug::drawBoundingBox( combined_transform * model_transform );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
#else
|
||||
return !isEmpty() && intersects( frustum, m_fit_bounding_box );
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OctTreeNode::isEmpty() const
|
||||
{
|
||||
return std::holds_alternative< OctTreeNodeLeaf >( m_node_data )
|
||||
&& std::get< OctTreeNodeLeaf >( m_node_data ).empty();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Returns true if the fit bounding box is larger than the virtual bounds
|
||||
*/
|
||||
void OctTreeNode::recalculateBounds()
|
||||
{
|
||||
if ( isBranch() ) [[likely]]
|
||||
{
|
||||
recalculateNodeBounds();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
FGL_ASSERT( isLeaf(), "Expected leaf, Got whatever the fuck this is instead" );
|
||||
recalculateLeafBounds();
|
||||
return;
|
||||
}
|
||||
|
||||
FGL_UNREACHABLE();
|
||||
}
|
||||
|
||||
std::vector< OctTreeNodeLeaf* > OctTreeNode::getAllLeafs()
|
||||
{
|
||||
ZoneScoped;
|
||||
std::vector< OctTreeNodeLeaf* > leafs {};
|
||||
leafs.reserve( LEAF_RESERVE_SIZE );
|
||||
this->getAllLeafs( leafs );
|
||||
return leafs;
|
||||
}
|
||||
|
||||
std::vector< OctTreeNodeLeaf* > OctTreeNode::getAllLeafsInFrustum( const Frustum& frustum )
|
||||
{
|
||||
ZoneScoped;
|
||||
std::vector< OctTreeNodeLeaf* > leafs {};
|
||||
leafs.reserve( LEAF_RESERVE_SIZE );
|
||||
this->getAllLeafsInFrustum( frustum, leafs );
|
||||
return leafs;
|
||||
}
|
||||
|
||||
void OctTreeNode::clear()
|
||||
{
|
||||
if ( std::holds_alternative< OctTreeNodeLeaf >( this->m_node_data ) )
|
||||
{
|
||||
std::get< OctTreeNodeLeaf >( this->m_node_data ).clear();
|
||||
}
|
||||
else if ( std::holds_alternative< OctTreeNodeArray >( this->m_node_data ) )
|
||||
{
|
||||
const auto& node_array { std::get< OctTreeNodeArray >( this->m_node_data ) };
|
||||
|
||||
FOR_EACH_OCTTREE_NODE
|
||||
{
|
||||
node_array[ x ][ y ][ z ]->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WorldCoordinate OctTreeNode::getCenter() const
|
||||
{
|
||||
return m_bounds.getPosition();
|
||||
}
|
||||
|
||||
WorldCoordinate OctTreeNode::getFitCenter() const
|
||||
{
|
||||
return m_fit_bounding_box.getPosition();
|
||||
}
|
||||
|
||||
void OctTreeNode::drawDebug() const
|
||||
{}
|
||||
|
||||
void OctTreeNode::recalculateChildBounds()
|
||||
{
|
||||
if ( isBranch() )
|
||||
{
|
||||
FOR_EACH_OCTTREE_NODE
|
||||
{
|
||||
auto& node { std::get< OctTreeNodeArray >( m_node_data )[ x ][ y ][ z ] };
|
||||
node->recalculateChildBounds();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
recalculateBounds();
|
||||
}
|
||||
}
|
||||
|
||||
OctTreeNode* OctTreeNode::findID( const GameObject::GameObjectID id )
|
||||
{
|
||||
ZoneScoped;
|
||||
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< OctTreeNodeLeaf >( m_node_data ) };
|
||||
|
||||
if ( std::ranges::
|
||||
find_if( game_objects, [ id ]( const GameObject& obj ) noexcept { return obj.getId() == id; } )
|
||||
!= game_objects.end() )
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( std::holds_alternative< OctTreeNodeArray >( this->m_node_data ) )
|
||||
{
|
||||
if ( m_skip != nullptr ) return m_skip->findID( id );
|
||||
|
||||
const auto& node_array { std::get< OctTreeNodeArray >( this->m_node_data ) };
|
||||
|
||||
FOR_EACH_OCTTREE_NODE
|
||||
{
|
||||
const auto& node { node_array[ x ][ y ][ z ]->findID( id ) };
|
||||
if ( node != nullptr ) return node;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FGL_UNREACHABLE();
|
||||
}
|
||||
|
||||
auto OctTreeNode::getGameObjectItter( const GameObject::GameObjectID id )
|
||||
{
|
||||
assert( std::holds_alternative< OctTreeNodeLeaf >( this->m_node_data ) );
|
||||
auto& game_objects { std::get< OctTreeNodeLeaf >( this->m_node_data ) };
|
||||
return std::ranges::
|
||||
find_if( game_objects, [ id ]( const GameObject& obj ) noexcept { return id == obj.getId(); } );
|
||||
}
|
||||
|
||||
bool OctTreeNode::canContain( const GameObject& obj ) const
|
||||
{
|
||||
return canContain( obj.getTransform().translation );
|
||||
}
|
||||
|
||||
bool OctTreeNode::canContain( const WorldCoordinate& coord ) const
|
||||
{
|
||||
const auto center { this->getCenter() };
|
||||
// top right forward
|
||||
const auto high_center { center.vec() + glm::vec3( this->m_bounds.scale() ) };
|
||||
// bottom left back
|
||||
const auto low_center { center.vec() - glm::vec3( this->m_bounds.scale() ) };
|
||||
|
||||
const bool under_high_center { glm::all( glm::lessThanEqual( coord.vec(), high_center ) ) };
|
||||
const bool above_low_center { glm::all( glm::greaterThan( coord.vec(), low_center ) ) };
|
||||
|
||||
return under_high_center && above_low_center;
|
||||
}
|
||||
|
||||
GameObject OctTreeNode::extract( const GameObject::GameObjectID id )
|
||||
{
|
||||
const auto itter { getGameObjectItter( id ) };
|
||||
auto game_object { std::move( *itter ) };
|
||||
auto& game_objects { std::get< OctTreeNodeLeaf >( this->m_node_data ) };
|
||||
game_objects.erase( itter );
|
||||
return game_object;
|
||||
}
|
||||
|
||||
OctTreeNode* OctTreeNode::getRoot()
|
||||
{
|
||||
if ( m_parent == nullptr ) return this;
|
||||
|
||||
return m_parent->getRoot();
|
||||
}
|
||||
|
||||
void OctTreeNode::getAllLeafs( std::vector< OctTreeNodeLeaf* >& out_leafs )
|
||||
{
|
||||
ZoneScoped;
|
||||
if ( std::holds_alternative< OctTreeNodeLeaf >( m_node_data ) )
|
||||
{
|
||||
auto& leaf { std::get< OctTreeNodeLeaf >( m_node_data ) };
|
||||
//No point in us giving back an empty leaf
|
||||
if ( !leaf.empty() ) out_leafs.emplace_back( &leaf );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_skip != nullptr )
|
||||
{
|
||||
m_skip->getAllLeafs( out_leafs );
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& nodes { std::get< OctTreeNodeArray >( m_node_data ) };
|
||||
|
||||
// If we have a node to skip to, Skip to it.
|
||||
|
||||
FOR_EACH_OCTTREE_NODE
|
||||
{
|
||||
auto ret { nodes[ x ][ y ][ z ]->getAllLeafs() };
|
||||
out_leafs.insert( out_leafs.end(), ret.begin(), ret.end() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OctTreeNode* OctTreeNode::optimizePath()
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
// The node returned here will be the optimal node to jump to. If we return nullptr then that means this node has multiple paths it can take.
|
||||
|
||||
if ( std::holds_alternative< NodeDataT >( m_node_data ) )
|
||||
{
|
||||
const auto& nodes { std::get< NodeDataT >( m_node_data ) };
|
||||
|
||||
OctTreeNode* optimal { nullptr };
|
||||
|
||||
FOR_EACH_OCTTREE_NODE
|
||||
{
|
||||
// Why did I skip the 0,0,0 node previously?
|
||||
// if ( x == 0 && y == 0 && z == 0 ) continue;
|
||||
const auto& node { nodes[ x ][ y ][ z ] };
|
||||
|
||||
if ( auto* optimal_ret = node->optimizePath(); optimal_ret != nullptr )
|
||||
{
|
||||
// If the node returns nullptr, Then it means that this node shouldn't jump to any node directly.
|
||||
if ( optimal != nullptr )
|
||||
{
|
||||
m_skip = nullptr;
|
||||
return this;
|
||||
}
|
||||
|
||||
optimal = optimal_ret;
|
||||
}
|
||||
}
|
||||
|
||||
m_skip = optimal;
|
||||
return optimal;
|
||||
}
|
||||
|
||||
if ( std::holds_alternative< LeafDataT >( m_node_data ) )
|
||||
{
|
||||
const auto& leaf_data { std::get< LeafDataT >( m_node_data ) };
|
||||
|
||||
if ( leaf_data.empty() ) return nullptr;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
FGL_UNREACHABLE();
|
||||
}
|
||||
|
||||
std::size_t OctTreeNode::reorganize()
|
||||
{
|
||||
ZoneScoped;
|
||||
std::size_t counter { 0 };
|
||||
if ( std::holds_alternative< NodeDataT >( m_node_data ) )
|
||||
{
|
||||
const auto& nodes { std::get< NodeDataT >( m_node_data ) };
|
||||
|
||||
FOR_EACH_OCTTREE_NODE
|
||||
{
|
||||
// Why did I skip the 0,0,0 node previously?
|
||||
// if ( x == 0 && y == 0 && z == 0 ) continue;
|
||||
const auto& node { nodes[ x ][ y ][ z ] };
|
||||
counter += node->reorganize();
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
if ( std::holds_alternative< LeafDataT >( m_node_data ) )
|
||||
{
|
||||
//Check if any of the nodes in this group need to be moved.
|
||||
|
||||
for ( auto& game_objects = std::get< LeafDataT >( m_node_data ); const auto& game_object : game_objects )
|
||||
{
|
||||
if ( !this->canContain( game_object ) )
|
||||
{
|
||||
++counter;
|
||||
//Need to move this game object.
|
||||
auto moved_game_object { this->extract( game_object ) };
|
||||
|
||||
//Insert at root
|
||||
//TODO: See if we can optimize this by traveling UP the tree.
|
||||
getRoot()->addGameObject( std::move( moved_game_object ) );
|
||||
}
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
FGL_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool OctTreeNode::isBoundsExpanded() const
|
||||
{
|
||||
return m_fit_bounding_box == m_bounds;
|
||||
/*
|
||||
const auto fit_points { m_fit_bounding_box.points() };
|
||||
for ( const auto& p : fit_points )
|
||||
{
|
||||
// Return true if a point is outside the bounds. This indicates that out bounding box is bigger than our bounds.
|
||||
if ( !m_bounds.contains( p ) ) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
*/
|
||||
}
|
||||
|
||||
void OctTreeNode::recalculateNodeBounds()
|
||||
{
|
||||
FGL_ASSERT( std::holds_alternative< NodeDataT >( m_node_data ), "Node data was not an array!" );
|
||||
const auto& nodes { std::get< NodeDataT >( m_node_data ) };
|
||||
|
||||
// We start out by telling all of our children to recalculate their bounds
|
||||
m_fit_bounding_box = static_cast< AxisAlignedBoundingBox< CoordinateSpace::World > >( m_bounds );
|
||||
|
||||
FOR_EACH_OCTTREE_NODE
|
||||
{
|
||||
// If true then the bounds were bigger then the inital bounding box. So we should try to combine it without current bounding box.
|
||||
m_fit_bounding_box = m_fit_bounding_box.combine( nodes[ x ][ y ][ z ]->m_fit_bounding_box );
|
||||
}
|
||||
|
||||
// if ( isBoundsExpanded() && m_parent )
|
||||
if ( m_parent != nullptr ) m_parent->recalculateBounds();
|
||||
}
|
||||
|
||||
void OctTreeNode::recalculateLeafBounds()
|
||||
{
|
||||
FGL_ASSERT( std::holds_alternative< LeafDataT >( m_node_data ), "Node data was not a leaf!" );
|
||||
const auto& data { std::get< LeafDataT >( m_node_data ) };
|
||||
|
||||
m_fit_bounding_box = static_cast< AxisAlignedBoundingBox< CoordinateSpace::World > >( m_bounds );
|
||||
|
||||
if ( data.empty() ) return;
|
||||
|
||||
// If true, Then the fit has already been set, and we should combine with it
|
||||
bool fit_set { false };
|
||||
|
||||
for ( const auto& game_object : data )
|
||||
{
|
||||
const Matrix< MatrixType::ModelToWorld > game_object_transform { game_object.getTransform().mat() };
|
||||
|
||||
for ( const ModelComponent* model : game_object.getComponents< ModelComponent >() )
|
||||
{
|
||||
const OrientedBoundingBox< CS::Model > model_bounding_box { ( *model )->getBoundingBox() };
|
||||
|
||||
const Matrix< MatrixType::ModelToWorld > model_transform { model->m_transform.mat() };
|
||||
|
||||
// Combine the game object and model transform
|
||||
const Matrix< MatrixType::ModelToWorld > combined_transform { model_transform * game_object_transform };
|
||||
|
||||
const OrientedBoundingBox< CoordinateSpace::World > world_bounding_box { combined_transform
|
||||
* model_bounding_box };
|
||||
|
||||
const auto aligned_bounding_box { world_bounding_box.alignToWorld() };
|
||||
|
||||
if ( fit_set ) [[likely]]
|
||||
m_fit_bounding_box = m_fit_bounding_box.combine( aligned_bounding_box );
|
||||
else
|
||||
{
|
||||
m_fit_bounding_box = aligned_bounding_box;
|
||||
fit_set = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Have our parent recalculate its bounds
|
||||
// if ( isBoundsExpanded() && m_parent )
|
||||
if ( m_parent != nullptr ) m_parent->recalculateBounds();
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,147 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 3/1/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "engine/primitives/boxes/AxisAlignedBoundingCube.hpp"
|
||||
#include "gameobjects/GameObject.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
constexpr std::size_t MAX_NODES_IN_LEAF { 32 };
|
||||
constexpr std::size_t STARTING_DEPTH { 1 };
|
||||
constexpr float ROOT_SPAN { std::numeric_limits< float >::max() };
|
||||
|
||||
struct Frustum;
|
||||
|
||||
constexpr std::uint8_t TOP { 1 };
|
||||
constexpr std::uint8_t BOTTOM { 0 };
|
||||
|
||||
constexpr std::uint8_t RIGHT { 1 };
|
||||
constexpr std::uint8_t LEFT { 0 };
|
||||
|
||||
constexpr std::uint8_t FORWARD { 1 };
|
||||
constexpr std::uint8_t BACK { 0 };
|
||||
|
||||
class OctTreeNode;
|
||||
class GameObject;
|
||||
|
||||
using OctTreeNodeArray = std::array< std::array< std::array< std::unique_ptr< OctTreeNode >, 2 >, 2 >, 2 >;
|
||||
using OctTreeNodeLeaf = std::vector< GameObject >;
|
||||
|
||||
static_assert( sizeof( OctTreeNodeArray ) == sizeof( OctTreeNode* ) * 2 * 2 * 2 );
|
||||
static_assert( sizeof( OctTreeNode* ) == sizeof( std::uint64_t ) );
|
||||
|
||||
struct FrameInfo;
|
||||
|
||||
void imGuiOctTreeSettings( const FrameInfo& info );
|
||||
|
||||
class OctTreeNode
|
||||
{
|
||||
//! Fit to each model
|
||||
AxisAlignedBoundingBox< CoordinateSpace::World > m_fit_bounding_box;
|
||||
|
||||
//! Real bounds of the node
|
||||
AxisAlignedBoundingCube< CoordinateSpace::World > m_bounds;
|
||||
|
||||
using NodeDataT = OctTreeNodeArray;
|
||||
using LeafDataT = OctTreeNodeLeaf;
|
||||
|
||||
std::variant< NodeDataT, LeafDataT > m_node_data;
|
||||
|
||||
OctTreeNode* m_parent;
|
||||
|
||||
//! Node to skip too when attempting to parse this node.
|
||||
//! This is done in order to prevent navigating through an empty set of nodes.
|
||||
//! This is only set if there is only one leaf filled with data.
|
||||
OctTreeNode* m_skip { nullptr };
|
||||
|
||||
public:
|
||||
|
||||
OctTreeNode() = delete;
|
||||
OctTreeNode( WorldCoordinate center, float span = ROOT_SPAN, OctTreeNode* parent = nullptr );
|
||||
|
||||
OctTreeNode( const OctTreeNode& other ) = delete;
|
||||
OctTreeNode( OctTreeNode&& other ) = delete;
|
||||
|
||||
~OctTreeNode();
|
||||
|
||||
OctTreeNode& operator=( const OctTreeNode& ) = delete;
|
||||
OctTreeNode& operator=( OctTreeNode&& ) = delete;
|
||||
void clear();
|
||||
WorldCoordinate getCenter() const;
|
||||
WorldCoordinate getFitCenter() const;
|
||||
void drawDebug() const;
|
||||
void recalculateChildBounds();
|
||||
|
||||
private:
|
||||
|
||||
//! Returns the node of a given ID (Searches down)
|
||||
OctTreeNode* findID( GameObject::GameObjectID id );
|
||||
|
||||
//! Returns true if the node contains a given ID
|
||||
bool contains( const GameObject::GameObjectID id ) { return findID( id ) != nullptr; }
|
||||
|
||||
//! Splits a node. Does nothing if node is not a leaf.
|
||||
void split( int depth = 1 );
|
||||
|
||||
OctTreeNode* getRoot();
|
||||
|
||||
//! returns true if this node should contain the given object
|
||||
bool canContain( const GameObject& obj ) const;
|
||||
bool canContain( const WorldCoordinate& coord ) const;
|
||||
|
||||
GameObject extract( GameObject::GameObjectID id );
|
||||
|
||||
GameObject extract( const GameObject& obj ) { return this->extract( obj.getId() ); }
|
||||
|
||||
bool isInFrustum( const Frustum& frustum ) const;
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
auto getGameObjectItter( GameObject::GameObjectID id );
|
||||
|
||||
void getAllLeafs( std::vector< OctTreeNodeLeaf* >& out_leafs );
|
||||
void getAllLeafsInFrustum( const Frustum& frustum, std::vector< OctTreeNodeLeaf* >& out_leafs );
|
||||
|
||||
OctTreeNode& operator[]( const WorldCoordinate coord ) const;
|
||||
|
||||
public:
|
||||
|
||||
//! Rebuilds the tree checking if nodes have moved.
|
||||
std::size_t reorganize();
|
||||
OctTreeNode* optimizePath();
|
||||
|
||||
//! Returns true if the fixed bounding box is larger then the inital bounding box
|
||||
bool isBoundsExpanded() const;
|
||||
|
||||
void recalculateNodeBounds();
|
||||
|
||||
void recalculateLeafBounds();
|
||||
|
||||
void recalculateBounds();
|
||||
|
||||
constexpr static std::size_t LEAF_RESERVE_SIZE { 1024 };
|
||||
|
||||
[[nodiscard]] std::vector< OctTreeNodeLeaf* > getAllLeafs();
|
||||
|
||||
[[nodiscard]] std::vector< OctTreeNodeLeaf* > getAllLeafsInFrustum( const Frustum& frustum );
|
||||
|
||||
//! Adds a game object, Will split the node if the auto split threshold is reached
|
||||
OctTreeNode* addGameObject( GameObject&& obj );
|
||||
bool isLeaf() const;
|
||||
bool isBranch() const;
|
||||
|
||||
std::size_t itemCount() const;
|
||||
const OctTreeNodeArray& getBranches() const;
|
||||
const OctTreeNodeLeaf& getLeaf() const;
|
||||
OctTreeNodeLeaf& getLeaf();
|
||||
};
|
||||
|
||||
#define FOR_EACH_OCTTREE_NODE \
|
||||
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 )
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,77 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 3/11/24.
|
||||
//
|
||||
|
||||
#include "QuadTree.hpp"
|
||||
|
||||
#include "engine/gameobjects/GameObject.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() ) };
|
||||
|
||||
const 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 );
|
||||
|
||||
FGL_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
|
||||
@@ -1,45 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 3/11/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "engine/primitives/Scale.hpp"
|
||||
#include "engine/primitives/points/Coordinate.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class GameObject;
|
||||
|
||||
enum class CoordinateSpace;
|
||||
class QuadTreeNode;
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
struct Frustum;
|
||||
|
||||
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( WorldCoordinate coord ) const;
|
||||
|
||||
public:
|
||||
|
||||
QuadTreeNode& operator[]( WorldCoordinate pos );
|
||||
|
||||
void split( int depth = 1 );
|
||||
|
||||
void addGameObject( GameObject&& obj );
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
5
src/shaders/bounds/axisalignedbb.slang
Normal file
5
src/shaders/bounds/axisalignedbb.slang
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
struct AxisAlignedBoundingBox
|
||||
{
|
||||
|
||||
};
|
||||
19
src/shaders/bvh.slang
Normal file
19
src/shaders/bvh.slang
Normal file
@@ -0,0 +1,19 @@
|
||||
#version 450
|
||||
|
||||
typedef uint32_t BVHIndex;
|
||||
typedef uint8_t BVHFlags;
|
||||
|
||||
public struct BVHNode
|
||||
{
|
||||
BVHFlags m_flags;
|
||||
float3 m_centerpoint;
|
||||
float3 m_scale;
|
||||
float4 m_rotation;
|
||||
|
||||
BVHIndex next_hit;
|
||||
BVHIndex next_miss;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#version 450
|
||||
|
||||
import camera;
|
||||
import objects.camera;
|
||||
|
||||
struct Vertex {
|
||||
vec4 position : SV_Position;
|
||||
|
||||
87
src/shaders/culling.slang
Normal file
87
src/shaders/culling.slang
Normal file
@@ -0,0 +1,87 @@
|
||||
#version 450
|
||||
|
||||
import vk.drawindexedindirect;
|
||||
import bounds.axisalignedbb;
|
||||
import objects.frustum;
|
||||
import objects.gamemodel;
|
||||
|
||||
|
||||
|
||||
struct ModelInstance
|
||||
{
|
||||
uint32_t model_index;
|
||||
mat4x4 matrix;
|
||||
};
|
||||
|
||||
RWStructuredBuffer< vk::DrawIndexedIndirectCommand > commands : COMMANDS;
|
||||
RWStructuredBuffer< ModelInstance > instances : COMMAND_INSTANCES;
|
||||
|
||||
ConstantBuffer< AxisAlignedBoundingBox > bounding_boxes[] : BOUNDS;
|
||||
|
||||
ConstantBuffer< Frustum > frustum : FRUSTUM;
|
||||
|
||||
ConstantBuffer< ModelInstance > model_instances[] : MODEL_INSTANCES;
|
||||
ConstantBuffer< GameModel > models[] : MODELS;
|
||||
|
||||
//TODO: shader command constants
|
||||
struct PushConstants
|
||||
{
|
||||
// number of total models and their instances
|
||||
uint32_t draw_count;
|
||||
uint32_t start_idx;
|
||||
uint32_t end_idx;
|
||||
};
|
||||
|
||||
ConstantBuffer<PushConstants> pc;
|
||||
|
||||
|
||||
[[shader("compute")]]
|
||||
[numthreads(64,1,1)]
|
||||
void main( uint3 dispatch_id : SV_DispatchThreadID)
|
||||
{
|
||||
// this will be dispatched with 0..N instances, each thread will be 1 instance from `model_instances`
|
||||
uint instance_index = pc.start_idx + dispatch_id.x; // global thread
|
||||
|
||||
if ( dispatch_id.x > pc.end_idx ) return;
|
||||
if ( instance_index > pc.draw_count ) return;
|
||||
|
||||
ModelInstance model_instance = model_instances[ instance_index ];
|
||||
var model_index = model_instance.model_index;
|
||||
|
||||
GameModel model = models[ model_index ];
|
||||
|
||||
//TODO: Cull (For now we will just pretend it's all in view)
|
||||
|
||||
const bool in_view = true;
|
||||
|
||||
if ( in_view )
|
||||
{
|
||||
uint32_t old_instance_count = 0;
|
||||
InterlockedAdd( commands[ model_index ].instance_count, 1, old_instance_count );
|
||||
|
||||
var command = &commands[ model_index ];
|
||||
|
||||
// if the old instance count was zero, we are the first command and need to populate.
|
||||
if ( old_instance_count == 0 )
|
||||
{
|
||||
// first index, populate the command's data
|
||||
|
||||
//TODO: Populate from GameModel once i've actually implemented that class properly
|
||||
command->first_index = 0;
|
||||
command->index_count = 0;
|
||||
command->vertex_offset = 0;
|
||||
|
||||
atomicAdd( command->first_instance, instance_index );
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
var idx = command->first_instance + old_instance_count;
|
||||
|
||||
instances[ instance_index ] = model_instances[ idx ];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#version 450
|
||||
|
||||
import camera;
|
||||
import gbuffer;
|
||||
import objects.camera;
|
||||
import objects.gbuffer;
|
||||
|
||||
struct LineVertex
|
||||
{
|
||||
|
||||
8
src/shaders/objects/frustum.slang
Normal file
8
src/shaders/objects/frustum.slang
Normal file
@@ -0,0 +1,8 @@
|
||||
#version 450
|
||||
|
||||
|
||||
struct Frustum
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
9
src/shaders/objects/gamemodel.slang
Normal file
9
src/shaders/objects/gamemodel.slang
Normal file
@@ -0,0 +1,9 @@
|
||||
#version 450
|
||||
|
||||
struct GameModel {
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
11
src/shaders/objects/gameobject.slang
Normal file
11
src/shaders/objects/gameobject.slang
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 450
|
||||
|
||||
|
||||
|
||||
struct GameObject
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
15
src/shaders/shadowmap.slang
Normal file
15
src/shaders/shadowmap.slang
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
import model.vertex;
|
||||
|
||||
struct CoarseVertex {
|
||||
float4 position : SV_Position;
|
||||
}
|
||||
|
||||
[shader("vertex")]
|
||||
CoarseVertex vertexMain( ModelVertex in_vertex )
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#version 450
|
||||
|
||||
import model.vertex;
|
||||
import camera;
|
||||
import gbuffer;
|
||||
import objects.camera;
|
||||
import objects.gbuffer;
|
||||
import material;
|
||||
|
||||
struct CoarseVertex {
|
||||
@@ -37,7 +37,7 @@ CoarseVertex vertexMain( ModelVertex in_vertex )
|
||||
}
|
||||
|
||||
[[vk::binding(0,3)]]
|
||||
ParameterBlock<Material> materials[] : MATERIALS;
|
||||
ConstantBuffer<Material> materials[] : MATERIALS;
|
||||
|
||||
[[vk::binding(0,2)]]
|
||||
Sampler2D[ ] tex : TEXTURES;
|
||||
|
||||
15
src/shaders/vk/drawindexedindirect.slang
Normal file
15
src/shaders/vk/drawindexedindirect.slang
Normal file
@@ -0,0 +1,15 @@
|
||||
#version 450
|
||||
|
||||
namespace vk
|
||||
{
|
||||
|
||||
struct DrawIndexedIndirectCommand
|
||||
{
|
||||
uint32_t index_count;
|
||||
uint32_t instance_count;
|
||||
uint32_t first_index;
|
||||
int32_t vertex_offset;
|
||||
uint32_t first_instance;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
// Created by kj16609 on 2/18/25.
|
||||
//
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "primitives/Transform.hpp"
|
||||
|
||||
#include "primitives/TransformComponent.hpp"
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
TEST_CASE( "Transform", "[transform]" )
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user