void RecomputeLinksetCompound()
{
try
{
// Suppress rebuilding while rebuilding. (We know rebuilding is on only one thread.)
Rebuilding = true;
// No matter what is being done, force the root prim's PhysBody and PhysShape to get set
// to what they should be as if the root was not in a linkset.
// Not that bad since we only get into this routine if there are children in the linkset and
// something has been updated/changed.
// Have to do the rebuild before checking for physical because this might be a linkset
// being destructed and going non-physical.
LinksetRoot.ForceBodyShapeRebuild(true);
// There is no reason to build all this physical stuff for a non-physical or empty linkset.
if (!LinksetRoot.IsPhysicallyActive || !HasAnyChildren)
{
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysicalOrNoChildren",
LinksetRoot.LocalID);
return; // Note the 'finally' clause at the botton which will get executed.
}
// Get a new compound shape to build the linkset shape in.
BSShape linksetShape = BSShapeCompound.GetReference(PhysicsScene);
// Compute a displacement for each component so it is relative to the center-of-mass.
// Bullet presumes an object's origin (relative <0,0,0> is its center-of-mass
OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass();
OMV.Quaternion invRootOrientation =
OMV.Quaternion.Normalize(OMV.Quaternion.Inverse(LinksetRoot.RawOrientation));
OMV.Vector3 origRootPosition = LinksetRoot.RawPosition;
// 'centerDisplacementV' is the vehicle relative distance from the simulator root position to the center-of-mass
OMV.Vector3 centerDisplacementV = (centerOfMassW - LinksetRoot.RawPosition)*invRootOrientation;
if (UseBulletSimRootOffsetHack || !BSParam.LinksetOffsetCenterOfMass)
{
// Zero everything if center-of-mass displacement is not being done.
centerDisplacementV = OMV.Vector3.Zero;
LinksetRoot.ClearDisplacement();
}
else
{
// The actual center-of-mass could have been set by the user.
centerDisplacementV = LinksetRoot.SetEffectiveCenterOfMassDisplacement(centerDisplacementV);
}
DetailLog("{0},BSLinksetCompound.RecumputeLinksetCompound,COM,rootPos={1},com={2},comDisp={3}",
LinksetRoot.LocalID, origRootPosition, centerOfMassW, centerDisplacementV);
// Add the shapes of all the components of the linkset
int memberIndex = 1;
ForEachMember((cPrim) =>
{
if (IsRoot(cPrim))
{
// Root shape is always index zero.
cPrim.LinksetChildIndex = 0;
}
else
{
cPrim.LinksetChildIndex = memberIndex;
memberIndex++;
}
// Get a reference to the shape of the child for adding of that shape to the linkset compound shape
BSShape childShape = cPrim.PhysShape.GetReference(PhysicsScene, cPrim);
// Offset the child shape from the center-of-mass and rotate it to root relative.
OMV.Vector3 offsetPos = (cPrim.RawPosition - origRootPosition)*invRootOrientation -
centerDisplacementV;
OMV.Quaternion offsetRot = OMV.Quaternion.Normalize(cPrim.RawOrientation)*invRootOrientation;
// Add the child shape to the compound shape being build
if (childShape.physShapeInfo.HasPhysicalShape)
{
PhysicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo,
childShape.physShapeInfo, offsetPos, offsetRot);
DetailLog(
"{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}",
LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot);
// Since we are borrowing the shape of the child, disable the original child body
if (!IsRoot(cPrim))
{
PhysicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
PhysicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION);
// We don't want collision from the old linkset children.
PhysicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody,
CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
cPrim.PhysBody.collisionType = CollisionType.LinksetChild;
}
}
else
{
// The linkset must be in an intermediate state where all the children have not yet
// been constructed. This sometimes happens on startup when everything is getting
// built and some shapes have to wait for assets to be read in.
// Just skip this linkset for the moment and cause the shape to be rebuilt next tick.
// One problem might be that the shape is broken somehow and it never becomes completely
// available. This might cause the rebuild to happen over and over.
InternalScheduleRebuild(LinksetRoot);
DetailLog(
"{0},BSLinksetCompound.RecomputeLinksetCompound,addChildWithNoShape,indx={1},cShape={2},offPos={3},offRot={4}",
LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot);
// Output an annoying warning. It should only happen once but if it keeps coming out,
// the user knows there is something wrong and will report it.
PhysicsScene.Logger.WarnFormat(
"{0} Linkset rebuild warning. If this happens more than one or two times, please report in the issue tracker",
LogHeader);
PhysicsScene.Logger.WarnFormat("{0} pName={1}, childIdx={2}, shape={3}",
LogHeader, LinksetRoot.Name, cPrim.LinksetChildIndex, childShape);
// This causes the loop to bail on building the rest of this linkset.
// The rebuild operation will fix it up next tick or declare the object unbuildable.
return true;
}
return false; // 'false' says to move anto the nex child in the list
});
// Replace the root shape with the built compound shape.
// Object removed and added to world to get collision cache rebuilt for new shape.
LinksetRoot.PhysShape.Dereference(PhysicsScene);
LinksetRoot.PhysShape = linksetShape;
PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, LinksetRoot.PhysBody);
PhysicsScene.PE.SetCollisionShape(PhysicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo);
PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, LinksetRoot.PhysBody);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}",
LinksetRoot.LocalID, LinksetRoot.PhysBody, linksetShape);
// With all of the linkset packed into the root prim, it has the mass of everyone.
LinksetMass = ComputeLinksetMass();
LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
if (UseBulletSimRootOffsetHack)
{
// Enable the physical position updator to return the position and rotation of the root shape.
// This enables a feature in the C++ code to return the world coordinates of the first shape in the
// compound shape. This aleviates the need to offset the returned physical position by the
// center-of-mass offset.
// TODO: either debug this feature or remove it.
PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody,
CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE);
}
}
finally
{
Rebuilding = false;
}
// See that the Aabb surround the new shape
PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo);
}
}