Пример #1
0
        // Called to update/change the body and shape for an object.
        // First checks the shape and updates that if necessary then makes
        //    sure the body is of the right type.
        // Return 'true' if either the body or the shape changed.
        // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
        //    the current shape or body is destroyed. This allows the caller to remove any
        //    higher level dependencies on the shape or body. Mostly used for LinkSets to
        //    remove the physical constraints before the body is destroyed.
        // Called at taint-time!!
        public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim,
                                    ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
        {
            PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");

            bool ret = false;

            // This lock could probably be pushed down lower but building shouldn't take long
            lock (m_collectionActivityLock)
            {
                // Do we have the correct geometry for this type of object?
                // Updates prim.BSShape with information/pointers to shape.
                // Returns 'true' of BSShape is changed to a new shape.
                bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback);
                // If we had to select a new shape geometry for the object,
                //    rebuild the body around it.
                // Updates prim.BSBody with information/pointers to requested body
                // Returns 'true' if BSBody was changed.
                bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
                                          prim.PhysShape, bodyCallback);
                ret = newGeom || newBody;
            }
            DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
                      prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);

            return(ret);
        }
Пример #2
0
        // Create a body object in Bullet.
        // Updates prim.BSBody with the information about the new body if one is created.
        // Returns 'true' if an object was actually created.
        // Called at taint-time.
        private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BulletShape shape,
                                BodyDestructionCallback bodyCallback)
        {
            bool ret = false;

            // the mesh, hull or native shape must have already been created in Bullet
            bool mustRebuild = !prim.PhysBody.HasPhysicalBody;

            // If there is an existing body, verify it's of an acceptable type.
            // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
            if (!mustRebuild)
            {
                CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr);
                if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY ||
                    !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
                {
                    // If the collisionObject is not the correct type for solidness, rebuild what's there
                    mustRebuild = true;
                }
            }

            if (mustRebuild || forceRebuild)
            {
                // Free any old body
                DereferenceBody(prim.PhysBody, true, bodyCallback);

                BulletBody aBody;
                Object     bodyPtr = null;
                if (prim.IsSolid)
                {
                    bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
                                                                prim.LocalID, prim.RawPosition, prim.RawOrientation);
                    if (DDetail)
                    {
                        DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString());
                    }
                }
                else
                {
                    bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
                                                                 prim.LocalID, prim.RawPosition, prim.RawOrientation);
                    if (DDetail)
                    {
                        DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString());
                    }
                }
                aBody = new BulletBody(prim.LocalID, bodyPtr);

                ReferenceBody(aBody, true);

                prim.PhysBody = aBody;

                ret = true;
            }

            return(ret);
        }
Пример #3
0
        // Release the usage of a body.
        // Called when releasing use of a BSBody. BSShape is handled separately.
        public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback)
        {
            if (body.ptr == IntPtr.Zero)
            {
                return;
            }

            lock (m_collectionActivityLock)
            {
                BodyDesc bodyDesc;
                if (Bodies.TryGetValue(body.ID, out bodyDesc))
                {
                    bodyDesc.referenceCount--;
                    bodyDesc.lastReferenced = System.DateTime.Now;
                    Bodies[body.ID]         = bodyDesc;
                    DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount);

                    // If body is no longer being used, free it -- bodies are never shared.
                    if (bodyDesc.referenceCount == 0)
                    {
                        Bodies.Remove(body.ID);
                        BSScene.TaintCallback removeOperation = delegate()
                        {
                            DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}",
                                      body.ID, body.ptr.ToString("X"));
                            // If the caller needs to know, pass the event up.
                            if (bodyCallback != null)
                            {
                                bodyCallback(body);
                            }

                            // Zero any reference to the shape so it is not freed when the body is deleted.
                            BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
                            // It may have already been removed from the world in which case the next is a NOOP.
                            BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
                            BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
                        };
                        // If already in taint-time, do the operations now. Otherwise queue for later.
                        if (inTaintTime)
                        {
                            removeOperation();
                        }
                        else
                        {
                            PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
                        }
                    }
                }
                else
                {
                    DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
                }
            }
        }
Пример #4
0
        // Create a body object in Bullet.
        // Updates prim.BSBody with the information about the new body if one is created.
        // Returns 'true' if an object was actually created.
        // Called at taint-time.
        private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape,
                                ShapeData shapeData, BodyDestructionCallback bodyCallback)
        {
            bool ret = false;

            // the mesh, hull or native shape must have already been created in Bullet
            bool mustRebuild = (prim.BSBody.ptr == IntPtr.Zero);

            // If there is an existing body, verify it's of an acceptable type.
            // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
            if (!mustRebuild)
            {
                CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.BSBody.ptr);
                if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY ||
                    !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
                {
                    // If the collisionObject is not the correct type for solidness, rebuild what's there
                    mustRebuild = true;
                }
            }

            if (mustRebuild || forceRebuild)
            {
                DereferenceBody(prim.BSBody, true, bodyCallback);

                BulletBody aBody;
                IntPtr     bodyPtr = IntPtr.Zero;
                if (prim.IsSolid)
                {
                    bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
                                                                shapeData.ID, shapeData.Position, shapeData.Rotation);
                    // DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
                }
                else
                {
                    bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
                                                                 shapeData.ID, shapeData.Position, shapeData.Rotation);
                    // DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
                }
                aBody = new BulletBody(shapeData.ID, bodyPtr);

                ReferenceBody(aBody, true);

                prim.BSBody = aBody;

                ret = true;
            }

            return(ret);
        }
Пример #5
0
        // Called to update/change the body and shape for an object.
        // First checks the shape and updates that if necessary then makes
        //    sure the body is of the right type.
        // Return 'true' if either the body or the shape changed.
        // Called at taint-time!!
        public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim,
                                    ShapeData shapeData, PrimitiveBaseShape pbs,
                                    ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
        {
            bool ret = false;

            // This lock could probably be pushed down lower but building shouldn't take long
            lock (m_collectionActivityLock)
            {
                // Do we have the correct geometry for this type of object?
                // Updates prim.BSShape with information/pointers to requested shape
                bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback);
                // If we had to select a new shape geometry for the object,
                //    rebuild the body around it.
                // Updates prim.BSBody with information/pointers to requested body
                bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, prim.BSShape, shapeData, bodyCallback);
                ret = newGeom || newBody;
            }
            DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}",
                      prim.LocalID, forceRebuild, ret, prim.BSBody, prim.BSShape);

            return(ret);
        }
Пример #6
0
        // Release the usage of a body.
        // Called when releasing use of a BSBody. BSShape is handled separately.
        public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback)
        {
            if (!body.HasPhysicalBody)
            {
                return;
            }

            lock (m_collectionActivityLock)
            {
                PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
                {
                    if (DDetail)
                    {
                        DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
                                  body.ID, body, inTaintTime);
                    }
                    // If the caller needs to know the old body is going away, pass the event up.
                    if (bodyCallback != null)
                    {
                        bodyCallback(body);
                    }

                    if (BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
                    {
                        BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
                        if (DDetail)
                        {
                            DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
                        }
                    }

                    // Zero any reference to the shape so it is not freed when the body is deleted.
                    BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, null);
                    BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
                });
            }
        }
Пример #7
0
        // Create a body object in Bullet.
        // Updates prim.BSBody with the information about the new body if one is created.
        // Returns 'true' if an object was actually created.
        // Called at taint-time.
        private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BodyDestructionCallback bodyCallback)
        {
            bool ret = false;

            // the mesh, hull or native shape must have already been created in Bullet
            bool mustRebuild = !prim.PhysBody.HasPhysicalBody;

            // If there is an existing body, verify it's of an acceptable type.
            // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
            if (!mustRebuild)
            {
            CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(prim.PhysBody);
            if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
                || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
            {
                // If the collisionObject is not the correct type for solidness, rebuild what's there
                mustRebuild = true;
                if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,forceRebuildBecauseChangingBodyType,bodyType={1}", prim.LocalID, bodyType);
            }
            }

            if (mustRebuild || forceRebuild)
            {
            // Free any old body
            DereferenceBody(prim.PhysBody, bodyCallback);

            BulletBody aBody;
            if (prim.IsSolid)
            {
                aBody = PhysicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape, prim.LocalID, prim.RawPosition, prim.RawOrientation);
                if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,body={1}", prim.LocalID, aBody);
            }
            else
            {
                aBody = PhysicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape, prim.LocalID, prim.RawPosition, prim.RawOrientation);
                if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody);
            }

            ReferenceBody(aBody);

            prim.PhysBody = aBody;

            ret = true;
            }

            return ret;
        }
Пример #8
0
        // Release the usage of a body.
        // Called when releasing use of a BSBody. BSShape is handled separately.
        // Called in taint time.
        public void DereferenceBody(BulletBody body, BodyDestructionCallback bodyCallback )
        {
            if (!body.HasPhysicalBody)
            return;

            PhysicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody");

            lock (m_collectionActivityLock)
            {
            if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body);
            // If the caller needs to know the old body is going away, pass the event up.
            if (bodyCallback != null) bodyCallback(body);

            if (PhysicsScene.PE.IsInWorld(PhysicsScene.World, body))
            {
                PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, body);
                if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
            }

            // Zero any reference to the shape so it is not freed when the body is deleted.
            PhysicsScene.PE.SetCollisionShape(PhysicsScene.World, body, null);
            PhysicsScene.PE.DestroyObject(PhysicsScene.World, body);
            }
        }
Пример #9
0
        // Called to update/change the body and shape for an object.
        // First checks the shape and updates that if necessary then makes
        //    sure the body is of the right type.
        // Return 'true' if either the body or the shape changed.
        // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
        //    the current shape or body is destroyed. This allows the caller to remove any
        //    higher level dependencies on the shape or body. Mostly used for LinkSets to
        //    remove the physical constraints before the body is destroyed.
        // Called at taint-time!!
        public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim,
            ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
        {
            PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");

            bool ret = false;

            // This lock could probably be pushed down lower but building shouldn't take long
            lock (m_collectionActivityLock)
            {
            // Do we have the correct geometry for this type of object?
            // Updates prim.BSShape with information/pointers to shape.
            // Returns 'true' of BSShape is changed to a new shape.
            bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback);
            // If we had to select a new shape geometry for the object,
            //    rebuild the body around it.
            // Updates prim.BSBody with information/pointers to requested body
            // Returns 'true' if BSBody was changed.
            bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, bodyCallback);
            ret = newGeom || newBody;
            }
            DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
                                prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);

            return ret;
        }
Пример #10
0
    // Called to update/change the body and shape for an object.
    // First checks the shape and updates that if necessary then makes
    //    sure the body is of the right type.
    // Return 'true' if either the body or the shape changed.
    // Called at taint-time!!
    public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, 
                    ShapeData shapeData, PrimitiveBaseShape pbs,
                    ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
    {
        bool ret = false;

        // This lock could probably be pushed down lower but building shouldn't take long
        lock (m_collectionActivityLock)
        {
            // Do we have the correct geometry for this type of object?
            // Updates prim.BSShape with information/pointers to requested shape
            bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback);
            // If we had to select a new shape geometry for the object,
            //    rebuild the body around it.
            // Updates prim.BSBody with information/pointers to requested body
            bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, 
                                    prim.BSShape, shapeData, bodyCallback);
            ret = newGeom || newBody;
        }
        DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}",
                                prim.LocalID, forceRebuild, ret, prim.BSBody, prim.BSShape);

        return ret;
    }
Пример #11
0
    // Create a body object in Bullet.
    // Updates prim.BSBody with the information about the new body if one is created.
    // Returns 'true' if an object was actually created.
    // Called at taint-time.
    private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, 
                            ShapeData shapeData, BodyDestructionCallback bodyCallback)
    {
        bool ret = false;

        // the mesh, hull or native shape must have already been created in Bullet
        bool mustRebuild = (prim.BSBody.ptr == IntPtr.Zero);

        // If there is an existing body, verify it's of an acceptable type.
        // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
        if (!mustRebuild)
        {
            CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.BSBody.ptr);
            if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
                || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
            {
                // If the collisionObject is not the correct type for solidness, rebuild what's there
                mustRebuild = true;
            }

        }

        if (mustRebuild || forceRebuild)
        {
            DereferenceBody(prim.BSBody, true, bodyCallback);

            BulletBody aBody;
            IntPtr bodyPtr = IntPtr.Zero;
            if (prim.IsSolid)
            {
                bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
                                        shapeData.ID, shapeData.Position, shapeData.Rotation);
                // DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
            }
            else
            {
                bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
                                        shapeData.ID, shapeData.Position, shapeData.Rotation);
                // DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
            }
            aBody = new BulletBody(shapeData.ID, bodyPtr);

            ReferenceBody(aBody, true);

            prim.BSBody = aBody;

            ret = true;
        }

        return ret;
    }
Пример #12
0
    // Release the usage of a body.
    // Called when releasing use of a BSBody. BSShape is handled separately.
    public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
    {
        if (body.ptr == IntPtr.Zero)
            return;

        lock (m_collectionActivityLock)
        {
            BodyDesc bodyDesc;
            if (Bodies.TryGetValue(body.ID, out bodyDesc))
            {
                bodyDesc.referenceCount--;
                bodyDesc.lastReferenced = System.DateTime.Now;
                Bodies[body.ID] = bodyDesc;
                DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount);

                // If body is no longer being used, free it -- bodies are never shared.
                if (bodyDesc.referenceCount == 0)
                {
                    Bodies.Remove(body.ID);
                    BSScene.TaintCallback removeOperation = delegate()
                    {
                        DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}",
                                                    body.ID, body.ptr.ToString("X"));
                        // If the caller needs to know the old body is going away, pass the event up.
                        if (bodyCallback != null) bodyCallback(body);

                        // Zero any reference to the shape so it is not freed when the body is deleted.
                        BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
                        // It may have already been removed from the world in which case the next is a NOOP.
                        BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
                        BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
                    };
                    // If already in taint-time, do the operations now. Otherwise queue for later.
                    if (inTaintTime)
                        removeOperation();
                    else
                        PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
                }
            }
            else
            {
                DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
            }
        }
    }
Пример #13
0
    // Release the usage of a body.
    // Called when releasing use of a BSBody. BSShape is handled separately.
    public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
    {
        if (body.ptr == IntPtr.Zero)
            return;

        lock (m_collectionActivityLock)
        {
            BSScene.TaintCallback removeOperation = delegate()
            {
                DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}",
                                            body.ID, body.ptr.ToString("X"), inTaintTime);
                // If the caller needs to know the old body is going away, pass the event up.
                if (bodyCallback != null) bodyCallback(body);

                // It may have already been removed from the world in which case the next is a NOOP.
                BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);

                // Zero any reference to the shape so it is not freed when the body is deleted.
                BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
                BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
            };
            // If already in taint-time, do the operations now. Otherwise queue for later.
            if (inTaintTime)
                removeOperation();
            else
                PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
        }
    }
Пример #14
0
    // Release the usage of a body.
    // Called when releasing use of a BSBody. BSShape is handled separately.
    public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
    {
        if (!body.HasPhysicalBody)
            return;

        lock (m_collectionActivityLock)
        {
            PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
            {
                if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
                                            body.ID, body, inTaintTime);
                // If the caller needs to know the old body is going away, pass the event up.
                if (bodyCallback != null) bodyCallback(body);

                if (BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
                {
                    BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
                    if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
                }

                // Zero any reference to the shape so it is not freed when the body is deleted.
                BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, null);
                BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
            });
        }
    }