Fixed consistency of Trajectories with different time steps #436
|
@ -239,21 +239,6 @@ namespace SHADE
|
||||||
|
|
||||||
void SHPhysicsSystem::SimulateBody(SHGhostBody& ghostBody, SimulateBodyInfo& simInfo, SimulateBodyOutput& output)
|
void SHPhysicsSystem::SimulateBody(SHGhostBody& ghostBody, SimulateBodyInfo& simInfo, SimulateBodyOutput& output)
|
||||||
{
|
{
|
||||||
// Check for a valid rigidbody
|
|
||||||
const auto* rigidBody = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(simInfo.bodyEID);
|
|
||||||
if (!rigidBody)
|
|
||||||
{
|
|
||||||
SHLOG_WARNING("Entity {} does not have a rigid body to simulate! This body will collide with everything!", simInfo.bodyEID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare simulation info (I'm basically declaring an entire body here)
|
|
||||||
float invMass = 1.0f / ghostBody.mass;
|
|
||||||
SHVec3 worldInvInertia = SHVec3::One;
|
|
||||||
SHVec3 worldCentroid = SHVec3::One;
|
|
||||||
|
|
||||||
// Asserts. Don't be an idiot.
|
|
||||||
SHASSERT(invMass > 0, "GhostBody's mass in invalid")
|
|
||||||
|
|
||||||
// Build raycast layer from colliders. If none exist....then this never stops simulating technically.
|
// Build raycast layer from colliders. If none exist....then this never stops simulating technically.
|
||||||
// I'm too lazy to handle that case, so I'll just throw an error.
|
// I'm too lazy to handle that case, so I'll just throw an error.
|
||||||
uint16_t raycastLayers = 0;
|
uint16_t raycastLayers = 0;
|
||||||
|
@ -275,64 +260,31 @@ namespace SHADE
|
||||||
raycastInfo.continuous = false;
|
raycastInfo.continuous = false;
|
||||||
raycastInfo.layers = raycastLayers;
|
raycastInfo.layers = raycastLayers;
|
||||||
|
|
||||||
bool terminate = true;
|
// Check for a valid rigidbody
|
||||||
|
const auto* rigidBody = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(simInfo.bodyEID);
|
||||||
|
if (!rigidBody)
|
||||||
|
{
|
||||||
|
SHLOG_WARNING("Entity {} does not have a rigid body to simulate! This body will collide with everything!", simInfo.bodyEID)
|
||||||
|
}
|
||||||
|
|
||||||
|
double accumulatedTime = 0.0f;
|
||||||
|
|
||||||
int iterationCounter = simInfo.maxSteps;
|
int iterationCounter = simInfo.maxSteps;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
accumulatedTime += simInfo.timeStep;
|
||||||
|
|
||||||
raycastInfo.distance = ghostBody.linearVelocity.Length() * simInfo.timeStep; // Do not take the entire velocity's length as that is for an entire second.
|
raycastInfo.distance = ghostBody.linearVelocity.Length() * simInfo.timeStep; // Do not take the entire velocity's length as that is for an entire second.
|
||||||
raycastInfo.ray.position = ghostBody.position;
|
raycastInfo.ray.position = ghostBody.position;
|
||||||
raycastInfo.ray.direction = SHVec3::Normalise(ghostBody.linearVelocity);
|
raycastInfo.ray.direction = SHVec3::Normalise(ghostBody.linearVelocity);
|
||||||
|
|
||||||
terminate = !Raycast(raycastInfo).empty() || iterationCounter == 0;
|
if (!Raycast(raycastInfo).empty())
|
||||||
if (terminate)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Compute world space data
|
while (accumulatedTime > fixedDT)
|
||||||
const SHMatrix R = SHMatrix::Rotate(ghostBody.orientation);
|
{
|
||||||
const SHMatrix RT = SHMatrix::Transpose(R);
|
simulateBody(ghostBody, simInfo);
|
||||||
|
accumulatedTime -= fixedDT;
|
||||||
SHMatrix localInertiaTensor = SHMatrix::Identity;
|
|
||||||
|
|
||||||
// Set the diagonals
|
|
||||||
localInertiaTensor.m[0][0] = ghostBody.localInvInertia.x;
|
|
||||||
localInertiaTensor.m[1][1] = ghostBody.localInvInertia.y;
|
|
||||||
localInertiaTensor.m[2][2] = ghostBody.localInvInertia.z;
|
|
||||||
|
|
||||||
localInertiaTensor *= RT;
|
|
||||||
const SHVec3 DIAGONALS { localInertiaTensor.m[0][0], localInertiaTensor.m[1][1], localInertiaTensor.m[2][2] };
|
|
||||||
|
|
||||||
worldInvInertia = R * DIAGONALS;
|
|
||||||
|
|
||||||
// Compute world centroid
|
|
||||||
worldCentroid = (R * ghostBody.localCentroid) + ghostBody.position;
|
|
||||||
|
|
||||||
// Apply forces
|
|
||||||
ghostBody.accumulatedForce += simInfo.force;
|
|
||||||
ghostBody.angularVelocity += worldInvInertia * SHVec3::Cross(ghostBody.position + simInfo.forceOffset, simInfo.force);
|
|
||||||
ghostBody.accumulatedTorque += simInfo.torque;
|
|
||||||
|
|
||||||
// Integrate Velocities
|
|
||||||
// Integrate forces and gravity into linear velocity
|
|
||||||
const SHVec3 LINEAR_ACCELERATION = ghostBody.accumulatedForce * invMass;
|
|
||||||
const SHVec3 GRAVITATIONAL_ACCELERATION = ghostBody.gravityScale ? worldState.settings.gravity * ghostBody.gravityScale : SHVec3::Zero;
|
|
||||||
|
|
||||||
ghostBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * simInfo.timeStep * ghostBody.linearLock;
|
|
||||||
ghostBody.angularVelocity += worldInvInertia * (ghostBody.accumulatedTorque * simInfo.timeStep);
|
|
||||||
|
|
||||||
// Apply drag (exponentially applied)
|
|
||||||
ghostBody.linearVelocity *= 1.0f / (1.0f + simInfo.timeStep * ghostBody.drag);
|
|
||||||
ghostBody.angularVelocity *= 1.0f / (1.0f + simInfo.timeStep * ghostBody.angularDrag);
|
|
||||||
|
|
||||||
// Integrate Positions & Orientations
|
|
||||||
const SHQuaternion QV = SHQuaternion{ ghostBody.angularVelocity.x * simInfo.timeStep, ghostBody.angularVelocity.y * simInfo.timeStep, ghostBody.angularVelocity.z * simInfo.timeStep, 0.0f } * 0.5f;
|
|
||||||
|
|
||||||
ghostBody.position += ghostBody.linearVelocity * simInfo.timeStep;
|
|
||||||
ghostBody.orientation += ghostBody.orientation * QV * SHQuaternion::FromEuler(ghostBody.angularLock);
|
|
||||||
ghostBody.orientation = SHQuaternion::Normalise(ghostBody.orientation);
|
|
||||||
|
|
||||||
// Clear forces
|
|
||||||
ghostBody.accumulatedForce = SHVec3::Zero;
|
|
||||||
ghostBody.accumulatedTorque = SHVec3::Zero;
|
|
||||||
|
|
||||||
if (!simInfo.continuousForce)
|
if (!simInfo.continuousForce)
|
||||||
{
|
{
|
||||||
|
@ -340,14 +292,16 @@ namespace SHADE
|
||||||
simInfo.torque = SHVec3::Zero;
|
simInfo.torque = SHVec3::Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (--iterationCounter == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (output.positions)
|
if (output.positions)
|
||||||
output.positions->emplace_back(ghostBody.position);
|
output.positions->emplace_back(ghostBody.position);
|
||||||
|
|
||||||
if (output.orientations)
|
if (output.orientations)
|
||||||
output.orientations->emplace_back(ghostBody.orientation);
|
output.orientations->emplace_back(ghostBody.orientation);
|
||||||
|
|
||||||
--iterationCounter;
|
|
||||||
|
|
||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,4 +448,61 @@ namespace SHADE
|
||||||
return onComponentRemovedEvent.get()->handle;
|
return onComponentRemovedEvent.get()->handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SHPhysicsSystem::simulateBody(SHGhostBody& ghostBody, const SimulateBodyInfo& simInfo) noexcept
|
||||||
|
{
|
||||||
|
float invMass = 1.0f / ghostBody.mass;
|
||||||
|
SHVec3 worldInvInertia = SHVec3::One;
|
||||||
|
SHVec3 worldCentroid = SHVec3::One;
|
||||||
|
|
||||||
|
SHASSERT(invMass > 0, "GhostBody's mass in invalid")
|
||||||
|
|
||||||
|
// Compute world space data
|
||||||
|
const SHMatrix R = SHMatrix::Rotate(ghostBody.orientation);
|
||||||
|
const SHMatrix RT = SHMatrix::Transpose(R);
|
||||||
|
|
||||||
|
SHMatrix localInertiaTensor = SHMatrix::Identity;
|
||||||
|
|
||||||
|
// Set the diagonals
|
||||||
|
localInertiaTensor.m[0][0] = ghostBody.localInvInertia.x;
|
||||||
|
localInertiaTensor.m[1][1] = ghostBody.localInvInertia.y;
|
||||||
|
localInertiaTensor.m[2][2] = ghostBody.localInvInertia.z;
|
||||||
|
|
||||||
|
localInertiaTensor *= RT;
|
||||||
|
const SHVec3 DIAGONALS { localInertiaTensor.m[0][0], localInertiaTensor.m[1][1], localInertiaTensor.m[2][2] };
|
||||||
|
|
||||||
|
worldInvInertia = R * DIAGONALS;
|
||||||
|
|
||||||
|
// Compute world centroid
|
||||||
|
worldCentroid = (R * ghostBody.localCentroid) + ghostBody.position;
|
||||||
|
|
||||||
|
// Apply forces
|
||||||
|
ghostBody.accumulatedForce += simInfo.force;
|
||||||
|
ghostBody.angularVelocity += worldInvInertia * SHVec3::Cross(ghostBody.position + simInfo.forceOffset, simInfo.force);
|
||||||
|
ghostBody.accumulatedTorque += simInfo.torque;
|
||||||
|
|
||||||
|
// Integrate Velocities
|
||||||
|
// Integrate forces and gravity into linear velocity
|
||||||
|
const SHVec3 LINEAR_ACCELERATION = ghostBody.accumulatedForce * invMass;
|
||||||
|
const SHVec3 GRAVITATIONAL_ACCELERATION = ghostBody.gravityScale ? worldState.settings.gravity * ghostBody.gravityScale : SHVec3::Zero;
|
||||||
|
|
||||||
|
ghostBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * fixedDT * ghostBody.linearLock;
|
||||||
|
ghostBody.angularVelocity += worldInvInertia * (ghostBody.accumulatedTorque * fixedDT);
|
||||||
|
|
||||||
|
// Apply drag (exponentially applied)
|
||||||
|
ghostBody.linearVelocity *= 1.0f / (1.0f + fixedDT * ghostBody.drag);
|
||||||
|
ghostBody.angularVelocity *= 1.0f / (1.0f + fixedDT * ghostBody.angularDrag);
|
||||||
|
|
||||||
|
// Integrate Positions & Orientations
|
||||||
|
const SHQuaternion QV = SHQuaternion{ ghostBody.angularVelocity.x, ghostBody.angularVelocity.y, ghostBody.angularVelocity.z, 0.0f } * fixedDT * 0.5f;
|
||||||
|
|
||||||
|
ghostBody.position += ghostBody.linearVelocity * simInfo.timeStep;
|
||||||
|
ghostBody.orientation += ghostBody.orientation * QV * SHQuaternion::FromEuler(ghostBody.angularLock);
|
||||||
|
ghostBody.orientation = SHQuaternion::Normalise(ghostBody.orientation);
|
||||||
|
|
||||||
|
// Clear forces
|
||||||
|
ghostBody.accumulatedForce = SHVec3::Zero;
|
||||||
|
ghostBody.accumulatedTorque = SHVec3::Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace SHADE
|
} // namespace SHADE
|
|
@ -274,5 +274,7 @@ namespace SHADE
|
||||||
|
|
||||||
SHEventHandle onComponentAdded (SHEventPtr onComponentAddedEvent);
|
SHEventHandle onComponentAdded (SHEventPtr onComponentAddedEvent);
|
||||||
SHEventHandle onComponentRemoved (SHEventPtr onComponentRemovedEvent);
|
SHEventHandle onComponentRemoved (SHEventPtr onComponentRemovedEvent);
|
||||||
|
|
||||||
|
void simulateBody (SHGhostBody& ghostBody, const SimulateBodyInfo& simInfo) noexcept;
|
||||||
};
|
};
|
||||||
} // namespace SHADE
|
} // namespace SHADE
|
Loading…
Reference in New Issue