feat: add more collider types + camera snapping fix

This commit is contained in:
2026-10-24 02:34:09 +02:00
parent 355dfa7851
commit 118016bcc7
26 changed files with 1525 additions and 354 deletions
@@ -1,6 +1,7 @@
#include <destrum/Components/MeshRendererComponent.h>
#include <destrum/Assets/AssetDatabase.h>
#include <destrum/Assets/AssetReference.h>
#include <destrum/Components/Physics/MeshShapeCollider.h>
#include <destrum/Serialization/MaterialSerialization.h>
#include <destrum/Graphics/GfxDevice.h>
#include <destrum/Graphics/Resources/PrimitiveMeshes.h>
@@ -254,6 +255,9 @@ void MeshRendererComponent::SetMeshID(MeshID id) {
meshKey = resources->meshes().getMeshKey(id);
}
}
if (GetGameObject()->GetComponent<MeshShapeCollider>() != nullptr) {
NotifyPhysicsChanged();
}
}
void MeshRendererComponent::SetMaterialID(MaterialID id) {
@@ -0,0 +1,29 @@
#include <destrum/Components/Physics/ConvexHullCollider.h>
#include <imgui.h>
nlohmann::json ConvexHullCollider::Serialize() const {
auto data = SerializeCollider();
data["maxVertices"] = m_MaxVertices;
return data;
}
void ConvexHullCollider::Deserialize(const nlohmann::json& data) {
DeserializeCollider(data);
if (data.contains("maxVertices")) {
SetMaxVertices(data.at("maxVertices").get<int>());
}
}
void ConvexHullCollider::DrawDebugLines(const glm::vec4& color) const {
DrawConvexHull(m_MaxVertices, color);
}
void ConvexHullCollider::ImGuiInspector() {
MeshShapeCollider::ImGuiInspector();
int maxVertices = m_MaxVertices;
if (ImGui::SliderInt("Max vertices", &maxVertices, MinVertices, MaxVertices)) {
SetMaxVertices(maxVertices);
}
}
@@ -0,0 +1,23 @@
#include <destrum/Components/Physics/CylinderCollider.h>
#include <destrum/Graphics/DebugDraw.h>
#include <imgui.h>
void CylinderCollider::DrawDebugLines(const glm::vec4& color) const {
DebugDraw::Cylinder(GetWorldCenter(), GetWorldRotation(), m_Radius, m_Height, color);
}
void CylinderCollider::ImGuiInspector() {
Collider::ImGuiInspector();
float radius = m_Radius;
if (ImGui::DragFloat("Radius", &radius, 0.05f, 0.0001f)) {
SetRadius(radius);
}
float height = m_Height;
if (ImGui::DragFloat("Height", &height, 0.05f, 0.0001f)) {
SetHeight(height);
}
}
@@ -0,0 +1,43 @@
#include <destrum/Components/Physics/MeshCollider.h>
#include <imgui.h>
#include <destrum/Components/Physics/Rigidbody.h>
#include <destrum/Graphics/DebugDraw.h>
#include <destrum/ObjectModel/GameObject.h>
bool MeshCollider::UsesConvexFallback() const {
const auto* rigidbody = GetGameObject()->GetComponent<Rigidbody>();
return rigidbody != nullptr && rigidbody->GetType() == RigidbodyType::Dynamic;
}
PhysicsShapeDesc MeshCollider::BuildPhysicsShape() const {
if (UsesConvexFallback()) {
return BuildConvexHullShape(DynamicHullVertices);
}
auto source = GetSourceMesh();
if (!source || source->indices.empty()) {
return {};
}
return PhysicsShapeDesc::Mesh(std::move(source), GetShapeScale(), m_CenterOffset, m_IsTrigger);
}
void MeshCollider::DrawDebugLines(const glm::vec4& color) const {
if (UsesConvexFallback()) {
DrawConvexHull(DynamicHullVertices, color);
return;
}
if (const auto source = GetSourceMesh()) {
DebugDraw::Triangles(GetShapeMatrix(), source->points, source->indices, color);
}
}
void MeshCollider::ImGuiInspector() {
MeshShapeCollider::ImGuiInspector();
if (UsesConvexFallback()) {
ImGui::TextColored(ImVec4{1.0f, 0.6f, 0.2f, 1.0f}, "Dynamic rigidbody: using a convex hull");
}
}
@@ -0,0 +1,118 @@
#include <destrum/Components/Physics/MeshShapeCollider.h>
#include <cmath>
#include <limits>
#include <imgui.h>
#include <glm/gtc/matrix_transform.hpp>
#include <destrum/Components/MeshRendererComponent.h>
#include <destrum/Graphics/DebugDraw.h>
#include <destrum/Graphics/RenderResources.h>
#include <destrum/Graphics/Renderer.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/ObjectModel/Transform.h>
#include <destrum/Physics/PhysicsMeshCache.h>
#include <destrum/Util/GameState.h>
namespace {
struct Bounds {
glm::vec3 center{0.0f};
glm::vec3 halfExtents{0.0f};
};
Bounds ComputeBounds(const PhysicsMeshData& mesh) {
glm::vec3 min{std::numeric_limits<float>::max()};
glm::vec3 max{std::numeric_limits<float>::lowest()};
for (const glm::vec3& point : mesh.points) {
min = glm::min(min, point);
max = glm::max(max, point);
}
return {(min + max) * 0.5f, (max - min) * 0.5f};
}
}
std::shared_ptr<const PhysicsMeshData> MeshShapeCollider::GetSourceMesh() const {
if (m_CustomMesh) {
return m_CustomMesh;
}
const auto* meshRenderer = GetGameObject()->GetComponent<MeshRendererComponent>();
if (meshRenderer == nullptr || meshRenderer->GetMeshID() == NULL_MESH_ID || !GameState::GetInstance().HasRenderer()) {
return nullptr;
}
const RenderResources* resources = GameState::GetInstance().Renderer().getResources();
if (resources == nullptr || !resources->meshes().isValid(meshRenderer->GetMeshID())) {
return nullptr;
}
const MeshCache& meshes = resources->meshes();
return PhysicsMeshCache::FromCPUMesh(meshes.getMeshKey(meshRenderer->GetMeshID()),
meshes.getCPUMesh(meshRenderer->GetMeshID()));
}
glm::vec3 MeshShapeCollider::GetShapeScale() const {
glm::vec3 scale = GetTransform().GetWorldScale();
for (int axis = 0; axis < 3; ++axis) {
if (std::abs(scale[axis]) < 0.0001f) {
scale[axis] = scale[axis] < 0.0f ? -0.0001f : 0.0001f;
}
}
return scale;
}
glm::mat4 MeshShapeCollider::GetShapeMatrix() const {
return glm::translate(glm::mat4{1.0f}, GetWorldCenter()) *
glm::mat4_cast(GetWorldRotation()) *
glm::scale(glm::mat4{1.0f}, GetShapeScale());
}
PhysicsShapeDesc MeshShapeCollider::BuildConvexHullShape(int maxVertices) const {
const auto source = GetSourceMesh();
if (!source || source->points.empty()) {
return {};
}
const glm::vec3 scale = GetShapeScale();
if (auto hull = PhysicsMeshCache::ConvexHull(source, maxVertices)) {
return PhysicsShapeDesc::ConvexHull(std::move(hull), scale, m_CenterOffset, m_IsTrigger);
}
const Bounds bounds = ComputeBounds(*source);
return PhysicsShapeDesc::Box(glm::max(glm::abs(bounds.halfExtents * scale), glm::vec3{0.01f}),
m_CenterOffset + bounds.center * scale,
m_IsTrigger);
}
void MeshShapeCollider::DrawConvexHull(int maxVertices, const glm::vec4& color) const {
const auto source = GetSourceMesh();
if (!source || source->points.empty()) {
return;
}
if (const auto hull = PhysicsMeshCache::ConvexHull(source, maxVertices)) {
DebugDraw::Triangles(GetShapeMatrix(), hull->points, hull->indices, color);
return;
}
const Bounds bounds = ComputeBounds(*source);
const glm::vec3 scale = GetShapeScale();
DebugDraw::Box(GetWorldCenter() + GetWorldRotation() * (bounds.center * scale),
GetWorldRotation(),
glm::max(glm::abs(bounds.halfExtents * scale), glm::vec3{0.01f}),
color);
}
void MeshShapeCollider::ImGuiInspector() {
Collider::ImGuiInspector();
if (m_CustomMesh) {
ImGui::TextDisabled("Custom mesh: %zu points", m_CustomMesh->points.size());
} else if (const auto source = GetSourceMesh()) {
ImGui::TextDisabled("Mesh: %zu vertices, %zu triangles", source->points.size(), source->indices.size() / 3);
} else {
ImGui::TextColored(ImVec4{1.0f, 0.6f, 0.2f, 1.0f}, "Needs a MeshRendererComponent with a mesh");
}
}