feat: improve fps controller / add fog and ambient light to the scene

This commit is contained in:
2026-10-16 09:29:59 +02:00
parent 21621001c2
commit 4c9b77c948
105 changed files with 609 additions and 110 deletions
+2 -2
View File
@@ -1,3 +1,4 @@
#include <algorithm>
#include <chrono>
#include <exception>
#include <SDL_vulkan.h>
@@ -274,8 +275,7 @@ if (!isRunning) break;
TracyPlot("Fixed Steps", static_cast<int64_t>(steps));
}
const float alpha = accumulator / fixedDt;
(void)alpha;
Time::GetInstance().SetFixedAlpha(std::clamp(accumulator / fixedDt, 0.0f, 1.0f));
{
ZoneScopedN("Swapchain Resize Check");
+10 -1
View File
@@ -106,7 +106,8 @@ nlohmann::json Rigidbody::Serialize() const {
{"friction", m_Friction},
{"restitution", m_Restitution},
{"useGravity", m_UseGravity},
{"allowSleep", m_AllowSleep}
{"allowSleep", m_AllowSleep},
{"freezeRotation", m_FreezeRotation}
};
}
@@ -129,6 +130,9 @@ void Rigidbody::Deserialize(const nlohmann::json& data) {
if (data.contains("allowSleep")) {
m_AllowSleep = data.at("allowSleep").get<bool>();
}
if (data.contains("freezeRotation")) {
m_FreezeRotation = data.at("freezeRotation").get<bool>();
}
}
void Rigidbody::ImGuiInspector() {
@@ -163,6 +167,11 @@ void Rigidbody::ImGuiInspector() {
SetAllowSleep(allowSleep);
}
bool freezeRotation = m_FreezeRotation;
if (ImGui::Checkbox("Freeze rotation", &freezeRotation)) {
SetFreezeRotation(freezeRotation);
}
if (HasPhysicsBody()) {
const glm::vec3 velocity = GetLinearVelocity();
ImGui::Text(
@@ -2,7 +2,6 @@
#include <destrum/FS/AssetFS.h>
#include <glm/glm.hpp>
#include <destrum/Util/DeltaTime.h>
#include "volk.h"
#include "glm/ext/matrix_transform.hpp"
@@ -98,26 +97,20 @@ void SkyboxPipeline::cleanup(VkDevice device)
void SkyboxPipeline::draw(
VkCommandBuffer cmd,
RenderResources& resources,
const Camera& camera)
const Camera& camera,
float rotation)
{
if (skyboxTextureId == NULL_IMAGE_ID) {
return;
}
const float rotationSpeed = 0.01f;
skyboxRotation = glm::rotate(
skyboxRotation,
rotationSpeed * static_cast<float>(Time::GetInstance().DeltaTime()),
glm::vec3(0.f, 1.f, 0.f));
pipeline->bind(cmd);
vkCmdSetPolygonModeEXT(cmd, VK_POLYGON_MODE_FILL);
resources.bindBindlessDescSet(cmd, pipelineLayout);
const glm::mat3 r = glm::mat3(skyboxRotation);
const glm::mat3 r = glm::mat3(glm::rotate(glm::mat4{1.f}, rotation, glm::vec3(0.f, 1.f, 0.f)));
// rotation only, translation in the inverse makes the sky jitter far from the origin
const glm::mat4 viewRotation = glm::mat4(glm::mat3(camera.GetViewMatrix()));
+1 -1
View File
@@ -306,7 +306,7 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "SkyboxPipeline::draw");
vkutil::ScopedCmdLabel label(cmd, "SkyboxPipeline::draw", vkutil::labelColors::sky);
skyboxPipeline->draw(cmd, *resources, camera);
skyboxPipeline->draw(cmd, *resources, camera, sceneData.skyboxRotation);
}
{
+38 -4
View File
@@ -7,6 +7,7 @@
#include <Jolt/Core/JobSystemThreadPool.h>
#include <Jolt/Core/TempAllocator.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/Body/BodyFilter.h>
#include <Jolt/Physics/Body/BodyInterface.h>
#include <Jolt/Physics/Body/MotionType.h>
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
@@ -95,6 +96,29 @@ namespace
}
}
// Raycasts skip triggers and optionally one body (e.g. the caster's own collider)
class RaycastBodyFilter final : public BodyFilter
{
public:
explicit RaycastBodyFilter(const BodyID& ignoredBody)
: m_IgnoredBody(ignoredBody)
{
}
bool ShouldCollide(const BodyID& inBodyID) const override
{
return inBodyID != m_IgnoredBody;
}
bool ShouldCollideLocked(const Body& inBody) const override
{
return !inBody.IsSensor();
}
private:
BodyID m_IgnoredBody;
};
namespace Layers
{
static constexpr ObjectLayer NON_MOVING = 0;
@@ -447,6 +471,10 @@ public:
settings.mIsSensor = desc.shape.isTrigger;
settings.mAllowSleeping = desc.allowSleep;
settings.mGravityFactor = desc.useGravity ? 1.0f : 0.0f;
if (desc.freezeRotation)
{
settings.mAllowedDOFs = EAllowedDOFs::TranslationX | EAllowedDOFs::TranslationY | EAllowedDOFs::TranslationZ;
}
settings.mUserData = reinterpret_cast<JPH::uint64>(desc.owner);
if (desc.type == RigidbodyType::Dynamic)
@@ -631,7 +659,8 @@ public:
bool Raycast(const glm::vec3& origin,
const glm::vec3& direction,
float maxDistance,
PhysicsRaycastHit& hit) const
PhysicsRaycastHit& hit,
PhysicsBodyHandle ignoreBody) const
{
if (maxDistance <= 0.0f || glm::length2(direction) <= 0.0000001f)
{
@@ -645,8 +674,12 @@ public:
ToJoltVec3(directionNormalized * maxDistance)
};
const BodyRecord* ignored = ignoreBody.IsValid() ? FindBody(ignoreBody) : nullptr;
const RaycastBodyFilter bodyFilter(ignored != nullptr ? ignored->bodyID : BodyID{});
RayCastResult result{};
if (!m_PhysicsSystem.GetNarrowPhaseQuery().CastRay(ray, result))
if (!m_PhysicsSystem.GetNarrowPhaseQuery().CastRay(
ray, result, BroadPhaseLayerFilter{}, ObjectLayerFilter{}, bodyFilter))
{
return false;
}
@@ -838,9 +871,10 @@ void JoltPhysicsWorld::AddImpulse(PhysicsBodyHandle body, const glm::vec3& impul
bool JoltPhysicsWorld::Raycast(const glm::vec3& origin,
const glm::vec3& direction,
float maxDistance,
PhysicsRaycastHit& hit) const
PhysicsRaycastHit& hit,
PhysicsBodyHandle ignoreBody) const
{
return m_Impl->Raycast(origin, direction, maxDistance, hit);
return m_Impl->Raycast(origin, direction, maxDistance, hit, ignoreBody);
}
std::vector<TriggerEvent> JoltPhysicsWorld::ConsumeTriggerEvents()
+1
View File
@@ -70,6 +70,7 @@ void PhysicsWorld::RegisterRigidbody(Rigidbody& rigidbody) {
desc.mass = rigidbody.GetMass();
desc.useGravity = rigidbody.UsesGravity();
desc.allowSleep = rigidbody.AllowsSleep();
desc.freezeRotation = rigidbody.FreezesRotation();
desc.material.friction = rigidbody.GetFriction();
desc.material.restitution = rigidbody.GetRestitution();
+5 -2
View File
@@ -165,7 +165,8 @@ void SimplePhysicsWorld::SyncDynamicBodiesToTransforms() {
bool SimplePhysicsWorld::Raycast(const glm::vec3& origin,
const glm::vec3& direction,
float maxDistance,
PhysicsRaycastHit& hit) const {
PhysicsRaycastHit& hit,
PhysicsBodyHandle ignoreBody) const {
if (maxDistance <= 0.0f) {
return false;
}
@@ -183,7 +184,9 @@ bool SimplePhysicsWorld::Raycast(const glm::vec3& origin,
for (const BodyRecord& body : m_Bodies) {
if (!body.alive || body.desc.shape.type == PhysicsShapeType::None ||
!body.desc.owner || body.desc.owner->IsBeingDestroyed() ||
!body.desc.owner->IsActiveInHierarchy()) {
!body.desc.owner->IsActiveInHierarchy() ||
body.desc.shape.isTrigger ||
(ignoreBody.IsValid() && body.handle.id == ignoreBody.id)) {
continue;
}
+31
View File
@@ -4,6 +4,10 @@
#include <destrum/Util/ImGuiUtils.h>
#include <algorithm>
#include <cmath>
#include <glm/gtc/constants.hpp>
#include <imgui.h>
unsigned int Scene::m_idCounter = 0;
@@ -131,6 +135,32 @@ void Scene::Update(float dt) {
}
}
m_Environment.skyboxRotation = std::fmod(
m_Environment.skyboxRotation + m_Environment.skyboxRotationSpeed * dt,
glm::two_pi<float>());
}
void Scene::DrawSettingsImGui() {
SceneEnvironment& environment = m_Environment;
ImGui::SeparatorText("Ambient");
ImGui::ColorEdit3("Ambient color", &environment.ambientColor.x);
ImGui::DragFloat("Ambient intensity", &environment.ambientIntensity, 0.01f, 0.0f, 100.0f);
ImGui::SeparatorText("Fog");
ImGui::ColorEdit3("Fog color", &environment.fogColor.x, ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_Float);
ImGui::DragFloat("Fog density", &environment.fogDensity, 0.001f, 0.0f, 1.0f, "%.4f");
ImGui::SeparatorText("Skybox");
float speedDegrees = glm::degrees(environment.skyboxRotationSpeed);
if (ImGui::DragFloat("Spin (deg/s)", &speedDegrees, 0.05f)) {
environment.skyboxRotationSpeed = glm::radians(speedDegrees);
}
float rotationDegrees = glm::degrees(environment.skyboxRotation);
if (ImGui::SliderFloat("Rotation (deg)", &rotationDegrees, -360.0f, 360.0f)) {
environment.skyboxRotation = glm::radians(rotationDegrees);
}
}
void Scene::FixedUpdate(float dt) {
@@ -200,6 +230,7 @@ void Scene::RenderImgui(bool* open) {
m_pendingAdditions,
m_SelectedObjectId,
[this] { return CreateGameObject("GameObject"); },
[this] { DrawSettingsImGui(); },
open);
}
@@ -300,6 +300,15 @@ bool SceneSerializer::Save(Scene& scene, const std::filesystem::path& path) {
if (scene.m_CurrentCameraId != InvalidObjectId) {
root["currentCamera"] = scene.m_CurrentCameraId;
}
const SceneEnvironment& environment = scene.GetEnvironment();
root["environment"] = {
{"ambientColor", {environment.ambientColor.x, environment.ambientColor.y, environment.ambientColor.z}},
{"ambientIntensity", environment.ambientIntensity},
{"fogColor", {environment.fogColor.x, environment.fogColor.y, environment.fogColor.z}},
{"fogDensity", environment.fogDensity},
{"skyboxRotationSpeed", environment.skyboxRotationSpeed},
{"skyboxRotation", environment.skyboxRotation},
};
for (const auto& objectPtr : scene.GetObjects()) {
if (!objectPtr || objectPtr->IsBeingDestroyed()) {
@@ -573,6 +582,23 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
scene.m_name = root.at("name").get<std::string>();
}
scene.m_CurrentCameraId = root.value("currentCamera", InvalidObjectId);
scene.m_Environment = SceneEnvironment{};
if (root.contains("environment") && root.at("environment").is_object()) {
const auto& data = root.at("environment");
SceneEnvironment& environment = scene.m_Environment;
const auto readColor = [&data](const char* key, glm::vec3& color) {
if (data.contains(key) && data.at(key).is_array() && data.at(key).size() == 3) {
const auto& value = data.at(key);
color = {value.at(0).get<float>(), value.at(1).get<float>(), value.at(2).get<float>()};
}
};
readColor("ambientColor", environment.ambientColor);
environment.ambientIntensity = data.value("ambientIntensity", environment.ambientIntensity);
readColor("fogColor", environment.fogColor);
environment.fogDensity = data.value("fogDensity", environment.fogDensity);
environment.skyboxRotationSpeed = data.value("skyboxRotationSpeed", environment.skyboxRotationSpeed);
environment.skyboxRotation = data.value("skyboxRotation", environment.skyboxRotation);
}
if (progressCallback) progressCallback(0.9f);
+4
View File
@@ -125,6 +125,7 @@ namespace ImGuiUtils {
const std::vector<std::shared_ptr<GameObject>>& pendingAdditions,
ObjectId& selectedObjectId,
const std::function<GameObject*()>& createGameObject,
const std::function<void()>& drawSceneSettings,
bool* open) {
RegisterEngineComponents();
@@ -176,6 +177,9 @@ namespace ImGuiUtils {
}
}
}
if (drawSceneSettings && ImGui::CollapsingHeader("Scene Settings")) {
drawSceneSettings();
}
ImGui::Separator();
const float availableWidth = ImGui::GetContentRegionAvail().x;