feat: add tracy / various fixes

This commit is contained in:
2026-06-24 03:00:53 +02:00
parent 067caf5fe6
commit 9d3d14a54d
33 changed files with 1492 additions and 336 deletions
+3
View File
@@ -47,3 +47,6 @@
[submodule "destrum/third_party/jolt"] [submodule "destrum/third_party/jolt"]
path = destrum/third_party/jolt path = destrum/third_party/jolt
url = https://github.com/jrouwe/JoltPhysics.git url = https://github.com/jrouwe/JoltPhysics.git
[submodule "destrum/third_party/tracy"]
path = destrum/third_party/tracy
url = https://github.com/wolfpld/tracy.git
+3 -1
View File
@@ -56,7 +56,7 @@ set(SRC_FILES
"src/Physics/PhysicsWorld.cpp" "src/Physics/PhysicsWorld.cpp"
"src/Physics/SimplePhysicsWorld.cpp" "src/Physics/SimplePhysicsWorld.cpp"
"src/Physics/PhysicsSceneBridge.cpp" "src/Physics/PhysicsSceneBridge.cpp"
src/Components/Physics/BoxCollider.cpp "src/Physics/JoltPhysicsWorld.cpp"
) )
add_library(destrum ${SRC_FILES}) add_library(destrum ${SRC_FILES})
@@ -89,6 +89,7 @@ target_link_libraries(destrum
assimp assimp
imgui imgui
Jolt Jolt
Tracy::TracyClient
PRIVATE PRIVATE
freetype::freetype freetype::freetype
@@ -99,6 +100,7 @@ target_compile_definitions(destrum
PUBLIC PUBLIC
VK_NO_PROTOTYPES VK_NO_PROTOTYPES
VMA_VULKAN_VERSION=1003000 VMA_VULKAN_VERSION=1003000
TRACY_VK_USE_SYMBOL_TABLE
# VOLK_DEFAULT_VISIBILITY # FIXME: doesn't work for some reason # VOLK_DEFAULT_VISIBILITY # FIXME: doesn't work for some reason
) )
Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.
+8
View File
@@ -60,6 +60,14 @@ protected:
bool resizePending = false; bool resizePending = false;
std::chrono::steady_clock::time_point lastResizeTime{}; 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);
}; };
@@ -29,6 +29,13 @@
class MeshCache; class MeshCache;
class ImguiPass; class ImguiPass;
#if defined(TRACY_ENABLE)
namespace tracy { class VkCtx; }
using DestrumTracyVkCtx = tracy::VkCtx*;
#else
using DestrumTracyVkCtx = void*;
#endif
namespace { namespace {
using ImmediateExecuteFunction = std::function<void(VkCommandBuffer)>; using ImmediateExecuteFunction = std::function<void(VkCommandBuffer)>;
@@ -126,12 +133,16 @@ public:
return swapchain.getImageView(imageIndex); return swapchain.getImageView(imageIndex);
} }
DestrumTracyVkCtx getTracyVkCtx() const { return tracyVkCtx; }
private: private:
vkb::Instance instance; vkb::Instance instance;
vkb::PhysicalDevice physicalDevice; vkb::PhysicalDevice physicalDevice;
vkb::Device device; vkb::Device device;
VmaAllocator allocator; VmaAllocator allocator;
DestrumTracyVkCtx tracyVkCtx = nullptr;
std::uint32_t graphicsQueueFamily; std::uint32_t graphicsQueueFamily;
VkQueue graphicsQueue; VkQueue graphicsQueue;
@@ -149,6 +160,8 @@ private:
ImageCache imageCache; ImageCache imageCache;
bool m_vSync = false;
static uint32_t BytesPerTexel(VkFormat fmt) { static uint32_t BytesPerTexel(VkFormat fmt) {
switch (fmt) { switch (fmt) {
case VK_FORMAT_R8_UNORM: return 1; case VK_FORMAT_R8_UNORM: return 1;
@@ -0,0 +1,61 @@
#pragma once
#include <cstdint>
#include <memory>
#include <glm/glm.hpp>
#include <destrum/Physics/PhysicsWorld.h>
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<Impl> m_Impl;
};
@@ -25,8 +25,8 @@ public:
// - Kinematic: Transform -> physics body before Step() // - Kinematic: Transform -> physics body before Step()
// - Dynamic: physics body -> Transform after Step() // - Dynamic: physics body -> Transform after Step()
void SyncKinematicBodiesToPhysics(); virtual void SyncKinematicBodiesToPhysics();
void SyncDynamicBodiesToTransforms(); virtual void SyncDynamicBodiesToTransforms();
virtual PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) = 0; virtual PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) = 0;
virtual void DestroyBody(PhysicsBodyHandle body) = 0; virtual void DestroyBody(PhysicsBodyHandle body) = 0;
+2 -1
View File
@@ -6,6 +6,7 @@
#include <destrum/Event.h> #include <destrum/Event.h>
#include <destrum/Scene/SceneManager.h> #include <destrum/Scene/SceneManager.h>
#include "destrum/Physics/JoltPhysicsWorld.h"
#include "destrum/Physics/PhysicsSceneBridge.h" #include "destrum/Physics/PhysicsSceneBridge.h"
class GameObject; class GameObject;
@@ -68,7 +69,7 @@ public:
private: private:
explicit Scene(const std::string& name); explicit Scene(const std::string& name);
PhysicsSceneBridge m_Physics{std::make_unique<SimplePhysicsWorld>()}; PhysicsSceneBridge m_Physics{std::make_unique<JoltPhysicsWorld>()};
std::string m_name; std::string m_name;
+151 -13
View File
@@ -10,7 +10,16 @@
#include "glm/gtx/transform.hpp" #include "glm/gtx/transform.hpp"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include <Jolt/Jolt.h> #include <tracy/Tracy.hpp>
#include <common/TracySystem.hpp>
struct TracyFrameScope
{
~TracyFrameScope()
{
FrameMark;
}
};
App::App() App::App()
{ {
@@ -19,7 +28,9 @@ App::App()
void App::init(const AppParams& params) void App::init(const AppParams& params)
{ {
m_params = params; m_params = params;
ZoneScopedN("App::init");
tracy::SetThreadName("Main Thread");
TracySetProgramName(params.appName.c_str());
AssetFS::GetInstance().Init(params.exeDir); AssetFS::GetInstance().Init(params.exeDir);
// AssetFS::GetInstance().Mount("engine", params.exeDir / "assets" / "engine"); // AssetFS::GetInstance().Mount("engine", params.exeDir / "assets" / "engine");
// AssetFS::GetInstance().Mount("game", params.exeDir / "assets" / "game"); // AssetFS::GetInstance().Mount("game", params.exeDir / "assets" / "game");
@@ -53,16 +64,21 @@ void App::init(const AppParams& params)
void App::run() void App::run()
{ {
Time::GetInstance().Update(); // initialize delta timing ZoneScopedN("App::run");
Time::GetInstance().Update();
const float fixedDt = static_cast<float>(Time::GetInstance().FixedDeltaTime()); const float fixedDt = static_cast<float>(Time::GetInstance().FixedDeltaTime());
const int maxSteps = 5; // prevent spiral of death const int maxSteps = 5;
float accumulator = 0.0f; float accumulator = 0.0f;
isRunning = true; isRunning = true;
while (isRunning) while (isRunning)
{ {
// ---- Update timing --- TracyFrameScope tracyFrame;
ZoneScopedN("App::Frame");
Time::GetInstance().Update(); Time::GetInstance().Update();
float dt = static_cast<float>(Time::GetInstance().DeltaTime()); float dt = static_cast<float>(Time::GetInstance().DeltaTime());
@@ -72,36 +88,46 @@ void App::run()
{ {
float newFPS = 1.0f / dt; float newFPS = 1.0f / dt;
avgFPS = std::lerp(avgFPS, newFPS, 0.1f); avgFPS = std::lerp(avgFPS, newFPS, 0.1f);
TracyPlot("FPS", avgFPS);
TracyPlot("Delta Time ms", dt * 1000.0f);
} }
accumulator += dt; accumulator += dt;
{
ZoneScopedN("Input BeginFrame + Camera");
InputManager::GetInstance().BeginFrame(); InputManager::GetInstance().BeginFrame();
camera.Update(dt); camera.Update(dt);
}
{
ZoneScopedN("SDL Events");
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event))
{ {
ZoneScopedN("SDL Event");
imguiPass.handleEvent(event); imguiPass.handleEvent(event);
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)
{ {
isRunning = false; isRunning = false;
break; break;
} }
if (event.type == SDL_WINDOWEVENT) if (event.type == SDL_WINDOWEVENT)
{ {
switch (event.window.event) switch (event.window.event)
{ {
case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_RESIZED:
{
resizePending = true; resizePending = true;
lastResizeTime = std::chrono::steady_clock::now(); lastResizeTime = std::chrono::steady_clock::now();
break; break;
} }
} }
}
const bool mouseEvent = const bool mouseEvent =
event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONDOWN ||
event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONUP ||
@@ -119,36 +145,94 @@ void App::run()
if (!capturedByImgui) if (!capturedByImgui)
{ {
ZoneScopedN("Input ProcessEvent");
if (InputManager::GetInstance().ProcessEvent(event)) if (InputManager::GetInstance().ProcessEvent(event))
{ {
isRunning = false; isRunning = false;
} }
} }
} }
}
if (!isRunning) break; if (!isRunning) break;
{
ZoneScopedN("ImGui BeginFrame");
imguiPass.beginFrame(); imguiPass.beginFrame();
}
{
ZoneScopedN("customUpdate");
customUpdate(dt); customUpdate(dt);
}
{
ZoneScopedN("Debug ImGui");
ImGui::Begin("Debug"); ImGui::Begin("Debug");
ImGui::Text("FPS: %.2f", avgFPS); ImGui::Text("FPS: %.2f", avgFPS);
ImGui::End(); 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(); imguiPass.endFrame();
}
{
ZoneScopedN("FixedUpdate");
int steps = 0; 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) while (accumulator >= fixedDt && steps < maxSteps)
{ {
// physics.Update(fixedDt); ZoneScopedN("Fixed Step");
customFixedUpdate(fixedDt);
// physics.Step(fixedDt); customFixedUpdate(fixedDt * m_PhysicsTimeScale);
// physics.SyncTransforms();
accumulator -= fixedDt; accumulator -= fixedDt;
steps++; steps++;
} }
if (steps == maxSteps) accumulator = 0.0f;
if (steps == maxSteps)
{
accumulator = 0.0f;
}
}
m_PhysicsStepsLastFrame = steps;
TracyPlot("Fixed Steps", static_cast<int64_t>(steps));
}
const float alpha = accumulator / fixedDt; const float alpha = accumulator / fixedDt;
(void)alpha;
{
ZoneScopedN("Swapchain Resize Check");
if (gfxDevice.needsSwapchainRecreate() || resizePending) if (gfxDevice.needsSwapchainRecreate() || resizePending)
{ {
@@ -171,18 +255,26 @@ void App::run()
spdlog::info("Recreating swapchain to size: {}x{}", w, h); spdlog::info("Recreating swapchain to size: {}x{}", w, h);
{
ZoneScopedN("Recreate Swapchain");
gfxDevice.recreateSwapchain(w, h); gfxDevice.recreateSwapchain(w, h);
onWindowResize(w, h); onWindowResize(w, h);
}
resizePending = false; resizePending = false;
continue; continue;
} }
}
{
ZoneScopedN("customDraw");
customDraw(); customDraw();
}
if (frameLimit) if (frameLimit)
{ {
ZoneScopedN("Frame Limit Sleep");
auto sleepTime = Time::GetInstance().SleepDuration(); auto sleepTime = Time::GetInstance().SleepDuration();
if (sleepTime.count() > 0) if (sleepTime.count() > 0)
{ {
@@ -199,3 +291,49 @@ void App::cleanup()
spdlog::info("Cleaning up"); spdlog::info("Cleaning up");
customCleanup(); 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();
}
+88 -5
View File
@@ -19,13 +19,17 @@
#include "destrum/Graphics/imageLoader.h" #include "destrum/Graphics/imageLoader.h"
#include "destrum/Util/GameState.h" #include "destrum/Util/GameState.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "tracy/Tracy.hpp"
#include "tracy/Tracy.hpp"
#include "tracy/TracyVulkan.hpp"
GfxDevice::GfxDevice(): imageCache(*this) { GfxDevice::GfxDevice(): imageCache(*this) {
} }
void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) { void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) {
VK_CHECK(volkInitialize()); VK_CHECK(volkInitialize());
m_vSync = vSync;
instance = vkb::InstanceBuilder{} instance = vkb::InstanceBuilder{}
.set_app_name(appName.c_str()) .set_app_name(appName.c_str())
.set_app_version(1, 0, 0) .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)); 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 { // create white texture
std::uint32_t pixel = 0xFFFFFFFF; std::uint32_t pixel = 0xFFFFFFFF;
whiteImageId = createImage( 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) { void GfxDevice::recreateSwapchain(int width, int height) {
assert(width != 0 && height != 0); assert(width != 0 && height != 0);
waitIdle(); waitIdle();
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, true); swapchain.recreateSwapchain(*this, swapchainFormat, width, height, m_vSync);
} }
VkCommandBuffer GfxDevice::beginFrame() { VkCommandBuffer GfxDevice::beginFrame()
{
ZoneScopedN("GfxDevice::beginFrame");
{
ZoneScopedN("Swapchain BeginFrame");
swapchain.beginFrame(getCurrentFrameIndex()); swapchain.beginFrame(getCurrentFrameIndex());
}
const auto& frame = getCurrentFrame(); const auto& frame = getCurrentFrame();
const auto& cmd = frame.commandBuffer; const auto& cmd = frame.commandBuffer;
const auto cmdBeginInfo = VkCommandBufferBeginInfo{ const auto cmdBeginInfo = VkCommandBufferBeginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
}; };
{
ZoneScopedN("vkBeginCommandBuffer");
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
}
return cmd; return cmd;
} }
@@ -188,17 +231,34 @@ VulkanImmediateExecutor& GfxDevice::GetImmediateExecuter() {
} }
void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) { void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) {
ZoneScopedN("GfxDevice::endFrame");
// get swapchain image // 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) { if (swapchainImage == VK_NULL_HANDLE) {
spdlog::info("Swapchain is freaky, skipping frame..."); spdlog::info("Swapchain is freaky, skipping frame...");
return; return;
} }
// Fences are reset here to prevent the deadlock in case swapchain becomes dirty // Fences are reset here to prevent the deadlock in case swapchain becomes dirty
{
ZoneScopedN("Swapchain ResetFences");
swapchain.resetFences(getCurrentFrameIndex()); swapchain.resetFences(getCurrentFrameIndex());
}
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; { auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; {
ZoneScopedN("Clear Swapchain Image");
const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL); vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
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) { if (true) {
ZoneScopedN("Copy DrawImage To Swapchain");
// copy from draw image into swapchain // copy from draw image into swapchain
vkutil::transitionImage( vkutil::transitionImage(
cmd, cmd,
@@ -248,6 +310,8 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
// swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
if (props.imguiPass) { if (props.imguiPass) {
ZoneScopedN("ImGui Render");
props.imguiPass->render( props.imguiPass->render(
cmd, cmd,
swapchainImage, swapchainImage,
@@ -258,18 +322,37 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
} }
// prepare for present // prepare for present
{
ZoneScopedN("Transition Swapchain To Present");
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
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)); VK_CHECK(vkEndCommandBuffer(cmd));
}
// swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex); // swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex);
{
ZoneScopedN("Swapchain SubmitAndPresent");
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex()); swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
}
frameNumber++; frameNumber++;
FrameMark;
} }
void GfxDevice::cleanup() { void GfxDevice::cleanup() {
#if defined(TRACY_ENABLE)
if (tracyVkCtx) {
TracyVkDestroy(tracyVkCtx);
tracyVkCtx = nullptr;
}
#endif
} }
void GfxDevice::waitIdle() { void GfxDevice::waitIdle() {
@@ -98,9 +98,9 @@ void MeshPipeline::draw(VkCommandBuffer cmd,
for (const auto& dcIdx : drawCommands) { for (const auto& dcIdx : drawCommands) {
const auto& dc = dcIdx; const auto& dc = dcIdx;
// if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) { if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) {
// continue; continue;
// } }
ActualDrawCalls++; ActualDrawCalls++;
+49 -3
View File
@@ -5,6 +5,8 @@
#include "destrum/Util/GameState.h" #include "destrum/Util/GameState.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "tracy/TracyVulkan.hpp"
GameRenderer::GameRenderer(MeshCache& meshCache, MaterialCache& matCache): meshCache{meshCache}, materialCache{matCache} { GameRenderer::GameRenderer(MeshCache& meshCache, MaterialCache& matCache): meshCache{meshCache}, materialCache{matCache} {
} }
@@ -41,6 +43,11 @@ void GameRenderer::endDrawing() {
} }
void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData) { void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData) {
ZoneScopedN("GameRenderer::draw");
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "GameRenderer::draw");
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Skinning");
for (const auto& dc : meshDrawCommands) { for (const auto& dc : meshDrawCommands) {
if (!dc.skinnedMesh) { if (!dc.skinnedMesh) {
@@ -48,6 +55,8 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
} }
skinningPipeline->doSkinning(cmd, gfxDevice.getCurrentFrameIndex(), meshCache, dc); skinningPipeline->doSkinning(cmd, gfxDevice.getCurrentFrameIndex(), meshCache, dc);
} }
}
const auto gpuSceneData = GPUSceneData{ const auto gpuSceneData = GPUSceneData{
.view = sceneData.camera.GetViewMatrix(), .view = sceneData.camera.GetViewMatrix(),
.proj = sceneData.camera.GetProjectionMatrix(), .proj = sceneData.camera.GetProjectionMatrix(),
@@ -59,29 +68,52 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
.fogDensity = sceneData.fogDensity, .fogDensity = sceneData.fogDensity,
.materialsBuffer = materialCache.getMaterialDataBufferAddress(), .materialsBuffer = materialCache.getMaterialDataBufferAddress(),
}; };
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Material Buffer Barrier");
vkutil::bufferHostWriteToShaderReadBarrier( vkutil::bufferHostWriteToShaderReadBarrier(
cmd, cmd,
materialCache.getMaterialDataBuffer().buffer, materialCache.getMaterialDataBuffer().buffer,
0, 0,
VK_WHOLE_SIZE VK_WHOLE_SIZE
); );
sceneDataBuffer.uploadNewData(cmd, gfxDevice.getCurrentFrameIndex(), (void*)&gpuSceneData, sizeof(GPUSceneData)); }
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Upload Scene Data");
sceneDataBuffer.uploadNewData(
cmd,
gfxDevice.getCurrentFrameIndex(),
(void*)&gpuSceneData,
sizeof(GPUSceneData)
);
}
const auto& drawImage = gfxDevice.getImage(drawImageId); const auto& drawImage = gfxDevice.getImage(drawImageId);
const auto& depthImage = gfxDevice.getImage(depthImageId); const auto& depthImage = gfxDevice.getImage(depthImageId);
// vkutil::cmdBeginLabel(cmd, "Geometry"); // vkutil::cmdBeginLabel(cmd, "Geometry");
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Transition Draw Image");
vkutil::transitionImage( vkutil::transitionImage(
cmd, cmd,
drawImage.image, drawImage.image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
}
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Transition Depth Image");
vkutil::transitionImage( vkutil::transitionImage(
cmd, cmd,
depthImage.image, depthImage.image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
}
const auto renderInfo = vkutil::createRenderingInfo({ const auto renderInfo = vkutil::createRenderingInfo({
.renderExtent = drawImage.getExtent2D(), .renderExtent = drawImage.getExtent2D(),
@@ -91,7 +123,15 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
.depthImageClearValue = 1.f, .depthImageClearValue = 1.f,
}); });
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdBeginRendering");
vkCmdBeginRendering(cmd, &renderInfo.renderingInfo); vkCmdBeginRendering(cmd, &renderInfo.renderingInfo);
}
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::draw");
meshPipeline->draw( meshPipeline->draw(
cmd, cmd,
drawImage.getExtent2D(), drawImage.getExtent2D(),
@@ -102,14 +142,20 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
sceneDataBuffer.getBuffer(), sceneDataBuffer.getBuffer(),
meshDrawCommands, meshDrawCommands,
sortedMeshDrawCommands); sortedMeshDrawCommands);
}
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "SkyboxPipeline::draw");
skyboxPipeline->draw(cmd, gfxDevice, camera); skyboxPipeline->draw(cmd, gfxDevice, camera);
}
{
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdEndRendering");
vkCmdEndRendering(cmd); vkCmdEndRendering(cmd);
}
// vkutil::cmdEndLabel(cmd); // vkutil::cmdEndLabel(cmd);
} }
void GameRenderer::cleanup(GfxDevice& gfxDevice) { void GameRenderer::cleanup(GfxDevice& gfxDevice) {
+77 -4
View File
@@ -6,6 +6,7 @@
#include <destrum/Graphics/Init.h> #include <destrum/Graphics/Init.h>
#include "volk.h" #include "volk.h"
#include "tracy/Tracy.hpp"
void Swapchain::initSync(VkDevice device) { void Swapchain::initSync(VkDevice device) {
@@ -23,9 +24,14 @@ void Swapchain::initSync(VkDevice device) {
} }
void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint32_t width, std::uint32_t height, bool vSync) { void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint32_t width, std::uint32_t height, bool vSync) {
ZoneScopedN("Swapchain::createSwapchain");
m_gfxDevice = gfxDevice; m_gfxDevice = gfxDevice;
assert(format == VK_FORMAT_B8G8R8A8_SRGB && "TODO: test other formats"); assert(format == VK_FORMAT_B8G8R8A8_SRGB && "TODO: test other formats");
vSync = true; // vSync = true;
{
ZoneScopedN("vkb::SwapchainBuilder::build");
auto res = vkb::SwapchainBuilder{gfxDevice->getDevice()} auto res = vkb::SwapchainBuilder{gfxDevice->getDevice()}
.set_desired_format(VkSurfaceFormatKHR{ .set_desired_format(VkSurfaceFormatKHR{
@@ -44,9 +50,14 @@ void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint
// string_VkResult(res.full_error().vk_result))); // string_VkResult(res.full_error().vk_result)));
} }
m_swapchain = res.value(); m_swapchain = res.value();
}
{
ZoneScopedN("Get Swapchain Images / Views");
images = m_swapchain.get_images().value(); images = m_swapchain.get_images().value();
imageViews = m_swapchain.get_image_views().value(); imageViews = m_swapchain.get_image_views().value();
}
imageRenderSemaphores.resize(images.size()); imageRenderSemaphores.resize(images.size());
@@ -55,6 +66,8 @@ void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint
}; };
for (auto& sem: imageRenderSemaphores) { for (auto& sem: imageRenderSemaphores) {
ZoneScopedN("Create Image Render Semaphore");
VK_CHECK(vkCreateSemaphore(m_gfxDevice->getDevice(), &sci, nullptr, &sem)); VK_CHECK(vkCreateSemaphore(m_gfxDevice->getDevice(), &sci, nullptr, &sem));
} }
@@ -71,6 +84,8 @@ void Swapchain::recreateSwapchain(
std::uint32_t height, std::uint32_t height,
bool vSync) bool vSync)
{ {
ZoneScopedN("Swapchain::recreateSwapchain");
if (width == 0 || height == 0) { if (width == 0 || height == 0) {
dirty = true; dirty = true;
return; return;
@@ -78,10 +93,16 @@ void Swapchain::recreateSwapchain(
VkDevice device = gfxDevice.getDevice(); VkDevice device = gfxDevice.getDevice();
{
ZoneScopedN("vkDeviceWaitIdle");
vkDeviceWaitIdle(device); vkDeviceWaitIdle(device);
}
auto oldSwapchain = m_swapchain; auto oldSwapchain = m_swapchain;
{
ZoneScopedN("vkb::SwapchainBuilder::rebuild");
auto res = vkb::SwapchainBuilder{gfxDevice.getVkbDevice()} auto res = vkb::SwapchainBuilder{gfxDevice.getVkbDevice()}
.set_old_swapchain(oldSwapchain) .set_old_swapchain(oldSwapchain)
.set_desired_format(VkSurfaceFormatKHR{ .set_desired_format(VkSurfaceFormatKHR{
@@ -101,22 +122,38 @@ void Swapchain::recreateSwapchain(
// string_VkResult(res.full_error().vk_result))); // string_VkResult(res.full_error().vk_result)));
} }
m_swapchain = res.value();
}
{
ZoneScopedN("Destroy Old Image Render Semaphores");
for (auto sem : imageRenderSemaphores) { for (auto sem : imageRenderSemaphores) {
vkDestroySemaphore(device, sem, nullptr); vkDestroySemaphore(device, sem, nullptr);
} }
imageRenderSemaphores.clear(); imageRenderSemaphores.clear();
}
{
ZoneScopedN("Destroy Old Image Views");
for (auto imageView : imageViews) { for (auto imageView : imageViews) {
vkDestroyImageView(device, imageView, nullptr); vkDestroyImageView(device, imageView, nullptr);
} }
imageViews.clear(); imageViews.clear();
}
{
ZoneScopedN("Destroy Old Swapchain");
vkb::destroy_swapchain(oldSwapchain); vkb::destroy_swapchain(oldSwapchain);
}
m_swapchain = res.value(); {
ZoneScopedN("Get New Swapchain Images / Views");
images = m_swapchain.get_images().value(); images = m_swapchain.get_images().value();
imageViews = m_swapchain.get_image_views().value(); imageViews = m_swapchain.get_image_views().value();
}
VkSemaphoreCreateInfo sci{ VkSemaphoreCreateInfo sci{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
@@ -125,6 +162,8 @@ void Swapchain::recreateSwapchain(
imageRenderSemaphores.resize(images.size()); imageRenderSemaphores.resize(images.size());
for (auto& sem : imageRenderSemaphores) { for (auto& sem : imageRenderSemaphores) {
ZoneScopedN("Create New Image Render Semaphore");
VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &sem)); VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &sem));
} }
@@ -148,14 +187,26 @@ void Swapchain::cleanup() {
} }
void Swapchain::beginFrame(int index) const { void Swapchain::beginFrame(int index) const {
ZoneScopedN("Swapchain::beginFrame");
auto& frame = frames[index]; auto& frame = frames[index];
{
ZoneScopedN("vkWaitForFences");
VK_CHECK(vkWaitForFences(m_gfxDevice->getDevice(), 1, &frame.renderFence, true, std::numeric_limits<std::uint64_t>::max())); VK_CHECK(vkWaitForFences(m_gfxDevice->getDevice(), 1, &frame.renderFence, true, std::numeric_limits<std::uint64_t>::max()));
} }
}
void Swapchain::resetFences(int index) const { void Swapchain::resetFences(int index) const {
ZoneScopedN("Swapchain::resetFences");
auto& frame = frames[index]; auto& frame = frames[index];
{
ZoneScopedN("vkResetFences");
VK_CHECK(vkResetFences(m_gfxDevice->getDevice(), 1, &frame.renderFence)); VK_CHECK(vkResetFences(m_gfxDevice->getDevice(), 1, &frame.renderFence));
} }
}
struct SwapchainAcquireResult { struct SwapchainAcquireResult {
VkResult result = VK_SUCCESS; VkResult result = VK_SUCCESS;
@@ -164,14 +215,24 @@ struct SwapchainAcquireResult {
}; };
std::pair<VkImage, int> Swapchain::acquireNextImage(int index) { std::pair<VkImage, int> Swapchain::acquireNextImage(int index) {
ZoneScopedN("Swapchain::acquireNextImage");
std::uint32_t swapchainImageIndex{}; std::uint32_t swapchainImageIndex{};
const auto result = vkAcquireNextImageKHR(
VkResult result = VK_SUCCESS;
{
ZoneScopedN("vkAcquireNextImageKHR");
result = vkAcquireNextImageKHR(
m_gfxDevice->getDevice(), m_gfxDevice->getDevice(),
m_swapchain, m_swapchain,
std::numeric_limits<std::uint64_t>::max(), std::numeric_limits<std::uint64_t>::max(),
frames[index].swapchainSemaphore, frames[index].swapchainSemaphore,
VK_NULL_HANDLE, VK_NULL_HANDLE,
&swapchainImageIndex); &swapchainImageIndex);
}
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
dirty = true; dirty = true;
return {images[swapchainImageIndex], swapchainImageIndex}; return {images[swapchainImageIndex], swapchainImageIndex};
@@ -188,6 +249,8 @@ void Swapchain::submitAndPresent(
uint32_t imageIndex, // from vkAcquireNextImageKHR uint32_t imageIndex, // from vkAcquireNextImageKHR
uint32_t frameIndex) // 0..FRAMES_IN_FLIGHT-1 uint32_t frameIndex) // 0..FRAMES_IN_FLIGHT-1
{ {
ZoneScopedN("Swapchain::submitAndPresent");
auto& frame = frames[frameIndex]; // ✅ per-frame auto& frame = frames[frameIndex]; // ✅ per-frame
VkSemaphore renderFinished = imageRenderSemaphores[imageIndex]; // ✅ per-image VkSemaphore renderFinished = imageRenderSemaphores[imageIndex]; // ✅ per-image
@@ -209,7 +272,11 @@ void Swapchain::submitAndPresent(
renderFinished); // ✅ signal semaphore (per-image) renderFinished); // ✅ signal semaphore (per-image)
VkSubmitInfo2 submit = vkinit::submitInfo(&cmdInfo, &waitInfo, &signalInfo); VkSubmitInfo2 submit = vkinit::submitInfo(&cmdInfo, &waitInfo, &signalInfo);
{
ZoneScopedN("vkQueueSubmit2");
VK_CHECK(vkQueueSubmit2(graphicsQueue, 1, &submit, frame.renderFence)); // ✅ fence (per-frame) VK_CHECK(vkQueueSubmit2(graphicsQueue, 1, &submit, frame.renderFence)); // ✅ fence (per-frame)
}
// present // present
VkPresentInfoKHR presentInfo{ VkPresentInfoKHR presentInfo{
@@ -221,7 +288,13 @@ void Swapchain::submitAndPresent(
.pImageIndices = &imageIndex, // ✅ imageIndex, NOT frameIndex .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; if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) dirty = true;
else if (res != VK_SUCCESS) dirty = true; else if (res != VK_SUCCESS) dirty = true;
} }
+692
View File
@@ -0,0 +1,692 @@
#include <destrum/Physics/JoltPhysicsWorld.h>
// Jolt/Jolt.h must be included before other Jolt headers.
#include <Jolt/Jolt.h>
#include <Jolt/Core/Factory.h>
#include <Jolt/Core/JobSystemThreadPool.h>
#include <Jolt/Core/TempAllocator.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/Body/BodyInterface.h>
#include <Jolt/Physics/Body/MotionType.h>
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
#include <Jolt/Physics/Collision/CastResult.h>
#include <Jolt/Physics/Collision/ObjectLayer.h>
#include <Jolt/Physics/Collision/RayCast.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/PhysicsSystem.h>
#include <Jolt/RegisterTypes.h>
#include <algorithm>
#include <atomic>
#include <cmath>
#include <cstdarg>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <thread>
#include <unordered_map>
#include <glm/gtx/norm.hpp>
#include <destrum/Components/Physics/Rigidbody.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/ObjectModel/Transform.h>
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<float>(v.GetX()),
static_cast<float>(v.GetY()),
static_cast<float>(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<JPH::uint>(1, hardwareThreads > 0 ? hardwareThreads - 1 : 1);
m_TempAllocator = std::make_unique<TempAllocatorImpl>(settings.tempAllocatorSizeBytes);
m_JobSystem = std::make_unique<JobSystemThreadPool>(
cMaxPhysicsJobs,
cMaxPhysicsBarriers,
static_cast<int>(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<JPH::uint64>(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<int>(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<GameObject*>(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<TempAllocatorImpl> m_TempAllocator;
std::unique_ptr<JobSystemThreadPool> m_JobSystem;
std::unordered_map<std::uint32_t, BodyRecord> m_Bodies;
std::uint32_t m_NextHandle{0};
};
JoltPhysicsWorld::JoltPhysicsWorld(const Settings& settings)
: m_Impl(std::make_unique<Impl>(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);
}
+2 -7
View File
@@ -24,12 +24,7 @@ void PhysicsSceneBridge::UnregisterGameObject(GameObject& object) {
} }
void PhysicsSceneBridge::FixedUpdate(float fixedDt) { void PhysicsSceneBridge::FixedUpdate(float fixedDt) {
if (auto* simple = dynamic_cast<SimplePhysicsWorld*>(m_World.get())) { m_World->SyncKinematicBodiesToPhysics();
simple->SyncKinematicBodiesToPhysics();
simple->Step(fixedDt);
simple->SyncDynamicBodiesToTransforms();
return;
}
m_World->Step(fixedDt); m_World->Step(fixedDt);
m_World->SyncDynamicBodiesToTransforms();
} }
+7
View File
@@ -122,3 +122,10 @@ set(USE_F16C ON CACHE BOOL "" FORCE)
set(USE_FMADD ON CACHE BOOL "" FORCE) set(USE_FMADD ON CACHE BOOL "" FORCE)
add_subdirectory(jolt/Build) add_subdirectory(jolt/Build)
# 3rdparty/tracy is the Tracy repo
option(TRACY_ENABLE "" ON)
option(TRACY_ON_DEMAND "" ON)
add_subdirectory(tracy)
Vendored Submodule
+1
+157 -124
View File
@@ -23,13 +23,16 @@
#include "destrum/Util/ModelDocUtils.h" #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); materialCache.init(gfxDevice);
renderer.init(gfxDevice, m_params.renderSize); renderer.init(gfxDevice, m_params.renderSize);
@@ -77,39 +80,6 @@ void LightKeeper::customInit() {
auto& scene = SceneManager::GetInstance().CreateScene("Main"); auto& scene = SceneManager::GetInstance().CreateScene("Main");
// auto testCube = std::make_shared<GameObject>("TestCube");
// auto meshComp = testCube->AddComponent<MeshRendererComponent>();
// 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<GameObject>(fmt::format("ChildCube{}", i));
//
// auto childMeshComp = childCube->AddComponent<MeshRendererComponent>();
// childMeshComp->SetMeshID(testMeshID);
// childMeshComp->SetMaterialID(testMaterialID);
//
// childCube->GetTransform().SetWorldScale(glm::vec3(0.1f));
//
// // Add orbit + self spin
// auto orbit = childCube->AddComponent<OrbitAndSpin>(orbitRadius, glm::vec3(0.0f));
// orbit->Randomize(1337u + (uint32_t)i); // stable random per index
//
// scene.Add(childCube);
}
// testCube->AddComponent<Spinner>(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<GameObject>("GlobeRoot");
globeRoot->GetTransform().SetWorldPosition(glm::vec3(0.0f));
globeRoot->AddComponent<Spinner>(glm::vec3(0, 1, 0), 1.0f); // spin around Y, rad/sec
scene.Add(globeRoot);
// scene.Add(testCube); // scene.Add(testCube);
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg"); // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg");
@@ -160,6 +130,11 @@ void LightKeeper::customInit() {
planeMeshComp->SetMaterialID(planeMaterialID); planeMeshComp->SetMaterialID(planeMaterialID);
planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f)); planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f));
planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f)); planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f));
planeObj->AddComponent<BoxCollider>(glm::vec3{10.0f, 0.5f, 10.0f});
auto* floorRb = planeObj->AddComponent<Rigidbody>();
floorRb->SetType(RigidbodyType::Static);
scene.GetPhysics().RegisterGameObject(*planeObj);
scene.Add(planeObj); scene.Add(planeObj);
const auto CharObj = std::make_shared<GameObject>("Character"); const auto CharObj = std::make_shared<GameObject>("Character");
@@ -284,74 +259,73 @@ void LightKeeper::customInit() {
// } // }
// {
// const auto CharObj = std::make_shared<GameObject>("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<MeshRendererComponent>();
// charMeshComp->SetMeshID(charMeshID);
// charMeshComp->SetMaterialID(charMaterialID);
//
// const auto animator = CharObj->AddComponent<Animator>();
// 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<SkeletalAnimation>(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);
// }
{ {
const auto CharObj = std::make_shared<GameObject>("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<MeshRendererComponent>();
charMeshComp->SetMeshID(charMeshID);
charMeshComp->SetMaterialID(charMaterialID);
const auto animator = CharObj->AddComponent<Animator>();
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<SkeletalAnimation>(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( auto cubeModel = ModelDoc::LoadModel(
AssetFS::GetInstance() AssetFS::GetInstance()
.GetFullPath("engine://cube.fbx") .GetFullPath("engine://cube.fbx")
@@ -361,35 +335,91 @@ void LightKeeper::customInit() {
ModelDocUtils::LogModelDocSummary(cubeModel, "cube.fbx"); ModelDocUtils::LogModelDocSummary(cubeModel, "cube.fbx");
const auto& cubePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(cubeModel, "game://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); const auto cubeMeshID = meshCache.addMesh(gfxDevice, cubePrimitive.mesh);
//
// for (int i{0}; i < 100; i++)
// {
// auto cube = std::make_shared<GameObject>("Cube");
//
// cube->AddComponent<BoxCollider>(glm::vec3{0.5f});
// cube->AddComponent<Rigidbody>();
//
// auto meshComp = cube->AddComponent<MeshRendererComponent>();
// meshComp->SetMeshID(cubeMeshID);
// meshComp->SetMaterialID(eliasMaterialID);
//
// cube->GetTransform().SetWorldPosition(glm::vec3(0.0f, i, 0.0f));
// cube->GetTransform().SetWorldScale(glm::vec3(0.005f));
//
// scene.Add(cube);
// scene.GetPhysics().RegisterGameObject(*cube);
// }
auto cube = std::make_shared<GameObject>("Cube"); // const int cubeCount = 10;
// const float spacing = 1.0f;
cube->AddComponent<BoxCollider>(glm::vec3{0.5f}); //
cube->AddComponent<Rigidbody>(); // for (int x = 0; x < cubeCount; x++)
// {
auto meshComp = cube->AddComponent<MeshRendererComponent>(); // for (int y = 0; y < cubeCount; y++)
meshComp->SetMeshID(cubeMeshID); // {
meshComp->SetMaterialID(charMaterialID); // for (int z = 0; z < cubeCount; z++)
// {
cube->GetTransform().SetWorldPosition(glm::vec3(0.0f, 2.0f, 0.0f)); // auto cube = std::make_shared<GameObject>("Cube");
cube->GetTransform().SetWorldScale(glm::vec3(0.005f)); //
// cube->AddComponent<BoxCollider>(glm::vec3{0.5f});
scene.Add(cube); // cube->AddComponent<Rigidbody>();
scene.GetPhysics().RegisterGameObject(*cube); //
} // auto meshComp = cube->AddComponent<MeshRendererComponent>();
// 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); camera.Update(dt);
SceneManager::GetInstance().Update(); SceneManager::GetInstance().Update();
if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_1)) { if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_1))
{
renderer.setRenderWireframe(!renderer.getRenderWireframe()); renderer.setRenderWireframe(!renderer.getRenderWireframe());
} }
} }
void LightKeeper::customDraw() { void LightKeeper::customDraw()
{
renderer.beginDrawing(gfxDevice); renderer.beginDrawing(gfxDevice);
const RenderContext ctx{ const RenderContext ctx{
@@ -423,14 +453,16 @@ void LightKeeper::customDraw() {
}); });
} }
void LightKeeper::customCleanup() { void LightKeeper::customCleanup()
{
auto device = gfxDevice.getDevice().device; auto device = gfxDevice.getDevice().device;
vkDeviceWaitIdle(device); vkDeviceWaitIdle(device);
SceneManager::GetInstance().Destroy(); SceneManager::GetInstance().Destroy();
if (skyboxCubemap) { if (skyboxCubemap)
{
skyboxCubemap.reset(); skyboxCubemap.reset();
} }
@@ -445,7 +477,8 @@ void LightKeeper::customFixedUpdate(float dt)
SceneManager::GetInstance().FixedUpdate(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}); renderer.resize(gfxDevice, glm::ivec2{newWidth, newHeight});
const float aspectRatio = static_cast<float>(newWidth) / static_cast<float>(newHeight); const float aspectRatio = static_cast<float>(newWidth) / static_cast<float>(newHeight);
camera.setAspectRatio(aspectRatio); camera.setAspectRatio(aspectRatio);