#include "PhysicsSystem.h" #include "src/Scene/Scene.h" #include "src/Scene/Components/BoxCollider.h" #include "src/Scene/Components/CapsuleColliderComponent.h" #include "src/Scene/Components/SphereCollider.h" #include "src/Scene/Components/MeshCollider.h" #include "src/Scene/Components/ModelComponent.h" #include #include "src/Scene/Entities/Entity.h" #include #include #include #include #include namespace Nuake { PhysicsSystem::PhysicsSystem(Scene* scene) { m_Scene = scene; } bool PhysicsSystem::Init() { Logger::Log("Initializing the physic system"); // We need to initialize shapes first, then bodies... InitializeShapes(); InitializeRigidbodies(); InitializeCharacterControllers(); InitializeQuakeMap(); Logger::Log("Physic system initialized successfully"); return true; } void PhysicsSystem::Update(Timestep ts) { if (!Engine::IsPlayMode()) { return; } InitializeShapes(); InitializeRigidbodies(); InitializeCharacterControllers(); ApplyForces(); PhysicsManager::Get().Step(ts); auto brushes = m_Scene->m_Registry.view(); for (auto e : brushes) { auto [transform, brush] = brushes.get(e); if (!brush.IsFunc) continue; brush.Targets.clear(); auto targetnameView = m_Scene->m_Registry.view(); for (auto e2 : targetnameView) { auto [ttransform, name] = targetnameView.get(e2); if (name.Name == brush.target) { brush.Targets.push_back(Entity{ e2, m_Scene }); } } } } void PhysicsSystem::FixedUpdate(Timestep ts) { if (!Engine::IsPlayMode()) return; auto view = m_Scene->m_Registry.view(); for (auto e : view) { auto [transform, rigidBodyComponent] = view.get(e); Entity ent = Entity({ e, m_Scene }); bool isTrigger = false; if (!rigidBodyComponent.GetRigidBody()) { continue; } if (rigidBodyComponent.Rigidbody->IsTrigger()) { auto globaltransform = transform.GetGlobalTransform(); Vector3 translation; glm::quat rotation; Vector3 scale; Vector3 skew; Vector4 perspective; glm::decompose(globaltransform, scale, rotation, translation, skew, perspective); PhysicsManager::Get().SetBodyTransform(ent, globaltransform[3], glm::normalize(transform.GetGlobalRotation())); } } } void PhysicsSystem::Exit() { PhysicsManager::Get().Reset(); } void PhysicsSystem::InitializeQuakeMap() { auto quakeBrushesView = m_Scene->m_Registry.view(); for (auto e : quakeBrushesView) { const auto [transformComponent, brushComponent] = quakeBrushesView.get(e); // Doesn't apply to non-solid brushes if ((!brushComponent.IsSolid && !brushComponent.IsTrigger)) { continue; } for (const auto& hull : brushComponent.Hulls) { const auto entity = Entity({ e, m_Scene }); const Vector3& startPosition = transformComponent.GetGlobalPosition(); const Matrix4& startTransform = transformComponent.GetGlobalTransform(); const Quat& startRotation = transformComponent.GetGlobalRotation(); const auto collisionShape = CreateRef(hull); auto rigidBody = CreateRef(0.0f, startPosition, startRotation, startTransform, collisionShape, entity, Vector3{ 0, 0, 0 }, brushComponent.IsFunc); brushComponent.Rigidbody.push_back(rigidBody); rigidBody->SetIsTrigger(brushComponent.IsTrigger); PhysicsManager::Get().RegisterBody(rigidBody); } } } void PhysicsSystem::InitializeShapes() { const auto boxView = m_Scene->m_Registry.view(); for (auto entity : boxView) { auto& boxComponent = boxView.get(entity); if (!boxComponent.Box) { boxComponent.Box = CreateRef(boxComponent.Size); } } const auto capsuleView = m_Scene->m_Registry.view(); for (auto entity : capsuleView) { auto& capsuleComponent = capsuleView.get(entity); if (!capsuleComponent.Capsule) { float radius = capsuleComponent.Radius; float height = capsuleComponent.Height; capsuleComponent.Capsule = CreateRef(radius, height); } } const auto cylinderView = m_Scene->m_Registry.view(); for (auto entity : cylinderView) { auto& cylinderComponent = cylinderView.get(entity); if (!cylinderComponent.Cylinder) { float radius = cylinderComponent.Radius; float height = cylinderComponent.Height; cylinderComponent.Cylinder = CreateRef(radius, height); } } const auto sphereView = m_Scene->m_Registry.view(); for (auto entity : sphereView) { auto& sphereComponent = sphereView.get(entity); if (!sphereComponent.Sphere) { sphereComponent.Sphere = CreateRef(sphereComponent.Radius); } } const auto meshColliderView = m_Scene->m_Registry.view(); for (auto e : meshColliderView) { Entity entity { e, m_Scene }; if (!entity.HasComponent()) { Logger::Log("Cannot use mesh collider without model component", "physics", WARNING); } auto& meshColliderComponent = meshColliderView.get(e); if (!meshColliderComponent.Shape) { const auto& modelComponent = entity.GetComponent(); if (modelComponent.ModelResource) { uint32_t subMeshId = meshColliderComponent.SubMesh; const std::vector>& submeshes = modelComponent.ModelResource->GetMeshes(); if (subMeshId >= submeshes.size()) { Logger::Log("Cannot create mesh collider, invalid submesh ID", "physics", WARNING); } Ref mesh = submeshes[subMeshId]; meshColliderComponent.Shape = CreateRef(mesh); } } } } void PhysicsSystem::InitializeRigidbodies() { auto view = m_Scene->m_Registry.view(); for (auto e : view) { auto [transform, rigidBodyComponent] = view.get(e); Entity ent = Entity({ e, m_Scene }); Ref rigidBody; Ref shape; bool isTrigger = false; if (rigidBodyComponent.GetRigidBody()) { continue; } if (ent.HasComponent()) { BoxColliderComponent& boxComponent = ent.GetComponent(); isTrigger = boxComponent.IsTrigger; shape = CreateRef(boxComponent.Size); } if (ent.HasComponent()) { auto& capsuleComponent = ent.GetComponent(); float radius = capsuleComponent.Radius; float height = capsuleComponent.Height; isTrigger = capsuleComponent.IsTrigger; shape = CreateRef(radius, height); } if (ent.HasComponent()) { auto& cylinderComponent = ent.GetComponent(); float radius = cylinderComponent.Radius; float height = cylinderComponent.Height; isTrigger = cylinderComponent.IsTrigger; shape = CreateRef(radius, height); } if (ent.HasComponent()) { const auto& component = ent.GetComponent(); isTrigger = component.IsTrigger; shape = CreateRef(component.Radius); } if (ent.HasComponent()) { if (!ent.HasComponent()) { Logger::Log("Cannot use mesh collider without model component", "physics", WARNING); } const auto& modelComponent = ent.GetComponent(); const auto& component = ent.GetComponent(); isTrigger = component.IsTrigger; if (modelComponent.ModelResource) { uint32_t subMeshId = component.SubMesh; const std::vector>& submeshes = modelComponent.ModelResource->GetMeshes(); if (subMeshId >= submeshes.size()) { Logger::Log("Cannot create mesh collider, invalid submesh ID", "physics", WARNING); } Ref mesh = submeshes[subMeshId]; shape = CreateRef(mesh); } } if (!shape) { continue; } rigidBody = CreateRef(rigidBodyComponent.Mass, transform.GetGlobalPosition(), transform.GetGlobalRotation(), transform.GetGlobalTransform(), shape, ent); rigidBody->SetLockXAxis(rigidBodyComponent.LockX); rigidBody->SetLockYAxis(rigidBodyComponent.LockY); rigidBody->SetLockZAxis(rigidBodyComponent.LockZ); rigidBody->SetIsTrigger(isTrigger); PhysicsManager::Get().RegisterBody(rigidBody); rigidBodyComponent.Rigidbody = rigidBody; } } void PhysicsSystem::InitializeCharacterControllers() { auto characterControllerView = m_Scene->m_Registry.view(); for (const auto& e : characterControllerView) { Entity entity = Entity({ e, m_Scene }); auto [transformComponent, characterControllerComponent] = characterControllerView.get(e); if (characterControllerComponent.GetCharacterController()) { continue; } Ref shape; bool hasValidShape = true; if (entity.HasComponent()) { const auto& capsuleColliderComponent = entity.GetComponent(); shape = capsuleColliderComponent.Capsule; } else if (entity.HasComponent()) { const auto& capsuleColliderComponent = entity.GetComponent(); shape = capsuleColliderComponent.Box; } else if (entity.HasComponent()) { const auto& capsuleColliderComponent = entity.GetComponent(); shape = capsuleColliderComponent.Sphere; } else if (entity.HasComponent()) { const auto& cylinderColliderComponent = entity.GetComponent(); shape = cylinderColliderComponent.Cylinder; } else { hasValidShape = false; Logger::Log("No shape was provided for character controller. Default one was provided.", "physics", WARNING); shape = CreateRef(1.0f, 0.5f); } Physics::CharacterControllerSettings settings { shape, characterControllerComponent.Friction, characterControllerComponent.MaxSlopeAngle, characterControllerComponent.AutoStepping, characterControllerComponent.StickToFloorStepDown, characterControllerComponent.StepDownExtra, characterControllerComponent.SteppingStepUp, characterControllerComponent.SteppingMinDistance, characterControllerComponent.SteppingForwardDistance }; auto characterController = CreateRef(std::move(settings)); characterController->SetEntity(entity); // Used to link back to the entity characterController->Position = transformComponent.GetGlobalPosition(); characterController->Rotation = transformComponent.GetGlobalRotation(); characterControllerComponent.SetCharacterController(characterController); PhysicsManager::Get().RegisterCharacterController(characterController); } } void PhysicsSystem::ApplyForces() { auto view = m_Scene->m_Registry.view(); for (auto e : view) { auto [transform, rigidBodyComponent] = view.get(e); Entity ent = Entity({ e, m_Scene }); Ref rigidBody; // Not initialized yet. if (!rigidBodyComponent.GetRigidBody() || rigidBodyComponent.QueuedForce == Vector3() || rigidBodyComponent.Mass == 0.0) { continue; } PhysicsManager::Get().GetWorld()->AddForceToRigidBody(ent, rigidBodyComponent.QueuedForce); rigidBodyComponent.QueuedForce = Vector3(); } } }