diff --git a/.gitmodules b/.gitmodules index 9ea9554..1cba461 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b54f5a..0598029 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cmake_modules/dependencies/json.cmake b/cmake_modules/dependencies/json.cmake new file mode 100644 index 0000000..930c20a --- /dev/null +++ b/cmake_modules/dependencies/json.cmake @@ -0,0 +1 @@ +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dependencies/json) \ No newline at end of file diff --git a/dependencies/json b/dependencies/json new file mode 160000 index 0000000..f3dc468 --- /dev/null +++ b/dependencies/json @@ -0,0 +1 @@ +Subproject commit f3dc4684b40a124cabc8554967c2cd8db54f15dd diff --git a/src/assets/TestScene.bin b/src/assets/TestScene.bin new file mode 100644 index 0000000..a4a320a --- /dev/null +++ b/src/assets/TestScene.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0078130a926d059adf04f16f0b3da55578499b049b10c37fc9794f989c082195 +size 25396 diff --git a/src/assets/TestScene.gltf b/src/assets/TestScene.gltf new file mode 100644 index 0000000..bdd49a4 --- /dev/null +++ b/src/assets/TestScene.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35913861368ad2e89c5fc38734c04128ae8f14bd591747639dc1ef49ce6af27e +size 4707 diff --git a/src/assets/blender/2-91/blender-2-91.bin b/src/assets/blender/2-91/blender-2-91.bin new file mode 100644 index 0000000..db3b8cd --- /dev/null +++ b/src/assets/blender/2-91/blender-2-91.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:662896dcba7256db266ee010f77d02cb852f8c481432e0ebff98142124f0acbe +size 89174512 diff --git a/src/assets/blender/2-91/blender-2-91.gltf b/src/assets/blender/2-91/blender-2-91.gltf new file mode 100644 index 0000000..52fc850 --- /dev/null +++ b/src/assets/blender/2-91/blender-2-91.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbaca1a42bb6270dca102c0d5116e6d82e2d55490b76e2813a7cc7ed64af13eb +size 807703 diff --git a/src/assets/blender/2-91/textures/Flower_Color Image.png b/src/assets/blender/2-91/textures/Flower_Color Image.png new file mode 100644 index 0000000..2821623 --- /dev/null +++ b/src/assets/blender/2-91/textures/Flower_Color Image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4d4419e8de605e3bf1141d88ac98ae466b23afef4b2a2794e6d1b0fdb630cb4 +size 249618 diff --git a/src/editor/src/components/ModelComponent.cpp b/src/editor/src/components/ModelComponent.cpp index 1b32493..aca8246 100644 --- a/src/editor/src/components/ModelComponent.cpp +++ b/src/editor/src/components/ModelComponent.cpp @@ -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 ) diff --git a/src/editor/src/gui/core.cpp b/src/editor/src/gui/core.cpp index 339f492..9642d6c 100644 --- a/src/editor/src/gui/core.cpp +++ b/src/editor/src/gui/core.cpp @@ -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" diff --git a/src/editor/src/gui/drawGameObject.cpp b/src/editor/src/gui/drawGameObject.cpp index 9ad01c4..0898a3b 100644 --- a/src/editor/src/gui/drawGameObject.cpp +++ b/src/editor/src/gui/drawGameObject.cpp @@ -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 }; diff --git a/src/editor/src/gui/drawStats.cpp b/src/editor/src/gui/drawStats.cpp index 04b8c0f..14798c2 100644 --- a/src/editor/src/gui/drawStats.cpp +++ b/src/editor/src/gui/drawStats.cpp @@ -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(); diff --git a/src/editor/src/gui/preview.cpp b/src/editor/src/gui/preview.cpp index 12455be..ba551db 100644 --- a/src/editor/src/gui/preview.cpp +++ b/src/editor/src/gui/preview.cpp @@ -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 ) ); diff --git a/src/editor/src/main.cpp b/src/editor/src/main.cpp index 6bf8a4b..4de2634 100644 --- a/src/editor/src/main.cpp +++ b/src/editor/src/main.cpp @@ -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 diff --git a/src/engine/EngineContext.cpp b/src/engine/EngineContext.cpp index fa51dd8..f3b2cae 100644 --- a/src/engine/EngineContext.cpp +++ b/src/engine/EngineContext.cpp @@ -11,7 +11,6 @@ #include #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() }; diff --git a/src/engine/EngineContext.hpp b/src/engine/EngineContext.hpp index 7bf539d..d8b480b 100644 --- a/src/engine/EngineContext.hpp +++ b/src/engine/EngineContext.hpp @@ -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(); diff --git a/src/engine/FrameInfo.hpp b/src/engine/FrameInfo.hpp index 3bc42a2..2f6f352 100644 --- a/src/engine/FrameInfo.hpp +++ b/src/engine/FrameInfo.hpp @@ -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; diff --git a/src/engine/KeyboardMovementController.cpp b/src/engine/KeyboardMovementController.cpp index 30449fe..c4a53e1 100644 --- a/src/engine/KeyboardMovementController.cpp +++ b/src/engine/KeyboardMovementController.cpp @@ -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 \ No newline at end of file diff --git a/src/engine/assets/AssetManager.hpp b/src/engine/assets/AssetManager.hpp index 1d6140b..b0c771d 100644 --- a/src/engine/assets/AssetManager.hpp +++ b/src/engine/assets/AssetManager.hpp @@ -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; } diff --git a/src/engine/assets/model/InstanceManager.cpp b/src/engine/assets/model/InstanceManager.cpp new file mode 100644 index 0000000..3355515 --- /dev/null +++ b/src/engine/assets/model/InstanceManager.cpp @@ -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 \ No newline at end of file diff --git a/src/engine/assets/model/InstanceManager.hpp b/src/engine/assets/model/InstanceManager.hpp new file mode 100644 index 0000000..6f10b8b --- /dev/null +++ b/src/engine/assets/model/InstanceManager.hpp @@ -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 \ No newline at end of file diff --git a/src/engine/assets/model/Model.cpp b/src/engine/assets/model/Model.cpp index 479e39f..eb8f2ce 100644 --- a/src/engine/assets/model/Model.cpp +++ b/src/engine/assets/model/Model.cpp @@ -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 diff --git a/src/engine/assets/model/Model.hpp b/src/engine/assets/model/Model.hpp index 42f5da4..6feac70 100644 --- a/src/engine/assets/model/Model.hpp +++ b/src/engine/assets/model/Model.hpp @@ -15,11 +15,16 @@ #include #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( diff --git a/src/engine/assets/model/ModelInstance.cpp b/src/engine/assets/model/ModelInstance.cpp new file mode 100644 index 0000000..b5f8c46 --- /dev/null +++ b/src/engine/assets/model/ModelInstance.cpp @@ -0,0 +1,8 @@ +// +// Created by kj16609 on 3/17/25. +// +#include "ModelInstance.hpp" + +namespace fgl::engine +{ +} // namespace fgl::engine \ No newline at end of file diff --git a/src/engine/assets/model/ModelInstance.hpp b/src/engine/assets/model/ModelInstance.hpp new file mode 100644 index 0000000..3a9abb4 --- /dev/null +++ b/src/engine/assets/model/ModelInstance.hpp @@ -0,0 +1,64 @@ +// +// Created by kj16609 on 3/17/25. +// +#pragma once +#include + +#include + +#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 \ No newline at end of file diff --git a/src/engine/assets/model/ModelManager.cpp b/src/engine/assets/model/ModelManager.cpp new file mode 100644 index 0000000..1456e71 --- /dev/null +++ b/src/engine/assets/model/ModelManager.cpp @@ -0,0 +1,10 @@ +// +// Created by kj16609 on 3/12/25. +// +#include "ModelManager.hpp" + + +namespace fgl::engine +{ + +} diff --git a/src/engine/assets/model/ModelManager.hpp b/src/engine/assets/model/ModelManager.hpp new file mode 100644 index 0000000..6d701fe --- /dev/null +++ b/src/engine/assets/model/ModelManager.hpp @@ -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 \ No newline at end of file diff --git a/src/engine/assets/model/builders/ModelBuilder.hpp b/src/engine/assets/model/builders/ModelBuilder.hpp index 1363f27..dd1361c 100644 --- a/src/engine/assets/model/builders/ModelBuilder.hpp +++ b/src/engine/assets/model/builders/ModelBuilder.hpp @@ -7,7 +7,7 @@ #include #include -#include "engine/primitives/TransformComponent.hpp" +#include "engine/primitives/Transform.hpp" namespace fgl::engine { diff --git a/src/engine/assets/model/builders/SceneBuilder.cpp b/src/engine/assets/model/builders/SceneBuilder.cpp index 4d79415..26acdf1 100644 --- a/src/engine/assets/model/builders/SceneBuilder.cpp +++ b/src/engine/assets/model/builders/SceneBuilder.cpp @@ -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 ); diff --git a/src/engine/assets/model/builders/SceneBuilder.hpp b/src/engine/assets/model/builders/SceneBuilder.hpp index 16dc5e0..6929fbd 100644 --- a/src/engine/assets/model/builders/SceneBuilder.hpp +++ b/src/engine/assets/model/builders/SceneBuilder.hpp @@ -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 { diff --git a/src/engine/assets/texture/Texture.cpp b/src/engine/assets/texture/Texture.cpp index 2e3ae4f..925a1f9 100644 --- a/src/engine/assets/texture/Texture.cpp +++ b/src/engine/assets/texture/Texture.cpp @@ -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 ); diff --git a/src/engine/assets/texture/Texture.hpp b/src/engine/assets/texture/Texture.hpp index bf7b19e..5076a53 100644 --- a/src/engine/assets/texture/Texture.hpp +++ b/src/engine/assets/texture/Texture.hpp @@ -91,6 +91,7 @@ namespace fgl::engine Texture() = delete; ~Texture(); + Texture( const std::shared_ptr& 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; diff --git a/src/engine/camera/Camera.hpp b/src/engine/camera/Camera.hpp index 76d3461..cf79d03 100644 --- a/src/engine/camera/Camera.hpp +++ b/src/engine/camera/Camera.hpp @@ -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 diff --git a/src/engine/camera/CompositeSwapchain.cpp b/src/engine/camera/CompositeSwapchain.cpp index cbd12e6..0c0327d 100644 --- a/src/engine/camera/CompositeSwapchain.cpp +++ b/src/engine/camera/CompositeSwapchain.cpp @@ -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 ) ); } } diff --git a/src/engine/gameobjects/GameObject.cpp b/src/engine/gameobjects/GameObject.cpp index 7ad1ae3..27c7b7b 100644 --- a/src/engine/gameobjects/GameObject.cpp +++ b/src/engine/gameobjects/GameObject.cpp @@ -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; diff --git a/src/engine/gameobjects/GameObject.hpp b/src/engine/gameobjects/GameObject.hpp index 25eb0aa..c5c3d9c 100644 --- a/src/engine/gameobjects/GameObject.hpp +++ b/src/engine/gameobjects/GameObject.hpp @@ -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(); diff --git a/src/engine/gameobjects/components/ComponentIDS.hpp b/src/engine/gameobjects/components/ComponentIDS.hpp index cc25525..dad8b55 100644 --- a/src/engine/gameobjects/components/ComponentIDS.hpp +++ b/src/engine/gameobjects/components/ComponentIDS.hpp @@ -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 ); diff --git a/src/engine/gameobjects/components/ModelComponent.cpp b/src/engine/gameobjects/components/ModelComponent.cpp new file mode 100644 index 0000000..9619a1e --- /dev/null +++ b/src/engine/gameobjects/components/ModelComponent.cpp @@ -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 diff --git a/src/engine/gameobjects/components/ModelComponent.hpp b/src/engine/gameobjects/components/ModelComponent.hpp index 859dc6e..7ac2fe8 100644 --- a/src/engine/gameobjects/components/ModelComponent.hpp +++ b/src/engine/gameobjects/components/ModelComponent.hpp @@ -6,20 +6,28 @@ #include #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 diff --git a/src/engine/gameobjects/components/TransformComponent.hpp b/src/engine/gameobjects/components/TransformComponent.hpp new file mode 100644 index 0000000..29b06bf --- /dev/null +++ b/src/engine/gameobjects/components/TransformComponent.hpp @@ -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 diff --git a/src/engine/gameobjects/components/interface/GameObjectComponent.hpp b/src/engine/gameobjects/components/interface/GameObjectComponent.hpp index 3ad435a..7c4c379 100644 --- a/src/engine/gameobjects/components/interface/GameObjectComponent.hpp +++ b/src/engine/gameobjects/components/interface/GameObjectComponent.hpp @@ -7,14 +7,14 @@ #include #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; } }; diff --git a/src/engine/lighting/lights/GlobalIlluminator.cpp b/src/engine/lighting/lights/GlobalIlluminator.cpp new file mode 100644 index 0000000..714e8a8 --- /dev/null +++ b/src/engine/lighting/lights/GlobalIlluminator.cpp @@ -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 \ No newline at end of file diff --git a/src/engine/lighting/lights/GlobalIlluminator.hpp b/src/engine/lighting/lights/GlobalIlluminator.hpp new file mode 100644 index 0000000..fd8b196 --- /dev/null +++ b/src/engine/lighting/lights/GlobalIlluminator.hpp @@ -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; +} \ No newline at end of file diff --git a/src/engine/lighting/shadows/ShadowMap.cpp b/src/engine/lighting/shadows/ShadowMap.cpp new file mode 100644 index 0000000..6e828da --- /dev/null +++ b/src/engine/lighting/shadows/ShadowMap.cpp @@ -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 \ No newline at end of file diff --git a/src/engine/lighting/shadows/ShadowMap.hpp b/src/engine/lighting/shadows/ShadowMap.hpp new file mode 100644 index 0000000..61b4054 --- /dev/null +++ b/src/engine/lighting/shadows/ShadowMap.hpp @@ -0,0 +1,47 @@ +// +// Created by kj16609 on 2/17/25. +// +#pragma once +#include + +#include + +#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; +} \ No newline at end of file diff --git a/src/engine/memory/buffers/vector/BufferVector.cpp b/src/engine/memory/buffers/vector/BufferVector.cpp index f88445b..0197bcc 100644 --- a/src/engine/memory/buffers/vector/BufferVector.cpp +++ b/src/engine/memory/buffers/vector/BufferVector.cpp @@ -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 \ No newline at end of file diff --git a/src/engine/memory/buffers/vector/BufferVector.hpp b/src/engine/memory/buffers/vector/BufferVector.hpp index 22f0cb0..7a7e47d 100644 --- a/src/engine/memory/buffers/vector/BufferVector.hpp +++ b/src/engine/memory/buffers/vector/BufferVector.hpp @@ -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 diff --git a/src/engine/memory/buffers/vector/DeviceVector.hpp b/src/engine/memory/buffers/vector/DeviceVector.hpp index f1ad2e0..c0dede5 100644 --- a/src/engine/memory/buffers/vector/DeviceVector.hpp +++ b/src/engine/memory/buffers/vector/DeviceVector.hpp @@ -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 ) { diff --git a/src/engine/memory/buffers/vector/IndexedVector.hpp b/src/engine/memory/buffers/vector/IndexedVector.hpp new file mode 100644 index 0000000..b916a4d --- /dev/null +++ b/src/engine/memory/buffers/vector/IndexedVector.hpp @@ -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 \ No newline at end of file diff --git a/src/engine/primitives/TransformComponent.cpp b/src/engine/primitives/Transform.cpp similarity index 65% rename from src/engine/primitives/TransformComponent.cpp rename to src/engine/primitives/Transform.cpp index 4bb2792..3747434 100644 --- a/src/engine/primitives/TransformComponent.cpp +++ b/src/engine/primitives/Transform.cpp @@ -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 diff --git a/src/engine/primitives/TransformComponent.hpp b/src/engine/primitives/Transform.hpp similarity index 86% rename from src/engine/primitives/TransformComponent.hpp rename to src/engine/primitives/Transform.hpp index c5204a5..43c38eb 100644 --- a/src/engine/primitives/TransformComponent.hpp +++ b/src/engine/primitives/Transform.hpp @@ -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 \ No newline at end of file diff --git a/src/engine/primitives/boxes/OrientedBoundingBox.hpp b/src/engine/primitives/boxes/OrientedBoundingBox.hpp index 3b672d7..493fb5b 100644 --- a/src/engine/primitives/boxes/OrientedBoundingBox.hpp +++ b/src/engine/primitives/boxes/OrientedBoundingBox.hpp @@ -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" ); diff --git a/src/engine/primitives/planes/PointPlane.cpp b/src/engine/primitives/planes/PointPlane.cpp index 35b0c45..67332e7 100644 --- a/src/engine/primitives/planes/PointPlane.cpp +++ b/src/engine/primitives/planes/PointPlane.cpp @@ -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 >; diff --git a/src/engine/primitives/planes/PointPlane.hpp b/src/engine/primitives/planes/PointPlane.hpp index 69125a7..1379a80 100644 --- a/src/engine/primitives/planes/PointPlane.hpp +++ b/src/engine/primitives/planes/PointPlane.hpp @@ -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 >; diff --git a/src/engine/primitives/vectors/NormalVector.hpp b/src/engine/primitives/vectors/NormalVector.hpp index 0452c53..eae42d6 100644 --- a/src/engine/primitives/vectors/NormalVector.hpp +++ b/src/engine/primitives/vectors/NormalVector.hpp @@ -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 diff --git a/src/engine/rendering/pipelines/v2/PipelineBuilder.cpp b/src/engine/rendering/pipelines/v2/PipelineBuilder.cpp index ab1f48e..a919306 100644 --- a/src/engine/rendering/pipelines/v2/PipelineBuilder.cpp +++ b/src/engine/rendering/pipelines/v2/PipelineBuilder.cpp @@ -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 }; diff --git a/src/engine/rendering/pipelines/v2/PipelineBuilder.hpp b/src/engine/rendering/pipelines/v2/PipelineBuilder.hpp index a2cb5b1..24fe019 100644 --- a/src/engine/rendering/pipelines/v2/PipelineBuilder.hpp +++ b/src/engine/rendering/pipelines/v2/PipelineBuilder.hpp @@ -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(); diff --git a/src/engine/scene/Scene.cpp b/src/engine/scene/Scene.cpp new file mode 100644 index 0000000..a8b463e --- /dev/null +++ b/src/engine/scene/Scene.cpp @@ -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 \ No newline at end of file diff --git a/src/engine/scene/Scene.hpp b/src/engine/scene/Scene.hpp new file mode 100644 index 0000000..2e6172f --- /dev/null +++ b/src/engine/scene/Scene.hpp @@ -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 \ No newline at end of file diff --git a/src/engine/scene/World.cpp b/src/engine/scene/World.cpp new file mode 100644 index 0000000..d99cd14 --- /dev/null +++ b/src/engine/scene/World.cpp @@ -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 diff --git a/src/engine/scene/World.hpp b/src/engine/scene/World.hpp new file mode 100644 index 0000000..be773d2 --- /dev/null +++ b/src/engine/scene/World.hpp @@ -0,0 +1,39 @@ +// +// Created by kj16609 on 3/2/25. +// +#pragma once + +#include + +#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 \ No newline at end of file diff --git a/src/engine/systems/prerender/CullingSystem.cpp b/src/engine/systems/prerender/CullingSystem.cpp index cd2e10a..86d88da 100644 --- a/src/engine/systems/prerender/CullingSystem.cpp +++ b/src/engine/systems/prerender/CullingSystem.cpp @@ -8,7 +8,6 @@ #include "engine/FrameInfo.hpp" #include "engine/camera/Camera.hpp" -#include "engine/tree/octtree/OctTreeNode.hpp" namespace fgl::engine { diff --git a/src/engine/systems/render/DrawPair.cpp b/src/engine/systems/render/DrawPair.cpp deleted file mode 100644 index 52bf8df..0000000 --- a/src/engine/systems/render/DrawPair.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// -// Created by kj16609 on 3/14/24. -// - -#include "DrawPair.hpp" - -#include - -#include - -#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 diff --git a/src/engine/systems/render/DrawPair.hpp b/src/engine/systems/render/DrawPair.hpp deleted file mode 100644 index 2a90655..0000000 --- a/src/engine/systems/render/DrawPair.hpp +++ /dev/null @@ -1,67 +0,0 @@ -// -// Created by kj16609 on 3/14/24. -// - -#pragma once -#include - -#include "engine/gameobjects/GameObject.hpp" -#include "engine/utils.hpp" - -namespace fgl::engine -{ - struct Frustum; - class OctTreeNode; - struct ModelMatrixInfo; - // - 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 diff --git a/src/engine/systems/render/EntityRendererSystem.cpp b/src/engine/systems/render/EntityRendererSystem.cpp index 48b67f2..3e3d4b2 100644 --- a/src/engine/systems/render/EntityRendererSystem.cpp +++ b/src/engine/systems/render/EntityRendererSystem.cpp @@ -7,14 +7,12 @@ #include #include -#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 ); diff --git a/src/engine/systems/render/EntityRendererSystem.hpp b/src/engine/systems/render/EntityRendererSystem.hpp index f05bfd1..b378445 100644 --- a/src/engine/systems/render/EntityRendererSystem.hpp +++ b/src/engine/systems/render/EntityRendererSystem.hpp @@ -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 >; diff --git a/src/engine/systems/render/ShadowRenderer.cpp b/src/engine/systems/render/ShadowRenderer.cpp new file mode 100644 index 0000000..8d7a91d --- /dev/null +++ b/src/engine/systems/render/ShadowRenderer.cpp @@ -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 \ No newline at end of file diff --git a/src/engine/systems/render/ShadowRenderer.hpp b/src/engine/systems/render/ShadowRenderer.hpp new file mode 100644 index 0000000..85a0cbc --- /dev/null +++ b/src/engine/systems/render/ShadowRenderer.hpp @@ -0,0 +1,30 @@ +// +// Created by kj16609 on 2/28/25. +// +#pragma once +#include + +#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 \ No newline at end of file diff --git a/src/engine/tree/Chunk.cpp b/src/engine/tree/Chunk.cpp deleted file mode 100644 index eb51de9..0000000 --- a/src/engine/tree/Chunk.cpp +++ /dev/null @@ -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 -*/ \ No newline at end of file diff --git a/src/engine/tree/Chunk.hpp b/src/engine/tree/Chunk.hpp deleted file mode 100644 index 05549fd..0000000 --- a/src/engine/tree/Chunk.hpp +++ /dev/null @@ -1,85 +0,0 @@ -// -// Created by kj16609 on 11/2/24. -// - -#pragma once -#include -#include -#include - -#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 -*/ \ No newline at end of file diff --git a/src/engine/tree/bvh/BVHTree.cpp b/src/engine/tree/bvh/BVHTree.cpp deleted file mode 100644 index 3963d2b..0000000 --- a/src/engine/tree/bvh/BVHTree.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// Created by kj16609 on 1/24/25. -// -#include "BVHTree.hpp" - -namespace fgl::engine -{ - -} \ No newline at end of file diff --git a/src/engine/tree/bvh/BVHTree.hpp b/src/engine/tree/bvh/BVHTree.hpp deleted file mode 100644 index 95614fb..0000000 --- a/src/engine/tree/bvh/BVHTree.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// -// Created by kj16609 on 1/24/25. -// -#pragma once -#include - -#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 \ No newline at end of file diff --git a/src/engine/tree/octtree/OctTreeNode.cpp b/src/engine/tree/octtree/OctTreeNode.cpp deleted file mode 100644 index a5c9799..0000000 --- a/src/engine/tree/octtree/OctTreeNode.cpp +++ /dev/null @@ -1,732 +0,0 @@ -// -// Created by kj16609 on 3/1/24. -// - -#include "OctTreeNode.hpp" - -#define GLM_ENABLE_EXPERIMENTAL -#include -#include - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#pragma GCC diagnostic ignored "-Weffc++" -#include -#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 \ No newline at end of file diff --git a/src/engine/tree/octtree/OctTreeNode.hpp b/src/engine/tree/octtree/OctTreeNode.hpp deleted file mode 100644 index 93fa49d..0000000 --- a/src/engine/tree/octtree/OctTreeNode.hpp +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/engine/tree/quadtree/QuadTree.cpp b/src/engine/tree/quadtree/QuadTree.cpp deleted file mode 100644 index 477abf5..0000000 --- a/src/engine/tree/quadtree/QuadTree.cpp +++ /dev/null @@ -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 diff --git a/src/engine/tree/quadtree/QuadTree.hpp b/src/engine/tree/quadtree/QuadTree.hpp deleted file mode 100644 index 238e2a8..0000000 --- a/src/engine/tree/quadtree/QuadTree.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// -// Created by kj16609 on 3/11/24. -// - -#pragma once - -#include -#include - -#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 diff --git a/src/shaders/bounds/axisalignedbb.slang b/src/shaders/bounds/axisalignedbb.slang new file mode 100644 index 0000000..92133bf --- /dev/null +++ b/src/shaders/bounds/axisalignedbb.slang @@ -0,0 +1,5 @@ + +struct AxisAlignedBoundingBox +{ + +}; diff --git a/src/shaders/bvh.slang b/src/shaders/bvh.slang new file mode 100644 index 0000000..48e8bf6 --- /dev/null +++ b/src/shaders/bvh.slang @@ -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; +} + + + + diff --git a/src/shaders/composition.slang b/src/shaders/composition.slang index 3e23f0d..1afce60 100644 --- a/src/shaders/composition.slang +++ b/src/shaders/composition.slang @@ -1,6 +1,6 @@ #version 450 -import camera; +import objects.camera; struct Vertex { vec4 position : SV_Position; diff --git a/src/shaders/culling.slang b/src/shaders/culling.slang new file mode 100644 index 0000000..f419c08 --- /dev/null +++ b/src/shaders/culling.slang @@ -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 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 ]; + } +} + + + + diff --git a/src/shaders/line.slang b/src/shaders/line.slang index 4c72c3d..f57de8b 100644 --- a/src/shaders/line.slang +++ b/src/shaders/line.slang @@ -1,7 +1,7 @@ #version 450 -import camera; -import gbuffer; +import objects.camera; +import objects.gbuffer; struct LineVertex { diff --git a/src/shaders/camera.slang b/src/shaders/objects/camera.slang similarity index 100% rename from src/shaders/camera.slang rename to src/shaders/objects/camera.slang diff --git a/src/shaders/objects/frustum.slang b/src/shaders/objects/frustum.slang new file mode 100644 index 0000000..88e4ea2 --- /dev/null +++ b/src/shaders/objects/frustum.slang @@ -0,0 +1,8 @@ +#version 450 + + +struct Frustum +{ + +}; + diff --git a/src/shaders/objects/gamemodel.slang b/src/shaders/objects/gamemodel.slang new file mode 100644 index 0000000..d434b78 --- /dev/null +++ b/src/shaders/objects/gamemodel.slang @@ -0,0 +1,9 @@ +#version 450 + +struct GameModel { + + + +}; + + diff --git a/src/shaders/objects/gameobject.slang b/src/shaders/objects/gameobject.slang new file mode 100644 index 0000000..2cef9b6 --- /dev/null +++ b/src/shaders/objects/gameobject.slang @@ -0,0 +1,11 @@ +#version 450 + + + +struct GameObject +{ + + + +} + diff --git a/src/shaders/gbuffer.slang b/src/shaders/objects/gbuffer.slang similarity index 100% rename from src/shaders/gbuffer.slang rename to src/shaders/objects/gbuffer.slang diff --git a/src/shaders/shadowmap.slang b/src/shaders/shadowmap.slang new file mode 100644 index 0000000..b1482fb --- /dev/null +++ b/src/shaders/shadowmap.slang @@ -0,0 +1,15 @@ + + +import model.vertex; + +struct CoarseVertex { + float4 position : SV_Position; +} + +[shader("vertex")] +CoarseVertex vertexMain( ModelVertex in_vertex ) +{ + + + +} diff --git a/src/shaders/textured.slang b/src/shaders/textured.slang index b578ac4..3be8d68 100644 --- a/src/shaders/textured.slang +++ b/src/shaders/textured.slang @@ -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 materials[] : MATERIALS; +ConstantBuffer materials[] : MATERIALS; [[vk::binding(0,2)]] Sampler2D[ ] tex : TEXTURES; diff --git a/src/shaders/vk/drawindexedindirect.slang b/src/shaders/vk/drawindexedindirect.slang new file mode 100644 index 0000000..7e1f253 --- /dev/null +++ b/src/shaders/vk/drawindexedindirect.slang @@ -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; +}; + +} \ No newline at end of file diff --git a/src/tests/src/math/transform.cpp b/src/tests/src/math/transform.cpp index cb7143a..8548208 100644 --- a/src/tests/src/math/transform.cpp +++ b/src/tests/src/math/transform.cpp @@ -2,9 +2,9 @@ // Created by kj16609 on 2/18/25. // -#include +#include "primitives/Transform.hpp" -#include "primitives/TransformComponent.hpp" +#include TEST_CASE( "Transform", "[transform]" ) {