public void UpdateDeactivationCandidacy(float dt)
{
//Get total velocity, and see if the entity is losing energy.
float velocity = owner.linearVelocity.LengthSquared() + owner.angularVelocity.LengthSquared();
bool isActive = IsActive;
if (isActive)
{
TryToCompressIslandHierarchy();
isSlowing = velocity <= previousVelocity;
if (IsDynamic)
{
//Update time entity's been under the low-velocity limit, or reset if it's not
if (velocity < DeactivationManager.velocityLowerLimitSquared)
velocityTimeBelowLimit += dt;
else
velocityTimeBelowLimit = 0;
if (!IsAlwaysActive)
{
if (!isDeactivationCandidate)
{
//See if the velocity has been low long enough to make this object a deactivation candidate.
if (velocityTimeBelowLimit > DeactivationManager.lowVelocityTimeMinimum &&
isSlowing) //Only deactivate if it is NOT increasing in speed.
{
IsDeactivationCandidate = true;
}
}
else
{
//See if velocity is high enough to make this object not a deactivation candidate.
if (velocityTimeBelowLimit <= DeactivationManager.lowVelocityTimeMinimum)
{
IsDeactivationCandidate = false;
}
}
}
else
IsDeactivationCandidate = false;
}
else
{
//If it's not dynamic, then deactivation candidacy is based entirely on whether or not the object has velocity (and the IsAlwaysActive state).
IsDeactivationCandidate = velocity == 0 && !IsAlwaysActive;
if (IsDeactivationCandidate)
{
//Update the flagging system.
//If time <= 0, the entity is considered active.
//Forcing a kinematic active needs to allow the system to run for a whole frame.
//This means that in here, if it is < 0, we set it to zero. It will still update for the rest of the frame.
//Then, next frame, when its == 0, set it to 1. It will be considered inactive unless it was activated manually again.
if (velocityTimeBelowLimit == 0)
velocityTimeBelowLimit = 1;
else if (velocityTimeBelowLimit < 0)
velocityTimeBelowLimit = 0;
}
else
{
//If velocity is not zero, then the flag is set to 'this is active.'
velocityTimeBelowLimit = -1;
}
if (velocityTimeBelowLimit <= 0)
{
//There's a single oddity we need to worry about in this case.
//An active kinematic object has no simulation island. Without intervention,
//an active kinematic object will not keep an island awake.
//To solve this, when we encounter active kinematic objects,
//tell simulation islands associated with connected objects that they aren't allowed to deactivate.
for (int i = 0; i < connections.Count; i++)
{
var connectedMembers = connections.Elements[i].entries;
for (int j = connectedMembers.Count - 1; j >= 0; j--)
{
//The change locker must be obtained before attempting to access the SimulationIsland.
//Path compression can force the simulation island to evaluate to null briefly.
//Do not permit the object to undergo path compression during this (brief) operation.
connectedMembers.Elements[j].Member.simulationIslandChangeLocker.Enter();
var island = connectedMembers.Elements[j].Member.SimulationIsland;
if (island != null)
{
//It's possible a kinematic entity to go inactive for one frame, allowing nearby entities to go to sleep.
//The next frame, it could wake up again. Because kinematics do not have simulation islands and thus
//do not automatically wake up touching dynamic entities, we must do so manually.
//This is safe because the island.Activate command is a single boolean set.
//We're also inside the island change locker, so we don't have to worry about the island changing beneath our feet.
island.Activate();
island.allowDeactivation = false;
}
connectedMembers.Elements[j].Member.simulationIslandChangeLocker.Exit();
}
}
}
}
}
previousVelocity = velocity;
//These will be 'eventually right.'
if (previouslyActive && !isActive)
OnDeactivated();
else if (!previouslyActive && isActive)
OnActivated();
previouslyActive = isActive;
}