Set center of mass relative to origin OR get default CoM

In my game, players construct ships from triangular surfaces, which are then converted to a mesh for physics and rendering. Surfaces can be different substances (such as wood or steel) and vary in thickness, which means the center of mass needs to be calculated dynamically based on the player’s design. The problem is, I can get where the center of mass should be relative to the origin, but using UActorComponent::SetCenterOfMass() only sets the offset relative to some default center of mass calculated based on the mesh vertices. In order for the calculation to work, I need to do one of the following:

  • Find a function to set the center of mass relative to the origin, not the default center of mass
  • Find the default center of mass so I can adjust the calculated one accordingly

Anyone know of a way to do one or both of these things?

Here’s an image to visualize the problem. This box is symmetrical, but one side is made from wood and the other from steel, so the CoM should not be perfectly in the center but closer to the steel side. The editor cursor shows the mesh’s origin. The green sphere is the default CoM, calculated automatically by the engine. You can see that it’s at the geometric center of the mesh. The red sphere is where the CoM should be, based on the fact that steel is several times heavier than wood.

EDIT: Upon further experimentation I can confirm the default center of mass is not any of the following:

  • The center of the ship’s bounding box
  • The average of all vertices
  • The above, but not counting duplicate vertices

So, whatever calculation is used to guess the CoM is either more complicated than these, or it’s using data I don’t have access to.

Have you found a solution?

I did tried this way:

// somewhere in header
bool bIsCOMPosDirty = true;

// in physics tick
auto Handle = staticMeshComponent->BodyInstance.ActorHandle;

if (Handle && bIsCOMPosDirty) {
    Chaos::FRigidBodyHandle_Internal* RigidHandle = Handle->GetPhysicsThreadAPI();

    if (RigidHandle) {
        FVector COMPos = ... calculate ... ;
        RigidHandle->SetCenterOfMass(COMpos);
        bIsCOMPosDirty = false;
    }
}

RigidHandle may not be ready when BeginPlay, so try to set COM once, when RigidHandle ready. May be there is a function “on rigid handle ready”, but I did not find it.