Custom Physics Engine

This project involved creating a basic physics engine from scratch in D3D11 using DirectXMath. It features basic collision detection and response between sphere, AABB and OBB colliders, a particle system, springs and a rigid body physics model. The project ultimately demonstrates the basics of rigid body and point mass physics. See a demonstration below:

OBB Colliders

Implementing OBB colliders was the first tough problem I encountered after getting the basic colliders down. OBB colliders should be able to rotate and detect collisions on any axes. In order to achieve this, a series of more rigorous checks must be performed compared to AABB or sphere colliders known as separating axis theorem. These checks involve projecting vertices of the collider onto positive and negative normals of each collider. If no overlaps are found then there is no collision and the function returns. If all the tests pass then a collision has been detected, the penetration depth is calculated and collision resolution occurs. Collisions are resolved by checking each object againt one another to see if theyre colliding. If they are then the penetration is resolved and the relevant impulses are applied. The code that handles collision resolution can be seen below:

  
    for (size_t i = 0; i < _gameObjects.size(); i++)
    {
      for (size_t j = 0; j < _gameObjects.size(); j++)
      {
        if (i <= j) continue;
        if (!(_gameObjects[i]->GetPhysicsModel()->IsCollideable() && _gameObjects[j]->GetPhysicsModel()->IsCollideable())) continue;
        CollisionManifold manifold = CollisionManifold();
        if (!_gameObjects[i]->GetPhysicsModel()->GetCollider()->CollidesWith(*_gameObjects[j]->GetPhysicsModel()->GetCollider(), manifold)) continue;
    
        vector3 relativeV = _gameObjects[i]->GetPhysicsModel()->GetVelocity() - _gameObjects[j]->GetPhysicsModel()->GetVelocity();
        vector3 collisionNormal = manifold.collisionNormal;
    
        float restitution = (_gameObjects[j]->GetPhysicsModel()->GetCollider()->GetRestitution() + _gameObjects[i]->GetPhysicsModel()->GetCollider()->GetRestitution()) * 0.5f;
        float inverseMassA = _gameObjects[i]->GetPhysicsModel()->GetInverseMass();
        float inverseMassB = _gameObjects[j]->GetPhysicsModel()->GetInverseMass();

        vector3 penetrationResolutionA = collisionNormal * (manifold.points[0].penetrationDepth * inverseMassA);
        vector3 penetrationResolutionB = collisionNormal * (manifold.points[0].penetrationDepth * inverseMassB);
    
        penetrationResolutionA *= 7.f;
        penetrationResolutionB *= 7.f;
          if (fabs(Vector::Magnitude(penetrationResolutionA)) > 1.0f)
            penetrationResolutionA = Vector::Normalise(penetrationResolutionA);
          if (fabs(Vector::Magnitude(penetrationResolutionB)) > 1.0f)
            penetrationResolutionB = Vector::Normalise(penetrationResolutionB);
    
        if (Vector::DotProduct(relativeV, collisionNormal) > 0.f)
        {
          _gameObjects[j]->GetPhysicsModel()->ApplyImpulse(penetrationResolutionB);
          _gameObjects[i]->GetPhysicsModel()->ApplyImpulse(vector3() - penetrationResolutionA);
        }
        else if(Vector::DotProduct(relativeV, collisionNormal) < 0.f)
        {
          _gameObjects[i]->GetPhysicsModel()->ApplyImpulse(penetrationResolutionA);
          _gameObjects[j]->GetPhysicsModel()->ApplyImpulse(vector3() - penetrationResolutionB);
        }
    
        float totalV = -(1 + restitution) * Vector::DotProduct(relativeV, collisionNormal);
        float impulse = totalV / (inverseMassA + inverseMassB);
        
        _gameObjects[i]->GetPhysicsModel()->ApplyImpulse((collisionNormal * impulse * inverseMassA));
        _gameObjects[j]->GetPhysicsModel()->ApplyImpulse(vector3() - (collisionNormal * impulse * inverseMassB));
      }
    }
  

Particle System

The particle system works via a particle manager which can create particle system instances which manage individual particles. Forces can be set to affect the particles in a system by collating the desired force generators into an array and including it as a parameter in particle system creation. The force generators I have created so far, which you can see acting in the demo, are a burst force, a vortex force and a wind force. I have made these inheret from a base force generator class so it would be trivial to expand this system with different forces. When creating a system, the other options available include: the mesh rendered, the location of the system, the number of particles, the time for the system to reset, the time until the system destroys itself, the delay between particle spawns, the variance in spawn positions of individual particles, whether the system loops, the mass of each particle, whether friction is applied to the system, whether gravity is applied and whether gravity is inverted. Overall, this basic particle system allowed me to experiment with generating different forces to create different and interesting effects.

Who Did What?

Original Framework by Staffs Uni

When Was it Made?

This project was worked on between late January to early March 2024

What Went Well?

I believe that I was able to implement a wide range of basic physics features to a good degree of accuracy. In particular, I think that the OBB collision detection is an extension of what was expected of me and so too was the particle system. I think both leave room for improvement at a later date which my implementations allow for.

What Could Be Better?

There are multiple aspects that could be improved upon. These include better collision resolution that take into account the point on the collider where a collision occurred, refactoring the particle system to take advantage of both the compute shader and GPU instancing and implementing some broad phase collision detection algorithm. I would also change the OBB collision detection to perform better checks which I found out about after completing the module.

 

Social Media

 

Rhys Elliott 2023. contact@rhyselliott.com