reactphysics3d icon indicating copy to clipboard operation
reactphysics3d copied to clipboard

User question about inertia tensor?

Open tomazos opened this issue 4 years ago • 4 comments

Super quick user question on ReactPhysics3D if you have a time...

I'm looking through the RP3D implementation and I notice that the inertia tensor in local space coodinates (ie mLocalInertiaTensors), is stored as a Vector3 and is stored as "the diagonal terms of a diagonal matrix".

I guess I don't understand why the inertia tensor in local space coordinates can only be diagonal. For an arbitrary shape can't the inertia tensor have off-diagonal terms (so called "products of inertia") ? or have I asked something silly?

tomazos avatar Feb 08 '21 06:02 tomazos

https://gamedev.stackexchange.com/q/188958/135719

tomazos avatar Feb 08 '21 06:02 tomazos

I think I've figured it out. It looks like RP3D only supports "regular" shapes that have a diagonal inertia tensor in local space coordinates. For example ConvexMeshShape::getLocalInertiaTensor says:

The local inertia tensor of the convex mesh is approximated using the inertia tensor of its bounding box.

and I assume its bounding box is an AABB that is aligned to its local space coordinates.

I take it all the other dynamic-capable shapes have a similar constraint.

tomazos avatar Feb 08 '21 06:02 tomazos

The local inertia tensor of the convex mesh is approximated using the inertia tensor of its bounding box.

The inertia tensor of ConvexMeshShape is only approximated using a bounding box when it is automatically computed using the RigidBody::updateLocalInertiaTensorFromColliders() or RigidBody::updateMassPropertiesFromColliders() methods but it is always possible to set your own custom inertia tensor using the RigidBody:: setLocalInertiaTensor() method for instance.

About the initial question. For any inertia tensor matrix, it is always possible to find an orthogonal basis made of three axes (called principal axes of inertia) where the inertia tensor matrix is diagonal.

In ReactPhysics3D (an many other physics engines), it is supposed that the local-space of the body is aligned with the principal axes of inertia and therefore, the inertia tensor can be set using a single Vector3. This is easier for most cases and also better in terms of performance.

Note that it should be possible to diagonalize any inertia tensor matrix to find the rotation to be performed to the collision shape so that its principal axes of inertia are aligned with the local-space of the body in order to be able to set the diagonal values of the inertia tensor as a Vector3. However, currently there is no method available in ReactPhysics3D to diagonalize a Matrix3x3. Maybe this could be added to help with this.

DanielChappuis avatar Feb 09 '21 22:02 DanielChappuis

Thanks for your help Daniel, this is much clearer now.

I guess we use the local space coordinates to orient our models forward-direction and upward-direction (using convention right-handed with Y-forward, Z-up) with the center of gravity at local space origin.

We could calculate their principle axes and calculate the quaternion offset from those and apply/reverse that adjustment when reading/writing the bodies with the physics engine I guess. This would allow storage of the inertia tensor accurately as diagonal.

The alternative would be to store the intertia tensor as a Matrix3x3, but then you would add a full matrix multiply in calculating the world-space inertia tensor.

ie rather than:

/// Return the inverse of the inertia tensor in world coordinates.
const Matrix3x3 RigidBody::getWorldInertiaTensorInverse(PhysicsWorld& world,
                                                        Entity bodyEntity) {
  Matrix3x3 orientation = world.mTransformComponents.getTransform(bodyEntity)
                              .getOrientation()
                              .getMatrix();
  const Vector3& inverseInertiaLocalTensor =
      world.mRigidBodyComponents.getInertiaTensorLocalInverse(bodyEntity);
  Matrix3x3 orientationTranspose = orientation.getTranspose();
  orientationTranspose[0] *= inverseInertiaLocalTensor.x;
  orientationTranspose[1] *= inverseInertiaLocalTensor.y;
  orientationTranspose[2] *= inverseInertiaLocalTensor.z;
  return orientation * orientationTranspose;
}

It would be like:

const Matrix3x3 RigidBody::getWorldInertiaTensorInverse(PhysicsWorld& world,
                                                        Entity bodyEntity) {
  Matrix3x3 orientation = world.mTransformComponents.getTransform(bodyEntity)
                              .getOrientation()
                              .getMatrix();
  const Matrix3x3& inverseInertiaLocalTensor =
      world.mRigidBodyComponents.getInertiaTensorLocalInverse(bodyEntity);
  Matrix3x3 orientationTranspose = orientation.getTranspose();
  return orientation * inverseInertiaLocalTensor * orientationTranspose;
}

instead, which is more expensive, plus 6 more floats per body. Well actually I guess inertia tensors are symmetic so you could get away with only 3 more floats and then shortcircuit the matrix multiply, but still its more expensive.

tomazos avatar Feb 10 '21 04:02 tomazos