diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 21aa9be78b..88460cc900 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -124,7 +124,9 @@ public sealed class BSCharacter : BSPhysObject PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() { PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); + PhysBody.Clear(); PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); + PhysShape.Clear(); }); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs index 65fac00316..6b1e304207 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs @@ -57,7 +57,7 @@ public abstract class BSConstraint : IDisposable if (m_enabled) { m_enabled = false; - if (m_constraint.ptr != IntPtr.Zero) + if (m_constraint.HasPhysicalConstraint) { bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}", @@ -65,7 +65,7 @@ public abstract class BSConstraint : IDisposable m_body1.ID, m_body1.ptr.ToString("X"), m_body2.ID, m_body2.ptr.ToString("X"), success); - m_constraint.ptr = System.IntPtr.Zero; + m_constraint.Clear(); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs index 23ef0523d8..b07355555f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs @@ -65,7 +65,7 @@ public sealed class BSConstraint6Dof : BSConstraint m_world = world; m_body1 = obj1; m_body2 = obj2; - if (obj1.ptr == IntPtr.Zero || obj2.ptr == IntPtr.Zero) + if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody) { world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", BSScene.DetailLogZero, world.worldID, @@ -83,7 +83,7 @@ public sealed class BSConstraint6Dof : BSConstraint world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}", BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"), obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); - if (m_constraint.ptr == IntPtr.Zero) + if (!m_constraint.HasPhysicalConstraint) { world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}", LogHeader, obj1.ID, obj2.ID); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 0df43108b6..777c5cb33a 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -32,6 +32,14 @@ using OMV = OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { + +// A BSPrim can get individual information about its linkedness attached +// to it through an instance of a subclass of LinksetInfo. +// Each type of linkset will define the information needed for its type. +public abstract class BSLinksetInfo +{ +} + public abstract class BSLinkset { // private static string LogHeader = "[BULLETSIM LINKSET]"; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index bc9f9be56e..fe5b5e3ecb 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -32,6 +32,31 @@ using OMV = OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { + +// When a child is linked, the relationship position of the child to the parent +// is remembered so the child's world position can be recomputed when it is +// removed from the linkset. +sealed class BSLinksetCompoundInfo : BSLinksetInfo +{ + public OMV.Vector3 OffsetPos; + public OMV.Quaternion OffsetRot; + public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r) + { + OffsetPos = p; + OffsetRot = r; + } + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append(""); + return buff.ToString(); + } +}; + public sealed class BSLinksetCompound : BSLinkset { private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; @@ -44,6 +69,7 @@ public sealed class BSLinksetCompound : BSLinkset // For compound implimented linksets, if there are children, use compound shape for the root. public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) { + // Returning 'unknown' means we don't have a preference. BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; if (IsRoot(requestor) && HasAnyChildren) { @@ -63,10 +89,10 @@ public sealed class BSLinksetCompound : BSLinkset // InternalRefresh(requestor); } + // Schedule a refresh to happen after all the other taint processing. private void InternalRefresh(BSPhysObject requestor) { DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,requestor={1}", LinksetRoot.LocalID, requestor.LocalID); - // Queue to happen after all the other taint processing PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", requestor.LocalID, delegate() { if (IsRoot(requestor) && HasAnyChildren) @@ -108,7 +134,7 @@ public sealed class BSLinksetCompound : BSLinkset { // The non-physical children can come back to life. BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); - // Don't force activation so setting of DISABLE_SIMULATION can stay. + // Don't force activation so setting of DISABLE_SIMULATION can stay if used. BulletSimAPI.Activate2(child.PhysBody.ptr, false); ret = true; } @@ -146,6 +172,10 @@ public sealed class BSLinksetCompound : BSLinkset if (!IsRoot(child)) { + // Because it is a convenient time, recompute child world position and rotation based on + // its position in the linkset. + RecomputeChildWorldPosition(child, true); + // Cause the current shape to be freed and the new one to be built. InternalRefresh(LinksetRoot); ret = true; @@ -154,6 +184,42 @@ public sealed class BSLinksetCompound : BSLinkset return ret; } + // When the linkset is built, the child shape is added + // to the compound shape relative to the root shape. The linkset then moves around but + // this does not move the actual child prim. The child prim's location must be recomputed + // based on the location of the root shape. + private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime) + { + BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo; + if (lci != null) + { + if (inTaintTime) + { + OMV.Vector3 oldPos = child.RawPosition; + child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos; + child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot; + DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}", + child.LocalID, oldPos, lci, child.RawPosition); + } + else + { + // TaintedObject is not used here so the raw position is set now and not at taint-time. + child.Position = LinksetRoot.RawPosition + lci.OffsetPos; + child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot; + } + } + else + { + // This happens when children have been added to the linkset but the linkset + // has not been constructed yet. So like, at taint time, adding children to a linkset + // and then changing properties of the children (makePhysical, for instance) + // but the post-print action of actually rebuilding the linkset has not yet happened. + // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}", + // LogHeader, child.LocalID); + DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID); + } + } + // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', // this routine will restore the removed constraints. // Called at taint-time!! @@ -192,8 +258,7 @@ public sealed class BSLinksetCompound : BSLinkset child.LocalID, child.PhysBody.ptr.ToString("X")); // Cause the child's body to be rebuilt and thus restored to normal operation - // TODO: position and rotation must be restored because the child could have moved - // based on the linkset. + RecomputeChildWorldPosition(child, false); child.ForceBodyShapeRebuild(false); if (!HasAnyChildren) @@ -228,43 +293,57 @@ public sealed class BSLinksetCompound : BSLinkset { if (!IsRoot(cPrim)) { - // Each child position and rotation is given relative to the root. - OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); - OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation; - OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation; + // Compute the displacement of the child from the root of the linkset. + // This info is saved in the child prim so the relationship does not + // change over time and the new child position can be computed + // when the linkset is being disassembled (the linkset may have moved). + BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo; + if (lci == null) + { + // Each child position and rotation is given relative to the root. + OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); + OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation; + OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation; + + // Save relative position for recomputing child's world position after moving linkset. + lci = new BSLinksetCompoundInfo(displacementPos, displacementRot); + cPrim.LinksetInfo = lci; + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci); + } DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}", - LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, displacementPos, displacementRot); + LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot); if (cPrim.PhysShape.isNativeShape) { - // A native shape is turning into a null collision shape because native + // A native shape is turning into a hull collision shape because native // shapes are not shared so we have to hullify it so it will be tracked // and freed at the correct time. This also solves the scaling problem // (native shapes scaled but hull/meshes are assumed to not be). + // TODO: decide of the native shape can just be used in the compound shape. + // Use call to CreateGeomNonSpecial(). BulletShape saveShape = cPrim.PhysShape; - cPrim.PhysShape.ptr = IntPtr.Zero; // Don't let the create free the child's shape + cPrim.PhysShape.Clear(); // Don't let the create free the child's shape + // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null); PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); BulletShape newShape = cPrim.PhysShape; cPrim.PhysShape = saveShape; - BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot); + BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos , lci.OffsetRot); } else { // For the shared shapes (meshes and hulls), just use the shape in the child. + // The reference count added here will be decremented when the compound shape + // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced). if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape)) { PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); } - BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, displacementPos, displacementRot); + BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos , lci.OffsetRot); } } - // TODO: need to phantomize the child prims left behind. - // Maybe just destroy the children bodies and shapes and have them rebuild on unlink. - // Selection/deselection might cause way too many build/destructions esp. for LARGE linksets. - return false; // 'false' says to move onto the next child in the list }); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index b05d4eeb9e..d2fc15c7ae 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -75,6 +75,7 @@ public abstract class BSPhysObject : PhysicsActor public string TypeName { get; protected set; } public BSLinkset Linkset { get; set; } + public BSLinksetInfo LinksetInfo { get; set; } // Return the object mass without calculating it or having side effects public abstract float RawMass { get; } @@ -253,7 +254,9 @@ public abstract class BSPhysObject : PhysicsActor SubscribedEventsMs = 0; PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() { - CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + // Make sure there is a body there because sometimes destruction happens in an un-ideal order. + if (PhysBody.HasPhysicalBody) + CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); } // Return 'true' if the simulator wants collision events diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 627393a982..d1d100d777 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -108,8 +108,8 @@ public sealed class BSPrim : BSPhysObject _mass = CalculateMass(); // No body or shape yet - PhysBody = new BulletBody(LocalID, IntPtr.Zero); - PhysShape = new BulletShape(IntPtr.Zero); + PhysBody = new BulletBody(LocalID); + PhysShape = new BulletShape(); DetailLog("{0},BSPrim.constructor,call", LocalID); // do the actual object creation at taint time @@ -143,7 +143,9 @@ public sealed class BSPrim : BSPhysObject DetailLog("{0},BSPrim.Destroy,taint,", LocalID); // If there are physical body and shape, release my use of same. PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); + PhysBody.Clear(); PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); + PhysShape.Clear(); }); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 933f573e78..74b4371efc 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -149,7 +149,7 @@ public sealed class BSShapeCollection : IDisposable // 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) + if (!body.HasPhysicalBody) return; lock (m_collectionActivityLock) @@ -243,12 +243,12 @@ public sealed class BSShapeCollection : IDisposable // Release the usage of a shape. public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback) { - if (shape.ptr == IntPtr.Zero) + if (!shape.HasPhysicalShape) return; PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate() { - if (shape.ptr != IntPtr.Zero) + if (shape.HasPhysicalShape) { if (shape.isNativeShape) { @@ -440,7 +440,7 @@ public sealed class BSShapeCollection : IDisposable } // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'. - private bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) + public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) { bool ret = false; bool haveShape = false; @@ -573,7 +573,7 @@ public sealed class BSShapeCollection : IDisposable // Native shapes are scaled in Bullet so set the scaling to the size newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); } - if (newShape.ptr == IntPtr.Zero) + if (!newShape.HasPhysicalShape) { PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", LogHeader, prim.LocalID, shapeType); @@ -590,7 +590,7 @@ public sealed class BSShapeCollection : IDisposable // Called at taint-time! private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback) { - BulletShape newShape = new BulletShape(IntPtr.Zero); + BulletShape newShape = new BulletShape(); float lod; System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); @@ -860,7 +860,7 @@ public sealed class BSShapeCollection : IDisposable private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim) { // If the shape was successfully created, nothing more to do - if (newShape.ptr != IntPtr.Zero) + if (newShape.HasPhysicalShape) return newShape; // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset @@ -919,7 +919,7 @@ public sealed class BSShapeCollection : IDisposable bool ret = false; // the mesh, hull or native shape must have already been created in Bullet - bool mustRebuild = (prim.PhysBody.ptr == IntPtr.Zero); + 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. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs index 83b9c37104..2d379bb110 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs @@ -136,7 +136,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys { if (m_mapInfo != null) { - if (m_mapInfo.terrainBody.ptr != IntPtr.Zero) + if (m_mapInfo.terrainBody.HasPhysicalBody) { BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr); // Frees both the body and the shape. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 83df360434..c28d69d934 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -151,13 +151,13 @@ public sealed class BSTerrainManager // Release all the terrain structures we might have allocated public void ReleaseGroundPlaneAndTerrain() { - if (m_groundPlane.ptr != IntPtr.Zero) + if (m_groundPlane.HasPhysicalBody) { if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr)) { BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr); } - m_groundPlane.ptr = IntPtr.Zero; + m_groundPlane.Clear(); } ReleaseTerrain(); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index 6ce767ddad..34730063b7 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -94,7 +94,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, indicesCount, indices, verticesCount, vertices), BSPhysicsShapeType.SHAPE_MESH); - if (m_terrainShape.ptr == IntPtr.Zero) + if (!m_terrainShape.HasPhysicalShape) { // DISASTER!! PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID); @@ -107,7 +107,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys Quaternion rot = Quaternion.Identity; m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot)); - if (m_terrainBody.ptr == IntPtr.Zero) + if (!m_terrainBody.HasPhysicalBody) { // DISASTER!! physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); @@ -140,7 +140,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys public override void Dispose() { - if (m_terrainBody.ptr != IntPtr.Zero) + if (m_terrainBody.HasPhysicalBody) { BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); // Frees both the body and the shape. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 2671995dd2..1559025ad5 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -53,6 +53,9 @@ public struct BulletSim // An allocated Bullet btRigidBody public struct BulletBody { + public BulletBody(uint id) : this(id, IntPtr.Zero) + { + } public BulletBody(uint id, IntPtr xx) { ID = id; @@ -64,6 +67,13 @@ public struct BulletBody public uint ID; public CollisionFilterGroups collisionGroup; public CollisionFilterGroups collisionMask; + + public void Clear() + { + ptr = IntPtr.Zero; + } + public bool HasPhysicalBody { get { return ptr != IntPtr.Zero; } } + public override string ToString() { StringBuilder buff = new StringBuilder(); @@ -103,6 +113,13 @@ public struct BulletShape public BSPhysicsShapeType type; public System.UInt64 shapeKey; public bool isNativeShape; + + public void Clear() + { + ptr = IntPtr.Zero; + } + public bool HasPhysicalShape { get { return ptr != IntPtr.Zero; } } + public override string ToString() { StringBuilder buff = new StringBuilder(); @@ -140,6 +157,12 @@ public struct BulletConstraint ptr = xx; } public IntPtr ptr; + + public void Clear() + { + ptr = IntPtr.Zero; + } + public bool HasPhysicalConstraint { get { return ptr != IntPtr.Zero; } } } // An allocated HeightMapThing which holds various heightmap info.