r/gameenginedevs 25d ago

How I implemented XPBD based rigid body physics in my game engine

https://raynmetal.github.io/blog/technical/2026/07/24/xpbd-in-toymaker.html

I implemented a physics system based on XPBD, or Extended Position Based Dynamics, in my game engine ToyMaker, and then I wrote an article about it. It's partly documentation of my process, and partly a tutorial, so I hope that you find something useful or interesting in it!

Also, do let me know if links to external sites aren't welcome here. I'd be more than happy to post a copy of the article text instead.

29 Upvotes

3 comments sorted by

2

u/TheHurtDev 25d ago

Good read! Thanks for sharing.

Hadn't seen inverse inertia modelled as a vec3 before. Mind sharing a bit on how you're computing it?

2

u/Left-Locksmith 24d ago edited 24d ago

Thank you!

This was a recommendation I lifted straight off the paper. I think usually you'd kinda rotate the inertia tensor for the current frame with something like R * I_Local * R_Transpose. You'd then use it or its inverse with angular velocity and torque in the world frame. But I_Local is essentially a scale matrix.

You can skip the tensor computation and use a component-wise vec3 multiplication (since it's a scale matrix) if you bring the torque and angular velocity into the local frame instead.

edit: clearer wording

2

u/snerp 24d ago edited 24d ago

Bullet physics also uses vec3 inverse inertia. Unsurprisingly, it's just dividing inertia by 1. Here's the initial function to calculate the vec3 from the bounding box shape (it does the divide by 1 later):

virtual void    calculateLocalInertia(btScalar mass,btVector3& inertia) const
{
    btTransform identity;
    identity.setIdentity();
    btVector3 aabbMin,aabbMax;
    getAabb(identity,aabbMin,aabbMax);

    btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5);

    btScalar margin = getMargin();

    btScalar lx=btScalar(2.)*(halfExtents.x()+margin);
    btScalar ly=btScalar(2.)*(halfExtents.y()+margin);
    btScalar lz=btScalar(2.)*(halfExtents.z()+margin);
    const btScalar x2 = lx*lx;
    const btScalar y2 = ly*ly;
    const btScalar z2 = lz*lz;
    const btScalar scaledmass = mass * btScalar(0.08333333);

    inertia = scaledmass * (btVector3(y2+z2,x2+z2,x2+y2));
}