I have eagerly awaited the opportunity to reach this significant milestone, and I am excited to document my experiences in this journey.

The engine currently features a graphical foundation for game development. With the integration of physics capabilities, it will enable the development of a diverse array of game types.

In this discussion, I will outline the primary structure of Bullet Physics integration within my custom engine, avoiding excessive technical detail.

Main structure

  • Physics World
  • Rigidbodies
  • Overlapping Physics Objects
  • Collision Components
  • Raycasting and Sweeping
  • Movement Component

Physics World

The Physics World serves as the foundational framework for managing all physics objects. It oversees the execution of their void PhysicsTick(float) functions, facilitating the simulation of physics interactions. Additionally, the Physics World is responsible for handling raycasting and sweeping operations.

Rigidbodies

Rigidbodies are the fundamental physics objects characterized by physical properties such as mass, velocity, and acceleration. Various types of forces—such as linear force, impulse, and torque—can be applied to these rigid bodies. Their collision responses are akin to those of solid blocks, and appropriate delegates are triggered during the stages of a collision: when it begins, continues, and concludes.

Overlapping Physics Objects

Overlapping Physics Objects are entities that respond to collision events without possessing distinct physical properties. Their collision responses are categorized as overlaps, and similar to Rigidbodies, relevant delegates are invoked when a collision begins, continues, and concludes.

Collision Components

Collision Components are integral to both Rigidbodies and Overlapping Physics Objects, defining the shapes and boundaries that facilitate interaction between physics entities. They play a crucial role in determining how objects collide and respond to one another.

Box Collision Component

Image 1

Sphere Collision Component

Image 2

Capsule Collision Component

Image 3

Multiple Collision Component

Image 5

Dynamic Mesh Collision Component

Image 4

Static Mesh Collision Component

Image 6

Raycasting and Sweeping

Raycasting and sweeping are essential operations within the Physics World that allow for the detection and interaction of objects along a specified path. These techniques are instrumental in providing feedback about potential collisions and interactions before they occur, enhancing the accuracy of the physics simulation.

Movement Component

The Movement Component is responsible for updating the positions and orientations of physics objects over time. It integrates with the Physics World to ensure that all movements are consistent with the applied physics rules and interactions.

Conclusion

In conclusion, I have successfully integrated physics functionality into my engine, equipping it with the capability to perform a wide range of essential physical operations. I hope you found this exploration of my integration of Bullet Physics into my engine insightful and informative.

Thank you for your attention, and I look forward to sharing more developments in the future.


Code snippets

You can find some important code snippets here. If you would like to research the repository, you can always reach it in GitHub.

PhysicsWorld

bool OverlappingDestroyedCallback(void* userPersistentData)
{
    return true;
}

bool OverlappingProcessedCallback(btManifoldPoint& manifoldPoint, void* body0, void* body1)
{
    engine->GetPhysicsWorld()->OnOverlappingCollisionContinue(manifoldPoint, static_cast<const btCollisionObject*>(body0), static_cast<const btCollisionObject*>(body1));
    return true;
}

void OverlappingStartedCallback(btPersistentManifold* const& manifold)
{
    engine->GetPhysicsWorld()->OnOverlappingCollisionBegin(manifold);
}

void OverlappingEndedCallback(btPersistentManifold* const& manifold)
{
    engine->GetPhysicsWorld()->OnOverlappingCollisionEnd(manifold);
}

void PhysicsWorld::PreInit()
{
    collisionConfiguration_ = new btDefaultCollisionConfiguration();
    dispatcher_ = new btCollisionDispatcher(collisionConfiguration_);
    ghostPairCallback_ = new btGhostPairCallback();
    broadphase_ = new btDbvtBroadphase();
    broadphase_->getOverlappingPairCache()->setInternalGhostPairCallback(ghostPairCallback_);

    btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
    solver_ = solver;

    dynamicsWorld_ = new btDiscreteDynamicsWorld(dispatcher_, broadphase_, solver_, collisionConfiguration_);
    dynamicsWorld_->setGravity(PhysicsUtils::FromVector3ToBtVector3(gravity_));
    dynamicsWorld_->setDebugDrawer(physicsDebugger_);

    gContactDestroyedCallback = OverlappingDestroyedCallback;
    gContactProcessedCallback = OverlappingProcessedCallback;
    gContactStartedCallback = OverlappingStartedCallback;
    gContactEndedCallback = OverlappingEndedCallback;
}

void PhysicsWorld::PhysicsTick(float deltaTime)
{
    dynamicsWorld_->stepSimulation(deltaTime);

    for (PhysicsObject* physicsObject : physicsObjects_)
    {
        if (physicsObject->GetIsActive() && physicsObject->GetPhysicsTickEnabled())
        {
            physicsObject->PhysicsTick(deltaTime);
        }
    }

    for(PhysicsMovementComponent* physicsMovementComponent : physicsMovementComponents_)
    {
        physicsMovementComponent->UpdateOwnerTransformation();
    }
}

void PhysicsWorld::OnOverlappingCollisionBegin(btPersistentManifold* const& monifoldPointPtr)
{
    const btCollisionObject* ghostObject1 = monifoldPointPtr->getBody0();
    const btCollisionObject* ghostObject2 = monifoldPointPtr->getBody1();

    GOKNAR_CORE_ASSERT(ghostObject1 && ghostObject2, "Neither of the bullet bodies can be nullptr!");

    PhysicsObject* collisionObject1 = (PhysicsObject*)ghostObject1->getUserPointer();
    PhysicsObject* collisionObject2 = (PhysicsObject*)ghostObject2->getUserPointer();
    
    CollisionComponent* collisionComponent1 = collisionObject1->GetCollisionComponent();
    CollisionComponent* collisionComponent2 = collisionObject2->GetCollisionComponent();

    GOKNAR_CORE_ASSERT(collisionComponent1 && collisionComponent2);

    if (!collisionComponent1->GetIsActive() || !collisionComponent2->GetIsActive())
    {
        return;
    }

    Vector3 worldPositionOnA = PhysicsUtils::FromBtVector3ToVector3(monifoldPointPtr->getContactPoint(0).getPositionWorldOnA());
    Vector3 hitNormal = PhysicsUtils::FromBtVector3ToVector3(monifoldPointPtr->getContactPoint(0).m_normalWorldOnB);

    Vector3 worldPositionOnB = PhysicsUtils::FromBtVector3ToVector3(monifoldPointPtr->getContactPoint(0).getPositionWorldOnB());

    collisionComponent1->OverlapBegin(collisionObject2, collisionComponent2, worldPositionOnA, hitNormal);
    collisionComponent2->OverlapBegin(collisionObject1, collisionComponent1, worldPositionOnB, hitNormal);
}

PhysicsObject

#ifndef __PHYSICSOBJECT_H__
#define __PHYSICSOBJECT_H__

#include "Core.h"

#include "Goknar/ObjectBase.h"
#include "Goknar/Physics/PhysicsTypes.h"

class btCollisionObject;
class CollisionComponent;

struct GOKNAR_API PhysicsObjectInitializationData
{
    float ccdMotionThreshold{ 0.f };
    float ccdSweptSphereRadius{ 0.f };
    int collisionFlag{ -1 };
    int activeState{ -1 };
};

class GOKNAR_API PhysicsObject : public ObjectBase
{
public:
    PhysicsObject();
    virtual ~PhysicsObject();

    virtual void PreInit();
    virtual void Init();
    virtual void PostInit();

    virtual void Destroy() override;

    virtual void SetIsActive(bool isActive) override;

    virtual void PhysicsTick(float deltaTime) {}

    void SetupPhysicsObjectInitializationData();

    void SetCcdMotionThreshold(float ccdMotionThreshold);
    void SetCcdSweptSphereRadius(float ccdSweptSphereRadius);

    btCollisionObject* GetBulletCollisionObject() const
    {
        return bulletCollisionObject_;
    }

    CollisionGroup GetCollisionGroup() const
    {
        return collisionGroup_;
    }

    void SetCollisionGroup(CollisionGroup collisionGroup)
    {
        collisionGroup_ = collisionGroup;
    }

    CollisionMask GetCollisionMask() const
    {
        return collisionMask_;
    }

    void SetCollisionMask(CollisionMask collisionMask)
    {
        collisionMask_ = collisionMask;
    }

    void SetCollisionFlag(CollisionFlag collisionFlag);
    CollisionFlag GetCollisionFlag() const;
    
    const std::string& GetTag() const
    {
        return tag_;
    }

    void SetTag(const std::string& tag)
    {
        tag_ = tag;
    }

    void SetPhysicsTickEnabled(bool physicsTickEnabled)
    {
        physicsTickEnabled_ = physicsTickEnabled;
    }

    bool GetPhysicsTickEnabled() const
    {
        return physicsTickEnabled_;
    }

    CollisionComponent* GetCollisionComponent() const
    {
        return collisionComponent_;
    }

protected:
    virtual void AddComponent(Component* component) override;
    virtual void DestroyInner() override;

    btCollisionObject* bulletCollisionObject_{ nullptr };
    CollisionComponent* collisionComponent_{ nullptr };

    PhysicsObjectInitializationData* physicsObjectInitializationData_{ nullptr };

    CollisionGroup collisionGroup_{ CollisionGroup::WorldDynamicBlock };
    CollisionMask collisionMask_{ CollisionMask::BlockAndOverlapAll };

    std::string tag_{ "None" };

    bool physicsTickEnabled_{ true };
private:
};

#endif

RigidBody

RigidBody::RigidBody() : PhysicsObject()
{
    rigidBodyInitializationData_ = new RigidBodyInitializationData();
    SetName("Rigidbody");
}

RigidBody::~RigidBody()
{
    delete rigidBodyInitializationData_;
    delete bulletMotionState_;
}

void RigidBody::PreInit()
{
    PhysicsObject::PreInit();
}

void RigidBody::Init()
{
    GOKNAR_ASSERT(collisionComponent_ != nullptr, "No collision component attached to RigidBody but is trying to init");

    btCollisionShape* bulletCollisionShape = collisionComponent_->GetBulletCollisionShape();
    GOKNAR_ASSERT(bulletCollisionShape != nullptr);

    bool isDynamic = (mass_ != 0.f);
    if (isDynamic)
    {
        bulletCollisionShape->calculateLocalInertia(mass_, rigidBodyInitializationData_->localInertia);
    }

    btTransform bulletTransform;
    bulletTransform.setIdentity();
    bulletTransform.setOrigin(PhysicsUtils::FromVector3ToBtVector3(worldPosition_));
    bulletTransform.setRotation(PhysicsUtils::FromQuaternionToBtQuaternion(worldRotation_));

    bulletMotionState_ = new btDefaultMotionState(bulletTransform);
    btRigidBody::btRigidBodyConstructionInfo rigidBodyInfo(mass_, bulletMotionState_, bulletCollisionShape, rigidBodyInitializationData_->localInertia);

    if (0.f <= rigidBodyInitializationData_->linearSleepingThreshold)
    {
        rigidBodyInfo.m_linearSleepingThreshold = rigidBodyInitializationData_->linearSleepingThreshold;
    }

    if (0.f <= rigidBodyInitializationData_->angularSleepingThreshold)
    {
        rigidBodyInfo.m_angularSleepingThreshold = rigidBodyInitializationData_->angularSleepingThreshold;
    }

    bulletRigidBody_ = new btRigidBody(rigidBodyInfo);
    bulletCollisionObject_ = bulletRigidBody_;

    engine->GetPhysicsWorld()->AddRigidBody(this);

    PhysicsObject::Init();
}

void RigidBody::PostInit()
{
    PhysicsObject::PostInit();

    SetupRigidBodyInitializationData();
}

void RigidBody::PhysicsTick(float deltaTime)
{
    const btVector3& bulletWorldPosition = bulletRigidBody_->getCenterOfMassPosition();
    const btQuaternion& bulletWorldRotation = bulletRigidBody_->getOrientation();

    PhysicsObject::SetWorldPosition(PhysicsUtils::FromBtVector3ToVector3(bulletWorldPosition), false);
    PhysicsObject::SetWorldRotation(PhysicsUtils::FromBtQuaternionToQuaternion(bulletWorldRotation));
}

void RigidBody::SetMass(float mass)
{
    if (mass_ == mass)
    {
        return;
    }

    mass_ = mass;

    if (!GetIsInitialized())
    {
        return;
    }

    bulletRigidBody_->setMassProps(mass_, bulletRigidBody_->getLocalInertia());
}

void RigidBody::SetLinearFactor(const Vector3& linearFactor)
{
    btVector3 btLinearFactor = PhysicsUtils::FromVector3ToBtVector3(linearFactor);

    if (!GetIsInitialized())
    {
        rigidBodyInitializationData_->linearFactor = btLinearFactor;
        return;
    }

    bulletRigidBody_->setLinearFactor(btLinearFactor);
}

void RigidBody::SetAngularFactor(const Vector3& angularFactor)
{
    btVector3 btAngularFactor = PhysicsUtils::FromVector3ToBtVector3(angularFactor);

    if (!GetIsInitialized())
    {
        rigidBodyInitializationData_->angularFactor = btAngularFactor;
        return;
    }

    bulletRigidBody_->setAngularFactor(btAngularFactor);
}

void RigidBody::SetWorldPosition(const Vector3& worldPosition, bool updateWorldTransformationMatrix)
{
    PhysicsObject::SetWorldPosition(worldPosition, updateWorldTransformationMatrix);

    if (!GetIsInitialized())
    {
        return;
    }

    bulletRigidBody_->activate();
    btTransform newBulletTransform = bulletRigidBody_->getCenterOfMassTransform();
    newBulletTransform.setOrigin(PhysicsUtils::FromVector3ToBtVector3(worldPosition));
    bulletRigidBody_->setCenterOfMassTransform(newBulletTransform);
}

void RigidBody::SetWorldRotation(const Quaternion& worldRotation, bool updateWorldTransformationMatrix)
{
    PhysicsObject::SetWorldRotation(worldRotation, updateWorldTransformationMatrix);

    if (!GetIsInitialized())
    {
        return;
    }

    bulletRigidBody_->activate();
    btTransform newBulletTransform = bulletRigidBody_->getCenterOfMassTransform();
    newBulletTransform.setRotation(PhysicsUtils::FromQuaternionToBtQuaternion(worldRotation));
    bulletRigidBody_->setCenterOfMassTransform(newBulletTransform);
}

void RigidBody::SetIsActive(bool isActive)
{
    PhysicsObject::SetIsActive(isActive);

    if (isActive)
    {
        bulletRigidBody_->activate();
    }
}

void RigidBody::SetLinearVelocity(const Vector3& velocity)
{
    btVector3 btVelocity = PhysicsUtils::FromVector3ToBtVector3(velocity);

    if(!GetIsInitialized())
    {
        rigidBodyInitializationData_->velocity = btVelocity;
        return;
    }

    bulletRigidBody_->activate();
    bulletRigidBody_->setLinearVelocity(btVelocity);
}

OverlappingPhysicsObject

#ifndef __OVERLAPPINGPHYSICSOBJECT_H__
#define __OVERLAPPINGPHYSICSOBJECT_H__

#include "Core.h"

#include "Goknar/Physics/PhysicsObject.h"
#include "Goknar/Physics/PhysicsTypes.h"

#include "LinearMath/btVector3.h"

class btRigidBody;

class GOKNAR_API OverlappingPhysicsObject : public PhysicsObject
{
public:
    OverlappingPhysicsObject();
    virtual ~OverlappingPhysicsObject();

    virtual void PreInit() override;
    virtual void Init() override;
    virtual void PostInit() override;

    virtual void Destroy() override;

    virtual void BeginGame() override;
    virtual void Tick(float deltaTime) override;

    virtual void PhysicsTick(float deltaTime) override;

    virtual void UpdateWorldTransformationMatrix() override;
protected:
    virtual void DestroyInner() override;

private:
};

#endif
OverlappingPhysicsObject::OverlappingPhysicsObject() : PhysicsObject()
{
}

OverlappingPhysicsObject::~OverlappingPhysicsObject()
{
}

void OverlappingPhysicsObject::PreInit()
{
    PhysicsObject::PreInit();

    btCollisionShape* bulletCollisionShape = collisionComponent_->GetBulletCollisionShape();
    GOKNAR_ASSERT(bulletCollisionShape != nullptr);

    bulletCollisionObject_ = new btPairCachingGhostObject();
    bulletCollisionObject_->setCollisionShape(bulletCollisionShape);
    bulletCollisionObject_->setWorldTransform(
        btTransform(
            PhysicsUtils::FromQuaternionToBtQuaternion(worldRotation_),
            PhysicsUtils::FromVector3ToBtVector3(worldPosition_))
    );

    engine->GetPhysicsWorld()->AddPhysicsObject(this);

}

void OverlappingPhysicsObject::BeginGame()
{
    PhysicsObject::BeginGame();
}

void OverlappingPhysicsObject::Tick(float deltaTime)
{
    PhysicsObject::Tick(deltaTime);
}

void OverlappingPhysicsObject::PhysicsTick(float deltaTime)
{
    PhysicsObject::PhysicsTick(deltaTime);
}

void OverlappingPhysicsObject::UpdateWorldTransformationMatrix()
{
    PhysicsObject::UpdateWorldTransformationMatrix();

    if(!GetIsInitialized())
    {
        return;
    }

    btTransform collisionObjectTransform;
    bulletCollisionObject_->setWorldTransform(btTransform(
        PhysicsUtils::FromQuaternionToBtQuaternion(worldRotation_),
        PhysicsUtils::FromVector3ToBtVector3(worldPosition_))
    );
}

Raycasting

bool PhysicsWorld::RaycastClosest(const RaycastData& raycastData, RaycastSingleResult& raycastClosest)
{
    btVector3 bulletFrom = PhysicsUtils::FromVector3ToBtVector3(raycastData.from);
    btVector3 bulletTo = PhysicsUtils::FromVector3ToBtVector3(raycastData.to);

    btCollisionWorld::ClosestRayResultCallback closestRayResultCallback(bulletFrom, bulletTo);

    closestRayResultCallback.m_collisionFilterGroup = (int)raycastData.collisionGroup;
    closestRayResultCallback.m_collisionFilterMask = (int)raycastData.collisionMask;

    dynamicsWorld_->rayTest(bulletFrom, bulletTo, closestRayResultCallback);

    if(closestRayResultCallback.hasHit())
    {
        raycastClosest.hitObject = (PhysicsObject*)closestRayResultCallback.m_collisionObject->getUserPointer();
        raycastClosest.hitFraction = closestRayResultCallback.m_closestHitFraction;
        raycastClosest.hitPosition = PhysicsUtils::FromBtVector3ToVector3(closestRayResultCallback.m_hitPointWorld);
        raycastClosest.hitNormal = PhysicsUtils::FromBtVector3ToVector3(closestRayResultCallback.m_hitNormalWorld);

        return true;
    }

    return false;
}

bool PhysicsWorld::RaycastAll(const RaycastData& raycastData, RaycastAllResult& raycastAllResult)
{
    btVector3 bulletFrom = PhysicsUtils::FromVector3ToBtVector3(raycastData.from);
    btVector3 bulletTo = PhysicsUtils::FromVector3ToBtVector3(raycastData.to);

    btCollisionWorld::AllHitsRayResultCallback allHitsRayResultCallback(bulletFrom, bulletTo);

    allHitsRayResultCallback.m_collisionFilterGroup = (int)raycastData.collisionGroup;
    allHitsRayResultCallback.m_collisionFilterMask = (int)raycastData.collisionMask;

    dynamicsWorld_->rayTest(bulletFrom, bulletTo, allHitsRayResultCallback);

    if(allHitsRayResultCallback.hasHit())
    {
        const int hitCount = allHitsRayResultCallback.m_collisionObjects.size();
        for (int hitIndex = 0; hitIndex < hitCount; hitIndex++)
        {
            raycastAllResult.hitResults.emplace_back(
                RaycastSingleResult(
                    (PhysicsObject*)allHitsRayResultCallback.m_collisionObjects[hitIndex]->getUserPointer(),
                    PhysicsUtils::FromBtVector3ToVector3(allHitsRayResultCallback.m_hitPointWorld[hitIndex]),
                    PhysicsUtils::FromBtVector3ToVector3(allHitsRayResultCallback.m_hitNormalWorld[hitIndex]),
                    allHitsRayResultCallback.m_hitFractions[hitIndex]
                )
            );
        }

        return true;
    }

    return false;
}

Sweeping



bool PhysicsWorld::SweepClosest(const SweepData& sweepData, RaycastSingleResult& result)
{
    GOKNAR_CORE_ASSERT(sweepData.collisionComponent->GetBulletCollisionShape()->isConvex());

    btConvexShape* bulletcollisionShape = (btConvexShape*)sweepData.collisionComponent->GetBulletCollisionShape();
    
    btTransform bulletFromTransform = PhysicsUtils::GetBulletTransform(sweepData.fromRotation, sweepData.fromPosition);
    btTransform bulletToTransform = PhysicsUtils::GetBulletTransform(sweepData.toRotation, sweepData.toPosition);

    btCollisionWorld::ClosestConvexResultCallback closestResultCallback(
        PhysicsUtils::FromVector3ToBtVector3(sweepData.fromPosition),
        PhysicsUtils::FromVector3ToBtVector3(sweepData.toPosition)
    );

    closestResultCallback.m_collisionFilterGroup = (int)sweepData.collisionGroup;
    closestResultCallback.m_collisionFilterMask = (int)sweepData.collisionMask;

    dynamicsWorld_->convexSweepTest(bulletcollisionShape, bulletFromTransform, bulletToTransform, closestResultCallback, sweepData.ccdPenetration);

    if(closestResultCallback.hasHit())
    {
        result.hitObject = (PhysicsObject*)closestResultCallback.m_hitCollisionObject->getUserPointer();
        result.hitFraction = closestResultCallback.m_closestHitFraction;
        result.hitPosition = PhysicsUtils::FromBtVector3ToVector3(closestResultCallback.m_hitPointWorld);
        result.hitNormal = PhysicsUtils::FromBtVector3ToVector3(closestResultCallback.m_hitNormalWorld);

        return true;
    }
    
    return false;
}

PhysicsMovementComponent


void PhysicsMovementComponent::Init()
{
    Component::Init();

    OverlappingPhysicsObject* ownerPhysicsObject = dynamic_cast<OverlappingPhysicsObject*>(GetOwner());
    GOKNAR_CORE_ASSERT(ownerPhysicsObject, "OverlappingPhysicsObjectMovementComponent can only be added to a OverlappingPhysicsObject object");
    ownerPhysicsObject_ = ownerPhysicsObject;

    collisionComponent_ = dynamic_cast<CollisionComponent*>(ownerPhysicsObject_->GetRootComponent());

    GOKNAR_CORE_ASSERT
    (
        collisionComponent_ && 
        dynamic_cast<CollisionComponent*>(collisionComponent_) && 
        collisionComponent_->GetBulletCollisionShape()->isConvex(), 
        "Relative collision component can only be a convex CollisionComponent"
    );

    btPairCachingGhostObject* bulletPairCachingGhostObject = (btPairCachingGhostObject*)ownerPhysicsObject->GetBulletCollisionObject();

    bulletKinematicCharacterController_ =
        new btKinematicCharacterController(
            bulletPairCachingGhostObject,
            (btConvexShape*)collisionComponent_->GetBulletCollisionShape(),
            initializationData_->stepHeight,
            PhysicsUtils::FromVector3ToBtVector3(Vector3::UpVector));

    PhysicsWorld* physicsWorld = engine->GetPhysicsWorld();
    physicsWorld->AddPhysicsMovementComponent(this);

    bulletPairCachingGhostObject->setUserPointer(this);

    bulletKinematicCharacterController_->setGravity(PhysicsUtils::FromVector3ToBtVector3(initializationData_->gravity));

    if (initializationData_->isVelocityForGivenDurationSet)
    {
        bulletKinematicCharacterController_->setVelocityForTimeInterval(PhysicsUtils::FromVector3ToBtVector3(initializationData_->movementVelocityForGivenDuration), initializationData_->movementVelocityForGivenDurationDuration);
    }

    if (initializationData_->isMovementDirectionSet)
    {
        bulletKinematicCharacterController_->setWalkDirection(PhysicsUtils::FromVector3ToBtVector3(initializationData_->movementDirection * movementSpeed_));
    }

    if (initializationData_->isLinearVelocitySet)
    {
        bulletKinematicCharacterController_->setLinearVelocity(PhysicsUtils::FromVector3ToBtVector3(initializationData_->linearVelocity));
    }
    
    if (initializationData_->isAngularVelocitySet)
    {
        bulletKinematicCharacterController_->setAngularVelocity(PhysicsUtils::FromVector3ToBtVector3(initializationData_->angularVelocity));
    }
    
    if (initializationData_->isImpulseSet)
    {
        bulletKinematicCharacterController_->applyImpulse(PhysicsUtils::FromVector3ToBtVector3(initializationData_->impulse));
    }

    bulletKinematicCharacterController_->setLinearDamping(initializationData_->linearDamping);
    bulletKinematicCharacterController_->setAngularDamping(initializationData_->angularDamping);
    bulletKinematicCharacterController_->setFallSpeed(initializationData_->fallSpeed);
    bulletKinematicCharacterController_->setJumpSpeed(initializationData_->jumpSpeed);
    bulletKinematicCharacterController_->setMaxJumpHeight(initializationData_->maxJumpHeight);
    bulletKinematicCharacterController_->setMaxSlope(initializationData_->slopeRadians);
    bulletKinematicCharacterController_->setMaxPenetrationDepth(initializationData_->maxPenetrationDepth);
    bulletKinematicCharacterController_->setUseGhostSweepTest(initializationData_->useGhostObjectSweepTest);
    bulletKinematicCharacterController_->setUpInterpolate(initializationData_->upInterpolate);
    
    delete initializationData_;
    initializationData_ = nullptr;
}

void PhysicsMovementComponent::UpdateOwnerTransformation()
{
    const btTransform& bulletWorldTransform = ownerPhysicsObject_->GetBulletCollisionObject()->getWorldTransform();

    ownerPhysicsObject_->SetWorldPosition(PhysicsUtils::FromBtVector3ToVector3(bulletWorldTransform.getOrigin()), false);
    ownerPhysicsObject_->SetWorldRotation(PhysicsUtils::FromBtQuaternionToQuaternion(bulletWorldTransform.getRotation()));
}

void PhysicsMovementComponent::SetMovementDirection(const Vector3& movementDirection)
{
    if (!GetIsInitialized())
    {
        initializationData_->isMovementDirectionSet = true;
        initializationData_->movementDirection = movementDirection;
        return;
    }

    bulletKinematicCharacterController_->setWalkDirection(PhysicsUtils::FromVector3ToBtVector3(movementDirection * movementSpeed_));
}

void PhysicsMovementComponent::SetMovementVelocityForGivenDuration(const Vector3& movementVelocity, float duration)
{
    if (!GetIsInitialized())
    {
        initializationData_->isVelocityForGivenDurationSet = true;
        initializationData_->movementVelocityForGivenDuration = movementVelocity;
        initializationData_->movementVelocityForGivenDurationDuration = duration;
        return;
    }

    bulletKinematicCharacterController_->setVelocityForTimeInterval(PhysicsUtils::FromVector3ToBtVector3(movementVelocity), duration);
}

void PhysicsMovementComponent::SetAngularVelocity(const Vector3& angularVelocity)
{
    if (!GetIsInitialized())
    {
        initializationData_->isAngularVelocitySet = true;
        initializationData_->angularVelocity = angularVelocity;
        return;
    }

    bulletKinematicCharacterController_->setAngularVelocity(PhysicsUtils::FromVector3ToBtVector3(angularVelocity));
}

Vector3 PhysicsMovementComponent::GetAngularVelocity() const
{
    if (!GetIsInitialized())
    {
        return initializationData_->angularVelocity;
    }

    return PhysicsUtils::FromBtVector3ToVector3(bulletKinematicCharacterController_->getAngularVelocity());
}

void PhysicsMovementComponent::SetLinearVelocity(const Vector3& linearVelocity)
{
    if (!GetIsInitialized())
    {
        initializationData_->isLinearVelocitySet = true;
        initializationData_->linearVelocity = linearVelocity;
        return;
    }

    bulletKinematicCharacterController_->setLinearVelocity(PhysicsUtils::FromVector3ToBtVector3(linearVelocity));
}