diff --git a/.gitmodules b/.gitmodules index eb037e1..b172a35 100644 --- a/.gitmodules +++ b/.gitmodules @@ -47,3 +47,6 @@ [submodule "destrum/third_party/jolt"] path = destrum/third_party/jolt url = https://github.com/jrouwe/JoltPhysics.git +[submodule "destrum/third_party/tracy"] + path = destrum/third_party/tracy + url = https://github.com/wolfpld/tracy.git diff --git a/destrum/CMakeLists.txt b/destrum/CMakeLists.txt index eec0721..c6136b6 100644 --- a/destrum/CMakeLists.txt +++ b/destrum/CMakeLists.txt @@ -56,7 +56,7 @@ set(SRC_FILES "src/Physics/PhysicsWorld.cpp" "src/Physics/SimplePhysicsWorld.cpp" "src/Physics/PhysicsSceneBridge.cpp" - src/Components/Physics/BoxCollider.cpp + "src/Physics/JoltPhysicsWorld.cpp" ) add_library(destrum ${SRC_FILES}) @@ -89,6 +89,7 @@ target_link_libraries(destrum assimp imgui Jolt + Tracy::TracyClient PRIVATE freetype::freetype @@ -99,6 +100,7 @@ target_compile_definitions(destrum PUBLIC VK_NO_PROTOTYPES VMA_VULKAN_VERSION=1003000 + TRACY_VK_USE_SYMBOL_TABLE # VOLK_DEFAULT_VISIBILITY # FIXME: doesn't work for some reason ) diff --git a/destrum/assets_src/238882.png b/destrum/assets_src/238882.png new file mode 100644 index 0000000..594f631 Binary files /dev/null and b/destrum/assets_src/238882.png differ diff --git a/destrum/assets_src/cube.fbx b/destrum/assets_src/cube.fbx index 891c16c..dea90d9 100644 Binary files a/destrum/assets_src/cube.fbx and b/destrum/assets_src/cube.fbx differ diff --git a/destrum/include/destrum/App.h b/destrum/include/destrum/App.h index 4577718..07a19d1 100644 --- a/destrum/include/destrum/App.h +++ b/destrum/include/destrum/App.h @@ -60,6 +60,14 @@ protected: bool resizePending = false; std::chrono::steady_clock::time_point lastResizeTime{}; + bool m_PhysicsPaused = false; + bool m_PhysicsStepOnce = false; + + float m_PhysicsTimeScale = 1.0f; + int m_PhysicsStepsLastFrame = 0; + + void drawPhysicsPanel(float dt, float fixedDt, float accumulator); + }; diff --git a/destrum/include/destrum/Graphics/GfxDevice.h b/destrum/include/destrum/Graphics/GfxDevice.h index bbdedba..6529768 100644 --- a/destrum/include/destrum/Graphics/GfxDevice.h +++ b/destrum/include/destrum/Graphics/GfxDevice.h @@ -29,6 +29,13 @@ class MeshCache; class ImguiPass; +#if defined(TRACY_ENABLE) +namespace tracy { class VkCtx; } +using DestrumTracyVkCtx = tracy::VkCtx*; +#else +using DestrumTracyVkCtx = void*; +#endif + namespace { using ImmediateExecuteFunction = std::function; @@ -126,12 +133,16 @@ public: return swapchain.getImageView(imageIndex); } + DestrumTracyVkCtx getTracyVkCtx() const { return tracyVkCtx; } + private: vkb::Instance instance; vkb::PhysicalDevice physicalDevice; vkb::Device device; VmaAllocator allocator; + DestrumTracyVkCtx tracyVkCtx = nullptr; + std::uint32_t graphicsQueueFamily; VkQueue graphicsQueue; @@ -149,6 +160,8 @@ private: ImageCache imageCache; + bool m_vSync = false; + static uint32_t BytesPerTexel(VkFormat fmt) { switch (fmt) { case VK_FORMAT_R8_UNORM: return 1; diff --git a/destrum/include/destrum/Physics/JoltPhysicsWorld.h b/destrum/include/destrum/Physics/JoltPhysicsWorld.h new file mode 100644 index 0000000..f7b16a2 --- /dev/null +++ b/destrum/include/destrum/Physics/JoltPhysicsWorld.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +#include + +#include + +class JoltPhysicsWorld final : public PhysicsWorld { +public: + struct Settings { + std::uint32_t maxBodies{65536}; + std::uint32_t numBodyMutexes{0}; + std::uint32_t maxBodyPairs{65536}; + std::uint32_t maxContactConstraints{10240}; + + // Jolt recommends a temp allocator for physics update allocations. + // 10 MB is the value used in their HelloWorld sample. + std::uint32_t tempAllocatorSizeBytes{10u * 1024u * 1024u}; + + // 0 means "hardware_concurrency - 1". + std::uint32_t workerThreadCount{0}; + + glm::vec3 gravity{0.0f, -9.81f, 0.0f}; + }; + + explicit JoltPhysicsWorld(const Settings& settings = Settings{}); + ~JoltPhysicsWorld() override; + + JoltPhysicsWorld(const JoltPhysicsWorld&) = delete; + JoltPhysicsWorld(JoltPhysicsWorld&&) noexcept = delete; + JoltPhysicsWorld& operator=(const JoltPhysicsWorld&) = delete; + JoltPhysicsWorld& operator=(JoltPhysicsWorld&&) noexcept = delete; + + void Step(float fixedDt) override; + + void SyncKinematicBodiesToPhysics() override; + void SyncDynamicBodiesToTransforms() override; + + PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) override; + void DestroyBody(PhysicsBodyHandle body) override; + + void SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform) override; + PhysicsTransform GetBodyTransform(PhysicsBodyHandle body) const override; + + void SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity) override; + glm::vec3 GetLinearVelocity(PhysicsBodyHandle body) const override; + + void AddForce(PhysicsBodyHandle body, const glm::vec3& force) override; + void AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse) override; + + bool Raycast(const glm::vec3& origin, + const glm::vec3& direction, + float maxDistance, + PhysicsRaycastHit& hit) const override; + +private: + class Impl; + std::unique_ptr m_Impl; +}; diff --git a/destrum/include/destrum/Physics/PhysicsWorld.h b/destrum/include/destrum/Physics/PhysicsWorld.h index 2dd84e9..c966f9c 100644 --- a/destrum/include/destrum/Physics/PhysicsWorld.h +++ b/destrum/include/destrum/Physics/PhysicsWorld.h @@ -25,8 +25,8 @@ public: // - Kinematic: Transform -> physics body before Step() // - Dynamic: physics body -> Transform after Step() - void SyncKinematicBodiesToPhysics(); - void SyncDynamicBodiesToTransforms(); + virtual void SyncKinematicBodiesToPhysics(); + virtual void SyncDynamicBodiesToTransforms(); virtual PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) = 0; virtual void DestroyBody(PhysicsBodyHandle body) = 0; diff --git a/destrum/include/destrum/Scene/Scene.h b/destrum/include/destrum/Scene/Scene.h index 6c491d1..25403cb 100644 --- a/destrum/include/destrum/Scene/Scene.h +++ b/destrum/include/destrum/Scene/Scene.h @@ -6,6 +6,7 @@ #include #include +#include "destrum/Physics/JoltPhysicsWorld.h" #include "destrum/Physics/PhysicsSceneBridge.h" class GameObject; @@ -68,7 +69,7 @@ public: private: explicit Scene(const std::string& name); - PhysicsSceneBridge m_Physics{std::make_unique()}; + PhysicsSceneBridge m_Physics{std::make_unique()}; std::string m_name; diff --git a/destrum/src/App.cpp b/destrum/src/App.cpp index cde4d64..c722488 100644 --- a/destrum/src/App.cpp +++ b/destrum/src/App.cpp @@ -10,7 +10,16 @@ #include "glm/gtx/transform.hpp" #include "spdlog/spdlog.h" -#include +#include +#include + +struct TracyFrameScope +{ + ~TracyFrameScope() + { + FrameMark; + } +}; App::App() { @@ -19,7 +28,9 @@ App::App() void App::init(const AppParams& params) { m_params = params; - + ZoneScopedN("App::init"); + tracy::SetThreadName("Main Thread"); + TracySetProgramName(params.appName.c_str()); AssetFS::GetInstance().Init(params.exeDir); // AssetFS::GetInstance().Mount("engine", params.exeDir / "assets" / "engine"); // AssetFS::GetInstance().Mount("game", params.exeDir / "assets" / "game"); @@ -53,16 +64,21 @@ void App::init(const AppParams& params) void App::run() { - Time::GetInstance().Update(); // initialize delta timing + ZoneScopedN("App::run"); + + Time::GetInstance().Update(); const float fixedDt = static_cast(Time::GetInstance().FixedDeltaTime()); - const int maxSteps = 5; // prevent spiral of death + const int maxSteps = 5; float accumulator = 0.0f; isRunning = true; + while (isRunning) { - // ---- Update timing --- + TracyFrameScope tracyFrame; + ZoneScopedN("App::Frame"); + Time::GetInstance().Update(); float dt = static_cast(Time::GetInstance().DeltaTime()); @@ -72,117 +88,193 @@ void App::run() { float newFPS = 1.0f / dt; avgFPS = std::lerp(avgFPS, newFPS, 0.1f); + TracyPlot("FPS", avgFPS); + TracyPlot("Delta Time ms", dt * 1000.0f); } accumulator += dt; - InputManager::GetInstance().BeginFrame(); - camera.Update(dt); - - - SDL_Event event; - while (SDL_PollEvent(&event)) { - imguiPass.handleEvent(event); - if (event.type == SDL_QUIT) + ZoneScopedN("Input BeginFrame + Camera"); + InputManager::GetInstance().BeginFrame(); + camera.Update(dt); + } + + { + ZoneScopedN("SDL Events"); + + SDL_Event event; + while (SDL_PollEvent(&event)) { - isRunning = false; - break; - } - if (event.type == SDL_WINDOWEVENT) - { - switch (event.window.event) + ZoneScopedN("SDL Event"); + + imguiPass.handleEvent(event); + + if (event.type == SDL_QUIT) { - case SDL_WINDOWEVENT_SIZE_CHANGED: - case SDL_WINDOWEVENT_RESIZED: + isRunning = false; + break; + } + + if (event.type == SDL_WINDOWEVENT) + { + switch (event.window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + case SDL_WINDOWEVENT_RESIZED: resizePending = true; lastResizeTime = std::chrono::steady_clock::now(); break; } } - } - const bool mouseEvent = - event.type == SDL_MOUSEBUTTONDOWN || - event.type == SDL_MOUSEBUTTONUP || - event.type == SDL_MOUSEMOTION || - event.type == SDL_MOUSEWHEEL; - const bool keyboardEvent = - event.type == SDL_KEYDOWN || - event.type == SDL_KEYUP || - event.type == SDL_TEXTINPUT; + const bool mouseEvent = + event.type == SDL_MOUSEBUTTONDOWN || + event.type == SDL_MOUSEBUTTONUP || + event.type == SDL_MOUSEMOTION || + event.type == SDL_MOUSEWHEEL; - const bool capturedByImgui = - (mouseEvent && imguiPass.wantsMouse()) || - (keyboardEvent && imguiPass.wantsKeyboard()); + const bool keyboardEvent = + event.type == SDL_KEYDOWN || + event.type == SDL_KEYUP || + event.type == SDL_TEXTINPUT; - if (!capturedByImgui) - { - if (InputManager::GetInstance().ProcessEvent(event)) + const bool capturedByImgui = + (mouseEvent && imguiPass.wantsMouse()) || + (keyboardEvent && imguiPass.wantsKeyboard()); + + if (!capturedByImgui) { - isRunning = false; + ZoneScopedN("Input ProcessEvent"); + + if (InputManager::GetInstance().ProcessEvent(event)) + { + isRunning = false; + } } } } + if (!isRunning) break; - imguiPass.beginFrame(); - customUpdate(dt); - - ImGui::Begin("Debug"); - ImGui::Text("FPS: %.2f", avgFPS); - ImGui::End(); - - imguiPass.endFrame(); - int steps = 0; - while (accumulator >= fixedDt && steps < maxSteps) { - // physics.Update(fixedDt); - customFixedUpdate(fixedDt); - // physics.Step(fixedDt); - // physics.SyncTransforms(); + ZoneScopedN("ImGui BeginFrame"); + imguiPass.beginFrame(); + } - accumulator -= fixedDt; - steps++; + { + ZoneScopedN("customUpdate"); + customUpdate(dt); + } + + { + ZoneScopedN("Debug ImGui"); + ImGui::Begin("Debug"); + ImGui::Text("FPS: %.2f", avgFPS); + ImGui::End(); + + drawPhysicsPanel(dt, fixedDt, accumulator); + + TracyPlot("FPS", avgFPS); + TracyPlot("Frame Time ms", dt * 1000.0f); + TracyPlot("Accumulator ms", accumulator * 1000.0f); + } + + { + ZoneScopedN("ImGui EndFrame"); + imguiPass.endFrame(); + } + + { + ZoneScopedN("FixedUpdate"); + + int steps = 0; + + if (m_PhysicsPaused) + { + // Important: prevent physics from building up a huge backlog while paused. + accumulator = 0.0f; + + if (m_PhysicsStepOnce) + { + ZoneScopedN("Single Physics Step"); + + customFixedUpdate(fixedDt * m_PhysicsTimeScale); + + steps = 1; + m_PhysicsStepOnce = false; + } + } + else + { + while (accumulator >= fixedDt && steps < maxSteps) + { + ZoneScopedN("Fixed Step"); + + customFixedUpdate(fixedDt * m_PhysicsTimeScale); + + accumulator -= fixedDt; + steps++; + } + + if (steps == maxSteps) + { + accumulator = 0.0f; + } + } + + m_PhysicsStepsLastFrame = steps; + + TracyPlot("Fixed Steps", static_cast(steps)); } - if (steps == maxSteps) accumulator = 0.0f; const float alpha = accumulator / fixedDt; + (void)alpha; - if (gfxDevice.needsSwapchainRecreate() || resizePending) { - auto now = std::chrono::steady_clock::now(); + ZoneScopedN("Swapchain Resize Check"); - if (resizePending && - now - lastResizeTime < std::chrono::milliseconds(100)) + if (gfxDevice.needsSwapchainRecreate() || resizePending) { + auto now = std::chrono::steady_clock::now(); + + if (resizePending && + now - lastResizeTime < std::chrono::milliseconds(100)) + { + continue; + } + + int w = 0; + int h = 0; + SDL_Vulkan_GetDrawableSize(window, &w, &h); + + if (w == 0 || h == 0) + { + continue; + } + + spdlog::info("Recreating swapchain to size: {}x{}", w, h); + + { + ZoneScopedN("Recreate Swapchain"); + gfxDevice.recreateSwapchain(w, h); + onWindowResize(w, h); + } + + resizePending = false; continue; } - - int w = 0; - int h = 0; - SDL_Vulkan_GetDrawableSize(window, &w, &h); - - if (w == 0 || h == 0) - { - continue; - } - - spdlog::info("Recreating swapchain to size: {}x{}", w, h); - - gfxDevice.recreateSwapchain(w, h); - onWindowResize(w, h); - - resizePending = false; - continue; } - customDraw(); - + { + ZoneScopedN("customDraw"); + customDraw(); + } if (frameLimit) { + ZoneScopedN("Frame Limit Sleep"); + auto sleepTime = Time::GetInstance().SleepDuration(); if (sleepTime.count() > 0) { @@ -199,3 +291,49 @@ void App::cleanup() spdlog::info("Cleaning up"); customCleanup(); } + +void App::drawPhysicsPanel(float dt, float fixedDt, float accumulator) +{ + ImGui::Begin("Physics"); + + ImGui::Text("Frame dt: %.3f ms", dt * 1000.0f); + ImGui::Text("Fixed dt: %.3f ms", fixedDt * 1000.0f); + ImGui::Text("Accumulator: %.3f ms", accumulator * 1000.0f); + ImGui::Text("Steps last frame: %d", m_PhysicsStepsLastFrame); + + ImGui::Separator(); + + if (ImGui::Button(m_PhysicsPaused ? "Resume Physics" : "Pause Physics")) + { + m_PhysicsPaused = !m_PhysicsPaused; + } + + ImGui::SameLine(); + + if (!m_PhysicsPaused) + { + ImGui::BeginDisabled(); + ImGui::Button("Step Physics"); + ImGui::EndDisabled(); + } + else + { + if (ImGui::Button("Step Physics")) + { + m_PhysicsStepOnce = true; + } + } + + ImGui::Checkbox("Paused", &m_PhysicsPaused); + + ImGui::Separator(); + + ImGui::SliderFloat("Physics Time Scale", &m_PhysicsTimeScale, 0.0f, 2.0f, "%.2fx"); + + if (ImGui::Button("Reset Time Scale")) + { + m_PhysicsTimeScale = 1.0f; + } + + ImGui::End(); +} \ No newline at end of file diff --git a/destrum/src/Graphics/GfxDevice.cpp b/destrum/src/Graphics/GfxDevice.cpp index 07d6acd..b84c45f 100644 --- a/destrum/src/Graphics/GfxDevice.cpp +++ b/destrum/src/Graphics/GfxDevice.cpp @@ -19,13 +19,17 @@ #include "destrum/Graphics/imageLoader.h" #include "destrum/Util/GameState.h" #include "spdlog/spdlog.h" +#include "tracy/Tracy.hpp" + +#include "tracy/Tracy.hpp" +#include "tracy/TracyVulkan.hpp" GfxDevice::GfxDevice(): imageCache(*this) { } void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) { VK_CHECK(volkInitialize()); - + m_vSync = vSync; instance = vkb::InstanceBuilder{} .set_app_name(appName.c_str()) .set_app_version(1, 0, 0) @@ -131,6 +135,34 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer)); } +#if defined(TRACY_ENABLE) + { + VkCommandBuffer tracyInitCmd = frames[0].commandBuffer; + +#if defined(TRACY_VK_USE_SYMBOL_TABLE) + tracyVkCtx = TracyVkContext( + instance, + physicalDevice, + device, + graphicsQueue, + tracyInitCmd, + vkGetInstanceProcAddr, + vkGetDeviceProcAddr + ); +#else + tracyVkCtx = TracyVkContext( + physicalDevice, + device, + graphicsQueue, + tracyInitCmd + ); +#endif + + static constexpr char ctxName[] = "Graphics Queue"; + TracyVkContextName(tracyVkCtx, ctxName, sizeof(ctxName) - 1); + } +#endif + { // create white texture std::uint32_t pixel = 0xFFFFFFFF; whiteImageId = createImage( @@ -166,19 +198,30 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) void GfxDevice::recreateSwapchain(int width, int height) { assert(width != 0 && height != 0); waitIdle(); - swapchain.recreateSwapchain(*this, swapchainFormat, width, height, true); + swapchain.recreateSwapchain(*this, swapchainFormat, width, height, m_vSync); } -VkCommandBuffer GfxDevice::beginFrame() { - swapchain.beginFrame(getCurrentFrameIndex()); +VkCommandBuffer GfxDevice::beginFrame() +{ + ZoneScopedN("GfxDevice::beginFrame"); + + { + ZoneScopedN("Swapchain BeginFrame"); + swapchain.beginFrame(getCurrentFrameIndex()); + } const auto& frame = getCurrentFrame(); const auto& cmd = frame.commandBuffer; + const auto cmdBeginInfo = VkCommandBufferBeginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; - VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); + + { + ZoneScopedN("vkBeginCommandBuffer"); + VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); + } return cmd; } @@ -188,17 +231,34 @@ VulkanImmediateExecutor& GfxDevice::GetImmediateExecuter() { } void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) { + ZoneScopedN("GfxDevice::endFrame"); + // get swapchain image - const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex()); + VkImage swapchainImage = VK_NULL_HANDLE; + std::uint32_t swapchainImageIndex = 0; + + { + ZoneScopedN("Swapchain AcquireNextImage"); + + const auto result = swapchain.acquireNextImage(getCurrentFrameIndex()); + swapchainImage = result.first; + swapchainImageIndex = result.second; + } + if (swapchainImage == VK_NULL_HANDLE) { spdlog::info("Swapchain is freaky, skipping frame..."); return; } // Fences are reset here to prevent the deadlock in case swapchain becomes dirty - swapchain.resetFences(getCurrentFrameIndex()); + { + ZoneScopedN("Swapchain ResetFences"); + swapchain.resetFences(getCurrentFrameIndex()); + } auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; { + ZoneScopedN("Clear Swapchain Image"); + const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL); swapchainLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -208,6 +268,8 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E } if (true) { + ZoneScopedN("Copy DrawImage To Swapchain"); + // copy from draw image into swapchain vkutil::transitionImage( cmd, @@ -248,6 +310,8 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E // swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; if (props.imguiPass) { + ZoneScopedN("ImGui Render"); + props.imguiPass->render( cmd, swapchainImage, @@ -258,18 +322,37 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E } // prepare for present - vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + { + ZoneScopedN("Transition Swapchain To Present"); - VK_CHECK(vkEndCommandBuffer(cmd)); + vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } +#if defined(TRACY_ENABLE) + TracyVkCollect(tracyVkCtx, cmd); +#endif + { + ZoneScopedN("vkEndCommandBuffer"); + VK_CHECK(vkEndCommandBuffer(cmd)); + } // swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex); - swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex()); + { + ZoneScopedN("Swapchain SubmitAndPresent"); + swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex()); + } frameNumber++; + FrameMark; } void GfxDevice::cleanup() { +#if defined(TRACY_ENABLE) + if (tracyVkCtx) { + TracyVkDestroy(tracyVkCtx); + tracyVkCtx = nullptr; + } +#endif } void GfxDevice::waitIdle() { diff --git a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp index bf256c7..d5a5b83 100644 --- a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp +++ b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp @@ -98,9 +98,9 @@ void MeshPipeline::draw(VkCommandBuffer cmd, for (const auto& dcIdx : drawCommands) { const auto& dc = dcIdx; - // if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) { - // continue; - // } + if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) { + continue; + } ActualDrawCalls++; diff --git a/destrum/src/Graphics/Renderer.cpp b/destrum/src/Graphics/Renderer.cpp index 96b5dc2..b9a621e 100644 --- a/destrum/src/Graphics/Renderer.cpp +++ b/destrum/src/Graphics/Renderer.cpp @@ -5,6 +5,8 @@ #include "destrum/Util/GameState.h" #include "spdlog/spdlog.h" +#include "tracy/TracyVulkan.hpp" + GameRenderer::GameRenderer(MeshCache& meshCache, MaterialCache& matCache): meshCache{meshCache}, materialCache{matCache} { } @@ -41,13 +43,20 @@ void GameRenderer::endDrawing() { } void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData) { + ZoneScopedN("GameRenderer::draw"); + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "GameRenderer::draw"); - for (const auto& dc : meshDrawCommands) { - if (!dc.skinnedMesh) { - continue; + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Skinning"); + + for (const auto& dc : meshDrawCommands) { + if (!dc.skinnedMesh) { + continue; + } + skinningPipeline->doSkinning(cmd, gfxDevice.getCurrentFrameIndex(), meshCache, dc); } - skinningPipeline->doSkinning(cmd, gfxDevice.getCurrentFrameIndex(), meshCache, dc); } + const auto gpuSceneData = GPUSceneData{ .view = sceneData.camera.GetViewMatrix(), .proj = sceneData.camera.GetProjectionMatrix(), @@ -59,40 +68,71 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& .fogDensity = sceneData.fogDensity, .materialsBuffer = materialCache.getMaterialDataBufferAddress(), }; - vkutil::bufferHostWriteToShaderReadBarrier( - cmd, - materialCache.getMaterialDataBuffer().buffer, - 0, - VK_WHOLE_SIZE - ); - sceneDataBuffer.uploadNewData(cmd, gfxDevice.getCurrentFrameIndex(), (void*)&gpuSceneData, sizeof(GPUSceneData)); + + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Material Buffer Barrier"); + + vkutil::bufferHostWriteToShaderReadBarrier( + cmd, + materialCache.getMaterialDataBuffer().buffer, + 0, + VK_WHOLE_SIZE + ); + } + + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Upload Scene Data"); + + sceneDataBuffer.uploadNewData( + cmd, + gfxDevice.getCurrentFrameIndex(), + (void*)&gpuSceneData, + sizeof(GPUSceneData) + ); + } const auto& drawImage = gfxDevice.getImage(drawImageId); const auto& depthImage = gfxDevice.getImage(depthImageId); // vkutil::cmdBeginLabel(cmd, "Geometry"); - vkutil::transitionImage( + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Transition Draw Image"); + + vkutil::transitionImage( cmd, drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } - vkutil::transitionImage( - cmd, - depthImage.image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Transition Depth Image"); + + vkutil::transitionImage( + cmd, + depthImage.image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + } const auto renderInfo = vkutil::createRenderingInfo({ - .renderExtent = drawImage.getExtent2D(), - .colorImageView = drawImage.imageView, - .colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f}, - .depthImageView = depthImage.imageView, - .depthImageClearValue = 1.f, - }); + .renderExtent = drawImage.getExtent2D(), + .colorImageView = drawImage.imageView, + .colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f}, + .depthImageView = depthImage.imageView, + .depthImageClearValue = 1.f, + }); - vkCmdBeginRendering(cmd, &renderInfo.renderingInfo); - meshPipeline->draw( + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdBeginRendering"); + + vkCmdBeginRendering(cmd, &renderInfo.renderingInfo); + } + + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::draw"); + + meshPipeline->draw( cmd, drawImage.getExtent2D(), gfxDevice, @@ -102,14 +142,20 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& sceneDataBuffer.getBuffer(), meshDrawCommands, sortedMeshDrawCommands); + } - skyboxPipeline->draw(cmd, gfxDevice, camera); + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "SkyboxPipeline::draw"); + skyboxPipeline->draw(cmd, gfxDevice, camera); + } - vkCmdEndRendering(cmd); + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdEndRendering"); + + vkCmdEndRendering(cmd); + } // vkutil::cmdEndLabel(cmd); - - } void GameRenderer::cleanup(GfxDevice& gfxDevice) { diff --git a/destrum/src/Graphics/Swapchain.cpp b/destrum/src/Graphics/Swapchain.cpp index 43f8ebc..df2ee10 100644 --- a/destrum/src/Graphics/Swapchain.cpp +++ b/destrum/src/Graphics/Swapchain.cpp @@ -6,6 +6,7 @@ #include #include "volk.h" +#include "tracy/Tracy.hpp" void Swapchain::initSync(VkDevice device) { @@ -23,30 +24,40 @@ void Swapchain::initSync(VkDevice device) { } void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint32_t width, std::uint32_t height, bool vSync) { + ZoneScopedN("Swapchain::createSwapchain"); + m_gfxDevice = gfxDevice; assert(format == VK_FORMAT_B8G8R8A8_SRGB && "TODO: test other formats"); - vSync = true; + // vSync = true; - auto res = vkb::SwapchainBuilder{gfxDevice->getDevice()} - .set_desired_format(VkSurfaceFormatKHR{ - .format = format, - .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, - }) - .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) - .set_desired_present_mode( - vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR) - .set_desired_extent(width, height) - .build(); - if (!res.has_value()) { - // throw std::runtime_error(std::format( - // "failed to create swapchain: error = {}, vk result = {}", - // res.full_error().type.message(), - // string_VkResult(res.full_error().vk_result))); + { + ZoneScopedN("vkb::SwapchainBuilder::build"); + + auto res = vkb::SwapchainBuilder{gfxDevice->getDevice()} + .set_desired_format(VkSurfaceFormatKHR{ + .format = format, + .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + }) + .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) + .set_desired_present_mode( + vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR) + .set_desired_extent(width, height) + .build(); + if (!res.has_value()) { + // throw std::runtime_error(std::format( + // "failed to create swapchain: error = {}, vk result = {}", + // res.full_error().type.message(), + // string_VkResult(res.full_error().vk_result))); + } + m_swapchain = res.value(); } - m_swapchain = res.value(); - images = m_swapchain.get_images().value(); - imageViews = m_swapchain.get_image_views().value(); + { + ZoneScopedN("Get Swapchain Images / Views"); + + images = m_swapchain.get_images().value(); + imageViews = m_swapchain.get_image_views().value(); + } imageRenderSemaphores.resize(images.size()); @@ -55,6 +66,8 @@ void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint }; for (auto& sem: imageRenderSemaphores) { + ZoneScopedN("Create Image Render Semaphore"); + VK_CHECK(vkCreateSemaphore(m_gfxDevice->getDevice(), &sci, nullptr, &sem)); } @@ -71,6 +84,8 @@ void Swapchain::recreateSwapchain( std::uint32_t height, bool vSync) { + ZoneScopedN("Swapchain::recreateSwapchain"); + if (width == 0 || height == 0) { dirty = true; return; @@ -78,45 +93,67 @@ void Swapchain::recreateSwapchain( VkDevice device = gfxDevice.getDevice(); - vkDeviceWaitIdle(device); + { + ZoneScopedN("vkDeviceWaitIdle"); + vkDeviceWaitIdle(device); + } auto oldSwapchain = m_swapchain; - auto res = vkb::SwapchainBuilder{gfxDevice.getVkbDevice()} - .set_old_swapchain(oldSwapchain) - .set_desired_format(VkSurfaceFormatKHR{ - .format = format, - .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, - }) - .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) - .set_desired_present_mode( - vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR) - .set_desired_extent(width, height) - .build(); + { + ZoneScopedN("vkb::SwapchainBuilder::rebuild"); - if (!res.has_value()) { - // throw std::runtime_error(std::format( - // "failed to create swapchain: error = {}, vk result = {}", - // res.full_error().type.message(), - // string_VkResult(res.full_error().vk_result))); + auto res = vkb::SwapchainBuilder{gfxDevice.getVkbDevice()} + .set_old_swapchain(oldSwapchain) + .set_desired_format(VkSurfaceFormatKHR{ + .format = format, + .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + }) + .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) + .set_desired_present_mode( + vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR) + .set_desired_extent(width, height) + .build(); + + if (!res.has_value()) { + // throw std::runtime_error(std::format( + // "failed to create swapchain: error = {}, vk result = {}", + // res.full_error().type.message(), + // string_VkResult(res.full_error().vk_result))); + } + + m_swapchain = res.value(); } - for (auto sem : imageRenderSemaphores) { - vkDestroySemaphore(device, sem, nullptr); + { + ZoneScopedN("Destroy Old Image Render Semaphores"); + + for (auto sem : imageRenderSemaphores) { + vkDestroySemaphore(device, sem, nullptr); + } + imageRenderSemaphores.clear(); } - imageRenderSemaphores.clear(); - for (auto imageView : imageViews) { - vkDestroyImageView(device, imageView, nullptr); + { + ZoneScopedN("Destroy Old Image Views"); + + for (auto imageView : imageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + imageViews.clear(); } - imageViews.clear(); - vkb::destroy_swapchain(oldSwapchain); + { + ZoneScopedN("Destroy Old Swapchain"); + vkb::destroy_swapchain(oldSwapchain); + } - m_swapchain = res.value(); + { + ZoneScopedN("Get New Swapchain Images / Views"); - images = m_swapchain.get_images().value(); - imageViews = m_swapchain.get_image_views().value(); + images = m_swapchain.get_images().value(); + imageViews = m_swapchain.get_image_views().value(); + } VkSemaphoreCreateInfo sci{ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, @@ -125,6 +162,8 @@ void Swapchain::recreateSwapchain( imageRenderSemaphores.resize(images.size()); for (auto& sem : imageRenderSemaphores) { + ZoneScopedN("Create New Image Render Semaphore"); + VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &sem)); } @@ -148,13 +187,25 @@ void Swapchain::cleanup() { } void Swapchain::beginFrame(int index) const { + ZoneScopedN("Swapchain::beginFrame"); + auto& frame = frames[index]; - VK_CHECK(vkWaitForFences(m_gfxDevice->getDevice(), 1, &frame.renderFence, true, std::numeric_limits::max())); + + { + ZoneScopedN("vkWaitForFences"); + VK_CHECK(vkWaitForFences(m_gfxDevice->getDevice(), 1, &frame.renderFence, true, std::numeric_limits::max())); + } } void Swapchain::resetFences(int index) const { + ZoneScopedN("Swapchain::resetFences"); + auto& frame = frames[index]; - VK_CHECK(vkResetFences(m_gfxDevice->getDevice(), 1, &frame.renderFence)); + + { + ZoneScopedN("vkResetFences"); + VK_CHECK(vkResetFences(m_gfxDevice->getDevice(), 1, &frame.renderFence)); + } } struct SwapchainAcquireResult { @@ -164,14 +215,24 @@ struct SwapchainAcquireResult { }; std::pair Swapchain::acquireNextImage(int index) { + ZoneScopedN("Swapchain::acquireNextImage"); + std::uint32_t swapchainImageIndex{}; - const auto result = vkAcquireNextImageKHR( - m_gfxDevice->getDevice(), - m_swapchain, - std::numeric_limits::max(), - frames[index].swapchainSemaphore, - VK_NULL_HANDLE, - &swapchainImageIndex); + + VkResult result = VK_SUCCESS; + + { + ZoneScopedN("vkAcquireNextImageKHR"); + + result = vkAcquireNextImageKHR( + m_gfxDevice->getDevice(), + m_swapchain, + std::numeric_limits::max(), + frames[index].swapchainSemaphore, + VK_NULL_HANDLE, + &swapchainImageIndex); + } + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { dirty = true; return {images[swapchainImageIndex], swapchainImageIndex}; @@ -188,6 +249,8 @@ void Swapchain::submitAndPresent( uint32_t imageIndex, // from vkAcquireNextImageKHR uint32_t frameIndex) // 0..FRAMES_IN_FLIGHT-1 { + ZoneScopedN("Swapchain::submitAndPresent"); + auto& frame = frames[frameIndex]; // ✅ per-frame VkSemaphore renderFinished = imageRenderSemaphores[imageIndex]; // ✅ per-image @@ -209,7 +272,11 @@ void Swapchain::submitAndPresent( renderFinished); // ✅ signal semaphore (per-image) VkSubmitInfo2 submit = vkinit::submitInfo(&cmdInfo, &waitInfo, &signalInfo); - VK_CHECK(vkQueueSubmit2(graphicsQueue, 1, &submit, frame.renderFence)); // ✅ fence (per-frame) + + { + ZoneScopedN("vkQueueSubmit2"); + VK_CHECK(vkQueueSubmit2(graphicsQueue, 1, &submit, frame.renderFence)); // ✅ fence (per-frame) + } // present VkPresentInfoKHR presentInfo{ @@ -221,7 +288,13 @@ void Swapchain::submitAndPresent( .pImageIndices = &imageIndex, // ✅ imageIndex, NOT frameIndex }; - VkResult res = vkQueuePresentKHR(graphicsQueue, &presentInfo); + VkResult res = VK_SUCCESS; + + { + ZoneScopedN("vkQueuePresentKHR"); + res = vkQueuePresentKHR(graphicsQueue, &presentInfo); + } + if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) dirty = true; else if (res != VK_SUCCESS) dirty = true; } diff --git a/destrum/src/Physics/JoltPhysicsWorld.cpp b/destrum/src/Physics/JoltPhysicsWorld.cpp new file mode 100644 index 0000000..83b9c1c --- /dev/null +++ b/destrum/src/Physics/JoltPhysicsWorld.cpp @@ -0,0 +1,692 @@ +#include + +// Jolt/Jolt.h must be included before other Jolt headers. +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace +{ + using namespace JPH; + + static std::atomic_uint32_t g_JoltInstanceCount{0}; + + static void TraceImpl(const char* inFMT, ...) + { + va_list list; + va_start(list, inFMT); + std::vprintf(inFMT, list); + std::printf("\n"); + va_end(list); + } + +#ifdef JPH_ENABLE_ASSERTS + static bool AssertFailedImpl(const char* inExpression, + const char* inMessage, + const char* inFile, + JPH::uint inLine) + { + std::printf("%s:%u: (%s) %s\n", + inFile, + inLine, + inExpression, + inMessage != nullptr ? inMessage : ""); + return true; + } +#endif + + void InitJoltGlobals() + { + if (g_JoltInstanceCount.fetch_add(1) == 0) + { + RegisterDefaultAllocator(); + + Trace = TraceImpl; + JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;) + + Factory::sInstance = new Factory(); + + RegisterTypes(); + } + } + + void ShutdownJoltGlobals() + { + if (g_JoltInstanceCount.fetch_sub(1) == 1) + { + UnregisterTypes(); + + delete Factory::sInstance; + Factory::sInstance = nullptr; + } + } + + namespace Layers + { + static constexpr ObjectLayer NON_MOVING = 0; + static constexpr ObjectLayer MOVING = 1; + static constexpr ObjectLayer NUM_LAYERS = 2; + } + + namespace BroadPhaseLayers + { + static constexpr BroadPhaseLayer NON_MOVING(0); + static constexpr BroadPhaseLayer MOVING(1); + static constexpr JPH::uint NUM_LAYERS(2); + } + + class ObjectLayerPairFilterImpl final : public ObjectLayerPairFilter + { + public: + bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override + { + switch (inObject1) + { + case Layers::NON_MOVING: + return inObject2 == Layers::MOVING; + + case Layers::MOVING: + return true; + + default: + JPH_ASSERT(false); + return false; + } + } + }; + + class BPLayerInterfaceImpl final : public BroadPhaseLayerInterface + { + public: + BPLayerInterfaceImpl() + { + m_ObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING; + m_ObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; + } + + JPH::uint GetNumBroadPhaseLayers() const override + { + return BroadPhaseLayers::NUM_LAYERS; + } + + BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override + { + JPH_ASSERT(inLayer < Layers::NUM_LAYERS); + return m_ObjectToBroadPhase[inLayer]; + } + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + const char* GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override + { + switch ((BroadPhaseLayer::Type)inLayer) + { + case (BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: + return "NON_MOVING"; + + case (BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: + return "MOVING"; + + default: + JPH_ASSERT(false); + return "INVALID"; + } + } +#endif + + private: + BroadPhaseLayer m_ObjectToBroadPhase[Layers::NUM_LAYERS]; + }; + + class ObjectVsBroadPhaseLayerFilterImpl final : public ObjectVsBroadPhaseLayerFilter + { + public: + bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override + { + switch (inLayer1) + { + case Layers::NON_MOVING: + return inLayer2 == BroadPhaseLayers::MOVING; + + case Layers::MOVING: + return true; + + default: + JPH_ASSERT(false); + return false; + } + } + }; + + [[nodiscard]] Vec3 ToJoltVec3(const glm::vec3& v) + { + return Vec3(v.x, v.y, v.z); + } + + [[nodiscard]] RVec3 ToJoltRVec3(const glm::vec3& v) + { + return RVec3(v.x, v.y, v.z); + } + + [[nodiscard]] Quat ToJoltQuat(const glm::quat& q) + { + return Quat(q.x, q.y, q.z, q.w); + } + + [[nodiscard]] glm::vec3 FromJoltVec3(Vec3Arg v) + { + return glm::vec3(v.GetX(), v.GetY(), v.GetZ()); + } + + [[nodiscard]] glm::vec3 FromJoltRVec3(RVec3Arg v) + { + return glm::vec3(static_cast(v.GetX()), + static_cast(v.GetY()), + static_cast(v.GetZ())); + } + + [[nodiscard]] glm::quat FromJoltQuat(QuatArg q) + { + return glm::quat(q.GetW(), q.GetX(), q.GetY(), q.GetZ()); + } + + [[nodiscard]] EMotionType ToJoltMotionType(RigidbodyType type) + { + switch (type) + { + case RigidbodyType::Static: + return EMotionType::Static; + + case RigidbodyType::Dynamic: + return EMotionType::Dynamic; + + case RigidbodyType::Kinematic: + return EMotionType::Kinematic; + + default: + return EMotionType::Static; + } + } + + [[nodiscard]] ObjectLayer ToJoltObjectLayer(RigidbodyType type) + { + return type == RigidbodyType::Static ? Layers::NON_MOVING : Layers::MOVING; + } + + [[nodiscard]] ShapeRefC CreateJoltShape(const PhysicsShapeDesc& desc) + { + switch (desc.type) + { + case PhysicsShapeType::Box: + { + BoxShapeSettings settings(ToJoltVec3(desc.halfExtents)); + ShapeSettings::ShapeResult result = settings.Create(); + + if (!result.IsValid()) + { + throw std::runtime_error( + std::string("Failed to create Jolt box shape: ") + std::string(result.GetError())); + } + + return result.Get(); + } + + case PhysicsShapeType::Sphere: + return new SphereShape(desc.radius); + + case PhysicsShapeType::Capsule: + { + // Jolt expects half-height of the cylindrical section, not full capsule height. + const float halfCylinderHeight = std::max(0.0f, (desc.height * 0.5f) - desc.radius); + return new CapsuleShape(halfCylinderHeight, desc.radius); + } + + case PhysicsShapeType::None: + default: + throw std::runtime_error("Cannot create Jolt body without a valid shape."); + } + } +} // namespace + +class JoltPhysicsWorld::Impl +{ +public: + explicit Impl(const Settings& settings) + : m_Settings(settings) + { + InitJoltGlobals(); + + const JPH::uint hardwareThreads = std::thread::hardware_concurrency(); + const JPH::uint workerThreads = + settings.workerThreadCount != 0 + ? settings.workerThreadCount + : std::max(1, hardwareThreads > 0 ? hardwareThreads - 1 : 1); + + m_TempAllocator = std::make_unique(settings.tempAllocatorSizeBytes); + m_JobSystem = std::make_unique( + cMaxPhysicsJobs, + cMaxPhysicsBarriers, + static_cast(workerThreads)); + + m_PhysicsSystem.Init( + settings.maxBodies, + settings.numBodyMutexes, + settings.maxBodyPairs, + settings.maxContactConstraints, + m_BPLayerInterface, + m_ObjectVsBroadPhaseLayerFilter, + m_ObjectLayerPairFilter); + + m_PhysicsSystem.SetGravity(ToJoltVec3(settings.gravity)); + } + + ~Impl() + { + BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface(); + + for (auto& [handleId, record] : m_Bodies) + { + if (!record.alive) + { + continue; + } + + bodyInterface.RemoveBody(record.bodyID); + bodyInterface.DestroyBody(record.bodyID); + } + + m_Bodies.clear(); + + ShutdownJoltGlobals(); + } + + PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) + { + ShapeRefC shape = CreateJoltShape(desc.shape); + + // This first backend ignores centerOffset for now. + // Later, use JPH::OffsetCenterOfMassShapeSettings for offset colliders. + + BodyCreationSettings settings( + shape, + ToJoltRVec3(desc.transform.position), + ToJoltQuat(desc.transform.rotation), + ToJoltMotionType(desc.type), + ToJoltObjectLayer(desc.type)); + + settings.mFriction = desc.material.friction; + settings.mRestitution = desc.material.restitution; + settings.mIsSensor = desc.shape.isTrigger; + settings.mAllowSleeping = desc.allowSleep; + settings.mGravityFactor = desc.useGravity ? 1.0f : 0.0f; + settings.mUserData = reinterpret_cast(desc.owner); + + if (desc.type == RigidbodyType::Dynamic) + { + settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia; + settings.mMassPropertiesOverride.mMass = std::max(0.0001f, desc.mass); + } + + const EActivation activation = + desc.type == RigidbodyType::Dynamic ? EActivation::Activate : EActivation::DontActivate; + + BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface(); + BodyID bodyID = bodyInterface.CreateAndAddBody(settings, activation); + + if (bodyID.IsInvalid()) + { + return {}; + } + + PhysicsBodyHandle handle{m_NextHandle++}; + + BodyRecord record{}; + record.alive = true; + record.bodyID = bodyID; + record.desc = desc; + + m_Bodies.emplace(handle.id, record); + + return handle; + } + + void DestroyBody(PhysicsBodyHandle body) + { + auto it = m_Bodies.find(body.id); + if (it == m_Bodies.end() || !it->second.alive) + { + return; + } + + BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface(); + bodyInterface.RemoveBody(it->second.bodyID); + bodyInterface.DestroyBody(it->second.bodyID); + + m_Bodies.erase(it); + } + + void Step(float fixedDt) + { + if (fixedDt <= 0.0f) + { + return; + } + + const int collisionSteps = std::max(1, static_cast(std::ceil(fixedDt / (1.0f / 60.0f)))); + + m_PhysicsSystem.Update( + fixedDt, + collisionSteps, + m_TempAllocator.get(), + m_JobSystem.get()); + } + + void SyncKinematicBodiesToPhysics() + { + BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface(); + + for (auto& [handleId, record] : m_Bodies) + { + if (!record.alive || record.desc.type != RigidbodyType::Kinematic || !record.desc.owner) + { + continue; + } + + Transform& transform = record.desc.owner->GetTransform(); + + bodyInterface.SetPositionAndRotation( + record.bodyID, + ToJoltRVec3(transform.GetWorldPosition()), + ToJoltQuat(transform.GetWorldRotation()), + EActivation::Activate); + } + } + + void SyncDynamicBodiesToTransforms() + { + BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface(); + + for (auto& [handleId, record] : m_Bodies) + { + if (!record.alive || record.desc.type != RigidbodyType::Dynamic || !record.desc.owner) + { + continue; + } + + RVec3 position{}; + Quat rotation{}; + bodyInterface.GetPositionAndRotation(record.bodyID, position, rotation); + + Transform& transform = record.desc.owner->GetTransform(); + transform.SetWorldPosition(FromJoltRVec3(position)); + transform.SetWorldRotation(FromJoltQuat(rotation)); + } + } + + void SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform) + { + if (BodyRecord* record = FindBody(body)) + { + m_PhysicsSystem.GetBodyInterface().SetPositionAndRotation( + record->bodyID, + ToJoltRVec3(transform.position), + ToJoltQuat(transform.rotation), + EActivation::Activate); + } + } + + PhysicsTransform GetBodyTransform(PhysicsBodyHandle body) const + { + const BodyRecord* record = FindBody(body); + if (!record) + { + return {}; + } + + RVec3 position{}; + Quat rotation{}; + m_PhysicsSystem.GetBodyInterface().GetPositionAndRotation(record->bodyID, position, rotation); + + return PhysicsTransform{ + .position = FromJoltRVec3(position), + .rotation = FromJoltQuat(rotation) + }; + } + + void SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity) + { + if (BodyRecord* record = FindBody(body)) + { + m_PhysicsSystem.GetBodyInterface().SetLinearVelocity(record->bodyID, ToJoltVec3(velocity)); + } + } + + glm::vec3 GetLinearVelocity(PhysicsBodyHandle body) const + { + const BodyRecord* record = FindBody(body); + if (!record) + { + return glm::vec3{0.0f}; + } + + return FromJoltVec3(m_PhysicsSystem.GetBodyInterface().GetLinearVelocity(record->bodyID)); + } + + void AddForce(PhysicsBodyHandle body, const glm::vec3& force) + { + if (BodyRecord* record = FindBody(body)) + { + m_PhysicsSystem.GetBodyInterface().AddForce(record->bodyID, ToJoltVec3(force)); + } + } + + void AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse) + { + if (BodyRecord* record = FindBody(body)) + { + m_PhysicsSystem.GetBodyInterface().AddImpulse(record->bodyID, ToJoltVec3(impulse)); + } + } + + bool Raycast(const glm::vec3& origin, + const glm::vec3& direction, + float maxDistance, + PhysicsRaycastHit& hit) const + { + if (maxDistance <= 0.0f || glm::length2(direction) <= 0.0000001f) + { + return false; + } + + const glm::vec3 directionNormalized = glm::normalize(direction); + + RRayCast ray{ + ToJoltRVec3(origin), + ToJoltVec3(directionNormalized * maxDistance) + }; + + RayCastResult result{}; + if (!m_PhysicsSystem.GetNarrowPhaseQuery().CastRay(ray, result)) + { + return false; + } + + hit.body = FindHandle(result.mBodyID); + hit.distance = result.mFraction * maxDistance; + hit.point = origin + directionNormalized * hit.distance; + hit.object = nullptr; + hit.normal = glm::vec3{0.0f, 1.0f, 0.0f}; + + BodyLockRead lock(m_PhysicsSystem.GetBodyLockInterface(), result.mBodyID); + if (lock.Succeeded()) + { + const Body& body = lock.GetBody(); + + hit.object = reinterpret_cast(body.GetUserData()); + hit.normal = FromJoltVec3( + body.GetWorldSpaceSurfaceNormal( + result.mSubShapeID2, + ray.GetPointOnRay(result.mFraction) + ) + ); + } + + return true; + } + +private: + struct BodyRecord + { + bool alive{false}; + BodyID bodyID{}; + PhysicsBodyDesc desc{}; + }; + + BodyRecord* FindBody(PhysicsBodyHandle body) + { + auto it = m_Bodies.find(body.id); + if (it == m_Bodies.end()) + { + return nullptr; + } + + return &it->second; + } + + const BodyRecord* FindBody(PhysicsBodyHandle body) const + { + auto it = m_Bodies.find(body.id); + if (it == m_Bodies.end()) + { + return nullptr; + } + + return &it->second; + } + + PhysicsBodyHandle FindHandle(BodyID bodyID) const + { + for (const auto& [handleId, record] : m_Bodies) + { + if (record.alive && record.bodyID == bodyID) + { + return PhysicsBodyHandle{handleId}; + } + } + + return {}; + } + + Settings m_Settings{}; + + BPLayerInterfaceImpl m_BPLayerInterface{}; + ObjectVsBroadPhaseLayerFilterImpl m_ObjectVsBroadPhaseLayerFilter{}; + ObjectLayerPairFilterImpl m_ObjectLayerPairFilter{}; + + PhysicsSystem m_PhysicsSystem{}; + + std::unique_ptr m_TempAllocator; + std::unique_ptr m_JobSystem; + + std::unordered_map m_Bodies; + std::uint32_t m_NextHandle{0}; +}; + +JoltPhysicsWorld::JoltPhysicsWorld(const Settings& settings) + : m_Impl(std::make_unique(settings)) +{ +} + +JoltPhysicsWorld::~JoltPhysicsWorld() = default; + +void JoltPhysicsWorld::Step(float fixedDt) +{ + m_Impl->Step(fixedDt); +} + +void JoltPhysicsWorld::SyncKinematicBodiesToPhysics() +{ + m_Impl->SyncKinematicBodiesToPhysics(); +} + +void JoltPhysicsWorld::SyncDynamicBodiesToTransforms() +{ + m_Impl->SyncDynamicBodiesToTransforms(); +} + +PhysicsBodyHandle JoltPhysicsWorld::CreateBody(const PhysicsBodyDesc& desc) +{ + return m_Impl->CreateBody(desc); +} + +void JoltPhysicsWorld::DestroyBody(PhysicsBodyHandle body) +{ + m_Impl->DestroyBody(body); +} + +void JoltPhysicsWorld::SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform) +{ + m_Impl->SetBodyTransform(body, transform); +} + +PhysicsTransform JoltPhysicsWorld::GetBodyTransform(PhysicsBodyHandle body) const +{ + return m_Impl->GetBodyTransform(body); +} + +void JoltPhysicsWorld::SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity) +{ + m_Impl->SetLinearVelocity(body, velocity); +} + +glm::vec3 JoltPhysicsWorld::GetLinearVelocity(PhysicsBodyHandle body) const +{ + return m_Impl->GetLinearVelocity(body); +} + +void JoltPhysicsWorld::AddForce(PhysicsBodyHandle body, const glm::vec3& force) +{ + m_Impl->AddForce(body, force); +} + +void JoltPhysicsWorld::AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse) +{ + m_Impl->AddImpulse(body, impulse); +} + +bool JoltPhysicsWorld::Raycast(const glm::vec3& origin, + const glm::vec3& direction, + float maxDistance, + PhysicsRaycastHit& hit) const +{ + return m_Impl->Raycast(origin, direction, maxDistance, hit); +} diff --git a/destrum/src/Physics/PhysicsSceneBridge.cpp b/destrum/src/Physics/PhysicsSceneBridge.cpp index 47ac84f..f189645 100644 --- a/destrum/src/Physics/PhysicsSceneBridge.cpp +++ b/destrum/src/Physics/PhysicsSceneBridge.cpp @@ -24,12 +24,7 @@ void PhysicsSceneBridge::UnregisterGameObject(GameObject& object) { } void PhysicsSceneBridge::FixedUpdate(float fixedDt) { - if (auto* simple = dynamic_cast(m_World.get())) { - simple->SyncKinematicBodiesToPhysics(); - simple->Step(fixedDt); - simple->SyncDynamicBodiesToTransforms(); - return; - } - + m_World->SyncKinematicBodiesToPhysics(); m_World->Step(fixedDt); + m_World->SyncDynamicBodiesToTransforms(); } diff --git a/destrum/third_party/CMakeLists.txt b/destrum/third_party/CMakeLists.txt index 97ecf76..4f23f46 100644 --- a/destrum/third_party/CMakeLists.txt +++ b/destrum/third_party/CMakeLists.txt @@ -122,3 +122,10 @@ set(USE_F16C ON CACHE BOOL "" FORCE) set(USE_FMADD ON CACHE BOOL "" FORCE) add_subdirectory(jolt/Build) + +# 3rdparty/tracy is the Tracy repo +option(TRACY_ENABLE "" ON) +option(TRACY_ON_DEMAND "" ON) + +add_subdirectory(tracy) + diff --git a/destrum/third_party/assimp b/destrum/third_party/assimp index e13e0b5..818f73f 160000 --- a/destrum/third_party/assimp +++ b/destrum/third_party/assimp @@ -1 +1 @@ -Subproject commit e13e0b5b7da1d6d80b2ee12b043f1253a34d2ff9 +Subproject commit 818f73f99ab429928d5b5c406352a356939a420b diff --git a/destrum/third_party/fmt b/destrum/third_party/fmt index 7ad8004..588b3a0 160000 --- a/destrum/third_party/fmt +++ b/destrum/third_party/fmt @@ -1 +1 @@ -Subproject commit 7ad8004d575a7eb5aed7b5911c1ae298f7258052 +Subproject commit 588b3a0f8f6a8bcf2a959cae882d5b2703e86737 diff --git a/destrum/third_party/freetype b/destrum/third_party/freetype index 23b6cd2..25a08f2 160000 --- a/destrum/third_party/freetype +++ b/destrum/third_party/freetype @@ -1 +1 @@ -Subproject commit 23b6cd27ff19b70cbf98e058cd2cf0647d5284ff +Subproject commit 25a08f24cfc0da879d1938352d026532f280b77e diff --git a/destrum/third_party/glfw b/destrum/third_party/glfw index dbadda2..567b1ec 160000 --- a/destrum/third_party/glfw +++ b/destrum/third_party/glfw @@ -1 +1 @@ -Subproject commit dbadda26835ec5089ef922e6c290bcf58cf12056 +Subproject commit 567b1ec2442d59525e24c19e8d413df6baf02496 diff --git a/destrum/third_party/glm b/destrum/third_party/glm index 8f6213d..6f14f47 160000 --- a/destrum/third_party/glm +++ b/destrum/third_party/glm @@ -1 +1 @@ -Subproject commit 8f6213d379a904f5ae910e09a114e066e25faf57 +Subproject commit 6f14f4792a0cde5d0cf2c910506724d61cb95834 diff --git a/destrum/third_party/jolt b/destrum/third_party/jolt index 33a8797..f26e382 160000 --- a/destrum/third_party/jolt +++ b/destrum/third_party/jolt @@ -1 +1 @@ -Subproject commit 33a879752128c3740f8277301aefbab06bad76fb +Subproject commit f26e382dd06653d8bf9de71371c4ccd92ab667d1 diff --git a/destrum/third_party/json b/destrum/third_party/json index 30b2817..25c58ac 160000 --- a/destrum/third_party/json +++ b/destrum/third_party/json @@ -1 +1 @@ -Subproject commit 30b28175e46827153d8a51d75540ef888aa83d8b +Subproject commit 25c58ac6bd88ccd55084e341f7ecb6ef88d527af diff --git a/destrum/third_party/sdl b/destrum/third_party/sdl index 3eba0b6..b8b3f5e 160000 --- a/destrum/third_party/sdl +++ b/destrum/third_party/sdl @@ -1 +1 @@ -Subproject commit 3eba0b6f8a21392f47b1b53a476e7633048de9b1 +Subproject commit b8b3f5ef2001cbe7c11f62d41a9bf47d4a2d8b07 diff --git a/destrum/third_party/spdlog b/destrum/third_party/spdlog index 32dd298..f544fce 160000 --- a/destrum/third_party/spdlog +++ b/destrum/third_party/spdlog @@ -1 +1 @@ -Subproject commit 32dd298dc2d60fe0454e70b818d64941392c5b41 +Subproject commit f544fce006850a58063277068948dcda5681a27c diff --git a/destrum/third_party/tinyexr b/destrum/third_party/tinyexr index 90a147c..97b265d 160000 --- a/destrum/third_party/tinyexr +++ b/destrum/third_party/tinyexr @@ -1 +1 @@ -Subproject commit 90a147c7114af250c5ac58cb04950c46e509cb8a +Subproject commit 97b265d8ce5e7b6ed351592add2beeb601ac5463 diff --git a/destrum/third_party/tinygltf b/destrum/third_party/tinygltf index 81bd50c..a434ee0 160000 --- a/destrum/third_party/tinygltf +++ b/destrum/third_party/tinygltf @@ -1 +1 @@ -Subproject commit 81bd50c1062fdb956e878efa2a9234b2b9ec91ec +Subproject commit a434ee02066c2d9b62a3504876aed38e6e399fe0 diff --git a/destrum/third_party/tracy b/destrum/third_party/tracy new file mode 160000 index 0000000..05cceee --- /dev/null +++ b/destrum/third_party/tracy @@ -0,0 +1 @@ +Subproject commit 05cceee0df3b8d7c6fa87e9638af311dbabc63cb diff --git a/destrum/third_party/vk-bootstrap b/destrum/third_party/vk-bootstrap index 7028d6f..9539275 160000 --- a/destrum/third_party/vk-bootstrap +++ b/destrum/third_party/vk-bootstrap @@ -1 +1 @@ -Subproject commit 7028d6f652fdca9da332b4b4df25860ae19302fb +Subproject commit 95392756ec055c6887e5a4e9cdaae0456e900673 diff --git a/destrum/third_party/vma b/destrum/third_party/vma index e722e57..3aa9212 160000 --- a/destrum/third_party/vma +++ b/destrum/third_party/vma @@ -1 +1 @@ -Subproject commit e722e57c891a8fbe3cc73ca56c19dd76be242759 +Subproject commit 3aa921224c154a0d2c43912bc88e1c42ce1f7607 diff --git a/destrum/third_party/volk b/destrum/third_party/volk index bf84ce9..477a354 160000 --- a/destrum/third_party/volk +++ b/destrum/third_party/volk @@ -1 +1 @@ -Subproject commit bf84ce9573cffcaa0dae4e5f4d77e44e0dfb9d8b +Subproject commit 477a354c50c233ac95fb52c4250815d08f6a1571 diff --git a/lightkeeper/src/Lightkeeper.cpp b/lightkeeper/src/Lightkeeper.cpp index d8a3b87..0d9271f 100644 --- a/lightkeeper/src/Lightkeeper.cpp +++ b/lightkeeper/src/Lightkeeper.cpp @@ -23,13 +23,16 @@ #include "destrum/Util/ModelDocUtils.h" -LightKeeper::LightKeeper() : App(), renderer(meshCache, materialCache) { +LightKeeper::LightKeeper() : App(), renderer(meshCache, materialCache) +{ } -LightKeeper::~LightKeeper() { +LightKeeper::~LightKeeper() +{ } -void LightKeeper::customInit() { +void LightKeeper::customInit() +{ materialCache.init(gfxDevice); renderer.init(gfxDevice, m_params.renderSize); @@ -48,7 +51,7 @@ void LightKeeper::customInit() { ); ModelDocUtils::LogModelDocSummary(kittyModel, "kitty.glb"); - const auto &kittyPrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(kittyModel, "game://kitty.glb"); + const auto& kittyPrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(kittyModel, "game://kitty.glb"); testMesh = kittyPrimitive.mesh; testMesh.name = "Test Mesh"; @@ -75,40 +78,7 @@ void LightKeeper::customInit() { camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f))); - auto &scene = SceneManager::GetInstance().CreateScene("Main"); - - // auto testCube = std::make_shared("TestCube"); - // auto meshComp = testCube->AddComponent(); - // meshComp->SetMeshID(testMeshID); - // meshComp->SetMaterialID(testMaterialID); - const int count = 100; - const float radius = 5.0f; - - const float orbitRadius = 5.0f; - - for (int i = 0; i < count; ++i) { - // auto childCube = std::make_shared(fmt::format("ChildCube{}", i)); - // - // auto childMeshComp = childCube->AddComponent(); - // childMeshComp->SetMeshID(testMeshID); - // childMeshComp->SetMaterialID(testMaterialID); - // - // childCube->GetTransform().SetWorldScale(glm::vec3(0.1f)); - // - // // Add orbit + self spin - // auto orbit = childCube->AddComponent(orbitRadius, glm::vec3(0.0f)); - // orbit->Randomize(1337u + (uint32_t)i); // stable random per index - // - // scene.Add(childCube); - } - // testCube->AddComponent(glm::vec3(0, 1, 0), glm::radians(10.0f)); // spin around Y, rad/sec - //rotate 180 around X axis - // testCube->GetTransform().SetLocalRotation(glm::quat(glm::vec3(glm::radians(180.0f), 0.0f, 0.0f))); - // - auto globeRoot = std::make_shared("GlobeRoot"); - globeRoot->GetTransform().SetWorldPosition(glm::vec3(0.0f)); - globeRoot->AddComponent(glm::vec3(0, 1, 0), 1.0f); // spin around Y, rad/sec - scene.Add(globeRoot); + auto& scene = SceneManager::GetInstance().CreateScene("Main"); // scene.Add(testCube); @@ -137,7 +107,7 @@ void LightKeeper::customInit() { ); ModelDocUtils::LogModelDocSummary(planeModel, "plane.glb"); - const auto &planePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(planeModel, "game://plane.glb"); + const auto& planePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(planeModel, "game://plane.glb"); const auto planeMeshID = meshCache.addMesh(gfxDevice, planePrimitive.mesh); const auto planeTexturePath = ModelDocUtils::PickTexturePath( @@ -160,6 +130,11 @@ void LightKeeper::customInit() { planeMeshComp->SetMaterialID(planeMaterialID); planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f)); planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f)); + planeObj->AddComponent(glm::vec3{10.0f, 0.5f, 10.0f}); + + auto* floorRb = planeObj->AddComponent(); + floorRb->SetType(RigidbodyType::Static); + scene.GetPhysics().RegisterGameObject(*planeObj); scene.Add(planeObj); const auto CharObj = std::make_shared("Character"); @@ -284,112 +259,167 @@ void LightKeeper::customInit() { // } + { + const auto CharObj = std::make_shared("Character"); + + ModelDoc::LoadOptions characterOptions{}; + characterOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode; + characterOptions.loadMaterials = true; + characterOptions.loadSkeleton = true; + characterOptions.loadAnimations = false; + + auto charModel = ModelDoc::LoadModel( + AssetFS::GetInstance() + .GetFullPath("engine://characterMedium.fbx") + .generic_string(), + characterOptions + ); + + const auto &charPrimitive = + ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow( + charModel, + "engine://characterMedium.fbx" + ); + + const auto charMeshID = meshCache.addMesh(gfxDevice, charPrimitive.mesh); + + const auto charTextureID = gfxDevice.loadImageFromFile( + AssetFS::GetInstance().GetFullPath("engine://textures/criminalMaleA.png") + ); + + const auto charMaterialID = materialCache.addMaterial(gfxDevice, { + .baseColor = ModelDocUtils::GetImportedBaseColor( + charModel, charPrimitive), + .diffuseTexture = charTextureID, + .name = ModelDocUtils::GetImportedMaterialName( + charModel, + charPrimitive, + "CharacterMaterial" + ), + }); + + const auto charMeshComp = CharObj->AddComponent(); + charMeshComp->SetMeshID(charMeshID); + charMeshComp->SetMaterialID(charMaterialID); + + const auto animator = CharObj->AddComponent(); + animator->setSkeleton(charModel.skeleton); + + auto runClips = ModelDoc::LoadAnimationClips( + AssetFS::GetInstance() + .GetFullPath("engine://run.fbx") + .generic_string(), + charModel.skeleton + ); + + for (auto &clip: runClips) { + spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration); + animator->addClip(std::make_shared(std::move(clip))); + } + + if (!runClips.empty()) { + animator->play("Root|Run"); + } + + CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f)); + CharObj->GetTransform().SetWorldPosition(glm::vec3(5, 0, 0)); + scene.Add(CharObj); + } + + auto cubeModel = ModelDoc::LoadModel( + AssetFS::GetInstance() + .GetFullPath("engine://cube.fbx") + .generic_string(), + staticModelOptions + ); + ModelDocUtils::LogModelDocSummary(cubeModel, "cube.fbx"); + + const auto& cubePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(cubeModel, "game://cube.fbx"); + + + const auto eliasTextuerPath = ModelDocUtils::PickTexturePath( + cubeModel, + cubePrimitive, + AssetFS::GetInstance().GetFullPath("game://grass.png") + ); + + const auto eliasTextueID = gfxDevice.loadImageFromFile(eliasTextuerPath); + const auto eliasMaterialID = materialCache.addMaterial(gfxDevice, { + .baseColor = ModelDocUtils::GetImportedBaseColor( + planeModel, planePrimitive), + .textureFilteringMode = + TextureFilteringMode::Anisotropic, + .diffuseTexture = eliasTextueID, + .name = ModelDocUtils::GetImportedMaterialName( + planeModel, planePrimitive, "GroundPlaneMaterial"), + }); + + const auto cubeMeshID = meshCache.addMesh(gfxDevice, cubePrimitive.mesh); + // + // for (int i{0}; i < 100; i++) // { - // const auto CharObj = std::make_shared("Character"); + // auto cube = std::make_shared("Cube"); // - // ModelDoc::LoadOptions characterOptions{}; - // characterOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode; - // characterOptions.loadMaterials = true; - // characterOptions.loadSkeleton = true; - // characterOptions.loadAnimations = false; + // cube->AddComponent(glm::vec3{0.5f}); + // cube->AddComponent(); // - // auto charModel = ModelDoc::LoadModel( - // AssetFS::GetInstance() - // .GetFullPath("engine://characterMedium.fbx") - // .generic_string(), - // characterOptions - // ); + // auto meshComp = cube->AddComponent(); + // meshComp->SetMeshID(cubeMeshID); + // meshComp->SetMaterialID(eliasMaterialID); // - // const auto &charPrimitive = - // ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow( - // charModel, - // "engine://characterMedium.fbx" - // ); + // cube->GetTransform().SetWorldPosition(glm::vec3(0.0f, i, 0.0f)); + // cube->GetTransform().SetWorldScale(glm::vec3(0.005f)); // - // const auto charMeshID = meshCache.addMesh(gfxDevice, charPrimitive.mesh); - // - // const auto charTextureID = gfxDevice.loadImageFromFile( - // AssetFS::GetInstance().GetFullPath("engine://textures/criminalMaleA.png") - // ); - // - // const auto charMaterialID = materialCache.addMaterial(gfxDevice, { - // .baseColor = ModelDocUtils::GetImportedBaseColor( - // charModel, charPrimitive), - // .diffuseTexture = charTextureID, - // .name = ModelDocUtils::GetImportedMaterialName( - // charModel, - // charPrimitive, - // "CharacterMaterial" - // ), - // }); - // - // const auto charMeshComp = CharObj->AddComponent(); - // charMeshComp->SetMeshID(charMeshID); - // charMeshComp->SetMaterialID(charMaterialID); - // - // const auto animator = CharObj->AddComponent(); - // animator->setSkeleton(charModel.skeleton); - // - // auto runClips = ModelDoc::LoadAnimationClips( - // AssetFS::GetInstance() - // .GetFullPath("engine://run.fbx") - // .generic_string(), - // charModel.skeleton - // ); - // - // for (auto &clip: runClips) { - // spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration); - // animator->addClip(std::make_shared(std::move(clip))); - // } - // - // if (!runClips.empty()) { - // animator->play("Root|Run"); - // } - // - // CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f)); - // CharObj->GetTransform().SetWorldPosition(glm::vec3(5, 0, 0)); - // scene.Add(CharObj); + // scene.Add(cube); + // scene.GetPhysics().RegisterGameObject(*cube); // } - { - auto cubeModel = ModelDoc::LoadModel( - AssetFS::GetInstance() - .GetFullPath("engine://cube.fbx") - .generic_string(), - staticModelOptions - ); - ModelDocUtils::LogModelDocSummary(cubeModel, "cube.fbx"); - - const auto &cubePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(cubeModel, "game://cube.fbx"); - const auto cubeMeshID = meshCache.addMesh(gfxDevice, cubePrimitive.mesh); - - auto cube = std::make_shared("Cube"); - - cube->AddComponent(glm::vec3{0.5f}); - cube->AddComponent(); - - auto meshComp = cube->AddComponent(); - meshComp->SetMeshID(cubeMeshID); - meshComp->SetMaterialID(charMaterialID); - - cube->GetTransform().SetWorldPosition(glm::vec3(0.0f, 2.0f, 0.0f)); - cube->GetTransform().SetWorldScale(glm::vec3(0.005f)); - - scene.Add(cube); - scene.GetPhysics().RegisterGameObject(*cube); - } + // const int cubeCount = 10; + // const float spacing = 1.0f; + // + // for (int x = 0; x < cubeCount; x++) + // { + // for (int y = 0; y < cubeCount; y++) + // { + // for (int z = 0; z < cubeCount; z++) + // { + // auto cube = std::make_shared("Cube"); + // + // cube->AddComponent(glm::vec3{0.5f}); + // cube->AddComponent(); + // + // auto meshComp = cube->AddComponent(); + // meshComp->SetMeshID(cubeMeshID); + // meshComp->SetMaterialID(eliasMaterialID); + // + // cube->GetTransform().SetWorldPosition(glm::vec3( + // (x - cubeCount / 2.0f) * spacing, + // y * spacing + 5, + // (z - cubeCount / 2.0f) * spacing + // )); + // + // cube->GetTransform().SetWorldScale(glm::vec3(0.005f)); + // + // scene.Add(cube); + // scene.GetPhysics().RegisterGameObject(*cube); + // } + // } + // } } -void LightKeeper::customUpdate(float dt) { +void LightKeeper::customUpdate(float dt) +{ camera.Update(dt); SceneManager::GetInstance().Update(); - if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_1)) { + if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_1)) + { renderer.setRenderWireframe(!renderer.getRenderWireframe()); } } -void LightKeeper::customDraw() { +void LightKeeper::customDraw() +{ renderer.beginDrawing(gfxDevice); const RenderContext ctx{ @@ -408,7 +438,7 @@ void LightKeeper::customDraw() { renderer.endDrawing(); const auto cmd = gfxDevice.beginFrame(); - const auto &drawImage = renderer.getDrawImage(gfxDevice); + const auto& drawImage = renderer.getDrawImage(gfxDevice); renderer.draw( cmd, gfxDevice, camera, GameRenderer::SceneData{ @@ -423,14 +453,16 @@ void LightKeeper::customDraw() { }); } -void LightKeeper::customCleanup() { +void LightKeeper::customCleanup() +{ auto device = gfxDevice.getDevice().device; vkDeviceWaitIdle(device); SceneManager::GetInstance().Destroy(); - if (skyboxCubemap) { + if (skyboxCubemap) + { skyboxCubemap.reset(); } @@ -445,7 +477,8 @@ void LightKeeper::customFixedUpdate(float dt) SceneManager::GetInstance().FixedUpdate(dt); } -void LightKeeper::onWindowResize(int newWidth, int newHeight) { +void LightKeeper::onWindowResize(int newWidth, int newHeight) +{ renderer.resize(gfxDevice, glm::ivec2{newWidth, newHeight}); const float aspectRatio = static_cast(newWidth) / static_cast(newHeight); camera.setAspectRatio(aspectRatio);