Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / 2D

Making a 2D Physics Engine: Mass, Inertia and Forces

5.00/5 (2 votes)
12 Nov 2018CPOL6 min read 23.2K  
Calculating the mass and inertia of a body and applying forces to it

Making a 2D Physics Engine: The Series

This is the fourth article in the Making a 2D Physics Engine Series. If you haven't already read all the articles in the series before this one, I strongly recommend that you take a detour and skim through them.

  1. Making a 2D Physics Engine: The Math
  2. Making a 2D Physics Engine: Spaces and Bodies
  3. Making a 2D Physics Engine: Shapes, Worlds and Integration
  4. Making a 2D Physics Engine: Mass, Inertia and Forces

Introduction

Every physical object has some mass and moment of inertia which is determined by its shape and density. In this article, we will assign mass and inertia to our bodies based on its Shape and provided density to make interactions more realistic. To keep things simple, we assume that objects have uniform density.

We will also learn how forces are applied in real life and emulate them in our physics engine.

The contents of this article are contained in the history of this commit of the 2D physics engine I am building in Rust along with this article series.

Do not fear! Collisions are coming soon.

Mass and Inertia Calculation

Preprocessing

Before we begin, we must make sure that the center of mass of our body is the origin in its local space. For shapes with uniform density, the center of mass is simply the centroid.

Circles

By our definition of a Circle, the centre of the circle is already its centroid. We do not need to do anything here.

Polygons

A Polygon, however, can be arbitrary. We must find the centroid of the vertices and subtract it from each vertex so that the polygon vertices are relative to the centroid as the center.

The centroid of the polygon is a weighted average of the vertices of the polygon, weighted by face triangle area. The formulas (and their explanation) for calculating face triangle area and polygon area are given later on in the article.

The centroid is calculated as follows:

rust
centroid = Vec2::ZERO;
        
for face in polygon {
    face_area = 0.5 * face.vertex_a.cross(face.vertex_b);
    centroid += (face.vertex_a + face.vertex_b) * face_area / (3.0 * polygon_area);
}

// Make the centroid the origin

for vertex in vertices {
    vertex -= centroid;
}

Mass

By the definition of mass, mass = density * volume. Being an additive quantity, volume (area in 2D) can be calculated by breaking the given shape into smaller shapes.

Circles

The area of a circle is \(\pi r^2\) or PI * radius * radius, which makes our mass definition very simple:

rust
mass = density * PI * radius * radius;

Polygons

The area of an arbitrary convex polygon does not have a fixed formula, but we can subdivide the polygon into triangles with a vertex at the centroid. The area of the polygon is the sum of the areas of the subdivided triangles.

Each subdivided triangle has a vertex at the origin (centroid), and two vertices as the vertices of a face of the polygon.

The area of a triangle with side vectors A and B is given by 0.5 * abs(A.cross(B)). This is a well known result whose derivation can easily be found online. Thus, for the vertices A and B, the side vectors of the corresponding subdivided triangle are A and B and the area is 0.5 * abs(A.cross(B)). The area of our polygon is simply the sum of the areas of these areas.

rust
area = 0;

for face in polygon {
    area += 0.5 * abs(face.vertex_a.cross(face.vertex_b));
}

The mass is now density * area.

Inertia

Rotations in 2D are about the axis perpendicular to the 2D plane, i.e. the Z-axis. Thus, the moment of inertia of a 2D shape is the moment of inertia of the shape about the Z-axis passing through the origin. It is analogous to mass in that it is a measure of the resistance a body offers to torque or rotational motion. Like mass, inertia is additive and the inertia of any shape can be calculated by summing the inertia of smaller shapes that constitute it.

Circles

The inertia of a circle about a perpendicular axis passing through its center is \(\frac{1}{2} m r^2\):

rust
inertia = 0.5 * mass * radius * radius;

Polygons

The inertia of an arbitrary convex polygon does not have a fixed formula, but like mass we can compute the inertia by subdividing the polygon into triangles with a vertex at the origin and finding their inertia.

The inertia of a triangle with a vertex at the origin and side vectors A and B about a perpendicular axis passing through the origin is given by mass * (A.sqrLength() + B.sqrLength() + A.dot(B)) / 6. The derivation of this formula is non-trivial and will not be included here for the sake of brevity. If you are up for a challenge (a lengthy one), however, you can try proving this result yourself by breaking the triangle up into thin rectangles parallel to the edge that does not contain the origin, i.e., the edge formed by A and B.

The inertia for the polygon can now be computed as follows:

rust
inertia = 0;
for face in polygon {
    A = face.vertex_a, B = face.vertex_b;
    mass_tri = density * 0.5 * abs(A.cross(B)); 
    inertia_tri = mass_tri * (A.sqrLength() + B.sqrLength() + A.dot(B)) / 6;
    inertia += inertia_tri;
}

Application of Forces

This section assumes that you have basic knowledge on what forces and torques are, how they are related to each other, and how they relate to acceleration. Forces were briefly discussed in the previous article, but the points covered there will be discussed again for relevance.

The main interaction mechanism of external agents (including the physics engine) with a body is going to be through forces (and indirectly torque). However, any and all interactions with a body should be on a per-frame or per-timestep basis. As such, all forces and torques applied to a body should be valid only for the current frame. This is how forces work in real life too, but it is not evident because our world is continuous, not discrete, and does not have any "timesteps".

When a force is applied on a body at a certain point, it produces a linear acceleration at the center of mass as well as a torque. However, there are two special cases to consider:

  1. Torque applied is zero: By the definition of torque, \(\vec{\tau} = \vec{r} \times \vec{F}\). Thus, when the force is applied at the center of mass, the resultant torque will be zero.
  2. Force applied is zero: A couple (a pair of forces equal in magnitude but opposite in direction) is used to generate torque without generating linear acceleration. The equal but opposite linear forces at the center of mass cancel each other out.

In our rigidbody structure, since we have independent force (at center of mass) and torque variables, we can implement each of these cases first and use these implementations to apply a force at an arbitrary point.

rust
pub fn add_force(&mut self, force: Vec2) {
    self.force += force;
}

pub fn add_torque(&mut self, torque: f32) {
    self.torque += torque;
}

pub fn add_force_at_pos(&mut self, force: Vec2, pos: Vec2) {
     self.add_force(force);
        self.add_torque(pos.cross(force));
    }

Impulses

Impulses are finite changes in momentum. They can also be considered as large forces applied over very short periods. However, we will consider them as momentum changes and directly modify the linear and angular velocities when an impulse is applied:

rust
pub fn add_impulse_at_pos(&mut self, impulse: Vec2, pos: Vec2) {
    self.velocity += impulse * self.inv_mass;
    self.angular_vel += pos.cross(impulse) * self.inv_inertia;
}

Future Steps

Congratulations! We have gone through the boring foundations of a physics engine and now can move on to something widely more interesting (and difficult): collisions. I advise you to brush up your linear algebra in time for the next article. Trust me, you will need it.

History

  • 28th January, 2018: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)