Files
pke/src/engine/physics/physicsworld.cpp
2026-03-07 03:14:10 +03:00

314 lines
8.8 KiB
C++

#include "engine/core.h"
#include "engine/log.h"
#include "engine/engine.h"
#include "engine/ientity.h"
#include "engine/camera.h"
#include "engine/physics/physicsworld.h"
#include "engine/physics/physicsdebugdraw.h"
#include "engine/physics/rigidbody.h"
#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
PhysicsWorld* g_PhysicsWorld = nullptr;
static void InternalTickCallback(btDynamicsWorld* world, btScalar timeStep)
{
SDL_assert(world);
SDL_assert(world->getWorldUserInfo());
static_cast<PhysicsWorld*>(world->getWorldUserInfo())->InternalTick();
}
PhysicsWorld::PhysicsWorld()
{
m_debugDraw = false;
m_collisionConfiguration = new btDefaultCollisionConfiguration();
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
m_overlappingPairCache = new btDbvtBroadphase();
m_solver = new btSequentialImpulseConstraintSolver();
m_world = new btDiscreteDynamicsWorld(m_dispatcher, m_overlappingPairCache, m_solver, m_collisionConfiguration);
m_btGhostPairCallback = new btGhostPairCallback();
m_world->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(m_btGhostPairCallback);
m_world->setInternalTickCallback(InternalTickCallback, this);
m_world->setDebugDrawer(&m_physicsDebugDraw);
m_accumulatedTime = 0.0f;
m_stepTime = (1.0f / 30.0f); // 30 fps
// set the standart gravity
//m_world->setGravity(btVector3(0, 0, 0));
//getWorld()->setGravity(btVector3(0.0, -15.0, 0.0));
}
PhysicsWorld::~PhysicsWorld()
{
m_world->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(nullptr);
// delete ghost pair callback
delete m_btGhostPairCallback;
// delete dynamics world
delete m_world;
// delete solver
delete m_solver;
// delete broadphase
delete m_overlappingPairCache;
// delete dispatcher
delete m_dispatcher;
// delete collision configuration
delete m_collisionConfiguration;
}
void PhysicsWorld::AddRigidBody(RigidBody* body)
{
SDL_assert(body->GetSDKBody() && "RigidBody is not properly initialized");
m_world->addRigidBody(body->GetSDKBody());
m_rigidbodies.push_back(body);
}
void PhysicsWorld::RemoveRigidBody(RigidBody* body)
{
SDL_assert(body->GetSDKBody() && "RigidBody is not properly initialized");
m_world->removeRigidBody(body->GetSDKBody());
auto it = std::find(m_rigidbodies.begin(), m_rigidbodies.end(), body);
if (it != m_rigidbodies.end())
m_rigidbodies.erase(it);
}
const std::vector<RigidBody*>& PhysicsWorld::GetRigidBodies()
{
return m_rigidbodies;
}
void PhysicsWorld::Step(float delta)
{
m_world->stepSimulation(delta);
// m_world->stepSimulation(delta, 12, m_stepTime);
#if 0
if (delta < 0.01f)
{
m_accumulatedTime += delta;
if (m_accumulatedTime > m_stepTime)
{
m_world->stepSimulation(m_stepTime);
m_accumulatedTime -= m_stepTime;
}
}
else
{
m_world->stepSimulation(delta);
}
#endif
//m_debugDraw = true;
if (m_debugDraw)
{
//DebugDrawTriggers();
int debugDrawMode = 0;
debugDrawMode |= btIDebugDraw::DBG_DrawAabb;
debugDrawMode |= btIDebugDraw::DBG_DrawWireframe;
// debugDrawMode |= btIDebugDraw::DBG_DrawContactPoints;
m_physicsDebugDraw.setDebugMode(debugDrawMode);
m_world->debugDrawWorld();
}
/*if (m_world)
m_world->stepSimulation(1 / 60.0f);
int debugDrawMode = 0;
debugDrawMode |= btIDebugDraw::DBG_DrawAabb;
debugDrawMode |= btIDebugDraw::DBG_DrawWireframe;
debugDrawMode |= btIDebugDraw::DBG_DrawContactPoints;
m_physicsDebugDraw.setDebugMode(debugDrawMode);
m_world->debugDrawWorld();*/
}
bool PhysicsWorld::TraceRay(TraceRayResult& _result, const glm::vec3& _rayBegin, const glm::vec3& _rayEnd, IEntityBase* _pIgnoreEntity)
{
btVector3 rayStart = glmVectorToBt(_rayBegin);
btVector3 rayEnd = glmVectorToBt(_rayEnd);
ClosestRayResultCallback RayResultCallback(rayStart, rayEnd, _pIgnoreEntity);
g_PhysicsWorld->GetWorld()->rayTest(rayStart, rayEnd, RayResultCallback);
if (RayResultCallback.hasHit())
{
_result.hit = true;
_result.pEntity = (IEntityBase*)RayResultCallback.m_collisionObject->getUserPointer();
_result.position = btVectorToGlm(RayResultCallback.m_hitPointWorld);
_result.normal = btVectorToGlm(RayResultCallback.m_hitNormalWorld);
}
return _result.hit;
}
void PhysicsWorld::ToggleDebugDraw()
{
m_debugDraw = !m_debugDraw;
}
void PhysicsWorld::InternalTick()
{
int numManifolds = m_world->getDispatcher()->getNumManifolds();
for (int i = 0; i < numManifolds; i++)
{
btPersistentManifold* contactManifold = m_world->getDispatcher()->getManifoldByIndexInternal(i);
btCollisionObject* obA = const_cast<btCollisionObject*>(contactManifold->getBody0());
btCollisionObject* obB = const_cast<btCollisionObject*>(contactManifold->getBody1());
int numContacts = contactManifold->getNumContacts();
for (int j = 0; j < numContacts; j++)
{
btManifoldPoint& pt = contactManifold->getContactPoint(j);
if (pt.getDistance() < 0.f)
{
const btVector3& ptA = pt.getPositionWorldOnA();
const btVector3& ptB = pt.getPositionWorldOnB();
const btVector3& normalOnB = pt.m_normalWorldOnB;
//RigidBody* rbA = static_cast<RigidBody*>(obA->getUserPointer());
//RigidBody* rbB = static_cast<RigidBody*>(obB->getUserPointer());
//if (!rbA || !rbB)
// continue;
//SDL_assert(rbA);
//SDL_assert(rbB);
IEntityBase* entA = static_cast<IEntityBase*>(obA->getUserPointer());
IEntityBase* entB = static_cast<IEntityBase*>(obB->getUserPointer());
if (!entA || !entB)
continue;
if (entA)
entA->OnCollide(entB);
if (entB)
entB->OnCollide(entA);
/*if (TriggerComponent* trigger = rbA->GetEntity()->GetComponent<TriggerComponent>())
{
trigger->OnCollide(rbA, rbB);
}
else if (TriggerComponent* trigger = rbB->GetEntity()->GetComponent<TriggerComponent>())
{
trigger->OnCollide(rbB, rbA);
}*/
}
}
}
}
void PhysicsWorld::AddCollisionModel(StaticMeshVertex* vertices, size_t verticesCount, uint32_t* indices, size_t indicesCount)
{
int index = (int)m_collisionModels.size();
m_collisionModels.resize((size_t)index + 1);
// Check for is indices are 32 bits
bool is32Bits = true;// sizeof(index_t) == sizeof(uint32_t);
m_collisionModels[index].triangleMesh = new btTriangleMesh(is32Bits);
m_collisionModels[index].triangleMesh->preallocateVertices((int)verticesCount);
m_collisionModels[index].triangleMesh->preallocateIndices((int)indicesCount);
// Initialize triangle mesh
uint32_t trianglesCount = 0;
for (uint32_t n = 0; n < indicesCount; n += 3) {
m_collisionModels[index].triangleMesh->addTriangle(
glmVectorToBt(vertices[indices[n]].position),
glmVectorToBt(vertices[indices[n + 1]].position),
glmVectorToBt(vertices[indices[n + 2]].position),
true);
trianglesCount++;
}
// Create shape
m_collisionModels[index].shape = new btBvhTriangleMeshShape(m_collisionModels[index].triangleMesh, true);
// Create collision body
m_collisionModels[index].object = new btCollisionObject();
m_collisionModels[index].object->setCollisionShape(m_collisionModels[index].shape);
//m_collisionModels[index].object->setUserPointer(&m_collisionModels[index]);
// Add to scene
m_world->addCollisionObject(m_collisionModels[index].object);
// Report
Msg("PhysicsWorld::AddCollisionModel: %i triangles %i bytes", trianglesCount, trianglesCount * sizeof(btVector3));
}
void PhysicsWorld::Reset()
{
int numModels = (int)m_collisionModels.size();
for (int i = 0; i < numModels; i++)
{
if (m_collisionModels[i].object)
{
// remove object from world
m_world->removeCollisionObject(m_collisionModels[i].object);
// reset shape
m_collisionModels[i].object->setCollisionShape(nullptr);
// delete
delete m_collisionModels[i].object;
m_collisionModels[i].object = nullptr;
}
if (m_collisionModels[i].shape)
{
delete m_collisionModels[i].shape;
m_collisionModels[i].shape = nullptr;
}
if (m_collisionModels[i].triangleMesh)
{
delete m_collisionModels[i].triangleMesh;
m_collisionModels[i].triangleMesh = nullptr;
}
}
Msg("PhysicsWorld: Destroyed %d static collision models", numModels);
m_collisionModels.clear();
}
//
ClosestRayResultCallback::ClosestRayResultCallback(const btVector3& rayFromWorld, const btVector3& rayToWorld, IEntityBase* entity) :
btCollisionWorld::ClosestRayResultCallback(rayFromWorld, rayToWorld),
m_entity(entity)
{
}
ClosestRayResultCallback::~ClosestRayResultCallback()
{
m_entity = nullptr;
}
bool ClosestRayResultCallback::needsCollision(btBroadphaseProxy* proxy0) const
{
bool needsCollision = inherited::needsCollision(proxy0);
btCollisionObject* collisionObject = (btCollisionObject*)proxy0->m_clientObject;
if (m_entity && collisionObject->getUserPointer() == m_entity)
return false;
return needsCollision;
}