private bool UseBulletSimRootOffsetHack = false; // Attempt to have Bullet track the coords of root compound shape
        private void RecomputeLinksetCompound()
        {
            try
            {
                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 linkset.
                if (!LinksetRoot.IsPhysicallyActive)
                {
                    DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysical", 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(m_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.RecomputeLinksetCompound,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(m_physicsScene, cPrim);

                    // Offset the child shape from the center-of-mass and rotate it to vehicle 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 built
                    m_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 origional child body
                    if (!IsRoot(cPrim))
                    {
                        m_physicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
                        m_physicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION);
                        // We don't want collisions from the old linkset children.
                        m_physicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
                        cPrim.PhysBody.collisionType = CollisionType.LinksetChild;
                    }

                    return(false); // 'false' says to move onto the next 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(m_physicsScene);
                LinksetRoot.PhysShape = linksetShape;
                m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody);
                m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo);
                m_physicsScene.PE.AddObjectToWorld(m_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.
                    m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE);
                }
            }
            finally
            {
                Rebuilding = false;
            }

            // See that the Aabb surrounds the new shape
            m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo);
        }
        // Attempt to have Bullet track the coords of root compound shape

        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);
        }