feat: add debug rendering for other types of colliders / split game into more logical functions / files
This commit is contained in:
@@ -51,6 +51,7 @@ set(SRC_FILES
|
||||
|
||||
"src/Graphics/BindlessSetManager.cpp"
|
||||
"src/Graphics/Camera.cpp"
|
||||
"src/Graphics/DebugDraw.cpp"
|
||||
"src/Graphics/ComputePipeline.cpp"
|
||||
"src/Graphics/Frustum.cpp"
|
||||
"src/Graphics/GfxDevice.cpp"
|
||||
@@ -77,6 +78,7 @@ set(SRC_FILES
|
||||
"src/Graphics/Resources/GPUImage.cpp"
|
||||
"src/Graphics/Resources/NBuffer.cpp"
|
||||
"src/Graphics/Resources/Cubemap.cpp"
|
||||
"src/Graphics/Resources/PrimitiveMeshes.cpp"
|
||||
|
||||
"src/Graphics/Pipelines/MeshPipeline.cpp"
|
||||
"src/Graphics/Pipelines/LineRenderingPass.cpp"
|
||||
@@ -96,6 +98,7 @@ set(SRC_FILES
|
||||
|
||||
"src/Scene/Scene.cpp"
|
||||
"src/Scene/SceneManager.cpp"
|
||||
"src/Scene/ModelSpawner.cpp"
|
||||
|
||||
"src/FS/AssetFS.cpp"
|
||||
"src/FS/Manifest.cpp"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Blender 4.0.2 MTL File: 'None'
|
||||
# www.blender.org
|
||||
|
||||
newmtl place_holder
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.500000
|
||||
d 1.000000
|
||||
illum 2
|
||||
Ps 0.000000
|
||||
Pc 0.000000
|
||||
Pcr 0.030000
|
||||
aniso 0.000000
|
||||
anisor 0.000000
|
||||
map_Kd texture_diffuse.png
|
||||
map_Bump -bm 1.000000 texture_normal.png
|
||||
map_Pr texture_roughness.png
|
||||
map_Pm texture_metallic.png
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 706 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 223 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 526 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 404 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
@@ -21,6 +21,8 @@ public:
|
||||
}
|
||||
|
||||
|
||||
void DrawDebugLines(const glm::vec4& color) const override;
|
||||
|
||||
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
|
||||
return PhysicsShapeDesc::Box(m_HalfExtents, m_CenterOffset, m_IsTrigger);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void DrawDebugLines(const glm::vec4& color) const override;
|
||||
|
||||
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
|
||||
return PhysicsShapeDesc::Capsule(m_Radius, m_Height, m_CenterOffset, m_IsTrigger);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
#include <destrum/Physics/PhysicsTypes.h>
|
||||
@@ -15,11 +16,14 @@ public:
|
||||
void ImGuiInspector() override;
|
||||
|
||||
[[nodiscard]] virtual PhysicsShapeDesc BuildPhysicsShape() const = 0;
|
||||
virtual void DrawDebugLines(const glm::vec4& color) const = 0;
|
||||
|
||||
[[nodiscard]] bool IsTrigger() const { return m_IsTrigger; }
|
||||
void SetTrigger(bool trigger) { m_IsTrigger = trigger; NotifyPhysicsChanged(); }
|
||||
|
||||
[[nodiscard]] const glm::vec3& GetCenterOffset() const { return m_CenterOffset; }
|
||||
[[nodiscard]] glm::vec3 GetWorldCenter() const;
|
||||
[[nodiscard]] glm::quat GetWorldRotation() const;
|
||||
void SetCenterOffset(const glm::vec3& offset) { m_CenterOffset = offset; NotifyPhysicsChanged(); }
|
||||
|
||||
protected:
|
||||
|
||||
@@ -20,6 +20,8 @@ public:
|
||||
return "SphereCollider";
|
||||
}
|
||||
|
||||
void DrawDebugLines(const glm::vec4& color) const override;
|
||||
|
||||
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
|
||||
return PhysicsShapeDesc::Sphere(m_Radius, m_CenterOffset, m_IsTrigger);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef DEBUGDRAW_H
|
||||
#define DEBUGDRAW_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
namespace DebugDraw {
|
||||
void Arc(const glm::vec3& center, const glm::vec3& axisA, const glm::vec3& axisB, float radius,
|
||||
float fromAngle, float toAngle, const glm::vec4& color, int segments = 16);
|
||||
void Circle(const glm::vec3& center, const glm::vec3& axisA, const glm::vec3& axisB, float radius,
|
||||
const glm::vec4& color, int segments = 32);
|
||||
|
||||
void Box(const glm::vec3& center, const glm::quat& rotation, const glm::vec3& halfExtents, const glm::vec4& color);
|
||||
void Sphere(const glm::vec3& center, float radius, const glm::vec4& color);
|
||||
|
||||
void Capsule(const glm::vec3& center, const glm::quat& rotation, float radius, float height, const glm::vec4& color);
|
||||
}
|
||||
|
||||
#endif // DEBUGDRAW_H
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef PRIMITIVEMESHES_H
|
||||
#define PRIMITIVEMESHES_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <destrum/Graphics/Resources/CPUMesh.h>
|
||||
|
||||
namespace PrimitiveMeshes {
|
||||
[[nodiscard]] CPUMesh MakeBox(const glm::vec3& size);
|
||||
}
|
||||
|
||||
#endif // PRIMITIVEMESHES_H
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef MODELSPAWNER_H
|
||||
#define MODELSPAWNER_H
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class GameObject;
|
||||
class GfxDevice;
|
||||
class RenderResources;
|
||||
class Scene;
|
||||
|
||||
namespace ModelDoc {
|
||||
struct LoadOptions;
|
||||
}
|
||||
|
||||
namespace ModelSpawner {
|
||||
// Loads a model and creates a root GameObject with one child per mesh primitive, using the model's PBR materials
|
||||
GameObject* SpawnStaticModel(
|
||||
Scene& scene,
|
||||
GfxDevice& gfxDevice,
|
||||
RenderResources& resources,
|
||||
std::string_view reference,
|
||||
const ModelDoc::LoadOptions& options,
|
||||
const std::string& name);
|
||||
}
|
||||
|
||||
#endif // MODELSPAWNER_H
|
||||
@@ -1,9 +1,14 @@
|
||||
#include <destrum/Components/Physics/BoxCollider.h>
|
||||
|
||||
#include <destrum/Graphics/DebugDraw.h>
|
||||
#include <destrum/Util/ImGuiUtils.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
void BoxCollider::DrawDebugLines(const glm::vec4& color) const {
|
||||
DebugDraw::Box(GetWorldCenter(), GetWorldRotation(), m_HalfExtents, color);
|
||||
}
|
||||
|
||||
nlohmann::json BoxCollider::Serialize() const {
|
||||
auto data = SerializeCollider();
|
||||
data["halfExtents"] = {m_HalfExtents.x, m_HalfExtents.y, m_HalfExtents.z};
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
#include <destrum/Components/Physics/CapsuleCollider.h>
|
||||
|
||||
#include <destrum/Graphics/DebugDraw.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
void CapsuleCollider::DrawDebugLines(const glm::vec4& color) const {
|
||||
DebugDraw::Capsule(GetWorldCenter(), GetWorldRotation(), m_Radius, m_Height, color);
|
||||
}
|
||||
|
||||
void CapsuleCollider::ImGuiInspector() {
|
||||
Collider::ImGuiInspector();
|
||||
|
||||
|
||||
@@ -2,8 +2,18 @@
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <destrum/ObjectModel/Transform.h>
|
||||
#include <destrum/Util/ImGuiUtils.h>
|
||||
|
||||
glm::vec3 Collider::GetWorldCenter() const {
|
||||
Transform& transform = GetTransform();
|
||||
return transform.GetWorldPosition() + transform.GetWorldRotation() * m_CenterOffset;
|
||||
}
|
||||
|
||||
glm::quat Collider::GetWorldRotation() const {
|
||||
return GetTransform().GetWorldRotation();
|
||||
}
|
||||
|
||||
void Collider::ImGuiInspector() {
|
||||
bool isTrigger = m_IsTrigger;
|
||||
if (ImGui::Checkbox("Is trigger", &isTrigger)) {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
#include <destrum/Components/Physics/SphereCollider.h>
|
||||
|
||||
#include <destrum/Graphics/DebugDraw.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
void SphereCollider::DrawDebugLines(const glm::vec4& color) const {
|
||||
DebugDraw::Sphere(GetWorldCenter(), m_Radius, color);
|
||||
}
|
||||
|
||||
nlohmann::json SphereCollider::Serialize() const {
|
||||
auto data = SerializeCollider();
|
||||
data["radius"] = m_Radius;
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <destrum/Graphics/DebugDraw.h>
|
||||
|
||||
#include <destrum/Graphics/Managers/LineRenderingManager.h>
|
||||
|
||||
#include <glm/gtc/constants.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
void DebugDraw::Arc(const glm::vec3& center, const glm::vec3& axisA, const glm::vec3& axisB, float radius,
|
||||
float fromAngle, float toAngle, const glm::vec4& color, int segments)
|
||||
{
|
||||
auto& lines = LineRenderingManager::GetInstance();
|
||||
segments = std::max(segments, 1);
|
||||
|
||||
const auto pointAt = [&](float angle) {
|
||||
return center + (axisA * std::cos(angle) + axisB * std::sin(angle)) * radius;
|
||||
};
|
||||
|
||||
glm::vec3 previous = pointAt(fromAngle);
|
||||
for (int i = 1; i <= segments; ++i) {
|
||||
const float t = static_cast<float>(i) / static_cast<float>(segments);
|
||||
const glm::vec3 next = pointAt(fromAngle + (toAngle - fromAngle) * t);
|
||||
lines.SubmitLine(previous, next, color);
|
||||
previous = next;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDraw::Circle(const glm::vec3& center, const glm::vec3& axisA, const glm::vec3& axisB, float radius,
|
||||
const glm::vec4& color, int segments)
|
||||
{
|
||||
Arc(center, axisA, axisB, radius, 0.0f, glm::two_pi<float>(), color, segments);
|
||||
}
|
||||
|
||||
void DebugDraw::Box(const glm::vec3& center, const glm::quat& rotation, const glm::vec3& halfExtents,
|
||||
const glm::vec4& color)
|
||||
{
|
||||
// Corner i has +x when bit 0 is set, +y for bit 1 and +z for bit 2
|
||||
std::array<glm::vec3, 8> corners{};
|
||||
for (std::size_t i = 0; i < corners.size(); ++i) {
|
||||
const glm::vec3 sign{
|
||||
(i & 1) != 0 ? 1.0f : -1.0f,
|
||||
(i & 2) != 0 ? 1.0f : -1.0f,
|
||||
(i & 4) != 0 ? 1.0f : -1.0f,
|
||||
};
|
||||
corners[i] = center + rotation * (sign * halfExtents);
|
||||
}
|
||||
|
||||
constexpr std::array<std::array<std::size_t, 2>, 12> edges{{
|
||||
{{0, 1}}, {{1, 3}}, {{3, 2}}, {{2, 0}},
|
||||
{{4, 5}}, {{5, 7}}, {{7, 6}}, {{6, 4}},
|
||||
{{0, 4}}, {{1, 5}}, {{2, 6}}, {{3, 7}},
|
||||
}};
|
||||
|
||||
auto& lines = LineRenderingManager::GetInstance();
|
||||
for (const auto& edge : edges) {
|
||||
lines.SubmitLine(corners[edge[0]], corners[edge[1]], color);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDraw::Sphere(const glm::vec3& center, float radius, const glm::vec4& color)
|
||||
{
|
||||
const glm::vec3 x{1.0f, 0.0f, 0.0f};
|
||||
const glm::vec3 y{0.0f, 1.0f, 0.0f};
|
||||
const glm::vec3 z{0.0f, 0.0f, 1.0f};
|
||||
Circle(center, x, z, radius, color);
|
||||
Circle(center, x, y, radius, color);
|
||||
Circle(center, z, y, radius, color);
|
||||
}
|
||||
|
||||
void DebugDraw::Capsule(const glm::vec3& center, const glm::quat& rotation, float radius, float height,
|
||||
const glm::vec4& color)
|
||||
{
|
||||
const glm::vec3 right = rotation * glm::vec3{1.0f, 0.0f, 0.0f};
|
||||
const glm::vec3 up = rotation * glm::vec3{0.0f, 1.0f, 0.0f};
|
||||
const glm::vec3 forward = rotation * glm::vec3{0.0f, 0.0f, 1.0f};
|
||||
|
||||
const float halfCylinder = std::max(height * 0.5f - radius, 0.0f);
|
||||
const glm::vec3 top = center + up * halfCylinder;
|
||||
const glm::vec3 bottom = center - up * halfCylinder;
|
||||
|
||||
Circle(top, right, forward, radius, color);
|
||||
Circle(bottom, right, forward, radius, color);
|
||||
|
||||
auto& lines = LineRenderingManager::GetInstance();
|
||||
for (const glm::vec3& side : {right, -right, forward, -forward}) {
|
||||
lines.SubmitLine(top + side * radius, bottom + side * radius, color);
|
||||
}
|
||||
|
||||
constexpr float pi = glm::pi<float>();
|
||||
Arc(top, right, up, radius, 0.0f, pi, color);
|
||||
Arc(top, forward, up, radius, 0.0f, pi, color);
|
||||
Arc(bottom, right, up, radius, pi, glm::two_pi<float>(), color);
|
||||
Arc(bottom, forward, up, radius, pi, glm::two_pi<float>(), color);
|
||||
}
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
@@ -263,7 +266,12 @@ MaterialID RenderResources::addModelMaterial(
|
||||
material.emissiveColor = sourceMaterial.emissiveColor;
|
||||
|
||||
material.diffuseTexture = loadTexture({Semantic::BaseColor, Semantic::Diffuse}, TextureIntent::ColorSrgb);
|
||||
material.normalTexture = loadTexture({Semantic::Normal}, TextureIntent::DataLinear);
|
||||
// Blender's OBJ exporter writes normal maps as map_Bump, which assimp imports as a height map
|
||||
std::string extension = std::filesystem::path{sourceMaterial.assetReference.path}.extension().string();
|
||||
std::ranges::transform(extension, extension.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
material.normalTexture = extension == ".obj"
|
||||
? loadTexture({Semantic::Normal, Semantic::Height}, TextureIntent::DataLinear)
|
||||
: loadTexture({Semantic::Normal}, TextureIntent::DataLinear);
|
||||
material.metallicTexture = loadTexture({Semantic::MetallicRoughness, Semantic::Metallic}, TextureIntent::DataLinear);
|
||||
material.roughnessTexture = loadTexture({Semantic::MetallicRoughness, Semantic::Roughness}, TextureIntent::DataLinear);
|
||||
material.occlusionTexture = loadTexture({Semantic::AmbientOcclusion}, TextureIntent::DataLinear);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <destrum/Graphics/Resources/PrimitiveMeshes.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
CPUMesh PrimitiveMeshes::MakeBox(const glm::vec3& size)
|
||||
{
|
||||
struct Face {
|
||||
glm::vec3 normal;
|
||||
glm::vec3 tangent;
|
||||
};
|
||||
constexpr std::array<Face, 6> faces{{
|
||||
{{1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}},
|
||||
{{-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
|
||||
{{0.0f, 1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{0.0f, -1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}},
|
||||
}};
|
||||
constexpr std::array<glm::vec2, 4> corners{{
|
||||
{-1.0f, -1.0f}, {1.0f, -1.0f}, {1.0f, 1.0f}, {-1.0f, 1.0f},
|
||||
}};
|
||||
|
||||
CPUMesh mesh{};
|
||||
mesh.name = "Box";
|
||||
mesh.minPos = -size * 0.5f;
|
||||
mesh.maxPos = size * 0.5f;
|
||||
|
||||
for (const Face& face : faces) {
|
||||
const glm::vec3 bitangent = glm::cross(face.normal, face.tangent);
|
||||
const float width = glm::dot(glm::abs(face.tangent), size);
|
||||
const float height = glm::dot(glm::abs(bitangent), size);
|
||||
const auto first = static_cast<std::uint32_t>(mesh.vertices.size());
|
||||
|
||||
for (const glm::vec2& corner : corners) {
|
||||
// v runs against the bitangent so images are upright on the side faces (w = -1 to match)
|
||||
mesh.vertices.push_back(CPUMesh::Vertex{
|
||||
.position = (face.normal + face.tangent * corner.x + bitangent * corner.y) * size * 0.5f,
|
||||
.uv_x = (corner.x + 1.0f) * 0.5f * width,
|
||||
.normal = face.normal,
|
||||
.uv_y = (1.0f - corner.y) * 0.5f * height,
|
||||
.tangent = glm::vec4{face.tangent, -1.0f},
|
||||
});
|
||||
}
|
||||
|
||||
for (const std::uint32_t index : {0u, 1u, 2u, 0u, 2u, 3u}) {
|
||||
mesh.indices.push_back(first + index);
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
#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/OffsetCenterOfMassShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/PhysicsSystem.h>
|
||||
#include <Jolt/RegisterTypes.h>
|
||||
@@ -440,13 +440,13 @@ public:
|
||||
{
|
||||
ShapeRefC shape = CreateJoltShape(desc.shape);
|
||||
|
||||
// Jolt's offset shape stores the offset from the shape to the center
|
||||
// of mass, so negate the component's center offset. This keeps the
|
||||
// body transform aligned with the owning GameObject.
|
||||
// Move the shape itself relative to the body origin, so the body transform stays aligned with
|
||||
// the owning GameObject (an offset center of mass shape would only move the center of mass)
|
||||
if (glm::length2(desc.shape.centerOffset) > 0.0000001f)
|
||||
{
|
||||
OffsetCenterOfMassShapeSettings offsetSettings(
|
||||
ToJoltVec3(-desc.shape.centerOffset),
|
||||
RotatedTranslatedShapeSettings offsetSettings(
|
||||
ToJoltVec3(desc.shape.centerOffset),
|
||||
Quat::sIdentity(),
|
||||
shape.GetPtr());
|
||||
ShapeSettings::ShapeResult result = offsetSettings.Create();
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <destrum/Scene/ModelSpawner.h>
|
||||
|
||||
#include <destrum/Assets/AssetManager.h>
|
||||
#include <destrum/Components/MeshRendererComponent.h>
|
||||
#include <destrum/Graphics/RenderResources.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/ObjectModel/Transform.h>
|
||||
#include <destrum/Scene/Scene.h>
|
||||
#include <destrum/Util/ModelDocUtils.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
GameObject* ModelSpawner::SpawnStaticModel(
|
||||
Scene& scene,
|
||||
GfxDevice& gfxDevice,
|
||||
RenderResources& resources,
|
||||
std::string_view reference,
|
||||
const ModelDoc::LoadOptions& options,
|
||||
const std::string& name)
|
||||
{
|
||||
const auto model = AssetManager::GetInstance().LoadModel(reference, options);
|
||||
ModelDocUtils::LogModelDocSummary(model, name);
|
||||
|
||||
std::vector<MaterialID> materialIDs;
|
||||
for (const auto& material : model.materials) {
|
||||
materialIDs.push_back(resources.addModelMaterial(gfxDevice, material));
|
||||
}
|
||||
|
||||
GameObject* root = scene.CreateGameObject(name);
|
||||
for (const auto& primitive : model.primitives) {
|
||||
GameObject* part = scene.CreateGameObject(primitive.nodeName.empty() ? primitive.mesh.name : primitive.nodeName);
|
||||
part->GetTransform().SetParent(&root->GetTransform(), false);
|
||||
|
||||
auto* meshRenderer = part->AddComponent<MeshRendererComponent>();
|
||||
meshRenderer->SetMeshID(resources.meshes().addMesh(gfxDevice, primitive.mesh));
|
||||
meshRenderer->SetMaterialID(primitive.materialIndex < materialIDs.size()
|
||||
? materialIDs[primitive.materialIndex]
|
||||
: resources.materials().getPlaceholderMaterialId());
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
Reference in New Issue
Block a user