fix: alot of stuff changed. Mostly bugfixxes / architechture changes

This commit is contained in:
2026-08-09 02:24:17 +02:00
parent 4b6f773c0c
commit 05384debbf
98 changed files with 5461 additions and 1751 deletions
+25 -4
View File
@@ -2,6 +2,8 @@
#include <stdexcept>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Components/Physics/Rigidbody.h>
#include <destrum/Scene/Scene.h>
Component::Component(GameObject& pParent, const std::string& name)
: Object(name),
@@ -14,6 +16,7 @@ Transform& Component::GetTransform() const {
void Component::Destroy() {
Object::Destroy();
NotifyPhysicsChanged();
}
void Component::Start() {
@@ -21,12 +24,30 @@ void Component::Start() {
void Component::SetEnabled(bool enabled) {
m_IsEnabled = enabled;
NotifyPhysicsChanged();
}
void Component::LateUpdate() {
void Component::NotifyPhysicsChanged() {
if (m_ParentGameObjectPtr != nullptr) {
if (auto* rigidbody = m_ParentGameObjectPtr->GetComponent<Rigidbody>();
rigidbody != nullptr && rigidbody->GetPhysicsWorld() != nullptr) {
rigidbody->GetPhysicsWorld()->RefreshRigidbody(*rigidbody);
} else {
m_ParentGameObjectPtr->RefreshPhysics();
}
}
}
void Component::FixedUpdate() {
void Component::Update(float dt) {
m_LastUpdateDeltaTime = dt;
}
void Component::LateUpdate(float dt) {
m_LastUpdateDeltaTime = dt;
}
void Component::FixedUpdate(float fixedDt) {
m_LastFixedDeltaTime = fixedDt;
}
void Component::ImGuiInspector() {
@@ -35,5 +56,5 @@ void Component::ImGuiInspector() {
void Component::ImGuiRender() {
}
void Component::Render(const RenderContext& ctx) {
}
void Component::Render(const RenderContext&) {
}
+123 -30
View File
@@ -1,32 +1,72 @@
#include <destrum/ObjectModel/GameObject.h>
#include <string>
#include <iostream>
#include <destrum/Components/Physics/Rigidbody.h>
#include <destrum/Scene/Scene.h>
#include <destrum/Util/DeltaTime.h>
#include "spdlog/spdlog.h"
#include <limits>
#include <stdexcept>
#include <string>
namespace {
[[nodiscard]] std::vector<Component*> SnapshotComponents(const GameObject& object) {
std::vector<Component*> components;
components.reserve(object.GetComponents().size());
for (const auto& component : object.GetComponents()) {
if (component != nullptr) {
components.push_back(component.get());
}
}
return components;
}
bool EnsureComponentStarted(Component& component) {
if (!component.HasStarted) {
component.Start();
component.HasStarted = true;
}
return !component.IsBeingDestroyed();
}
}
GameObject::~GameObject() {
// spdlog::debug("GameObject destroyed: {}", GetName());
if (!IsBeingDestroyed()) {
Destroy();
}
}
GameObject::GameObject(const std::string& name)
: Object(name),
m_Id(s_NextId++) {
m_Id(AllocateId()) {
}
void GameObject::SetIdForDeserialization(ObjectId id) {
if (id == InvalidObjectId || id >= std::numeric_limits<ObjectId>::max() - 1) {
throw std::out_of_range("Object ID is reserved or cannot be incremented");
}
m_Id = id;
// Prevent newly created objects from reusing loaded IDs.
if (id >= s_NextId) {
if (id != std::numeric_limits<ObjectId>::max() && id >= s_NextId) {
s_NextId = id + 1;
}
}
ObjectId GameObject::AllocateId() {
if (s_NextId == InvalidObjectId ||
s_NextId >= std::numeric_limits<ObjectId>::max() - 1) {
throw std::overflow_error("Object ID range exhausted");
}
return s_NextId++;
}
void GameObject::SetActiveDirty() {
m_ActiveDirty = true;
for (const Transform* child : m_TransformPtr.GetChildren()) {
child->GetOwner()->SetActiveDirty();
if (child != nullptr && child->GetOwner() != nullptr) {
child->GetOwner()->SetActiveDirty();
}
}
}
@@ -35,8 +75,10 @@ void GameObject::UpdateActiveState() {
if (parentPtr == nullptr) {
m_ActiveInHierarchy = m_Active;
} else {
} else if (parentPtr->GetOwner() != nullptr) {
m_ActiveInHierarchy = m_Active && parentPtr->GetOwner()->IsActiveInHierarchy();
} else {
m_ActiveInHierarchy = m_Active;
}
m_ActiveDirty = false;
@@ -50,61 +92,112 @@ bool GameObject::IsActiveInHierarchy() {
return m_ActiveInHierarchy;
}
void GameObject::Update() {
for (const auto& component : m_Components) {
void GameObject::Update(float dt) {
for (Component* component : SnapshotComponents(*this)) {
if (component->IsBeingDestroyed()) {
continue;
}
if (!component->isEnabled()) {
continue;
}
if (!component->HasStarted) {
component->Start();
component->HasStarted = true;
if (!EnsureComponentStarted(*component)) {
continue;
}
component->Update();
component->Update(dt);
}
}
void GameObject::LateUpdate() {
for (const auto& component : m_Components) {
if (component->isEnabled()) {
component->LateUpdate();
void GameObject::LateUpdate(float dt) {
for (Component* component : SnapshotComponents(*this)) {
if (component->isEnabled() && !component->IsBeingDestroyed()) {
if (!EnsureComponentStarted(*component)) {
continue;
}
component->LateUpdate(dt);
}
}
}
void GameObject::FixedUpdate() {
for (const auto& component : m_Components) {
if (component->isEnabled()) {
component->FixedUpdate();
void GameObject::FixedUpdate(float fixedDt) {
for (Component* component : SnapshotComponents(*this)) {
if (component->isEnabled() && !component->IsBeingDestroyed()) {
if (!EnsureComponentStarted(*component)) {
continue;
}
component->FixedUpdate(fixedDt);
}
}
}
void GameObject::Render(const RenderContext& ctx) const {
for (const auto& component : m_Components) {
if (component->isEnabled()) {
for (Component* component : SnapshotComponents(*this)) {
if (component->isEnabled() && !component->IsBeingDestroyed()) {
if (!EnsureComponentStarted(*component)) {
continue;
}
component->Render(ctx);
}
}
}
void GameObject::Destroy() {
if (IsBeingDestroyed()) {
return;
}
Object::Destroy();
if (m_Scene != nullptr) {
m_Scene->GetPhysics().UnregisterGameObject(*this);
}
for (Component* component : SnapshotComponents(*this)) {
if (auto* rigidbody = dynamic_cast<Rigidbody*>(component);
rigidbody != nullptr && rigidbody->GetPhysicsWorld() != nullptr) {
rigidbody->GetPhysicsWorld()->UnregisterRigidbody(*rigidbody);
}
}
m_TransformPtr.SetParent(nullptr);
for (const auto& component : m_Components) {
component->Destroy();
for (Component* component : SnapshotComponents(*this)) {
if (component != nullptr) {
component->Destroy();
}
}
for (const auto child : m_TransformPtr.GetChildren()) {
child->GetOwner()->Destroy();
// Destroying a child detaches it from this transform, so iterate over a
// snapshot rather than the live child vector.
const std::vector<Transform*> children = m_TransformPtr.GetChildren();
for (Transform* child : children) {
if (child != nullptr && child->GetOwner() != nullptr) {
child->GetOwner()->Destroy();
}
}
}
void GameObject::CleanupComponents() {
for (const auto& component : m_Components) {
if (!component || !component->IsBeingDestroyed()) {
continue;
}
if (auto* rigidbody = dynamic_cast<Rigidbody*>(component.get()); rigidbody != nullptr &&
rigidbody->GetPhysicsWorld() != nullptr) {
rigidbody->GetPhysicsWorld()->UnregisterRigidbody(*rigidbody);
}
}
std::erase_if(m_Components, [](const std::unique_ptr<Component>& component) {
return component->IsBeingDestroyed();
return component == nullptr || component->IsBeingDestroyed();
});
}
}
void GameObject::RefreshPhysics() {
if (m_Scene != nullptr) {
m_Scene->RefreshPhysics();
}
}
+4 -10
View File
@@ -1,19 +1,13 @@
#include <destrum/ObjectModel/Object.h>
#include <cassert>
#include <iostream>
#include "spdlog/spdlog.h"
Object::~Object() {
if (!m_BeingDestroyed) {
assert(false && "Objects destructor called before destroy");
}
// spdlog::debug("Object Destroyed: {}", m_Name);
}
void Object::Destroy() {
// spdlog::debug("Object marked for destruction: {}", m_Name);
if (m_BeingDestroyed) {
return;
}
m_BeingDestroyed = true;
}
+233 -102
View File
@@ -1,53 +1,119 @@
#include <destrum/ObjectModel/Transform.h>
#include <destrum/ObjectModel/GameObject.h>
#include <algorithm>
#include <cmath>
#include <stdexcept>
#include <unordered_set>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/gtx/quaternion.hpp>
namespace {
bool IsChildRecursive(const Transform* parent,
const Transform* target,
std::unordered_set<const Transform*>& visited) {
if (parent == nullptr || !visited.insert(parent).second) {
return false;
}
for (const Transform* candidate : parent->GetChildren()) {
if (candidate == target || IsChildRecursive(candidate, target, visited)) {
return true;
}
}
return false;
}
[[nodiscard]] glm::mat4 ComposeMatrix(const glm::vec3& position,
const glm::quat& rotation,
const glm::vec3& scale) {
return glm::translate(glm::mat4{1.0f}, position)
* glm::mat4_cast(rotation)
* glm::scale(glm::mat4{1.0f}, scale);
}
[[nodiscard]] bool DecomposeMatrix(const glm::mat4& matrix,
glm::vec3& position,
glm::quat& rotation,
glm::vec3& scale) {
glm::vec3 skew{};
glm::vec4 perspective{};
if (!glm::decompose(matrix, scale, rotation, position, skew, perspective)) {
return false;
}
if (glm::length(skew) > 0.0001f ||
glm::length(perspective - glm::vec4{0.0f, 0.0f, 0.0f, 1.0f}) > 0.0001f) {
return false;
}
const float rotationLength = glm::length(rotation);
if (rotationLength <= 0.000001f || !std::isfinite(rotationLength)) {
return false;
}
rotation = glm::normalize(rotation);
return true;
}
}
Transform::Transform(GameObject* owner): m_Owner(owner) {
}
Transform::~Transform() {
SetParent(nullptr);
if (m_Parent != nullptr) {
Transform* parent = m_Parent;
m_Parent = nullptr;
parent->RemoveChild(this);
}
for (auto it = m_Children.begin(); it != m_Children.end();) {
Transform* child = *it;
it = m_Children.erase(it);
child->SetParent(nullptr);
// Do not call SetParent while the owning object is being torn down. The
// parent may already be in its destructor, so update the links directly.
const std::vector<Transform*> children = std::move(m_Children);
m_Children.clear();
for (Transform* child : children) {
if (child == nullptr || child->m_Parent != this) {
continue;
}
child->m_Parent = nullptr;
child->SetPositionDirty();
}
}
const glm::vec3& Transform::GetWorldPosition() {
if (m_PositionDirty) {
UpdateWorldPosition();
if (m_PositionDirty || m_RotationDirty || m_ScaleDirty || m_MatrixDirty) {
UpdateWorldCache();
}
return m_WorldPosition;
}
const glm::quat& Transform::GetWorldRotation() {
if (m_RotationDirty) {
UpdateWorldRotation();
if (m_PositionDirty || m_RotationDirty || m_ScaleDirty || m_MatrixDirty) {
UpdateWorldCache();
}
return m_WorldRotation;
}
const glm::vec3& Transform::GetWorldScale() {
if (m_ScaleDirty) {
UpdateWorldScale();
if (m_PositionDirty || m_RotationDirty || m_ScaleDirty || m_MatrixDirty) {
UpdateWorldCache();
}
return m_WorldScale;
}
const glm::mat4& Transform::GetWorldMatrix() {
if (m_MatrixDirty) {
UpdateWorldMatrix();
if (m_PositionDirty || m_RotationDirty || m_ScaleDirty || m_MatrixDirty) {
UpdateWorldCache();
}
return m_WorldMatrix;
}
void Transform::SetWorldPosition(const glm::vec3& position) {
if (m_Parent == nullptr) {
SetLocalPosition(position);
} else {
SetLocalPosition(position - m_Parent->GetWorldPosition());
}
glm::mat4 worldMatrix = GetWorldMatrix();
worldMatrix[3] = glm::vec4(position, 1.0f);
SetLocalFromWorldMatrix(worldMatrix);
}
void Transform::SetWorldPosition(float x, float y, float z) {
@@ -55,11 +121,10 @@ void Transform::SetWorldPosition(float x, float y, float z) {
}
void Transform::SetWorldRotation(const glm::quat& rotation) {
if(m_Parent == nullptr) {
SetLocalRotation(rotation);
} else {
SetLocalRotation(glm::inverse(m_Parent->GetWorldRotation()) * rotation);
}
const glm::vec3 worldPosition = GetWorldPosition();
const glm::vec3 worldScale = GetWorldScale();
const glm::mat4 worldMatrix = ComposeMatrix(worldPosition, rotation, worldScale);
SetLocalFromWorldMatrix(worldMatrix);
}
void Transform::SetWorldRotation(const glm::vec3& rotation) {
@@ -75,11 +140,10 @@ void Transform::SetWorldScale(double x, double y, double z) {
}
void Transform::SetWorldScale(const glm::vec3& scale) {
if (m_Parent == nullptr) {
SetLocalScale(scale);
} else {
SetLocalScale(scale - m_Parent->GetWorldScale());
}
const glm::vec3 worldPosition = GetWorldPosition();
const glm::quat worldRotation = GetWorldRotation();
const glm::mat4 worldMatrix = ComposeMatrix(worldPosition, worldRotation, scale);
SetLocalFromWorldMatrix(worldMatrix);
}
void Transform::Move(const glm::vec3& move) {
@@ -87,7 +151,7 @@ void Transform::Move(const glm::vec3& move) {
}
void Transform::Move(double x, double y, double z) {
this->Move(glm::vec3(x, y, z));
Move(glm::vec3(x, y, z));
}
void Transform::SetLocalPosition(const glm::vec3& position) {
@@ -108,7 +172,10 @@ void Transform::SetLocalRotation(const glm::vec3& rotation) {
}
void Transform::SetLocalRotation(const glm::quat& rotation) {
m_LocalRotation = rotation;
const float length = glm::length(rotation);
m_LocalRotation = length > 0.000001f
? glm::normalize(rotation)
: glm::quat{1.0f, 0.0f, 0.0f, 0.0f};
SetRotationDirty();
}
@@ -122,132 +189,196 @@ void Transform::SetLocalScale(const glm::vec3& scale) {
}
void Transform::RemoveChild(Transform* transform) {
std::erase(m_Children, transform);
const auto it = std::find(m_Children.begin(), m_Children.end(), transform);
if (it != m_Children.end()) {
m_Children.erase(it);
}
}
void Transform::AddChild(Transform* transform) {
// if (transform == this or transform == nullptr) {
// return;
// }
//
// if (transform->m_Parent) {
// transform->m_Parent->RemoveChild(transform);
// }
//
// transform->SetParent(this);
m_Children.push_back(transform);
//
// transform->SetPositionDirty();
}
void Transform::SetParent(Transform* parent, bool useWorldPosition) {
if (parent == m_Parent or parent == this or IsChild(parent)) {
if (transform == nullptr || transform == this) {
return;
}
if (parent == nullptr) {
SetLocalPosition(GetWorldPosition());
} else {
if (std::find(m_Children.begin(), m_Children.end(), transform) != m_Children.end()) {
return;
}
m_Children.push_back(transform);
}
void Transform::SetParent(Transform* parent, bool useWorldPosition) {
if (parent == m_Parent) {
return;
}
// A transform cannot be parented to itself or to one of its descendants.
if (parent == this || IsChild(parent)) {
return;
}
if (parent != nullptr) {
GameObject* parentOwner = parent->GetOwner();
if (parentOwner == nullptr || parentOwner->IsBeingDestroyed() ||
(m_Owner != nullptr && m_Owner->GetScene() != parentOwner->GetScene())) {
return;
}
}
glm::mat4 worldMatrix{1.0f};
if (useWorldPosition) {
worldMatrix = GetWorldMatrix();
}
Transform* previousParent = m_Parent;
if (previousParent != nullptr) {
previousParent->RemoveChild(this);
}
m_Parent = parent;
if (m_Parent != nullptr) {
m_Parent->AddChild(this);
}
if (m_Owner != nullptr) {
m_Owner->SetActiveDirty();
m_Owner->RefreshPhysics();
}
try {
if (useWorldPosition) {
SetLocalPosition(GetWorldPosition() - parent->GetWorldPosition());
SetLocalFromWorldMatrix(worldMatrix);
} else {
SetPositionDirty();
}
} catch (...) {
if (m_Parent != nullptr) {
m_Parent->RemoveChild(this);
}
m_Parent = previousParent;
if (m_Parent != nullptr) {
m_Parent->AddChild(this);
}
SetPositionDirty();
}
if (m_Parent) {
m_Parent->RemoveChild(this);
}
m_Parent = parent;
if (m_Parent) {
m_Parent->AddChild(this);
throw;
}
}
bool Transform::IsChild(Transform* child) const {
return std::ranges::find(m_Children, child) != m_Children.end();
if (child == nullptr) {
return false;
}
std::unordered_set<const Transform*> visited;
return IsChildRecursive(this, child, visited);
}
const std::vector<Transform*>& Transform::GetChildren() const {
// std::vector<Transform*> validChildren;
// for (auto* child : m_Children) {
// if (child && !child->GetOwner()->IsBeingDestroyed()) {
// validChildren.push_back(child);
// }
// }
// return validChildren;
return m_Children;
}
GameObject *Transform::GetOwner() const {
GameObject* Transform::GetOwner() const {
return m_Owner;
}
void Transform::SetPositionDirty() {
m_PositionDirty = true;
m_RotationDirty = true;
m_ScaleDirty = true;
m_MatrixDirty = true;
for (const auto child: m_Children) {
child->SetPositionDirty();
for (Transform* child : m_Children) {
if (child != nullptr) {
child->SetPositionDirty();
}
}
}
void Transform::SetRotationDirty() {
m_PositionDirty = true;
m_RotationDirty = true;
m_ScaleDirty = true;
m_MatrixDirty = true;
for(Transform* childPtr : m_Children) {
if(not childPtr->m_RotationDirty) {
childPtr->SetRotationDirty();
for (Transform* child : m_Children) {
if (child != nullptr) {
child->SetRotationDirty();
}
}
}
void Transform::SetScaleDirty() {
m_PositionDirty = true;
m_RotationDirty = true;
m_ScaleDirty = true;
m_MatrixDirty = true;
for(Transform* childPtr : m_Children) {
if(not childPtr->m_ScaleDirty) {
childPtr->SetScaleDirty();
for (Transform* child : m_Children) {
if (child != nullptr) {
child->SetScaleDirty();
}
}
}
void Transform::UpdateWorldPosition() {
if (m_Parent) {
m_WorldPosition = m_Parent->GetWorldPosition() + m_LocalPosition;
} else {
m_WorldPosition = m_LocalPosition;
}
m_PositionDirty = false;
UpdateWorldCache();
}
void Transform::UpdateWorldRotation() {
if (m_Parent == nullptr) {
m_WorldRotation = m_LocalRotation;
} else {
m_WorldRotation = m_LocalRotation * m_Parent->GetWorldRotation();
}
m_RotationDirty = false;
UpdateWorldCache();
}
void Transform::UpdateWorldScale() {
if(m_Parent == nullptr) {
m_WorldScale = m_LocalScale;
} else {
m_WorldScale = m_LocalScale * m_Parent->GetWorldScale();
}
m_ScaleDirty = false;
UpdateWorldCache();
}
void Transform::UpdateWorldMatrix() {
const glm::mat4 trans = glm::translate(glm::mat4(1.0f), GetWorldPosition());
const glm::mat4 rot = glm::mat4_cast(GetWorldRotation());
const glm::mat4 scale = glm::scale(glm::mat4(1.0f), GetWorldScale());
m_WorldMatrix = trans * rot * scale;
m_MatrixDirty = false;
UpdateWorldCache();
}
void Transform::SetLocalFromWorldMatrix(const glm::mat4& worldMatrix) {
glm::mat4 localMatrix = worldMatrix;
if (m_Parent != nullptr) {
const glm::mat4 parentMatrix = m_Parent->GetWorldMatrix();
if (std::abs(glm::determinant(parentMatrix)) <= 0.000001f) {
throw std::runtime_error("Cannot set a world transform under a singular parent");
}
localMatrix = glm::inverse(parentMatrix) * worldMatrix;
}
glm::vec3 position{};
glm::quat rotation{};
glm::vec3 scale{};
if (DecomposeMatrix(localMatrix, position, rotation, scale)) {
m_LocalPosition = position;
m_LocalRotation = rotation;
m_LocalScale = scale;
} else {
throw std::runtime_error("World transform cannot be represented as position/rotation/scale");
}
SetPositionDirty();
}
void Transform::UpdateWorldCache() {
if (!m_PositionDirty && !m_RotationDirty && !m_ScaleDirty && !m_MatrixDirty) {
return;
}
const glm::mat4 localMatrix = ComposeMatrix(m_LocalPosition, m_LocalRotation, m_LocalScale);
m_WorldMatrix = m_Parent != nullptr
? m_Parent->GetWorldMatrix() * localMatrix
: localMatrix;
if (!DecomposeMatrix(m_WorldMatrix, m_WorldPosition, m_WorldRotation, m_WorldScale)) {
m_WorldPosition = glm::vec3(m_WorldMatrix[3]);
m_WorldRotation = m_Parent != nullptr
? glm::normalize(m_Parent->GetWorldRotation() * m_LocalRotation)
: m_LocalRotation;
m_WorldScale = m_LocalScale;
}
m_PositionDirty = false;
m_RotationDirty = false;
m_ScaleDirty = false;
m_MatrixDirty = false;
}