Skip to content

Instantly share code, notes, and snippets.

@Memresable
Last active September 4, 2025 16:40
Show Gist options
  • Save Memresable/6e0309bd721ec374ff9afc6396d087a1 to your computer and use it in GitHub Desktop.
Save Memresable/6e0309bd721ec374ff9afc6396d087a1 to your computer and use it in GitHub Desktop.
Demystifying Rotational Dynamics in Rigid Bodies

Demystifying Rotational Dynamics in Rigid Bodies

Rigid bodies mainly have 6 degrees of freedom in 3 dimensions (3 translational and 3 rotational), you can represent the particles as rigid bodies with only 3 translational degrees which is straightforward and their equations of motion aren't difficult to formalize, but what if you want to extend from particles to much more than that? Say... system of particles connected to each other to create a whole body!

First, let's suppose you have two particles, one that has an infinite mass which cannot represent motion unless an infinite force is applied, and the other one which has some finite amount of mass which is able to move around, but now let's actually link them together by a massless rigid rod or an infinitely stiff spring like so

rigid-rod

Here because the rigid rod is infinitely stiff and the yellow particle is unable to move, then that means the green particle is unable to move away from or closer to the yellow particle because remember, the rod is infinitely stiff and it doesn't deform at all, and the link itself doesn't really have any side-effects that puts into the green particle, all of this starts to create a very interesting motion.

And the motion it creates is in the following video

rotational-motion-test.mp4

As you can see, in both the particle simulation and the rigid body simulation, the green particle was collided by the blue particle, the green particle didn't move in a straight line in both cases, but it did move in a circular line, and it's speed was exactly the speed of the blue particle throughout time, so you can think of the green particle as if it was moving in a straight line still (Which would become more clear later on!). And since it's a rotational motion, we can actually represent or approximate it's motion in polar coordinates.

Note

in the particle simulation case, the green particle was losing some bits of energy after the collision because of the way it works, it mainly uses system of constraint forces to simulate it. So the lower the step size is the better, and imagine the case of infinitely small step size where it's exactly correct for the rest of the simulation, but that's impossible with the constrained computer resources and floating point precession issues.

So suppose you have linear velocity going in a straight line, then converting it into an angular velocity is going to require us an origin to rotate around and it's distance from the origin, we pick an $\vec{r}$ vector that's described as a "radius" which connects a particle to an origin, and then we can divide the particle's velocity $\vec{v}$ by the radius vector $\vec{r}$ as lengths like in the following formula

$$\huge \omega = \frac{\| \vec{v} \|}{\| \vec{r} \|}$$

Now the formula may not make sense at first, but suppose the particle from the origin is at a distance of 1, then it's speed is exactly aligned with the rotation speed, however if it's more or less than 1, then it's speed would still be assumed at a distance of 1 which is not correct! Because of this we will get speed that's faster or slower than expected. To fix this, we essentially divide the linear velocity by the magnitude of $\vec{r}$ which should then give us the answer we're looking for.

To give a more concrete example, suppose we have three particles at radius of 1, 1.5 and 2. (The manifold sizes are not to scale here! But the angles are accurate)

radi-basis

And each particle is at speed of $\pi$ radians per second (measured in rad/s) and they start moving, we then get the following

radi-speed

Notice the particle that is in radius 2, it's half angle of radius 1, but their arc lengths are the same! The arc length in this context is the distance travelled over the circular manifold's surface, it's defined as

$$\huge \overset \frown {L} = r \cdot \theta $$

So, multiplying the angle $\huge \frac{\pi}{2}$ by radius of 2 gives us $\pi$, which is correct! All of this is demonstrated within the desmos link I've put up for you https://www.desmos.com/calculator/3tsc1eld4q.

Within desmos, there are three points on top of the ones that are on the circular manifolds, now in the first row try to increase the $\alpha$ value with the slider, notice as you increase at the smaller values the three points move at the same rate as the ones on the circle, their speed are exactly aligned, but as you keep increasing the $\alpha$ value even further the points start to move away from the ones on the circles, but the ones that are moving in a line are still close to one another, and the speed of the ones on the circles are still moving at the same speed.

So one way to think of rotating points is that they are essentially moving in a straight line in their own space, but relative to you they are rotating, and as we've noticed their distance from their origin makes a difference for us in our familiar cartesian coordinate system. This is a very important point to digest so take a moment to think about it and keep playing around with the desmos demo.

Okay so that out of the way, there's a problem here, the angular velocity is only a magnitude in this case, but it does not have a representation for direction! We mainly need to know which direction it's rotating at as well, this is where the concept axis of rotation comes into play.

Important

In case you don't know what an axis of rotation is, I highly recommend reading the Appendix A which is at the bottom of this article, it contains a short explanation as well as pointers to existing resources.

We could do a neat little trick and use a product that does this for us, the one we're mainly interested in is called the cross product. By definition

$$\huge \begin{align*} \vec{N} &= \vec{A} \times \vec{B} \\ &= \| \vec{A} \| \| \vec{B} \| \text{sin} (\theta) \vec{N} \end{align*}$$

Where $\times$ is the cross product, for now we're going to assume the vectors at an angle of $\huge \frac{\pi}{2}$ radians (or 90 degrees) which eliminates the $\text{sin} (\theta)$ term, we'll come back to it later. Then in 3D space, our original equation becomes

$$\huge \vec{\omega} = \frac{\|\vec{r}\| \|\vec{v}\|}{\|\vec{r}\|^2} \vec{N} = \frac{\vec{r} \times \vec{v}}{\| \vec{r} \|^2} $$

The mysterious $\vec{N}$ here is the unit axis of rotation or the unit axis that is normal to an oriented plane

angular-velocity

Notice the rotational direction, it rotates in the same order as the product $\vec{r} \times \vec{v}$, it goes from $\vec{r}$ to $\vec{v}$, which is a counter-clockwise rotation, so for our case (by the right-handed convention) the angular velocity $\vec{\omega}$ points upwards.

And compressing the 3D formula gives us the following

$$\huge \begin{align*} \vec{\omega} &= \frac{\| \vec{r} \| \| \vec{v} \|}{\| \vec{r} \|^2} \vec{N} \\ &= \frac{\| \vec{v} \|}{\| \vec{r} \|} \vec{N} \\ &= \omega \vec{N} \end{align*} $$

So really, all we did was to try and get the axis of rotation which also encodes a direction and then scale it by the original rotation rate magnitude, sweat!

rotational-velocity.mp4

Now that's nice and all, but how do we accelerate the particle as well as taking it's mass (which is it's resistant from external forces) into account? We can first look into the concept of momentum.

By turning vectors into scalars (for now which is more correctly defined in 2D), the linear momentum of a particle is defined as

$$\huge P = m v $$

Where $v$ here is defined as

$$\huge v = \omega r $$

Or to be more specific

$$\huge v = \left(\frac{v}{r}\right) r $$

We can turn it into angular momentum by

$$\huge L = r P $$

Then we can now separate mass $m$ and distance $r$ from angular velocity like so

$$\huge \begin{align} L &= r P \\ &= r (m v) \\ &= r (m (\omega r)) \\ &= r^2 (m \omega) \\ &= (m r^2) \omega \\ &= I \omega \\ \end{align} \qquad \small \text{Where} \space\space \small \omega = \small\frac{v}{r}\space $$

Here notice that we get both $I$ and $\omega$, $I$ in this case is the moment of inertia, it determines how hard it is to rotate a particle.

Warning

The concept of moments of inertia is usually understood in the wrong way unfortunately, the idea of "how hard it is to rotate a body" is vague and is not clear on what it actually means. The chapter on system of particles later on would get into it in more detail using collisions with the conservation laws. It's not obvious right now on what it is exactly, but we'll go over the acceleration part quickly for it.

Alright that seems handy, however we still need to accelerate the particle. We can take the time derivative of the angular momentum to get the equivalent of linear force

$$\huge \begin{align*} \frac{dL}{dt} &= \frac{dI}{dt} \omega + I \frac{d\omega}{dt} \qquad \normalsize \color{gray}\frac{dI}{dt} \to 0 \\ &= I \frac{d\omega}{dt} \\ &= I \alpha \\ &= \tau \end{align*} $$

Here $\tau$ is torque, it's essentially angular force as opposed to linear force, also taking the time derivative of the angular velocity gives us angular acceleration (defined as alpha $\alpha$). The moment of inertia $I$ in a rotating plane isn't that interesting, the magnitude of $r$ doesn't change when rotating and that's to say the rigid body does not deform as such. This however starts to get more interesting with the full 3 rotational degrees of freedom later, and as a hint: It still does not deform!

angular-acceleration.mp4

Now getting back to the moment of inertia, suppose now you have a torque to be applied to a particle of mass 1, then

$$\huge \omega' = \omega + I^{-1} H $$

$H$ in our case is the angular impulse, it's defined as

$$\huge H = \int_{t_0}^{t_1} \tau(t) dt $$

So, assuming that $\tau$ equals to

$$\huge \tau = r F $$

Then if force $F = \pi \space rad/s^2$ and radius $r = 2$, then dividing torque by the moment of inertia gives us the angular acceleration which becomes

$$\huge \begin{align*} \alpha &= I^{-1} \tau \\ &= \frac{1}{mr^2} (r F) \\ &= \frac{1}{1 \cdot 2^2} (2 \cdot \pi) \\ &= \frac{\pi}{2} \end{align*} $$

Notice that it moves at half the angle, but remember that it's only an angle! The arc length however is the same as the one of radius 1, using our previous formula on getting the arc length then gives us $\pi$.

Now we haven't talked about the case where the free particle doesn't hit the constrained particle at a perfect angle, what happens then? The $\text{sin} (\theta)$ term that is within the cross product $\vec{A} \times \vec{B}$ starts to appear, but is that physical? More on that in the next section, we will deal with system of particles which would also involve the concept of center of masses in more detail.

System Of Particles

Ok so far we have derived pretty much the equations of motion for a single particle around an infinite mass, but what if we extend it to more than just one particle? Which then becomes system of particles tied altogether!

It's actually surprisingly straightforward, remember when we said that rods generally are rigid or infinitely stiff? Since they don't make side-effects, then we can treat multiple particles as one! (Sort of...)

And to prove that is the case, I've again modified the simulation to handle it

system-particle-rigid-comp.mp4

As shown, you would notice that the forces are being balanced exactly the same way as the rigid body equations of motion method. But notice now that it is harder to rotate than before and the blue particle now bounces off a little.

To explain all of this, we need to first look into how linear motion works in one dimension with system of particles, which should hopefully make it easier to understand the rotational case with a clever example.

Suppose you have multiple particles tied together with rigid rods, then they are essentially one whole body, which also means we can treat them as a giant particle. Let's ignore the rotational aspect for now and focus on the linear motion, we have 4 particles and each particle has a mass of 1, then the total mass of the system is 4, very simple

$$\huge \begin{align*} m_\text{total} &= \sum_{i=1}^{4} m_i \\ &= 1 + 1 + 1 + 1 \\ &= 4 \end{align*} $$

If the system is moving at a velocity of one, then the total momentum is

$$\huge \begin{align*} P_\text{total} &= \sum_{i=1}^{4} m_i v_i \\ &= (1 \cdot 1) + (1 \cdot 1) + (1 \cdot 1) + (1 \cdot 1) \\ &= 4 \end{align*} $$

That's the same as a single particle moving at a velocity of one with mass 4.

So rather than thinking of a body as multiple particles, we could instead unify the particles into what's called center of mass and base our equations of motion on it. Essentially, center of mass is defined as

$$\huge \begin{align*} X_\text{com} &= \frac{\sum_{i=1}^{n} m_i x_i}{\sum_{i=1}^{n} m_i} \\ &= \frac{m_\text{total} \cdot x_\text{total}}{m_\text{total}} \end{align*} $$

The moving center of mass is just the whole body moving as well, taking it's derivative gives us the velocity of the system

$$\huge \begin{align*} \frac{d}{dt} X_\text{com} &= \frac{d}{dt} \left( \frac{m_\text{total} \cdot x_\text{total}}{m_\text{total}} \right) \\ &= \frac{m_\text{total} \cdot v_\text{total}}{m_\text{total}} \\ &= V_\text{com} \end{align*} $$

This may not sound important for now, but it will be important shortly once we dive further into rotations.

If we have a particle of mass 1 and we collide a body with it, then we get the following behavior using the particle simulation

one-vs-four-collision.mp4

The behavior shown was an elastic collision. Notice in the above system the blue particle stopped since it's entire kinetic energy was entirely transferred towards the green particle, in the bottom system however the gray particle didn't stop and it instead bounced off with less energy than before.

We could verify this using the collision force formula which makes sure the conservation of momentum and the conservation of energy laws are taken into account. If you want to understand how the collision forces are derived and used then seek Appendix C which uses the conservation laws only, along references of [12] and [13].

Now calculating the impulse for the above system is as such

$$\huge \begin{align*} J &= \frac{-2 \cdot (v_b - v_a)}{m_a^{-1} + m_b^{-1}} \\ &= \frac{- 2 \cdot (0 - 2)}{1 + 1} \\ &= \frac{4}{2} \\ &= 2 \end{align*} $$

Then by newton's third law

$$\huge J_a = -J_b $$

Then applying it to both particles

$$\huge \begin{align*} v_a' &= v_a - \frac{J}{m_a} \\ &= 2 - \frac{2}{1} \\ &= 0 \end{align*} $$

$$\huge \begin{align*} v_b' &= v_b - \frac{J}{m_b} \\ &= 0 + \frac{2}{1} \\ &= 2 \end{align*} $$

Which is exactly what we saw in the video! Doing it again for the underneath system gives us

$$\huge \begin{align*} J &= \frac{-2 \cdot (v_b - v_a)}{m_a^{-1} + m_b^{-1}} \\ &= \frac{- 2 \cdot (0 - 2)}{1 + 0.25} \\ &= \frac{4}{1.25} \\ &= 3.2 \end{align*} $$

Then again applying it to both particles

$$\huge \begin{align*} v_a' &= v_a - \frac{J}{m_a} \\ &= 2 - \frac{3.2}{1} \\ &= -1.2 \end{align*} $$

$$\huge \begin{align*} v_b' &= v_b - \frac{J}{m_b} \\ &= 0 + \frac{3.2}{4} \\ &= 0.8 \end{align*} $$

Which is also correct!

So now that we know how linear motions work and how they conserve momentum, let's move on to the rotational part which isn't going to be any different really.

It's important to notice that the linear motion was actually a rotation around a point at infinity with an infinite mass and with an infinite radius.

You may ask: "What? How is that even possible?". It's better to describe with an image of various polar coordinates with various center of masses colored in pink with the connected particles colored in green acting as a system

coord-inf

Notice in the coordinate with an infinite radius the masses are essentially approximately moving in a straight line, as the radius gets smaller the lines get curved and the center of masses get closer, eventually the connected green particles start to look like they are rotating about some origin within some small range of radius.

So what does this have to do with our linear motion consisting system of particles? Well, notice in the coordinate with an infinite radius, the two particles are within a system moving in a straight line, if we had a particle colliding with a system in a straight line then we get the behavior we just analyzed before.

To prove this, let's now compare both linear and angular motions of two systems of particles (or bodies) that get collided. We will assume the center of mass of the linear part is at infinity (even though it's in the middle of the system of particles, but that's besides the point).

Here's one with equal masses

equal-masses.mp4

And another one with un-equal masses

un-equal-masses.mp4

Notice that both linear and rotational motions behave exactly the same way in this case! So our theorem is proven.

Though why stop there with the demos, let's look at the math as well.

Suppose the following formula from Appendix C

$$\huge J = -\frac{2 \cdot (\vec{v}_b - \vec{v}_a) \cdot \vec{N}}{\left(I_a^{-1} ( \vec{r}_a \times \vec{N} ) \times \vec{r}_a\right) \cdot \vec{N} + \left(I_b^{-1} ( \vec{r}_b \times \vec{N} ) \times \vec{r}_b\right) \cdot \vec{N} } $$

It would seem daunting at first, but our goal is to get to the same formulation as the linear impulse assuming the radius is at infinity.

Let's assume the radius for both bodies A and B are at infinity, so that means $\| \vec{r}_a \| = \| \vec{r}_b \| = \infty$, and normals are perfectly perpendicular to the radii vectors $\vec{r} \cdot \vec{N} = 0$, and also velocities are perfectly aligned with the separation axis $\vec{v} \cdot \vec{N} = \| \vec{v} \|$, then

$$\huge \begin{align*} \vec{v}_a &= \frac{\vec{r}_a \times \vec{v}_a}{\| \vec{r}_a \|^{2}} \times \vec{r}_a \\ &= \vec{\omega}_a \times \vec{r}_a \\ &= \vec{v}_a \end{align*} $$

And that applies to $\vec{v}_b$ as well, also note that $\vec{\omega}_a$ is infinitely small so crossing it with $\vec{r}_a$ gets us back at the amount we had before, make sure this makes sense to you. Now what about the denominator? Same story, let's assume the moment of inertia about the Y axis is

$$\huge I_{yy} = m r^2 $$

Then

$$\huge \begin{align*} \left( I_a^{-1} (\vec{r}_a \times \vec{N}) \times \vec{r}_a \right) \cdot \vec{N} &= \left( \frac{(\vec{r}_a \times \vec{N})}{m_a r_a^{2}} \times \vec{r}_a \right) \cdot \vec{N} \\ &= \left( \frac{\vec{N}}{m_a} \right) \cdot \vec{N} \\ &= \frac{1}{m_a} \\ &= m_a^{-1} \end{align*} $$

Bingo! So re-arranging the original formula based on what we found gives us

$$\huge J = -\frac{2 \cdot (\vec{v}_b - \vec{v}_a) \cdot \vec{N}}{m_a^{-1} + m_b^{-1}} $$

Which means that it is in fact a rotational motion at infinity as well. And by the way, this also applies to rotations around some near origins as long as the $\vec{r}$ vectors are close to an exact perpendicular angle to the separating axis $\vec{N}$, notice how in the equations above we neglected the $sin(\theta)$ terms based on that assumption alone.

So now that we're done explaining the conservation behaviors within systems. Let's get into the concept of angular momentum with system of particles, the total angular momentum of the system is defined as

$$ \huge L_{total} = \sum_{i=1}^{n} r_i (m_i v_i) = \sum_{i=1}^{n} r_i P_i $$

You could also take the derivative of the total angular momentum to get the total torque as well

$$ \huge \tau_{total} = \sum_{i=1}^{n} r_i (m_i a_i) = \sum_{i=1}^{n} r_i F_i $$

Then we can now get the moment of inertia for the system as follows

$$\huge \begin{align*} L_{total} &= \sum_{i=1}^{n} r_i (m_i v_i) \\ &= \sum_{i=1}^{n} r_i (m_i (\omega_i r_i)) \\ &= \sum_{i=1}^{n} (m_i r_i^2) \omega_i \\ &= \sum_{i=1}^{n} I_i \omega_i \\ &= I_\text{total} \omega_\text{total} \\ \end{align*} $$

Then multiplying the total angular momentum by the inverse of the total angular velocity gives us the total moment of inertia!

$$\huge \begin{align*} L_{total} &= I_\text{total} \omega_\text{total} \\ \Longrightarrow \omega_\text{total}^{-1}L_{total} &= \omega_\text{total}^{-1}(I_\text{total} \omega_\text{total}) \\ \omega_\text{total}^{-1}L_{total} &= I_\text{total} \\ \end{align*} \quad \normalsize \text{where} \space\space \omega_\text{total}^{-1} = \frac{1}{\omega_\text{total}} $$

Which essentially equals

$$\huge I_\text{total} = \sum_{i=1}^{n} r_i^2m_i $$

One thing to notice however is that let's suppose you have a ring of radius 1 that contains few particles, and one particle is in another ring of radius 0.5 like so

radii-infl

As the outstanding particle starts to get closer and closer to the center of mass, it starts to have less influence on the inertia of the system, and it makes sense, the infinite mass has no influence on rotations at all to begin with!

There's also another case where you have a very far point mass and a close one, if you collide with the close one you may realize that you need a lot of energy to rotate the whole body than if you collide with the far one. This is because the amount of distance to travel has increased significantly, hence $r$ gets very large and this makes sense, the distance between the two must not change which is a constraint, so you're forced to put much more effort rotating the entire system.

Also so far we have assumed we had an infinite mass particle as our center of rotation so far, but we haven't talked about the case where there isn't an infinite mass, what happens?

Our body has been rotating around an infinite mass particle, that particle defines our center of rotation which is also at the same time the center of mass since it's the heaviest mass in the system. You can sum the masses and average them out like so

$$\huge \vec{C} = \frac{\sum_{i=1}^{n} \vec{r}_i m_i}{\sum_{i=1}^{n} m_i} $$

This formula would give you a point that's the center of the system. To prove this, suppose one of the particles has a large mass of 1000 in comparison to the other particles that have mass of 1, then summing them up and dividing by the total mass of the system gives us the center of rotation which lies the closest to the large mass particle, which as you expect if you have an infinite mass then it directly sits within that mass.

A better example would be a line shape consisting of two particles of mass 1

line-com

The pink point that's at the center of the system is the center of mass.

The $\vec{r}$ vector has been so far as

$$\huge \vec{r} = \vec{x} - \vec{C} $$

Which essentially says the vector that takes from the center of mass $\vec{C}$ to a point mass $\vec{x}$ is our radius.

Okay so now let's talk about both linear and rotational motions combined, what kind of motions do we get in which particular situation?

Suppose in a scenario where we do a collision of one particle that goes right towards the center of mass of a body like so

collide-com.mp4

There are no rotations involved, just purely linear motions. The reason for this is that all the energy has gone straight through the center of mass which is the entire system by itself.

However that's not primarily the case when we don't collide into it like so

collide-rot.mp4

Notice that after one of the purple particles were collided, the yellow particle stopped completely! And the collided purple particle started moving in a straight line, but it was also grabbing the other one it was attached to, and since it was grabbing it, it was also being pushed towards it since there's two forces that are being extorted by the rod according to Newton's third law, this back and forth behavior creates a rotational motion as a result that keeps going on forever like so

com-intensity

This behavior would be important later on when we touch on 3 dimensions, especially with a term called "inertial torque" and the conservation of angular momentum of a system.

Also as you would've probably noticed from the video above, the center of mass was moving in a straight line!

Looking at the motion from the perspective of the center of mass for the rigid body shows that it is rotating and moving at a constant velocity

rotation-com-persp.mp4

Also these sorts of motion have a connection with the $\text{sin}(\theta)$ terms from the cross products, I'll let you think about it.

Stepping Into 3 Dimensions

Handling rotations in 2D is mostly straightforward, however in 3D it gets a lot more complicated, but the exact same principles transfers over from 2D to 3D just fine, just need a slightly different way of looking at it.

And there's really nothing new in 3 dimensions except for one interesting term that appears when taking the derivative of the 3D angular momentum and the fact that the derived moment of inertia usually isn't aligned with the orientation of the rigid body in world space.

So far all our particles have been rotating within a plane where a whole rigid body has 1 degree of rotation, in 3D however a rigid body has 3 degrees of rotations, each degree of rotation represents a rotational plane.

To extend our momentum equations to handle various oriented planes, we need to use the cross product. The angular momentum of a particle is

$$\huge \begin{align*} \vec{L} &= \vec{r} \times \vec{P} \\ &= \|\vec{r}\| \|\vec{P}\| \text{sin}(\theta) \vec{N} \\ &= I \vec{\omega} \end{align*} $$

Which so far seems similar to the 2D formula, nothing mysterious yet...

Now the moment of inertia in 3D however is interesting, we will try to derive the total angular momentum again using the cross product like so

$$\huge \begin{align*} \vec{L}_\text{total} &= \sum_{i=1}^{n} \vec{r}_i \times \vec{P}_i \\ &= \sum_{i=1}^{n} \vec{r}_i \times (m_i \vec{v}_i) \\ &= \sum_{i=1}^{n} \vec{r}_i \times (m_i (\vec{\omega}_i \times \vec{r_i})) \\ &= \sum_{i=1}^{n} m_i (\vec{r}_i \times (\vec{\omega}_i \times \vec{r_i})) \\ &= \sum_{i=1}^{n} m_i ((\vec{r}_i \cdot \vec{r}_i) \vec{\omega}_i - (\vec{r}_i \cdot \vec{\omega}_i) \vec{r}_i) \\ &= \sum_{i=1}^{n} m_i ((\vec{r}_i \cdot \vec{r}_i) \vec{\omega}_i - (\vec{r}_i \otimes \vec{r}_i) \vec{\omega}_i) \\ &= \sum_{i=1}^{n} m_i ((\vec{r}_i \cdot \vec{r}_i)E - \vec{r}_i \otimes \vec{r}_i) \vec{\omega}_i \\ &= \sum_{i=1}^{n} I_i \vec{\omega}_i \\ &= I_\text{total} \vec{\omega}_\text{total} \end{align*} $$

Where $\otimes$ is the outer product and $E$ is the identity matrix. And now we've got our total angular momentum for 3 dimensions! The equation looks the exact same as the two dimensional case, except for the fact that the moment of inertia is now not a scalar, but a 3x3 matrix, expanding $I$ then becomes

$$\huge \begin{align*} ((\vec{r}_i \cdot \vec{r}_i)E - \vec{r}_i \otimes \vec{r}_i) &= \small \begin{bmatrix} (r_x r_x + r_y r_y + r_z r_z) & 0 & 0 \\ 0 & (r_x r_x + r_y r_y + r_z r_z) & 0 \\ 0 & 0 & (r_x r_x + r_y r_y + r_z r_z) \end{bmatrix} - \begin{bmatrix} r_x r_x & r_y r_x & r_z r_x \\ r_x r_y & r_y r_y & r_z r_y \\ r_x r_z & r_y r_z & r_z r_z \\ \end{bmatrix} \\ &= \huge \begin{bmatrix} r_y^2 + r_z^2 & - r_y r_x & - r_z r_x \\ - r_x r_y & r_x^2 + r_z^2 & - r_z r_y \\ - r_x r_z & - r_y r_z & r_x^2 + r_y^2 \end{bmatrix} \\ &= I \end{align*} $$

Also be aware that it's an object and not a transformation operation, which makes it a so called "rank-2 tensor" and that raises a question: why?

Normally if you analyze the angular motion of a 3D rigid body you would notice that the total angular momentum doesn't point in the same direction as it's total angular velocity, which seems weird when you think about it, though there's another way of looking at it but that's for a bit later.

Taking the time derivative of the angular momentum is also interesting, the derivative of the moment of inertia would be quite dense to derive by hand so we will use a concept of body and world space to make things easier.

Note

There's a document that includes the derivation for the derivatives of moments of inertia by hand in 3 dimensions in reference [14], specifically section 3.0.2 called "Angular Motion".

To turn the angular momentum from body space into world space we use the rotation matrix $R$ like so

$$\huge \begin{align*} R \vec{L}_\text{body} = R (I_\text{body} \vec{\omega}_\text{body}) = \vec{L}_\text{world} \end{align*} $$

Taking the time derivative of the world space angular momentum then becomes

$$\huge \begin{align*} \frac{d}{dt} \vec{L}_\text{world} &= \frac{d R}{dt} \left(I_\text{body} \vec{\omega}_\text{body}\right) + R \left(\frac{d I_\text{body}}{dt} \vec{\omega}_\text{body}\right) + R \left(I_\text{body} \frac{d \vec{\omega}_\text{body}}{dt}\right) \\ &= \frac{d R}{dt} \left(I_\text{body} \vec{\omega}_\text{body}\right) + R \left(I_\text{body} \vec{\alpha}_\text{body} \right) \qquad \qquad \small where \space \frac{d I_\text{body}}{dt} = 0 \\ &= R \left(\vec{\omega}_{body} \times I_\text{body} \vec{\omega}_\text{body} \right) + R \left(I_\text{body} \vec{\alpha}_\text{body} \right) \qquad \small where \space \frac{d R}{dt} = R \cdot [\vec{\omega}_\text{body}]_{\times} \\ \end{align*} $$

The total sum of the external torques and the inertial torque is sometimes referred to as "Euler's second law of motion". Also notice the $\vec{\omega}_\text{body} \times I_\text{body} \vec{\omega}_\text{body}$ term (or $\vec{\omega}_\text{body} \times \vec{L}_\text{body}$), this is called an inertial torque, by definition, it quite literally is a torque caused by the body's own inertia, and it plays a significant major role in conserving the angular momentum properly in 3 dimensions.

Also the term is more known to be called "Torque-free precession", the Torque-free part is where it's interesting.

Note

In case you're confused by the $[\vec{\omega}_\text{body}]_{\times}$ part within the $\vec{\omega}_\text{body} \times I_\text{body} \vec{\omega}_\text{body}$ term, that's the angular velocity tensor in body's local space, for more info on how the rotation matrix derivatives work like in this case look up reference [5], but since it's such a clever and a very simple way of deriving the derivatives in rotating frames, I'm going to explain it.

To get a velocity of a point in space that's rotating around an axis, we can do

$$\huge \vec{v} = \vec{\omega} \times \vec{r} $$

Which is essentially just the derivative of $\vec{r}$ in a rotating frame! Now if we have two points from the world frame and the body frame, the point from the body frame is constant and it does not move, however the point in world frame is not constant, and let's also have a map that puts things from the body oriented frame into the world oriented frame as $R$, then the point that is in world frame is defined as

$$\huge \vec{P}_\text{world} = R \cdot \vec{P}_\text{body} $$

The time derivative of $\vec{P}_\text{world}$ is

$$\huge \begin{align*} \frac{d}{dt} \vec{P}_\text{world} &= \vec{\omega}_\text{world} \times (R \cdot \vec{P}_\text{body}) \\ &= \vec{v}_\text{world} \end{align*} $$

Which gets us the velocity in world frame. Now we could interpret the world angular velocity as a tensor $[\vec{\omega}_{\text{world}}]_{\times}$.

One important thing to mention is the cross product skew-symmetric matrix, it's defined as

$$\huge [\vec{a}]_{\times} = \begin{bmatrix} 0 & -a_z & a_y \\ a_z & 0 & -a_x \\ -a_y & a_x & 0 \end{bmatrix} $$

It's a rank-two tensor! Which is essentially

$$\huge [\vec{a}]_{\times} \vec{b} = \vec{a} \times \vec{b} $$

Then suppose we have an angular velocity tensor that's in the body frame and we want to transform it into the world frame, the general rule for transforming the coordinates of rank-two tensors in rotating frames is

$$\huge R(T_\text{body})R^T = T_\text{world} $$

Look look up reference [18] for a quick proof or other resources on tensors. Now we could reformulate our derivative of $\vec{P}_\text{world}$ formula as

$$\huge \begin{align*} \frac{d}{dt} \vec{P}_\text{world} &= R([\vec{\omega}_\text{body}]_{\times})R^T (R \cdot \vec{P}_\text{body}) \\ &= R (\vec{\omega}_\text{body} \times \vec{P}_\text{body}) \end{align*} $$

Notice $R^T R$ is just the identity matrix, which does nothing! Rotation matrices generally have the property of their transpose being the same as their inverses, that is because they are what's called an orthogonal matrices. Also we finally got the formula that we used earlier to derive the inertial torque! However we now know that it's just the derivative of $\vec{P}_\text{body}$ in the rotating body frame which is then transformed to the world frame.

Caution

The inertial torque term would make no sense looking at the formula alone, you have to observe it by looking at a rigid body in motion with antisymmetric mass distribution and do analysis with the term, most resources would be completely hand-wavy about it by bringing up known effects that are caused by it, the most known being the Dzhanibekov Effect, but that is not the best way to understand it! It's actually a fairly intuitive term that causes an obvious motion which isn't difficult to digest, but was completely overlooked by ridiculous examples unfortunately.

There's a very good video on doing the analysis of the inertial torque term that's in reference [1]. I did however try my best to explain what it does in the next few paragraphs which is a little different than the conventional way of teaching it, it certainly worked for me.

Suppose you have two particles A and B that have the same mass that are also on a sphere's manifold surface separated by some angle, and they are constrained to some infinite mass particle by some distance so A and B cannot move away from it, also A and B can't move away from each other, we want to apply an impulse $\vec{J}$ to particle A which is completely perpendicular to the radius vector $\vec{r}_a$ of A

particles-on-surf

Then it starts to move, but it also drags particle B into it as well, and that also in return pulls A into B by the rod's forces as per Newton's third law, and this would cause a rotational motion and a translational motion on the sphere's surface manifold, and this already sounds very familiar to the 2D case!

3d-rot.mp4

Looking at it from the perspective of the particles center of rotation shows an interesting behavior

3d-rot-persp.mp4

This looks very similar to the case we've analyzed before and it's happening again here. Now you may be wondering: "What does this have to do with the inertial torque term we just derived?"

Remember when we mentioned there are cases where the angular momentum isn't always aligned with the angular velocity direction? That is mainly dependent on the mass distribution of the system, if you apply an impulse you may not get the angular velocity direction you would expect. To give an example, we have to rely on an actual physical intuition rather than the equations for things to make sense.

As shown above, when particle A started moving, it started to "drag" particle B into it, but how exactly does it do that with only using angular velocity? By taking the conservation of angular momentum into account!

More specifically, the momentum of the system is by average rotating in a very specific motion depending on the initial conditions. when A started moving, it had to drag B into it as well, and that also means both must rotate around their principal axis of rotation since their angular velocity is not constant and is not parallel to the angular momentum! It's almost as if we are doing both translation and rotation at the same time within the spherical surface manifold.

So the angular momentum must be conserved throughout time, it's direction and length must not change if there are no external torques applied on the system (which is where the torque-free part comes from within the "Torque-free precession" term), which is a constraint! The following video shows the behavior more clearly

ang-moment-conserve.mp4

The red vector is the angular momentum, the yellow vector is the angular velocity and the green vector is the inertial torque which is pushing angular velocity in it's direction. Notice how the entire system is rotating about the angular momentum as we would expect!

And here's a comparison between the particle sim (where it approximates angular momentums and the inertial torques as vectors as shown) and the rigid body sim, you would notice their motions match and conserve momentums in the exact same way

inertla-torque-compare.mp4

To explain the behavior in more detail, notice in the first frame of rotation, particle A was rotating around particle B, but as the time goes it started to rotate around it's center of rotation and then later on stopped and particle B started to rotate around particle A instead, this cycle continues forever, this is exactly the behavior we got in the 2D case! Only now that it looks more obfuscated than before.

Now I'll be trying to shove the 3D case into the 2D case and emulate it in 2D, notice the angular velocities are right above the center of rotation

2d-inertial-torque

Each step is rotating the systems around the dotted angular velocities, notice their center of rotation is essentially moving in a straight line, you can try doing this by hand and show that this behavior actually works. This is almost like a workaround for the fact that you don't have a translation state to keep track of when moving in a spherical manifold.

Here's another one doing the exact same thing but in 3D

3d-inertial-torque

All of this can be linked back to other types of behaviors like Gyroscopic Precession or the Dzhanibekov Effect, the important thing to remember here is that all of these behaviors are caused by the body's own inertia which we saw how that happens already. In case you're interested, look up the two references [3] and [4] which are mainly about the analysis of the Dzhanibekov Effect and the Gyroscopic Precession behaviors respectively.

Now that out of the way, there's another thing to mention, if a rigid body gets rotated, then it's moments of inertia changes! Not because it deforms for our case, but mainly because the distribution of the particles in the system changes, which also affects how the energies gets distributed by the external and the internal forces. A good example is the following

diff-inertias

Here we apply in both systems an impulse to one of the particles by the blue vectors, notice their resultant angular velocities (in yellow) aren't the same! So how do we calculate the new inertia tensor? Do we need to re-do all the work again?

Not really, since we know that points never deform in our case, then the distance from each other must be constant. We could instead orient a radius vector $\vec{r}$ by a rotation matrix $R$ and our new vector is basically $\vec{r'} = R \cdot \vec{r}$. So, with all of that in mind, by using our definition of the inertia tensor, it then becomes

$$\huge \begin{align*} I' &= (\vec{r'} \cdot \vec{r'})E - \vec{r'} \otimes \vec{r'} \\ &= \left( (R \cdot \vec{r})^T (R \cdot \vec{r}) \right)E - (R \cdot \vec{r})(R \cdot \vec{r})^T \\ &= R \left( ( \vec{r} \cdot \vec{r}) E \right) R^T - R ( \vec{r} \space \vec{r}^T ) R^T \\ &= R \left( (\vec{r} \cdot \vec{r}) E - \vec{r} \otimes \vec{r} \right) R^T \\ &= R \left( I \right) R^T \end{align*} $$

Note that both the dot products and the outer products have their equivalents $\vec{a} \cdot \vec{b} = (\vec{a}^T) \vec{b}$ and $\vec{a} \otimes \vec{b} = \vec{a} (\vec{b}^T)$ respectively (one gives a scalar which is a 1x1 matrix resulted from 1x3 and 3x1 matrix product, and another gives a 3x3 matrix resulted from 3x1 and 1x3 matrix product).

A good way of interpreting $R(I)R^T$ is by using world and local frames of the torque formula. Let's say we have a torque and we apply it into a body, then

$$\huge \begin{align*} \vec{\omega}_{\text{world}}' &= \vec{\omega}_\text{world} + R(I^{-1}_\text{local})R^T \cdot R\left( \vec{\tau}_\text{local} \cdot \Delta t \right) \\ &= \vec{\omega}_\text{world} + R \left( I^{-1}_\text{local} \cdot (\vec{\tau}_\text{local} \Delta t) \right) \\ \end{align*} $$

Notice the $R^T R$ cancels so we get the final formula. This shows that forces indeed happen in the local space of the rigid body, here's some visual on that

world-torque-frame body-frame-torque

Applying forces in either the body's frame or the world frame give the exact same results. The $\vec{\omega}_W$ is the angular velocity expressed in world frame and likewise the force vector $\vec{F}_W$ is expressed as well in world frame, their equivalents which are in the body frame instead are $\vec{\omega}_B$ and $\vec{F}_B$.

Implementation of The Dynamics

Now that we're done explaining everything we needed to know, we're ready to implement the dynamics, which, for a basic one, is pretty simple.

Warning

The following implementation is not meant to be the best way of doing it, it's meant to be a good starting point which is probably good enough for most people. And be aware that there are tons of ways to implement rigid bodies, this is not the only way of doing things at all.

There are many sorts of things you need to handle properly to get a very robust dynamics, this article in particular does seem to do a very good job at covering it https://mizerski.medium.com/motion-of-spinning-bodies-b51175194512, along Erin Catto's slides on numerical methods as well https://box2d.org/files/ErinCatto_NumericalMethods_GDC2015.pdf

A typical rigid body data structure is something like

struct Rigid_Body
{
	Vector3 position;
	Quaternion orientation;

	Vector3 center_of_mass;

	Vector3 inv_mass;
	Vector3 linear_velocity;

	Matrix3x3 inertia;
	Vector3 angular_velocity;
};

Now the inertia tensor is the interesting part, as per the equation we defined earlier, you could have each individual particle's inertia calculated and summed up altogether, or if you have a pre-defined shape like a box or a capsule then you may need to pre-calculate them using a continuous integration over a volume, I've done some derivations in Appendix B already so you may wanna check that out.

We will use the box shape and calculate it's inertia like so

Matrix3x3 calculate_box_inertia(float min_width, float max_width, float min_height, float max_height, float min_depth, float max_depth, float mass)
{
	float dx = max_width - min_width;
	float dy = max_depth - min_depth;
	float dz = max_height - min_height;

	Matrix3x3 inertia = {};
	inertia[0][0] = mass * (dy * dy + dz * dz) / 12.0;
	inertia[1][1] = mass * (dx * dx + dz * dz) / 12.0;
	inertia[2][2] = mass * (dx * dx + dy * dy) / 12.0;

	return inertia;
}

We will define some helper functions which would abstract some work for us to be used later. First, we may want to get the inertia tensor in the world frame, we just do the following

Matrix3x3 get_inertia_world_frame(Rigid_Body& body)
{
	// world space orientation matrix
	Matrix3x3 world_orient = convert_quat_to_matrix3x3(body.orientation);

	// update the inertia to be in the world space configuration
	Matrix3x3 world_inertia = world_orient * body.inertia * transpose(world_orient);

	return world_inertia;
}
Matrix3x3 get_inverse_inertia_world_frame(Rigid_Body& body)
{
	// world space orientation matrix
	Matrix3x3 world_orient = convert_quat_to_matrix3x3(body.orientation);

	// take the inverse and update the inertia to be in the world space configuration
	Matrix3x3 world_inertia = world_orient * inverse(body.inertia) * transpose(world_orient);

	return world_inertia;
}

Then getting the world center of mass is like so

Vector3 get_world_center_of_mass(Rigid_Body& body)
{
	return rotate_vector3_by_quat(body.orientation, body.center_of_mass) + body.position;
}

Now It may depend on how you want to handle applying forces to bodies, the common way is to apply forces as impulses which is straightforward

void apply_linear_impulse(Rigid_Body& body, Vector3 impulse)
{
	body.velocity += body.inv_mass * impulse;
}

void apply_angular_impulse(Rigid_Body& body, Vector3 impulse)
{
	Matrix3 inv_inertia = get_inverse_inertia_world_frame(body.inertia);
	body.angular_velocity += inv_inertia * impulse;
}

void apply_impulse(Rigid_Body& body, Vector3 world_point, impulse)
{
	apply_linear_impulse(body, impulse);

	Vector3 world_COM = get_world_center_of_mass(body);
	Vector3 r = world_point - world_COM;
	Vector3 H = cross_vector3(r, impulse);
	apply_angular_impulse(body, H);
}

Then a box rigid body constructer

Rigid_Body create_box(Vector3 positiion, Quaternion orientation, Vector3 extent, float mass)
{
	Rigid_Body body = {};

	// set an initial position and orientation
	body.position = position;
	body.orientation = orientation;

	// the inv_mass is used to describe infininte masses, 0 is infinite,
	// mass of 20 of a body is just it's inverse 1/20 and so on. to get back
	// the normal mass you just invert it "mass = 1/inv_mass".
	body.inv_mass = (mass != 0.0) ? 1/mass : 0.0;

	// here we use the "extent" which is the maximum width/height/depth
	// of the box altogether.
	body.inertia = calculate_box_inertia(-extent.x, extent.x, -extent.y, extent.y, -extent.z, extent.z, mass);

	return body;
}

Now updating the rigid body's state would have one minor issue, the inertial torque cannot be calculated if we don't have a second moment of inertia in world space, the workaround is to work within the local space since the inertia tensor is constant there, hence we can just calculate it like so

void update_rigid_body_state(Rigid_Body& body, float dt)
{
	body.position += body.linear_velocity * dt;

	// in case the angular velocity is not a zero vector then update the orientation,
	// this is mainly to make sure we don't produce bogus results out of zeros
	if body.angular_velocity != get_zero_vector()
	{
		Matrix3x3 world_orient = convert_quat_to_matrix3(body.orientation);

		// angular velocity in the body frame
		Vector3 local_omega = transpose(world_orient) * body.angular_velocity;

		// this is inertial torque converted to an impulse
		Vector3 inertial_impulse = cross_vector3(local_omega, body.inertia * local_omega) * dt;

		// calculate the jacobian of the impulse
		Matrix3x3 J = body.inertia + dt * (skew_matrix3x3(local_omega) * body.inertia - skew_matrix3x3(body.inertia * local_omega));

		// single iteration of the newton's method
		local_omega = local_omega - inverse(J) * inertial_impulse;

		// transform the angular velocity back into world space and then assign it into the rigid body's angular velocity
		body.angular_velocity = world_orient * local_omega;

		// get the new delta of the rotation
		Quaternion theta = quaternion_angle_axis(
			/* angle */ length(body.angular_velocity) * dt,
			/* axis */  normalize(body.angular_velocity)
		);
		// make sure to normalize the quaternion so things don't diverge too much by the accumlating floating point precision error
		body.orientation = normalize_quat(theta * body.orientation);

		// calculate the new position relative to the oriented center of mass in case it's not within the origin of the local coordinate frame
		Vector3 world_COM = get_world_center_of_mass(body);
		body.position = world_COM + rotate_vector3_by_quat(theta, body.position - world_COM);
	}
}

Believe it or not, that's pretty much the bare minimum to accurately simulate a rigid body!

One thing to note however is within the update_rigid_body_state(...) function, the inertial torque is calculating an implicit force, to do this we needed to use newton's method, this requires multiple iterations since a single iteration is not enough from my experience and sometimes it can produce really bad results. The following is an example of how to do proper integration to make newton's method more into an effect with substepping

void update_simulation(Physics_State& state, float dt)
{
	// get time difference between frames

	// ...

	// apply gravity to bodies

	// ...
	
	// some other stuff applied to bodies
	
	// ...

	// do multiple iterations on every rigid body with substepping to make
	// the inertial torque more "stable"
	float num_of_steps = 10;
	float substep = dt / num_of_steps;
	for int step = 0; step < num_of_steps; step += 1
	{
		for &body in state.rigid_bodies {
			update_rigid_body_state(body, substep)
		]
	}
}

This should make the inertial torque effect more stable since now we're doing more iterations than one, you can increase in certain cases if 10 iterations isn't enough, for me in the extreme cases (which are unlikely in the normal scenarios) I had to use between 100-225. Also adding air drag may stabilize the motion as well. For more details on this, refer to Erin's slides on numerical methods.

Now what can we do with all of this? Pretty much anything you could think of! Multibody dynamics which involves gluing multiple rigid bodies into one giant ragdoll or a vehicle or whatever using various types of constraints. Maybe also combining it with fluids like air or water as well.

All of that sounds nice, but maybe we should do something easier and more achievable, like attaching a rigid body to a spring or simulating the Dzhanibekov Effect or even collisions with the floor.

Springs

This is simply done using Hooke's law, the force of a spring is described as

$$\huge \vec{F} = k \vec{x} $$

Where $k$ is the stiffness of the spring, and $\vec{x}$ is the error vector which is the difference between two points and we're aiming for both points to be in the same place.

Implementing that in code

Rigid_Body body = /*...*/;

// point mass that's from within the rigid body, which is
// also in the rigid body's local space and NOT the world space!
Vector3 point_mass = Vector3(/* ... */);

// state varbiels
float previous_length = 0.0;

// simulation loop
while keep_simulating
{
	// get the time difference between frames
	float dt = /* ... */;

	// apply gravity
	apply_linear_impulse(body, Vector3(0,-9.8,0) * dt);

	// the point mass in world space
	Vector3 world_point_mass = rotate_vector3_by_quat(body.orientation, point_mass) + body.position;
	
	// some arbitrary point in world space which is our target
	Vector3 target_point = Vector3(/* ... */);

	// calculate the spring length
	Vector3 spring_len = (target_point - world_point_mass);
	
	// calculate the spring force that forces the body's point mass
	// to move towards the arbitrary world point
	float spring_stiffness = 15.0;
	Vector3 spring_force = spring_len * spring_stiffness;

	// calculate the damping forces
	float damping_magnitude = 1.0;
	Vector3 spring_velocity = (spring_len - previous_spring_len) / dt;
	Vector3 spring_damper = damping_magnitude * spring_velocity;

	previous_spring_len = spring_len;

	// apply forces to the body
	apply_impulse(body, world_point_mass, (spring_force + spring_damper) * dt);

	// ...

	// update rigid body state

	// ...
}

Then we should get something similar to the following

spring-forces.mp4

The white point is the target point and the yellow point is the point mass that's on the box body.

Dzhanibekov Effect

This one is pretty simple, we set the rigid body at a specific initial orientation and an initial angular velocity where it makes this effect doable. For more details look up reference [3].

The idea is that the effect only happens in the intermediate axis of the inertia, we know that in 3 dimensions there are three rotating planes, so suppose that one of them represent the least moments of inertia which is $I_1$ and another represents the largest moments of inertia which is $I_3$, the one in between $I_2$ is where it makes the effect most visible

$$\huge I_1 < I_2 < I_3 $$

For the following, the intermediate inertial axis would be the x axis which is in between y and z axes, y is the largest moment of inertia and z is the least moment of inertia

dzhanibekov-effect.mp4

And here's another showing the angular momentum (in red) and angular velocity (in yellow) vectors (both scaled down for visibility)

dzhanibekov-velocities.mp4

And here's a comparison to a particle sim which relies on the same constraints as the rigid bodies in terms of making the effect doable

dzhanibekov-effect-particles.mp4

Collisions

We could try doing some collisions with spheres, I've already covered most of the important bits in Appendix C. I think showing some code first should be self-explanatory.

The moment of inertia for a sphere is

$$\huge I_{\text{sphere}} = \begin{bmatrix} \frac{2}{5} M R^2 & 0 & 0 \\ 0 & \frac{2}{5} M R^2 & 0 \\ 0 & 0 & \frac{2}{5} M R^2 \end{bmatrix} $$

And is calculated as follows

Matrix3x3 calculate_sphere_inertia(float radius, float mass)
{
	Matrix3x3 inertia = {};
	inertia[0][0] = mass * radius * radius * 2/5;
	inertia[1][1] = mass * radius * radius * 2/5;
	inertia[2][2] = mass * radius * radius * 2/5;
	return inertia;
}

And here's how to calculate the collision impulses for the sphere body

Rigid_Body sphere = /*...*/;

#define SPHERE_RADIUS 0.35

// simulation loop
while keep_simulating
{
	// get the time difference between frames

	// ...

	// apply gravity to rigid bodies

	// ...

	// the collision normal of the floor
	Vector3 collision_normal = Vector3(0, 1, 0);

	// is the sphere interpenetrating with the floor
	if dot_product(collision_normal, sphere.position) < SPHERE_RADIUS
	{
		// get our contact point
		Vector3 contact_point = sphere.position - collision_normal * SPHERE_RADIUS;
		
		// get absolute value of the depth
		float depth = abs(contact_point.y);

		// radius vector
		Vector3 R = contact_point - sphere.position;

		Vector3 relative_velocity = sphere.linear_velocity + cross_vector3(sphere.angular_velocity, R);

		// if they are not separating already, then start applying impulses
		if dot_product(relative_velocity, collision_normal) > 0
		{
			Matrix3x3 inv_inertia = get_inverse_inertia_world_frame(&sphere);
			float linear_factor = sphere.inv_mass;
			float angular_factor = dot_product(inv_inertia * cross_vector3(R, collision_normal), collision_normal);

			float restitution = 1.75; // (1 + e) = (1 + 0.75) | where e = [0,1]
			float normal_impulse = -restitution * dot_product(relative_velocity, collision_normal) / (linear_factor + angular_factor);

			// apply normal impulse
			apply_impulse(&sphere, contact_point, -normal_impulse * collision_normal);

			// get the friction normal by projection the velocity into the
			// collision normal plane and normalized it, only apply it
			// if there's any movement within the xz plane
			Vector3 tang_dir = relative_velocity - collision_normal * dot_product(collision_normal, relative_velocity);
			if tang_dir != get_zero_vector()
			{
				tang_dir = normalize(tang_dir);

				linear_factor = sphere.inv_mass;
				angular_factor = dot_product(inv_inertia * cross_vector3(R, tang_dir), tang_dir);

				float friction = 0.6;
				float friction_impulse = -friction * dot_product(relative_velocity, tang_dir) / (linear_factor + angular_factor);

				// apply static friction
				apply_impulse(sphere, contact_point, friction_impulse * tang_dir);
			}
		}
		else {
			// don't do anything since they're moving away from each other!
		}

		// project the sphere into the floor's surface to remove the interpenetration
		sphere.position += collision_normal * depth;
	}

	// ...

	// update rigid body state

	// ...
}

And here's a simulation of it

sphere-collision.mp4

Now that out of the way, let's explain what's happening here. The interesting part of all of this is friction.

Without friction, the ball wouldn't rotate because the normal impulses go straight through the center of mass of the ball which just moves the entire rigid body in the normal's direction and that's to be expected. However with friction, that's not the case! The friction impulses do not go through the center of mass and we could make this more obvious with an image of how friction actually happens at the micro level

micro-friction

Notice within the image, in reality bodies surfaces aren't perfectly smooth and their surface usually collide with other materials at the micro level causing this behavior which also forces the ball to roll.

Note

Now you may also know that objects have two states of friction, one where they try to stick to the surface of another material, and another state where they just can't and they start slipping. Both states are called static and dynamic frictions respectively. A known model of this is called Coulomb's friction.

What we've modelled however is the static friction only for demonstrating the frictional behavior and how it relates to the rotational dynamics of the bodies, you could model the dynamic friction if you want.

Another thing to mention is that collisions on shapes that aren't spheres like boxes do cause rotations even in cases there are no frictions involved.

So far the spheres could only have one contact point with the floor, but with boxes they can have as much as four simultaneous points altogether on the floor! To demonstrate this let's show the problem in more detail with an image

two-impulse

The case of two contacts defines an edge collision with the floor, there will be two torques that when summed up, you get the resultant torque in circular green which rotates the entire body in one axis and hence we get $\vec{\omega}$. If you have four contacts which is when one of the faces of the box hits the floor the torques from one side to another cancel and they will create a pure translational motion for the body!

Now unfortunately though we can't simply model this in the simulation, the reason being is that if there are multiple contacts happening at the same time then they would affect each other and this creates a system of forces which is not trivial to handle. This is a very off-topic discussion and a very complicated problem to solve and does not fit this article but you can look into how people handle this problem with the so called Systems of Constraints Solvers, essentially you have a contact which could be made up of three constraints at once and you also get three other contacts which then totals the constraints to be 12 for four contacts, this cannot be solved directly with a regular system of linear equations solver however because of constraint conditions which are the inequalities (i.e. when the two bodies are already separating, then no forces are to be applied, so C >= 0 or C = 0) they eventually turn into a so called Non-Linear/Linear Complementarity Problem (usually thrown as terms like NCP and LCP respectively) also sometimes referred to as a Mixed-LCP for systems that involve different types of equality constraints altogether, I would recommend looking up three references which are [24], [25] and [26], also look into Solver2D which delves into iterative solvers that solve NCP and LCP problems in different ways for different types of constraints which are possibly not necessarily the most accurate but they are efficient for what they were designed for.

Conclusion

What we've derived so far throughout the article was more known as Newton-Euler Equations, It's essentially the combination of Newton's equations and Euler's equations into one, though we primarily focused on Euler's equations for the most part.

I've tried to cover the most important bits in this article and clarify what they actually are and where they came from. There are however a few things that I haven't covered like the alignment of the angular velocity and one of the inertia's principle axes (or the eigenvectors) which then causes a "stable" motion, we've covered the instability of rigid bodies which is related to that, there's also the fact that you can get a diagonalized moment of inertia tensor which doesn't involve it's off-diagonal elements which is pretty handy when doing computations on rigid bodies on paper or even simplifying computations when doing rigid body simulation, here's a resource that goes into it, you basically need to find a specific orientation by a rotation matrix $R$ that diagonalizes a generic 3x3 tensor $I$. And also look into what's called parallel axis theorem which is mainly for rigid shapes that aren't within the origin in the coordinate system so you essentially try to align the tensor with the center of mass within the local frame and treating it as the origin rather than the center of the local frame which is the actual origin.

I'm looking forward to do more mechanics and such using Lagrangian Mechanics, one resource I'm aware of is called "Structure and Interpretation of Classical Mechanics" which is reference [19], another one is in reference [11] which I've seen mentioned quite a lot in various resources but I haven't delved much into it however.

Alright, that's about it. Thank you for reading the article and have a good day!


Appendix A - Axis of Rotation

Best way to describe a rotation is by using Euler's formula $e^{i \theta} = cos( \theta ) + i sin(\theta)$, suppose you have a number that squares to $-1$ such as $i^2 = -1$, and a 2 dimensional system (real $1$ and imaginary $i$) and you pick a point on the circular line that sits on a plane, if you multiply the point by the imaginary number multiple times, you would notice a pattern

imag-rot

Every time you multiply the complex number by the imaginary number $i$, you get a 90 degree or $\huge \frac{\pi}{2}$ radian rotation around some origin ($1 \cdot i = i$, $i \cdot i = -1$, $-1 \cdot i = -i$, and $-i \cdot i = 1$), the equation may still seem a little magical however and we can now instead interpret the complex number as $e^{\vec{N} \theta} = \vec{a} \cdot \vec{b} + \vec{a} \times \vec{b}$, the neat thing about the new formula is that you can now interpret this as a rotation on a plane and we can prove this.

The dot product of two vectors $\vec{a} \cdot \vec{b}$ is defined as $\vec{a} \cdot \vec{b} = \| \vec{a} \| \| \vec{b} \| cos(\theta)$, and likewise for the cross product $\vec{a} \times \vec{b} = \| \vec{a} \| \| \vec{b} \| sin(\theta) \vec{N}$, then interpreting them as the Euler's formula becomes $e^{\vec{N} \theta} = \| \vec{a} \| \| \vec{b} \| cos(\theta) + \vec{N} \| \vec{a} \| \| \vec{b} \| cos(\theta)$, since we would assume that both $\vec{a}$ and $\vec{b}$ are normalized, then $\| \vec{a} \| \| \vec{b} \| = 1$, which then becomes $e^{\vec{N} \theta} = cos(\theta) + \vec{N} sin(\theta)$! And that's the formula we're familiar with, except that it's more intuitive now.

The $\vec{N}$ here is the unit axis of rotation, it's normal to a plane, and if we have a vector that sits within that plane, then we can simply do $e^{\vec{N} \theta} \cdot \vec{v}$ which transforms $\vec{v}$ into a different place in the circular line which is a rotation!

Also the cross product requires a specific order, in general $\vec{a} \times \vec{b} \neq \vec{b} \times \vec{a}$. Suppose the following

circulation-cross

In case we do $\vec{A} \times \vec{B} = \vec{P}$, then for our right-handed coordinate system, this becomes a clockwise rotation, and likewise for $\vec{A} \times \vec{C} = \vec{Q}$, it becomes a counter-clockwise rotation, and it's intuitively obvious, though there needs some careful consideration when using the product.

The handedness in question is a common figure, if you crawl your fingers and put your thumbs up, then your thumb is your axis of rotation and your crawled fingers are your rotation direction around the axis, it's essentially the following

handedness

Both handedness have the same axis of rotation pointing in the same direction, but the rotation direction differ like in our case (counter-clockwise for right-handed, clockwise for left-handed), because of this behavior, the vector coming out of the cross product is technically not a "vector", but instead a "pseudovector" or even a "bivector", but that's not important for now as long as we're consistent with the handedness of the coordinate system.

Now we can map $e^{N \theta}$ (where $N$ now is an imaginary number for the following example) directly into a 2x2 matrix $R$ and we get the following

$$\huge \begin{align*} e^{N \theta} \cdot \vec{v} &= (\text{cos}(\theta) + N \text{sin}(\theta)) \cdot (v_x + N v_y) \\ &= (\text{cos}(\theta) \cdot v_x + N \text{sin}(\theta) \cdot v_x) + (N \text{cos}(\theta) \cdot v_y - \text{sin}(\theta) \cdot v_y) \\ &= \begin{bmatrix} \text{cos}(\theta) \cdot v_x - \text{sin}(\theta) \cdot v_y \\ \text{sin}(\theta) \cdot v_x + \text{cos}(\theta) \cdot v_y \end{bmatrix} \\ &= \begin{bmatrix} \text{cos}(\theta) & -\text{sin}(\theta) \\ \text{sin}(\theta) & \text{cos}(\theta) \end{bmatrix} \cdot \begin{bmatrix} v_x \\ v_y \end{bmatrix} \\ &= R \vec{v} \end{align*} $$

You can think of the formula as a coordinate transformation (or a linear map operation) on $\vec{v}$ like so

imag-delta

And the way to see this in 3D is a rotation on an XY plane

imag-delta-3d

And notice that $N$ became a vector ($\vec{N}$) which is normal to the XY plane! We can interpret $\vec{N}$ as the $\vec{z}$ axis normal to the XY plane. The formula in 3D is formalized as such (which gets us a 3x3 matrix)

$$\huge \begin{align*} R \vec{v} &= \begin{bmatrix} cos(\theta) & -sin(\theta) & 0 \\ sin(\theta) & cos(\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} v_x \\ v_y \\ v_z \end{bmatrix} \\ \end{align*} $$

Alright, so to generalize this to 3D, we simply just turn the imaginary number $N$ into a vector $\vec{N}$, and as long as the vector $\vec{v}$ sits within the plane of the axis of rotation $\vec{N}$ then nothing really changes, and this also turns our Euler's formula $e^{\vec{N} \theta}$ into a quaternion! However there's a problem, if the vector $\vec{v}$ is outside of the oriented plane, then the rotation could possibly become invalid because of a so called "isoclinic rotations", where an extra unwanted rotation happens, the 4 dimensional system (notice the wording here!) of the quaternion mainly causes this, to fix this we can simply apply two rotations and instead apply half the rotation magnitude to then cancel the extra rotation that's caused by the extra degree of rotation like so $e^{\vec{N} \frac{\theta}{2}} \cdot \vec{v} \cdot e^{-\vec{N} \frac{\theta}{2}}$, now it looks verbose but if we give the exponential the symbol $q$, and if we know the inverse of the exponential is $(e^{\vec{N} \frac{\theta}{2}})^{-1} = e^{-\vec{N} \frac{\theta}{2}}$, then it becomes $q\vec{v}q^{-1}$, and this is the rotation formula in the general case with quaternions in 3 dimensions.

Note

For more proper technical reason behind the $q\vec{v}q^{-1}$ equation, it's mainly because the quaternion group $\text{SU(2)}$ double covers the $\text{SO}(3)$ group by topology, that's to say $q\vec{v}q^{-1} = (-q)\vec{v}(-q)^{-1}$, so using the multiplication we can say that both $q$ and $-q$ are the same when mapped to $\text{SO}(3)$ and so they act as a 2:1 mapping, which also explains the reasoning behind the double angle that's caused by the two multiplications of the quaternion $q$ on the vector $\vec{v}$.

For a geometric intuition behind it, there's a great video that goes through it which is mainly about spinors but they are "isomorphic" to quaternions by their properties https://youtu.be/ACZC_XEyg9U.

Another geometric intuition behind it is through the reflection operators from Geometric Algebra in reference [16], this one is much easier to comprehend but it doesn't really explain the reasoning behind the double cover very well and is a bit hand-wavy, it however goes on why the general rotation formula is $q\vec{v}q^{-1}$ and not $q\vec{v}$ using two reflections operators to represent a rotation. A better and more sensible (and more standardized) way to look at it is by using Rodrigues' rotation formula which is connected to the general rotation formula $q\vec{v}q^{-1}$ and is more compatible to the $q\vec{v}$ formula as well, I've already proven it in another article in reference [15] specifically in it's section Appendix A.

Covering the entirety of rotations here is going to be tedious. I'd recommend the documents & videos referenced at the bottom of this article in the Reference & Further Reading section, specifically [20] which is a friendly introduction to rotations in general, for more detailed analysis of rotations, then I highly recommend considering the other three references being [21], [22] and [23] as well which delve into the rotation groups ( i.e. $\text{SO}(3)$ ) and their connections to their double cover ( i.e. $\text{SU}(2)$ ) and what lie algebras and lie groups are for.

Appendix B - Deriving Moments of Inertia for Common Shapes

So far we've defined our inertia as the sum of the moments of every particle in a system, however it was a discrete sum and not a continuous sum, usually the bodies we deal with have their masses being continuous like a box or a sphere, so it's better to define it in terms of those and find their inertias.

The total sum for moments of inertia over a continuous volume is defined as

$$\huge \begin{align*} I_\text{total} &= \int_{V} \rho(\vec{r}) \cdot ((\vec{r} \cdot \vec{r})E - \vec{r} \otimes \vec{r}) \space dV \\ \end{align*} $$

One important thing to notice here is the density function $\rho(\vec{r})$, it's defined as

$$\huge \rho = \frac{M}{V} $$

Which is the mass and the volume ratio, this doesn't really mean anything on it's own to be honest, a better way to understand this is to give an example.

A very good use of the density function is the integral volume $\int_{V} dV$, what we want is to cancel the volume $V$ that's within the density function and the integral, since multiplying the total volume $V_\text{total}$ and the density volume $\frac{1}{V}$ gives us mass by the following relation

$$\huge M = \rho \int_{V} dV $$

And this is also how bodies in real world weight as much. A cube can be of a mass of 1kg relative to it's size and so on, a large volume for that mass has very low density unlike a low size which by then has a large density. So far we've been using a pure discrete sum assuming that every point mass had an infinite amount of density relative to it's infinitely small size which is definitely not realistic!

So that's pretty much all there is to it, nothing fancy about how the continuous sum is used here.

Now expanding the inertia tensor then becomes

$$\huge \begin{align*} I_\text{total} &= \normalsize \begin{bmatrix} \int_{V} \rho(\vec{r}) \cdot (r_y^2 + r_z^2) dV & \int_{V} \rho(\vec{r}) \cdot (- r_y r_x) dV & \int_{V} \rho(\vec{r}) \cdot (- r_z r_x) dV \\ \int_{V} \rho(\vec{r}) \cdot (- r_x r_y) dV & \int_{V} \rho(\vec{r}) \cdot (r_x^2 + r_z^2) dV & \int_{V} \rho(\vec{r}) \cdot (- r_z r_y) dV \\ \int_{V} \rho(\vec{r}) \cdot (- r_x r_z) dV & \int_{V} \rho(\vec{r}) \cdot (- r_y r_z) dV & \int_{V} \rho(\vec{r}) \cdot (r_x^2 + r_y^2) dV \end{bmatrix} \\ \end{align*} $$

Which is pretty verbose, but that's not important, what's important to notice here is that we have to derive the integral for every single element in the matrix, however we mainly care about the upper triangular part of the tensor and the diagonal elements, the lower triangular part is just the transpose of the upper one, so we could save some effort there.

Let's derive the moment of inertia tensor for a cube. For the first element of the tensor, the integral is

$$\huge \begin{align*} I_{11} &= \frac{M}{w \cdot h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{-\frac{d}{2}}^{\frac{d}{2}} \int_{-\frac{w}{2}}^{\frac{w}{2}} (y^2 + z^2) \space dx dy dz \\ &= \frac{M}{w \cdot h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{-\frac{d}{2}}^{\frac{d}{2}} (y^2 + z^2) \space dy dz \int_{-\frac{w}{2}}^{\frac{w}{2}} dx \\ &= \frac{M}{h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{-\frac{d}{2}}^{\frac{d}{2}} (y^2 + z^2) \space dy dz \\ &= \frac{M}{h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} \left[ \frac{1}{3} y^3 + y \cdot z^2 \right]_{-\frac{d}{2}}^{\frac{d}{2}} dz \\ &= \frac{M}{h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} (\frac{1}{24} d^3 + \frac{d}{2} \cdot z^2 - (-\frac{1}{24} d^3 - \frac{d}{2} \cdot z^2)) \space dz \\ \end{align*} $$

$$\huge \begin{align*} &= \frac{M}{h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} \left( \frac{1}{12} d^3 + d \cdot z^2 \right) \space dz \\ &= \frac{M}{h \cdot d} \left[z \cdot \frac{1}{12} d^3 + d \cdot \frac{1}{3} z^3 \right]_{-\frac{h}{2}}^{\frac{h}{2}} \\ &= \frac{M}{h \cdot d} \left( \frac{h}{2} \cdot \frac{1}{12} d^3 + d \cdot \frac{1}{24} h^3 - (-\frac{h}{2} \cdot \frac{1}{12} d^3 - d \cdot \frac{1}{24} h^3) \right) \\ &= \frac{M}{h \cdot d} \left(h \cdot \frac{1}{12} d^3 + d \cdot \frac{1}{12} h^3 \right) \\ &= \frac{1}{12} \left( \frac{M}{h \cdot d} \left(h \cdot d^3 + d \cdot h^3 \right) \right) \\ &= \frac{M}{12} \left( d^2 + h^2 \right) \\ \end{align*} $$

And that's for the first element of the diagonal, the rest is pretty much in the same pattern, what about the upper-triangular part? Let's do one of them

$$\huge \begin{align*} I_{12} &= \frac{M}{w \cdot h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{-\frac{d}{2}}^{\frac{d}{2}} \int_{-\frac{w}{2}}^{\frac{w}{2}} -(y * x) \space dx dy dz \\ &= \frac{M}{w \cdot h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{-\frac{d}{2}}^{\frac{d}{2}} \left[ -\frac{1}{2}( y * x^2) \right]_{-\frac{w}{2}}^{\frac{w}{2}} \space dy dz \\ &= \frac{M}{w \cdot h \cdot d} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{-\frac{d}{2}}^{\frac{d}{2}} \left( -\frac{1}{8} y * w^2 + \frac{1}{8} y * w^2 \right) \space dy dz \\ &= 0 \end{align*} $$

Notice that we just stopped at the first integral, the reason being is that the inner values are summed to basically zero, continuing the summations would give us zeros all the way through. and this pattern repeats for the rest of the off-diagonal elements.

Finishing off the rest of the elements, the final tensor is then

$$\huge I_\text{cube} = \begin{bmatrix} \frac{M}{12} d^2 + h^2 & 0 & 0 \\ 0 & \frac{M}{12} w^2 + h^2 & 0 \\ 0 & 0 & \frac{M}{12} w^2 + d^2 \\ \end{bmatrix} $$

That was for the cube. We could also derive the moment of inertia of a cylinder which is more interesting.

We will be using the cylindrical coordinate system instead of the cartesian one we've used a little earlier which is defined as

$$\huge V = \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \int_{-\pi}^{\pi} r \space d \theta d r d z $$

Now doing our first integral

$$\huge \begin{align*} I_{11} &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \int_{-\pi}^{\pi} \left( y^2 + z^2 \right) r \space d \theta d r d z \\ &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \int_{-\pi}^{\pi} \left( r^2 \text{sin}(\theta)^2 + z^2 \right) r \space d \theta d r d z \\ &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \left[ r^2 \left( \frac{\theta}{2} - \frac{1}{4} \text{sin}(2 \theta) \right) r + \theta z^2 r \right]_{-\pi}^{\pi} d r d z \\ &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \left( r^3 \pi + 2 \pi z^2 r \right) d r d z \\ &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \left[ \frac{1}{4} r^4 \pi + \frac{1}{2} r^2 * (2 \pi z^2) \right]_{0}^{R} d z \\ \end{align*} $$ $$\huge \begin{align*} &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \left( \frac{1}{4} R^4 \pi + \frac{1}{2} R^2 * (2 \pi z^2) \right) d z \\ &= \frac{M}{\pi R^2 h} \left[ z * (\frac{1}{4} R^4 \pi) + \frac{1}{2} R^2 * \frac{1}{3} (2 \pi z^3) \right]_{-\frac{h}{2}}^{\frac{h}{2}} \\ &= \frac{M}{\pi R^2 h} \left( \frac{1}{12} \pi h R^2 ( 3 R^2 \pi + h^2) \right) \\ &= \frac{1}{12} M \left( 3 R^2 + h^2 \right) \\ \end{align*} $$

That's for the first element of the diagonal row, the second element in the row is similar, the third however has both $x$ and $y$

$$\huge \begin{align*} I_{33} &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \int_{-\pi}^{\pi} \left( r^2 \text{cos}(\theta)^2 + r^2 \text{sin}(\theta)^2 \right) r \space d \theta d r d z \\ &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \int_{-\pi}^{\pi} r^3 \space d \theta d r d z \\ &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \left[ \theta * r^3 \right]_{-\pi}^{\pi} d r d z \\ &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \int_{0}^{R} \left( 2 \pi r^3 \right) d r d z \\ \end{align*} $$ $$\huge \begin{align*} &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \left[ \frac{1}{4} 2 \pi r^4 \right]_{0}^{R} d z \\ &= \frac{M}{\pi R^2 h} \int_{-\frac{h}{2}}^{\frac{h}{2}} \left( \frac{1}{4} 2 \pi R^4 \right) d z \\ &= \frac{M}{\pi R^2 h} \left[ z * \frac{1}{4} 2 \pi R^4 \right]_{-\frac{h}{2}}^{\frac{h}{2}} \\ &= \frac{M}{\pi R^2 h} \left( \frac{1}{4} 2 \pi R^4 h \right) \\ &= \frac{1}{2} M R^2 \\ \end{align*} $$

Now that was pretty much it, for the off-diagonal elements you could try to integrate them yourself and the values are pretty much zeros. The moment of inertia for a cylinder is then

$$\huge I_\text{cylinder} = \begin{bmatrix} \frac{1}{12} M (3 R^2 + h^2) & 0 & 0 \\ 0 & \frac{1}{12} M (3 R^2 + h^2) & 0 \\ 0 & 0 & \frac{1}{2} M R^2 \\ \end{bmatrix} $$

Appendix C - Deriving Collision Impulses Using Conservation Laws Only

Suppose the following

$$\huge m_a v_a + m_b v_b = m_a v_a' + m_b v_b' $$

It basically says the momentum of the system for both bodies must be conserved which has to be equal before and after the collision, after the collision both were moving at the exact same speeds, so that assumes $v_a' = v_b' \space$, reformulating it then becomes

$$\huge \begin{align*} m_a v_a + m_b v_b &= m_a v_a' + m_b v_b' \\ (m_a + m_b) v_f &= m_a v_a + m_b v_b \\ \Longrightarrow \space v_f &= \frac{m_a v_a + m_b v_b}{m_a + m_b} \end{align*} $$

Which is coincidentally just the velocity of the center of mass for both the particle and the body combined moving as one! Which could also be interpreted as sticking the particle into the body. Now $v_f$ is just the derivative of the center of mass

$$\huge \begin{align*} \frac{d}{dt} X_\text{com} &= \frac{d}{dt} \left(\frac{m_a x_a + m_b x_b}{m_a + m_b}\right) \\ &= \frac{m_a v_a + m_b v_b}{m_a + m_b} \\ &= V_\text{com} \end{align*} $$

Now to make it applicable into both objects, we need to turn it into an impulse, we haven't defined what an impulse is.

An impulse for our case is simply an instantaneous force, it's defined as

$$\huge \begin{align*} J &= \lim_{t_1 \to t_0} \int_{t_0}^{t_1} F(t) dt \\ &= P(t_1) - P(t_0) \\ &= \Delta P \\ &= mv' - mv \end{align*} $$

The difference between $t_0$ and $t_1$ is so small that it's almost zero, our force we're trying to model is almost instant so it makes sense, the problem however is that making it exactly instant is going to blow our equation up and makes the force infinitely large, so we take the limit for the integral to only let $t_1$ approach towards $t_0$, but never equal to $t_0$.

Turning our momentum equation into an impulse then becomes

$$\huge \begin{align*} J_a &= \Delta P_a \\ &= m_a v_a - m_a v_a' \\ &= m_a v_a - m_a (\frac{m_a v_a + m_b v_b}{m_a + m_b}) \\ \end{align*} $$

Now by Newton's third law, if there's a force, there's an equal and opposite force as well, in our case it's an impulse or an instantaneous force

$$\huge J_a = -J_b $$

Which is great! However we haven't taken the conservation of energy so far into account, doing it that way makes the total energy non-conservative, so a way around this is to take the conservation of energy into account as well.

Kinetic energy is defined as

$$\huge T = \frac{1}{2} m v^2 $$

So the conservation of energy is

$$\huge \begin{align*} T_a + T_b &= T_a' + T_b' \\ \frac{1}{2} m_a v_a^2 + \frac{1}{2} m_b v_b^2 &= \frac{1}{2} m_a v_a^{2'} + \frac{1}{2} m_b v_b^{2'} \end{align*} $$

Which basically says the total energy before the collision must be the same after the collision, continuing

$$\huge \begin{align*} \frac{1}{2} m_a v_a^2 + \frac{1}{2} m_b v_b^2 &= \frac{1}{2} m_a v_a^{2'} + \frac{1}{2} m_b v_b^{2'} \\ m_a v_a^2 + m_b v_b^2 &= m_a v_a^{2'} + m_b v_b^{2'} \\ m_a (v_a^2 - v_a^{2'}) &= -m_b (v_b^2 - v_b^{2'}) \\ m_a(v_a - v_a')(v_a + v_a') &= -m_b(v_b - v_b')(v_b + v_b') \\ \end{align*} $$

Now from conservation of momentum we know that

$$\huge m_a (v_a - v_a') = -m_b(v_b - v_b') $$

Which they would cancel in the energy formula

$$\huge \begin{align*} m_a(v_a - v_a')(v_a + v_a') &= -m_b(v_b - v_b')(v_b + v_b') \\ v_a + v_a' &= (v_b + v_b') \\ \end{align*} $$

Re-arranging the terms then becomes

$$\huge v_b' = (v_a + v_a') - v_b $$

Plugging it back into the momentum formula then becomes

$$\huge \begin{align*} m_a v_a' &= m_b v_b - (m_b v_a + m_b v_a' - m_b v_b) + m_a v_a \\ (m_a + m_b) v_a' &= (m_a - m_b) v_a + 2 m_b v_b \\ v_a' &= \frac{(m_a - m_b)}{m_a + m_b} v_a + \frac{2 m_b}{m_a + m_b} v_b \end{align*} $$

Which now accounts for the conservation of energy law! Turning it into an impulse then becomes

$$\huge \begin{align*} m_a(v_a - v_a') &= m_a v_a - m_a \left(\frac{(m_a - m_b)}{m_a + m_b} v_a + \frac{2 m_b}{m_a + m_b} v_b\right) \\ &= J_a \end{align*} $$

Then again from newton's third law

$$\huge J_a = -J_b $$

So we got pretty much everything we want, now however we want to generalize this concept to more than one dimension to also include rotations for 3D, we can use a so called "separating axis", essentially it's the unit/normalized axis that contains the separation between two bodies

sep-axis

How do we make sure they separate only along the axis of separation? It's simply just dotting the difference velocity of two bodies with the separating axis itself! This essentially transfers the problem from an n-dimensional problem into a 1-dimensional problem which makes things consistent

$$\huge \vec{v}_\text{rel} = (v_b - v_a) \cdot \vec{N} $$

bounce

Now we need to re-arrange our previous formula to handle this case like so

$$\huge \begin{align*} (m_a + m_b) * J_a &= (m_a + m_b) m_a v_a - m_a \left((m_a - m_b) v_a + 2 m_b v_b\right) \\ &= m_a m_a v_a + m_b m_a v_a - m_a m_a v_a + \\ & \qquad m_a m_b v_a - 2 m_b m_a v_b \\ &= 2 m_a m_b v_a - 2 m_b m_a v_b \\ &= 2 m_a m_b (v_a - v_b) \\ \Longrightarrow J_a &= -\frac{2 m_a m_b (v_b - v_a)}{m_a + m_b} \end{align*} $$

Notice in the nominator, there's $v_b - v_a$, that's relative velocity! We can take the dot product of the velocities with the separating axis like so

$$\huge J_a = -\frac{2 m_a m_b (\vec{v}_b - \vec{v}_a) \cdot \vec{N}}{m_a + m_b} $$

Notice that $v_a$ and $v_b$ became vectors which are n-dimensional vectors.

One other thing, if we simulate an infinitely large mass like a floor, then the other body should completely bounce off the floor, but notice that if $m_b$ is infinitely large, then the term would blow up! And the floating point precession in computers can't handle such large number.

The workaround is to use inverse masses, if the inverse of mass of a particle is infinitely large then it cannot be moved unless there's an infinite force applied to it, essentially it would be approximately zero

$$\huge \begin{align*} m^{-1} &= \frac{1}{20} = 0.05 \\ m^{-1} &= \frac{1}{2000000000} = 5^{-10} \\ m^{-1} &= \frac{1}{200000000000000000000000} = 5^{-24} \\ m^{-1} &= \frac{1}{\infty} \approx 0 \\ \end{align*} $$

So our impulse equation becomes

$$\huge J = -\frac{2 \cdot (\vec{v}_b - \vec{v}_a) \cdot \vec{N}}{m_a^{-1} + m_b^{-1}} $$

Try calculating impulses using this equation and see if it's equivalent to our previous one.

Now to make the elasticity variant from in-elastic to completely elastic, we can use a restitution term $e$ that's in the range of $[0,1]$, then our impulse equation becomes

$$\huge J = -\frac{(1 + e) \cdot (\vec{v}_b - \vec{v}_a) \cdot \vec{N}}{m_a^{-1} + m_b^{-1}} $$

Now to include rotations into the impulse equation, we need to treat rotations as linear motions to simplify things a lot, one way to achieve this is by using the following formula

$$\huge \vec{v} = \vec{\omega} \times \vec{r} $$

Which is just the velocity of $\vec{r}$ in the rotating frame. If you've read most of the above article, then you may remember that we said that you could interpret rotations as linear if you're within the polar space.

So what does it look like in practice?

rot-frame-deriv

At every point mass their velocities are exactly tangent to the rotating circular manifold surface.

Remember that $\vec{\omega}$ is assuming of radius one, so taking the cross product with a specific point mass $\vec{r}$ gives us the derivative of a point mass with the correct speeds, which is linear velocity, in a rotating frame.

Each body has a total velocity like so

$$\huge \vec{v}_\text{total} = \vec{v} + \vec{\omega} \times \vec{r} $$

And suppose we want to separate the two bodies only along the axis itself for both linear and angular combined when applying impulses

$$\huge \vec{v}' = \vec{v} + m^{-1}(J \cdot \vec{N}) $$

$$\huge \vec{\omega}' = \vec{\omega} + I^{-1} (J \cdot (\vec{r} \times \vec{N})) $$

Doing some re-arrangement to our impulse formula using the total velocities then becomes

$$\huge J = - \frac{(1 + e) \cdot (\vec{v}_b - \vec{v}_a) \cdot \vec{N}}{m_a^{-1} + m_b^{-1} + (I_a^{-1}(\vec{r}_a \times \vec{N}) \times \vec{r}_a + I_b^{-1}(\vec{r}_b \times \vec{N}) \times \vec{r}_b) \cdot \vec{N}} $$

Check reference [2] for more detailed derivation of this formula if you're interested.

Appendix D - Deriving Collision Impulses using Constraint Forces

I've already covered the basics like Impulses and such in the Appendix C, so read that first.

Here we will derive the collision impulses based on constraints instead. Constraints for our case are represented as a function $g(x)$ where you minimize/maximize the function $f(x)$ on it. There is a topic that deals with this sorts of stuff called Mathematical Optimization, more specifically Constrained Optimization.

The whole idea is that constraint forces on the system must never do any work, this is the Principle of Virtual Work which is also usually referred to as the d'Alembert's principle, I heavily recommend referring to references [8], [9] and [10] since they explain how (almost) all of this works with constraints, covering them here would be too far.

Now we will derive the impulse using a constraint condition for two bodies contained within a system and derive the constraint forces.

First, the bodies must be separate from each other, assuming that the following condition is satisfied

$$\huge C(\vec{x}_a, \vec{x}_b,\vec{n}) = \vec{n} \cdot (\vec{x}_b - \vec{x}_a) \geq 0 $$

Where $\vec{n}$ is a normalized normal vector which has a direction of going from contact points $\vec{x}_a$ to $\vec{x}_b$ and it defines the surface normal of body A, it's our separation vector.

We are mainly interested in balancing the energies within the system using the condition to separate them from each other.

We simply take the derivative of the constraint function with respect to time and we will get the following

$$\huge \begin{align*} \frac{d C}{dt} = \vec{n} \cdot (\frac{d \vec{x}_b}{dt} - \frac{d \vec{x}_a}{dt}) \end{align*} $$

Note that the normal is constant for our case, so it's derivative is zero (some people like to take it's derivative and is probably done for a good reason!) Here assuming the total velocity of a contact point being

$$\huge \frac{d \vec{x}}{dt} = \vec{v} + \vec{\omega} \times \vec{r} $$

Now continuing using that

$$\huge \begin{align*} \frac{d C}{dt} &= \vec{n} \cdot (\vec{v}_b + \vec{\omega}_b \times \vec{r}_b - \vec{v}_a - \vec{\omega}_a \times \vec{r}_a) \\ &= \vec{n} \cdot \vec{v}_b + \vec{n} \cdot (\vec{\omega}_b \times \vec{r}_b) - \vec{n} \cdot \vec{v}_a - \vec{n} \cdot (\vec{\omega}_a \times \vec{r}_a) \end{align*} $$

Here we want to separate the velocities from the normal vectors which is our separation direction, knowing that $a \cdot (b \times c) = b \cdot (c \times a)$, then

$$\huge \begin{align*} \frac{d C}{dt} &= \vec{n} \cdot \vec{v}_b + \vec{\omega} \cdot (\vec{r}_b \times \vec{n}) - \vec{n} \cdot \vec{v}_a - \vec{\omega}_a \cdot (\vec{r}_a \times \vec{n}) \\ &= \begin{bmatrix} -\vec{n} \space \space -\vec{r}_a \times \vec{n} \space \space \vec{n} \space \space \vec{r}_b \times \vec{n} \end{bmatrix} \cdot \begin{bmatrix} \vec{v}_a \\ \vec{\omega}_a \\ \vec{v}_b \\ \vec{\omega}_b \end{bmatrix} \\ &= J \vec{v} \end{align*} $$

Then we have $J$ and $\vec{v}$, which are the jacobian (1x12) and the velocity (12x1) matrices respectively, they represent 12 degrees of freedom. The energy of the system is projected into the jacobian where each block within it are the directions the bodies aren't allowed to move towards.

The mass of the system is defined as

$$\huge \begin{bmatrix} M_a & 0 & 0 & 0 \\ 0 & I_a & 0 & 0 \\ 0 & 0 & M_b & 0 \\ 0 & 0 & 0 & I_b \end{bmatrix} $$

$M$ mass matrices are 3x3, similar to moments of inertia $I$ which are 3x3 as well, both defined as blocks, so the zeros $0$ are essentially the 3x3 zero matrices.

Now assuming we have some scalar that has a symbol as $\lambda$ which is a force, and the mass matrix is $A = JWJ^T$, the $W$ here is $M^{-1}$ which is the inverted mass matrix, and the projected velocity as $b = J \vec{v}$, then $A \lambda + b &gt;= 0$, which satisfies our conditions.

A simpler way to interpret that is by ignoring the angular velocities for now, that gives us 6 total degrees of freedom to constraint, and we can reduce it even further to 4 degrees of freedom (2 translational degrees for body A and similarly for body B), then

$$\huge \begin{align*} JWJ^T &= \begin{bmatrix} -n_x \space -n_y \space n_x \space n_y \end{bmatrix} \cdot \begin{bmatrix} m_a^{-1} & 0 & 0 & 0 \\\ 0 & m_a^{-1} & 0 & 0 \\\ 0 & 0 & m_b^{-1} & 0 \\\ 0 & 0 & 0 & m_b^{-1} \\\ \end{bmatrix} \cdot \begin{bmatrix} -n_x \\ -n_y \\ n_x \\ n_y \end{bmatrix} \\ &= \begin{bmatrix} -n_x \space -n_y \space n_x \space n_y \end{bmatrix} \cdot \left( \begin{bmatrix} -m_a^{-1} \cdot n_x \\ -m_a^{-1} \cdot n_y \\ m_b^{-1} \cdot n_x \\ m_b^{-1} \cdot n_y \end{bmatrix} \right) \\ &= (m_a^{-1} \cdot n_x^2) + (m_a^{-1} \cdot n_y^2) + (m_b^{-1} \cdot n_x^2) + (m_b^{-1} \cdot n_y^2) \\ \end{align*} $$

This is our calculated effective mass of the system which is a scalar, assuming that $\vec{n}$ is normalized, then

$$\huge JWJ^T = m_a^{-1} + m_b^{-1} $$

And the projected energies into the jacobian are

$$\huge \begin{align*} J \vec{v} &= \begin{bmatrix} -\vec{n} \space \space \vec{n} \end{bmatrix} \cdot \begin{bmatrix} \vec{v}_a \\ \vec{v}_b \end{bmatrix} \\ &= -\vec{n} \cdot \vec{v}_a + \vec{n} \cdot \vec{v}_b \\ &= \vec{n} \cdot (\vec{v}_b - \vec{v}_a) \end{align*} $$

Now solving for $\lambda$

$$\huge \begin{align*} \lambda &= \frac{-J\vec{v}}{JWJ^T} \\ &= -\frac{(v_b - v_a) \cdot \vec{n}}{m_a^{-1} + m_b^{-1}} \end{align*} $$

Which is our constraint impulse! It's the exact same formula we derived in Appendix C minus the restitution term.

That was for inelastic impulses, for elastic impulses you have to factor in the restitution term and it should still conserve the momentums and energies just fine.

References & Further Readings

[1] Euler's Equations of Rigid Body Dynamics Derived | Qualitative Analysis | Build Rigid Body Intuition https://youtu.be/z4-vL6lxR8U

[2] Physically Based Modeling Rigid Body Simulation https://graphics.pixar.com/pbm2001/pdf/notesg.pdf

[3] Rigid Body Motion and the Dzhanibekov Effect https://www.youtube.com/watch?v=NJLdW4DHRcA

[4] The Mystery of Gyroscopic Motion: How Does It Do That? https://www.youtube.com/watch?v=YiQVna7UTiQ

[5] Time Derivative of Rotation Matrices: A Tutorial https://arxiv.org/pdf/1609.06088

[6] Moment of inertia https://en.wikipedia.org/wiki/Moment_of_inertia

[7] Triple Integrals for Volumes of Some Classic Shapes https://sites.math.washington.edu//~aloveles/Math324Fall2013/f13m324TripleIntegralExamples.pdf

[8] A Quick Tutorial on Multibody Dynamics https://fab.cba.mit.edu/classes/865.18/design/optimization/dynamics_1.pdf

[9] Linear-Time Dynamics using Lagrange Multipliers https://www.cs.cmu.edu/~baraff/papers/sig96.pdf

[10] Physically Based Modeling Constrained Dynamics https://graphics.pixar.com/pbm2001/pdf/notesf.pdf

[11] Classical Mechanics Third Edition https://www.math.toronto.edu/khesin/biblio/GoldsteinPooleSafkoClassicalMechanics.pdf

[12] Conservation Of Momentum https://en.wikipedia.org/wiki/Conservation_of_momentum

[13] Elastic collision https://en.wikipedia.org/wiki/Elastic_collision

[14] A COMPLETE DERIVATION OF THE NEWTON-EULER EQUATIONS https://www.panikul.am/assets/Newton_Euler_Derivation.pdf

[15] A Simple And Intuitive Explanation of How Gizmos Work In 3D Space https://gist.github.com/Memresable/4b9b8921b23f86d16b23afea87ae0318

[16] Rotors: A practical introduction for 3D graphics https://jacquesheunis.com/post/rotors/

[17] Lecture L26 - 3D Rigid Body Dynamics: The Inertia Tensor https://ocw.mit.edu/courses/16-07-dynamics-fall-2009/dd277ec654440f4c2b5b07d6c286c3fd_MIT16_07F09_Lec26.pdf

[18] Transformation of axes https://www.doitpoms.ac.uk/tlplib/tensors/transformation.php

[19] Structure and Interpretation of Classical Mechanics https://tgvaughan.github.io/sicm/toc.html

[20] Lie Theory for Roboticists https://www.youtube.com/watch?v=csolG83gCV8

[21] A micro Lie Theory for state estimation in robotics https://arxiv.org/pdf/1812.01537

[22] General Lie Group/Theory resources https://www.ethaneade.com/

[23] Quaternion kinematics for the error-state Kalman filter https://arxiv.org/pdf/1711.02508

[24] SIGGRAPH'22 Course: Contact and Friction Simulation for Computer Graphics https://siggraphcontact.github.io/

[25] M3 Challenge David Baraff Lunch Talk 2016 https://youtu.be/vNTHveVpDDc?feature=shared

[26] Numerical methods for linear complementarity problems in physics-based animation https://dl.acm.org/doi/10.1145/2504435.2504443#supplementary-materials

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment